1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-19 21:10:12 +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; 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(); auto affectedHexes = spellObst.getAffectedTiles();
obstacleHexes.insert(affectedHexes.cbegin(), affectedHexes.cend()); obstacleHexes.insert(affectedHexes.cbegin(), affectedHexes.cend());
}; };

View File

@ -531,44 +531,44 @@ vstd::RNG * HypotheticBattle::HypotheticServerCallback::getRNG()
return &rngStub; 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()); 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) HypotheticBattle::HypotheticEnvironment::HypotheticEnvironment(HypotheticBattle * owner_, const Environment * upperEnvironment)

View File

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

View File

@ -29,20 +29,20 @@
bool CCallback::teleportHero(const CGHeroInstance *who, const CGTownInstance *where) bool CCallback::teleportHero(const CGHeroInstance *who, const CGTownInstance *where)
{ {
CastleTeleportHero pack(who->id, where->id, 1); CastleTeleportHero pack(who->id, where->id, 1);
sendRequest(&pack); sendRequest(pack);
return true; return true;
} }
void CCallback::moveHero(const CGHeroInstance *h, const int3 & destination, bool transit) void CCallback::moveHero(const CGHeroInstance *h, const int3 & destination, bool transit)
{ {
MoveHero pack({destination}, h->id, transit); MoveHero pack({destination}, h->id, transit);
sendRequest(&pack); sendRequest(pack);
} }
void CCallback::moveHero(const CGHeroInstance *h, const std::vector<int3> & path, bool transit) void CCallback::moveHero(const CGHeroInstance *h, const std::vector<int3> & path, bool transit)
{ {
MoveHero pack(path, h->id, transit); MoveHero pack(path, h->id, transit);
sendRequest(&pack); sendRequest(pack);
} }
int CCallback::selectionMade(int selection, QueryID queryID) 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); QueryReply pack(queryID, reply);
pack.player = *player; pack.player = *player;
return sendRequest(&pack); return sendRequest(pack);
} }
void CCallback::recruitCreatures(const CGDwelling * obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level) 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; return;
RecruitCreatures pack(obj->id, dst->id, ID, amount, level); RecruitCreatures pack(obj->id, dst->id, ID, amount, level);
sendRequest(&pack); sendRequest(pack);
} }
bool CCallback::dismissCreature(const CArmedInstance *obj, SlotID stackPos) bool CCallback::dismissCreature(const CArmedInstance *obj, SlotID stackPos)
@ -80,14 +80,14 @@ bool CCallback::dismissCreature(const CArmedInstance *obj, SlotID stackPos)
return false; return false;
DisbandCreature pack(stackPos,obj->id); DisbandCreature pack(stackPos,obj->id);
sendRequest(&pack); sendRequest(pack);
return true; return true;
} }
bool CCallback::upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID) bool CCallback::upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID)
{ {
UpgradeCreature pack(stackPos,obj->id,newID); UpgradeCreature pack(stackPos,obj->id,newID);
sendRequest(&pack); sendRequest(pack);
return false; return false;
} }
@ -95,54 +95,54 @@ void CCallback::endTurn()
{ {
logGlobal->trace("Player %d ended his turn.", player->getNum()); logGlobal->trace("Player %d ended his turn.", player->getNum());
EndTurn pack; EndTurn pack;
sendRequest(&pack); sendRequest(pack);
} }
int CCallback::swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2) int CCallback::swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
{ {
ArrangeStacks pack(1,p1,p2,s1->id,s2->id,0); ArrangeStacks pack(1,p1,p2,s1->id,s2->id,0);
sendRequest(&pack); sendRequest(pack);
return 0; return 0;
} }
int CCallback::mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2) int CCallback::mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
{ {
ArrangeStacks pack(2,p1,p2,s1->id,s2->id,0); ArrangeStacks pack(2,p1,p2,s1->id,s2->id,0);
sendRequest(&pack); sendRequest(pack);
return 0; return 0;
} }
int CCallback::splitStack(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2, int val) 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); ArrangeStacks pack(3,p1,p2,s1->id,s2->id,val);
sendRequest(&pack); sendRequest(pack);
return 0; return 0;
} }
int CCallback::bulkMoveArmy(ObjectInstanceID srcArmy, ObjectInstanceID destArmy, SlotID srcSlot) int CCallback::bulkMoveArmy(ObjectInstanceID srcArmy, ObjectInstanceID destArmy, SlotID srcSlot)
{ {
BulkMoveArmy pack(srcArmy, destArmy, srcSlot); BulkMoveArmy pack(srcArmy, destArmy, srcSlot);
sendRequest(&pack); sendRequest(pack);
return 0; return 0;
} }
int CCallback::bulkSplitStack(ObjectInstanceID armyId, SlotID srcSlot, int howMany) int CCallback::bulkSplitStack(ObjectInstanceID armyId, SlotID srcSlot, int howMany)
{ {
BulkSplitStack pack(armyId, srcSlot, howMany); BulkSplitStack pack(armyId, srcSlot, howMany);
sendRequest(&pack); sendRequest(pack);
return 0; return 0;
} }
int CCallback::bulkSmartSplitStack(ObjectInstanceID armyId, SlotID srcSlot) int CCallback::bulkSmartSplitStack(ObjectInstanceID armyId, SlotID srcSlot)
{ {
BulkSmartSplitStack pack(armyId, srcSlot); BulkSmartSplitStack pack(armyId, srcSlot);
sendRequest(&pack); sendRequest(pack);
return 0; return 0;
} }
int CCallback::bulkMergeStacks(ObjectInstanceID armyId, SlotID srcSlot) int CCallback::bulkMergeStacks(ObjectInstanceID armyId, SlotID srcSlot)
{ {
BulkMergeStacks pack(armyId, srcSlot); BulkMergeStacks pack(armyId, srcSlot);
sendRequest(&pack); sendRequest(pack);
return 0; return 0;
} }
@ -151,7 +151,7 @@ bool CCallback::dismissHero(const CGHeroInstance *hero)
if(player!=hero->tempOwner) return false; if(player!=hero->tempOwner) return false;
DismissHero pack(hero->id); DismissHero pack(hero->id);
sendRequest(&pack); sendRequest(pack);
return true; return true;
} }
@ -160,7 +160,7 @@ bool CCallback::swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation
ExchangeArtifacts ea; ExchangeArtifacts ea;
ea.src = l1; ea.src = l1;
ea.dst = l2; ea.dst = l2;
sendRequest(&ea); sendRequest(ea);
return true; 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) void CCallback::assembleArtifacts(const ObjectInstanceID & heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)
{ {
AssembleArtifacts aa(heroID, artifactSlot, assemble, assembleTo); AssembleArtifacts aa(heroID, artifactSlot, assemble, assembleTo);
sendRequest(&aa); sendRequest(aa);
} }
void CCallback::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped, bool backpack) void CCallback::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped, bool backpack)
{ {
BulkExchangeArtifacts bma(srcHero, dstHero, swap, equipped, backpack); BulkExchangeArtifacts bma(srcHero, dstHero, swap, equipped, backpack);
sendRequest(&bma); sendRequest(bma);
} }
void CCallback::scrollBackpackArtifacts(ObjectInstanceID hero, bool left) 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); ManageBackpackArtifacts mba(hero, ManageBackpackArtifacts::ManageCmd::SCROLL_RIGHT);
if(left) if(left)
mba.cmd = ManageBackpackArtifacts::ManageCmd::SCROLL_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) void CCallback::manageHeroCostume(ObjectInstanceID hero, size_t costumeIndex, bool saveCostume)
{ {
ManageEquippedArtifacts mea(hero, costumeIndex, saveCostume); ManageEquippedArtifacts mea(hero, costumeIndex, saveCostume);
sendRequest(&mea); sendRequest(mea);
} }
void CCallback::eraseArtifactByClient(const ArtifactLocation & al) void CCallback::eraseArtifactByClient(const ArtifactLocation & al)
{ {
EraseArtifactByClient ea(al); EraseArtifactByClient ea(al);
sendRequest(&ea); sendRequest(ea);
} }
bool CCallback::buildBuilding(const CGTownInstance *town, BuildingID buildingID) bool CCallback::buildBuilding(const CGTownInstance *town, BuildingID buildingID)
@ -213,7 +231,7 @@ bool CCallback::buildBuilding(const CGTownInstance *town, BuildingID buildingID)
return false; return false;
BuildStructure pack(town->id,buildingID); BuildStructure pack(town->id,buildingID);
sendRequest(&pack); sendRequest(pack);
return true; return true;
} }
@ -223,7 +241,7 @@ bool CCallback::visitTownBuilding(const CGTownInstance *town, BuildingID buildin
return false; return false;
VisitTownBuilding pack(town->id, buildingID); VisitTownBuilding pack(town->id, buildingID);
sendRequest(&pack); sendRequest(pack);
return true; return true;
} }
@ -232,10 +250,10 @@ void CBattleCallback::battleMakeSpellAction(const BattleID & battleID, const Bat
assert(action.actionType == EActionType::HERO_SPELL); assert(action.actionType == EActionType::HERO_SPELL);
MakeAction mca(action); MakeAction mca(action);
mca.battleID = battleID; 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()); int requestID = cl->sendRequest(request, *getPlayerID());
if(waitTillRealize) if(waitTillRealize)
@ -249,12 +267,18 @@ int CBattleCallback::sendRequest(const CPackForServer * request)
return requestID; 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 ) void CCallback::swapGarrisonHero( const CGTownInstance *town )
{ {
if(town->tempOwner == *player || (town->garrisonHero && town->garrisonHero->tempOwner == *player )) if(town->tempOwner == *player || (town->garrisonHero && town->garrisonHero->tempOwner == *player ))
{ {
GarrisonHeroSwap pack(town->id); 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; if(hero->tempOwner != *player) return;
BuyArtifact pack(hero->id,aid); 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) 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.r1 = id1;
pack.r2 = id2; pack.r2 = id2;
pack.val = val1; pack.val = val1;
sendRequest(&pack); sendRequest(pack);
} }
void CCallback::setFormation(const CGHeroInstance * hero, EArmyFormation mode) void CCallback::setFormation(const CGHeroInstance * hero, EArmyFormation mode)
{ {
SetFormation pack(hero->id, mode); SetFormation pack(hero->id, mode);
sendRequest(&pack); sendRequest(pack);
} }
void CCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero, const HeroTypeID & nextHero) 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); HireHero pack(hero->getHeroType(), townOrTavern->id, nextHero);
pack.player = *player; pack.player = *player;
sendRequest(&pack); sendRequest(pack);
} }
void CCallback::save( const std::string &fname ) void CCallback::save( const std::string &fname )
@ -310,7 +334,7 @@ void CCallback::gamePause(bool pause)
{ {
GamePause pack; GamePause pack;
pack.player = *player; pack.player = *player;
sendRequest(&pack); sendRequest(pack);
} }
else else
{ {
@ -324,14 +348,14 @@ void CCallback::sendMessage(const std::string &mess, const CGObjectInstance * cu
PlayerMessage pm(mess, currentObject? currentObject->id : ObjectInstanceID(-1)); PlayerMessage pm(mess, currentObject? currentObject->id : ObjectInstanceID(-1));
if(player) if(player)
pm.player = *player; pm.player = *player;
sendRequest(&pm); sendRequest(pm);
} }
void CCallback::buildBoat( const IShipyard *obj ) void CCallback::buildBoat( const IShipyard *obj )
{ {
BuildBoat bb; BuildBoat bb;
bb.objid = dynamic_cast<const CGObjectInstance*>(obj)->id; bb.objid = dynamic_cast<const CGObjectInstance*>(obj)->id;
sendRequest(&bb); sendRequest(bb);
} }
CCallback::CCallback(CGameState * GS, std::optional<PlayerColor> Player, CClient * C) CCallback::CCallback(CGameState * GS, std::optional<PlayerColor> Player, CClient * C)
@ -373,7 +397,7 @@ void CCallback::dig( const CGObjectInstance *hero )
{ {
DigWithHero dwh; DigWithHero dwh;
dwh.id = hero->id; dwh.id = hero->id;
sendRequest(&dwh); sendRequest(dwh);
} }
void CCallback::castSpell(const CGHeroInstance *hero, SpellID spellID, const int3 &pos) 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.hid = hero->id;
cas.sid = spellID; cas.sid = spellID;
cas.pos = pos; cas.pos = pos;
sendRequest(&cas); sendRequest(cas);
} }
int CCallback::mergeOrSwapStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2) 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; MakeAction ma;
ma.ba = action; ma.ba = action;
ma.battleID = battleID; ma.battleID = battleID;
sendRequest(&ma); sendRequest(ma);
} }
void CBattleCallback::battleMakeTacticAction(const BattleID & battleID, const BattleAction & action ) void CBattleCallback::battleMakeTacticAction(const BattleID & battleID, const BattleAction & action )
@ -424,7 +448,7 @@ void CBattleCallback::battleMakeTacticAction(const BattleID & battleID, const Ba
MakeAction ma; MakeAction ma;
ma.ba = action; ma.ba = action;
ma.battleID = battleID; ma.battleID = battleID;
sendRequest(&ma); sendRequest(ma);
} }
std::optional<BattleAction> CBattleCallback::makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) 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 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 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 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 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 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 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 bool swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation &l2)=0;
virtual void scrollBackpackArtifacts(ObjectInstanceID hero, bool left) = 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 manageHeroCostume(ObjectInstanceID hero, size_t costumeIndex, bool saveCostume) = 0;
virtual void assembleArtifacts(const ObjectInstanceID & heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)=0; virtual void assembleArtifacts(const ObjectInstanceID & heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)=0;
virtual void eraseArtifactByClient(const ArtifactLocation & al)=0; virtual void eraseArtifactByClient(const ArtifactLocation & al)=0;
@ -123,7 +127,7 @@ class CBattleCallback : public IBattleCallback
std::optional<PlayerColor> player; std::optional<PlayerColor> player;
protected: 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; CClient *cl;
public: public:
@ -179,6 +183,9 @@ public:
void assembleArtifacts(const ObjectInstanceID & heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo) override; 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 bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped = true, bool backpack = true) override;
void scrollBackpackArtifacts(ObjectInstanceID hero, bool left) 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 manageHeroCostume(ObjectInstanceID hero, size_t costumeIdx, bool saveCostume) override;
void eraseArtifactByClient(const ArtifactLocation & al) override; void eraseArtifactByClient(const ArtifactLocation & al) override;
bool buildBuilding(const CGTownInstance *town, BuildingID buildingID) override; bool buildBuilding(const CGTownInstance *town, BuildingID buildingID) override;
@ -187,6 +194,7 @@ public:
bool dismissCreature(const CArmedInstance *obj, SlotID stackPos) override; bool dismissCreature(const CArmedInstance *obj, SlotID stackPos) override;
bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE) override; bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE) override;
void endTurn() override; void endTurn() override;
void spellResearch(const CGTownInstance *town, SpellID spellAtSlot, bool accepted) override;
void swapGarrisonHero(const CGTownInstance *town) override; void swapGarrisonHero(const CGTownInstance *town) override;
void buyArtifact(const CGHeroInstance *hero, ArtifactID aid) 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; 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() endif()
if (ENABLE_CLIENT) if (ENABLE_CLIENT)
set(FFMPEG_COMPONENTS avutil swscale avformat avcodec) find_package(ffmpeg COMPONENTS avutil swscale avformat avcodec swresample)
if(APPLE_IOS AND NOT USING_CONAN)
list(APPEND FFMPEG_COMPONENTS swresample)
endif()
find_package(ffmpeg COMPONENTS ${FFMPEG_COMPONENTS})
find_package(SDL2 REQUIRED) find_package(SDL2 REQUIRED)
find_package(SDL2_image REQUIRED) find_package(SDL2_image REQUIRED)
@ -666,6 +662,10 @@ if(NOT TARGET minizip::minizip)
add_library(minizip::minizip ALIAS minizip) add_library(minizip::minizip ALIAS minizip)
endif() endif()
if(ENABLE_LAUNCHER OR ENABLE_EDITOR)
add_subdirectory(vcmiqt)
endif()
if(ENABLE_LAUNCHER) if(ENABLE_LAUNCHER)
add_subdirectory(launcher) add_subdirectory(launcher)
endif() 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.9" : "压倒性的",
"vcmi.adventureMap.monsterThreat.levels.10" : "致命的", "vcmi.adventureMap.monsterThreat.levels.10" : "致命的",
"vcmi.adventureMap.monsterThreat.levels.11" : "无法取胜", "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.confirmRestartGame" : "你想要重新开始游戏吗?",
"vcmi.adventureMap.noTownWithMarket" : "没有足够的市场。", "vcmi.adventureMap.noTownWithMarket" : "没有足够的市场。",

View File

@ -12,7 +12,9 @@
"vcmi.adventureMap.monsterThreat.levels.9" : "Overpowering", "vcmi.adventureMap.monsterThreat.levels.9" : "Overpowering",
"vcmi.adventureMap.monsterThreat.levels.10" : "Deadly", "vcmi.adventureMap.monsterThreat.levels.10" : "Deadly",
"vcmi.adventureMap.monsterThreat.levels.11" : "Impossible", "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.confirmRestartGame" : "Are you sure you want to restart the game?",
"vcmi.adventureMap.noTownWithMarket" : "There are no available marketplaces!", "vcmi.adventureMap.noTownWithMarket" : "There are no available marketplaces!",
@ -59,6 +61,13 @@
"vcmi.spellBook.search" : "search...", "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.serverConnecting" : "Connecting...",
"vcmi.mainMenu.serverAddressEnter" : "Enter address:", "vcmi.mainMenu.serverAddressEnter" : "Enter address:",
"vcmi.mainMenu.serverConnectionFailed" : "Failed to connect", "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.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.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.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.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.modsToEnable" : "{Following mods are required}",
"vcmi.server.errors.modsToDisable" : "{Following mods must be disabled}", "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.openCommander.help" : "Shows details about the commander of this hero.",
"vcmi.heroWindow.openBackpack.hover" : "Open artifact backpack window", "vcmi.heroWindow.openBackpack.hover" : "Open artifact backpack window",
"vcmi.heroWindow.openBackpack.help" : "Opens window that allows easier artifact backpack management.", "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", "vcmi.tavernWindow.inviteHero" : "Invite hero",

View File

@ -12,7 +12,9 @@
"vcmi.adventureMap.monsterThreat.levels.9" : "Überwältigend", "vcmi.adventureMap.monsterThreat.levels.9" : "Überwältigend",
"vcmi.adventureMap.monsterThreat.levels.10" : "Tödlich", "vcmi.adventureMap.monsterThreat.levels.10" : "Tödlich",
"vcmi.adventureMap.monsterThreat.levels.11" : "Unmöglich", "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.confirmRestartGame" : "Seid Ihr sicher, dass Ihr das Spiel neu starten wollt?",
"vcmi.adventureMap.noTownWithMarket" : "Kein Marktplatz verfügbar!", "vcmi.adventureMap.noTownWithMarket" : "Kein Marktplatz verfügbar!",
@ -58,6 +60,13 @@
"vcmi.spellBook.search" : "suchen...", "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.serverConnecting" : "Verbinde...",
"vcmi.mainMenu.serverAddressEnter" : "Addresse eingeben:", "vcmi.mainMenu.serverAddressEnter" : "Addresse eingeben:",
"vcmi.mainMenu.serverConnectionFailed" : "Verbindung fehlgeschlagen", "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 if(!playerNames.empty()) //if have custom set of player names - use it
localPlayerNames = playerNames; localPlayerNames = playerNames;
else 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(); gameChat->resetMatchState();
lobbyClient->resetMatchState(); lobbyClient->resetMatchState();
@ -853,7 +858,7 @@ void CServerHandler::onPacketReceived(const std::shared_ptr<INetworkConnection>
if(getState() == EClientState::DISCONNECTING) if(getState() == EClientState::DISCONNECTING)
return; return;
CPack * pack = logicConnection->retrievePack(message); auto pack = logicConnection->retrievePack(message);
ServerHandlerCPackVisitor visitor(*this); ServerHandlerCPackVisitor visitor(*this);
pack->visit(visitor); pack->visit(visitor);
} }
@ -938,14 +943,14 @@ void CServerHandler::visitForLobby(CPackForLobby & lobbyPack)
void CServerHandler::visitForClient(CPackForClient & clientPack) void CServerHandler::visitForClient(CPackForClient & clientPack)
{ {
client->handlePack(&clientPack); client->handlePack(clientPack);
} }
void CServerHandler::sendLobbyPack(const CPackForLobby & pack) const void CServerHandler::sendLobbyPack(const CPackForLobby & pack) const
{ {
if(getState() != EClientState::STARTING) if(getState() != EClientState::STARTING)
logicConnection->sendPack(&pack); logicConnection->sendPack(pack);
} }
bool CServerHandler::inLobbyRoom() const bool CServerHandler::inLobbyRoom() const

View File

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

View File

@ -163,7 +163,7 @@ void CClient::save(const std::string & fname)
} }
SaveGame save_game(fname); SaveGame save_game(fname);
sendRequest(&save_game, PlayerColor::NEUTRAL); sendRequest(save_game, PlayerColor::NEUTRAL);
} }
void CClient::endNetwork() 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()); ApplyClientNetPackVisitor afterVisitor(*this, *gameState());
ApplyFirstClientNetPackVisitor beforeVisitor(*this, *gameState()); ApplyFirstClientNetPackVisitor beforeVisitor(*this, *gameState());
pack->visit(beforeVisitor); pack.visit(beforeVisitor);
logNetwork->trace("\tMade first apply on cl: %s", typeid(*pack).name()); logNetwork->trace("\tMade first apply on cl: %s", typeid(pack).name());
{ {
boost::unique_lock lock(CGameState::mutex); boost::unique_lock lock(CGameState::mutex);
gs->apply(pack); gs->apply(pack);
} }
logNetwork->trace("\tApplied on gs: %s", typeid(*pack).name()); logNetwork->trace("\tApplied on gs: %s", typeid(pack).name());
pack->visit(afterVisitor); pack.visit(afterVisitor);
logNetwork->trace("\tMade second apply on cl: %s", typeid(*pack).name()); logNetwork->trace("\tMade second apply on cl: %s", typeid(pack).name());
delete pack;
} }
int CClient::sendRequest(const CPackForServer * request, PlayerColor player) int CClient::sendRequest(const CPackForServer & request, PlayerColor player)
{ {
static ui32 requestCounter = 1; static ui32 requestCounter = 1;
ui32 requestID = requestCounter++; 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); waitingRequest.pushBack(requestID);
request->requestID = requestID; request.requestID = requestID;
request->player = player; request.player = player;
CSH->logicConnection->sendPack(request); CSH->logicConnection->sendPack(request);
if(vstd::contains(playerint, player)) if(vstd::contains(playerint, player))
playerint[player]->requestSent(request, requestID); playerint[player]->requestSent(&request, requestID);
return requestID; return requestID;
} }

View File

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

View File

@ -14,6 +14,7 @@
#include "CPlayerInterface.h" #include "CPlayerInterface.h"
#include "CGameInfo.h" #include "CGameInfo.h"
#include "windows/GUIClasses.h" #include "windows/GUIClasses.h"
#include "windows/CCastleInterface.h"
#include "mapView/mapHandler.h" #include "mapView/mapHandler.h"
#include "adventureMap/AdventureMapInterface.h" #include "adventureMap/AdventureMapInterface.h"
#include "adventureMap/CInGameConsole.h" #include "adventureMap/CInGameConsole.h"
@ -172,6 +173,12 @@ void ApplyClientNetPackVisitor::visitSetMovePoints(SetMovePoints & pack)
callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroMovePointsChanged, h); 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) void ApplyClientNetPackVisitor::visitFoWChange(FoWChange & pack)
{ {
for(auto &i : cl.playerint) for(auto &i : cl.playerint)

View File

@ -835,9 +835,9 @@ OptionsTab::HandicapWindow::HandicapWindow()
if(i == 0) if(i == 0)
{ {
if(isIncome) 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) 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 else
anim.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SMALRES"), GameResID(resource), 0, 15 + xPos + (j == 0 ? 10 : 0), 35)); 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 = std::make_shared<CTextInput>(Rect(6, 3, 95, 15), EFonts::FONT_SMALL, ETextAlignment::CENTER, false);
labelPlayerNameEdit->setText(name); 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; }; 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(); 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); labelHandicap = std::make_shared<CMultiLineLabel>(Rect(55, 23, 46, 24), EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, labelHandicapText);
handicap = std::make_shared<LRClickableArea>(Rect(56, 24, 49, font->getLineHeight()*2), [](){ handicap = std::make_shared<LRClickableArea>(Rect(53, 23, 50, 24), [](){
if(!CSH->isHost()) if(!CSH->isHost())
return; return;

View File

@ -33,7 +33,9 @@ extern "C" {
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h> #include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h> #include <libswscale/swscale.h>
#include <libswresample/swresample.h>
} }
// Define a set of functions to read data // Define a set of functions to read data
@ -501,8 +503,35 @@ std::pair<std::unique_ptr<ui8 []>, si64> CAudioInstance::extractAudio(const Vide
int numChannels = codecpar->ch_layout.nb_channels; int numChannels = codecpar->ch_layout.nb_channels;
#endif #endif
samples.reserve(44100 * 5); // arbitrary 5-second buffer samples.reserve(44100 * 5); // arbitrary 5-second buffer to reduce reallocations
if (formatProperties.isPlanar && numChannels > 1)
{
// Format is 'planar', which is not supported by wav / SDL
// Use swresample part of ffmpeg to deplanarize audio into format supported by wav / SDL
auto sourceFormat = static_cast<AVSampleFormat>(codecpar->format);
auto targetFormat = av_get_alt_sample_fmt(sourceFormat, false);
SwrContext * swr_ctx = swr_alloc();
#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 (;;) for (;;)
{ {
decodeNextFrame(); decodeNextFrame();
@ -511,22 +540,34 @@ std::pair<std::unique_ptr<ui8 []>, si64> CAudioInstance::extractAudio(const Vide
if (!frame) if (!frame)
break; break;
int samplesToRead = frame->nb_samples * numChannels; size_t samplesToRead = frame->nb_samples * numChannels;
int bytesToRead = samplesToRead * formatProperties.sampleSizeBytes; size_t bytesToRead = samplesToRead * formatProperties.sampleSizeBytes;
frameSamplesBuffer.resize(std::max(frameSamplesBuffer.size(), bytesToRead));
uint8_t * frameSamplesPtr = frameSamplesBuffer.data();
if (formatProperties.isPlanar && numChannels > 1) int result = swr_convert(swr_ctx, &frameSamplesPtr, frame->nb_samples, (const uint8_t **)frame->data, frame->nb_samples);
{
// Workaround for lack of resampler if (result < 0)
// Currently, ffmpeg on conan systems is built without sws resampler throwFFmpegError(result);
// 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 size_t samplesToCopy = result * numChannels;
samples.reserve(samples.size() + bytesToRead); size_t bytesToCopy = samplesToCopy * formatProperties.sampleSizeBytes;
for (int sm = 0; sm < frame->nb_samples; ++sm) samples.insert(samples.end(), frameSamplesBuffer.begin(), frameSamplesBuffer.begin() + bytesToCopy);
for (int ch = 0; ch < numChannels; ++ch) }
samples.insert(samples.end(), frame->data[ch] + sm * formatProperties.sampleSizeBytes, frame->data[ch] + (sm+1) * formatProperties.sampleSizeBytes ); swr_free(&swr_ctx);
} }
else 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); samples.insert(samples.end(), frame->data[0], frame->data[0] + bytesToRead);
} }
} }

View File

@ -18,6 +18,10 @@
#include "../render/IRenderHandler.h" #include "../render/IRenderHandler.h"
#include "../lib/filesystem/Filesystem.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() void AssetGenerator::generateAll()
{ {
@ -138,16 +142,17 @@ void AssetGenerator::createPlayerColoredBackground(const PlayerColor & player)
std::shared_ptr<IImage> texture = GH.renderHandler().loadImage(locator, EImageBlitMode::OPAQUE); 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 = { 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( filterSettings["red" ].convertTo<std::vector<float>>() ),
ColorFilter::genRangeShifter( 0, 0, 0, 0.45, 1.20, 4.50 ), // blue ColorFilter::genRangeShifter( filterSettings["blue" ].convertTo<std::vector<float>>() ),
ColorFilter::genRangeShifter( 0.40, 0.27, 0.23, 1.10, 1.20, 1.15 ), // tan ColorFilter::genRangeShifter( filterSettings["tan" ].convertTo<std::vector<float>>() ),
ColorFilter::genRangeShifter( -0.27, 0.10, -0.27, 0.70, 1.70, 0.70 ), // green ColorFilter::genRangeShifter( filterSettings["green" ].convertTo<std::vector<float>>() ),
ColorFilter::genRangeShifter( 0.47, 0.17, -0.27, 1.60, 1.20, 0.70 ), // orange ColorFilter::genRangeShifter( filterSettings["orange"].convertTo<std::vector<float>>() ),
ColorFilter::genRangeShifter( 0.12, -0.1, 0.25, 1.15, 1.20, 2.20 ), // purple ColorFilter::genRangeShifter( filterSettings["purple"].convertTo<std::vector<float>>() ),
ColorFilter::genRangeShifter( -0.13, 0.23, 0.23, 0.90, 1.20, 2.20 ), // teal ColorFilter::genRangeShifter( filterSettings["teal" ].convertTo<std::vector<float>>() ),
ColorFilter::genRangeShifter( 0.44, 0.15, 0.25, 1.00, 1.00, 1.75 ) // pink ColorFilter::genRangeShifter( filterSettings["pink" ].convertTo<std::vector<float>>() )
}; };
assert(player.isValidPlayer()); assert(player.isValidPlayer());

View File

@ -70,6 +70,13 @@ ColorFilter ColorFilter::genRangeShifter( float minR, float minG, float minB, fl
1.f); 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 ) ColorFilter ColorFilter::genMuxerShifter( ChannelMuxer r, ChannelMuxer g, ChannelMuxer b, float a )
{ {
return ColorFilter(r, g, b, a); return ColorFilter(r, g, b, a);

View File

@ -44,6 +44,7 @@ public:
/// Generates object that transforms each channel independently /// 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( 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 /// Generates object that performs arbitrary mixing between any channels
static ColorFilter genMuxerShifter( ChannelMuxer r, ChannelMuxer g, ChannelMuxer b, float a ); 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; customSubtitle = ValText;
size = imageSize; size = imageSize;
font = fnt; font = fnt;
newLine = false;
assert(size < sizeInvalid); assert(size < sizeInvalid);
@ -471,7 +472,8 @@ void CComponentBox::placeComponents(bool selectable)
//start next row //start next row
if ((pos.w != 0 && rows.back().width + comp->pos.w + distance > pos.w) // row is full 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; prevComp = nullptr;
rows.push_back (RowData (0,0,0)); rows.push_back (RowData (0,0,0));

View File

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

View File

@ -1782,6 +1782,7 @@ CFortScreen::CFortScreen(const CGTownInstance * town):
ui32 fortSize = static_cast<ui32>(town->creatures.size()); ui32 fortSize = static_cast<ui32>(town->creatures.size());
if(fortSize > town->town->creatures.size() && town->creatures.back().second.empty()) if(fortSize > town->town->creatures.size() && town->creatures.back().second.empty())
fortSize--; 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)); 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()); 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()); ui32 fortSize = static_cast<ui32>(town->creatures.size());
if(fortSize > town->town->creatures.size() && town->creatures.back().second.empty()) if(fortSize > town->town->creatures.size() && town->creatures.back().second.empty())
fortSize--; fortSize--;
fortSize = std::min(fortSize, static_cast<ui32>(GameConstants::CREATURES_PER_TOWN)); // for 8 creatures + portal of summoning
if(fortSize == GameConstants::CREATURES_PER_TOWN) if(fortSize == GameConstants::CREATURES_PER_TOWN)
return ImagePath::builtin("TPCASTL8"); return ImagePath::builtin("TPCASTL8");
@ -1966,7 +1968,7 @@ void CFortScreen::RecruitArea::showPopupWindow(const Point & cursorPosition)
} }
CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner, const ImagePath & imagename) CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner, const ImagePath & imagename)
: CWindowObject(BORDERED, imagename) : CWindowObject(BORDERED, imagename), townId(owner->town->id)
{ {
OBJECT_CONSTRUCTION; 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); 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 = 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)}, {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)} {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++) for(size_t j=0; j<spellCount; j++)
{ {
if(i<owner->town->mageGuildLevel() && owner->town->spells[i].size()>j) if(i<town->mageGuildLevel() && town->spells[i].size()>j)
spells.push_back(std::make_shared<Scroll>(positions[i][j], owner->town->spells[i][j].toSpell())); spells.push_back(std::make_shared<Scroll>(positions[i][j], town->spells[i][j].toSpell(), townId));
else else
emptyScrolls.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("TPMAGES.DEF"), 1, 0, positions[i][j].x, positions[i][j].y)); 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) CMageGuildScreen::Scroll::Scroll(Point position, const CSpell *Spell, ObjectInstanceID townId)
: spell(Spell) : spell(Spell), townId(townId)
{ {
OBJECT_CONSTRUCTION; OBJECT_CONSTRUCTION;
@ -2017,6 +2035,60 @@ CMageGuildScreen::Scroll::Scroll(Point position, const CSpell *Spell)
void CMageGuildScreen::Scroll::clickPressed(const Point & cursorPosition) void CMageGuildScreen::Scroll::clickPressed(const Point & cursorPosition)
{ {
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)); LOCPLINT->showInfoDialog(spell->getDescriptionTranslated(0), std::make_shared<CComponent>(ComponentType::SPELL, spell->id));
} }

View File

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

View File

@ -20,6 +20,8 @@
#include "render/Canvas.h" #include "render/Canvas.h"
#include "CPlayerInterface.h" #include "CPlayerInterface.h"
#include "../../CCallback.h"
#include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/networkPacks/ArtifactLocation.h" #include "../../lib/networkPacks/ArtifactLocation.h"
@ -41,17 +43,38 @@ CHeroBackpackWindow::CHeroBackpackWindow(const CGHeroInstance * hero, const std:
}; };
addSet(arts); addSet(arts);
arts->setHero(hero); 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.w = stretchedBackground->pos.w = arts->pos.w + 2 * windowMargin;
pos.h = stretchedBackground->pos.h = arts->pos.h + quitButton->pos.h + 3 * windowMargin; pos.h = stretchedBackground->pos.h = arts->pos.h + buttons.back()->pos.h + 3 * windowMargin;
quitButton->moveTo(Point(pos.x + pos.w / 2 - quitButton->pos.w / 2, pos.y + arts->pos.h + 2 * 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); statusbar = CGStatusBar::create(0, pos.h, ImagePath::builtin("ADROLLVR.bmp"), pos.w);
pos.h += statusbar->pos.h; pos.h += statusbar->pos.h;
addUsedEvents(LCLICK);
center(); center();
} }
void CHeroBackpackWindow::notFocusedClick()
{
close();
}
void CHeroBackpackWindow::showAll(Canvas & to) void CHeroBackpackWindow::showAll(Canvas & to)
{ {
CIntObject::showAll(to); CIntObject::showAll(to);

View File

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

View File

@ -473,7 +473,7 @@ CKingdomInterface::CKingdomInterface()
generateButtons(); generateButtons();
statusbar = CGStatusBar::create(std::make_shared<CPicture>(ImagePath::builtin("KSTATBAR"), 10,pos.h - 45)); 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()); 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); picture = std::make_shared<CAnimImage>(AnimationPath::builtin("ITPT"), iconIndex, 0, 5, 6);
openTown = std::make_shared<LRClickableAreaOpenTown>(Rect(5, 6, 58, 64), town); 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)); 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)); 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(); 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(); growth[i]->update();
available[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)) if(wordBreak != ui32(-1))
{ {
currPos = wordBreak; 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; opened = false;
color = ""; color = "";

View File

@ -548,11 +548,12 @@ void CTavernWindow::addInvite()
if(!inviteableHeroes.empty()) if(!inviteableHeroes.empty())
{ {
int imageIndex = heroToInvite ? (*CGI->heroh)[heroToInvite->getHeroType()]->imageIndex : 156; // 156 => special id for random
if(!heroToInvite) if(!heroToInvite)
heroToInvite = (*RandomGeneratorUtil::nextItem(inviteableHeroes, CRandomGenerator::getDefault())).second; 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")); 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)); }); 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(); setButtons();
setCreaturePurchaseCards(); setCreaturePurchaseCards();
maxAllCards(cards); maxAllCards(cards);
center();
} }

View File

@ -302,7 +302,7 @@
"backpackSize" : -1, "backpackSize" : -1,
// if heroes are invitable in tavern // if heroes are invitable in tavern
"tavernInvite" : false, "tavernInvite" : false,
// minimai primary skills for heroes // minimal primary skills for heroes
"minimalPrimarySkills": [ 0, 0, 1, 1] "minimalPrimarySkills": [ 0, 0, 1, 1]
}, },
@ -311,7 +311,21 @@
// How many new building can be built in a town per day // How many new building can be built in a town per day
"buildingsPerTurnCap" : 1, "buildingsPerTurnCap" : 1,
// Chances for a town with default buildings to receive corresponding dwelling level built in start // 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": "combat":
@ -555,6 +569,22 @@
"valueType" : "BASE_NUMBER" "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

@ -52,7 +52,11 @@
"additionalProperties" : false, "additionalProperties" : false,
"properties" : { "properties" : {
"buildingsPerTurnCap" : { "type" : "number" }, "buildingsPerTurnCap" : { "type" : "number" },
"startingDwellingChances" : { "type" : "array" } "startingDwellingChances" : { "type" : "array" },
"spellResearch" : { "type" : "boolean" },
"spellResearchCost" : { "type" : "array" },
"spellResearchPerDay" : { "type" : "array" },
"spellResearchCostExponentPerResearch" : { "type" : "array" }
} }
}, },
"combat": { "combat": {
@ -148,5 +152,12 @@
"perHero" : { "type" : "object" } "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", "description" : "Format used to define main mod file (mod.json) in VCMI",
"required" : [ "name", "description", "modType", "version", "author", "contact" ], "required" : [ "name", "description", "modType", "version", "author", "contact" ],
"definitions" : { "definitions" : {
"fileListOrObject" : {
"oneOf" : [
{
"type" : "array",
"items" : { "type" : "string", "format" : "textFile" }
},
{
"type" : "object"
}
]
},
"localizable" : { "localizable" : {
"type" : "object", "type" : "object",
"additionalProperties" : false, "additionalProperties" : false,
@ -35,9 +46,8 @@
"description" : "If set to true, vcmi will skip validation of current translation json files" "description" : "If set to true, vcmi will skip validation of current translation json files"
}, },
"translations" : { "translations" : {
"type" : "array",
"description" : "List of files with translations for this language", "description" : "List of files with translations for this language",
"items" : { "type" : "string", "format" : "textFile" } "$ref" : "#/definitions/fileListOrObject"
} }
} }
} }
@ -122,10 +132,18 @@
"description" : "If set to true, mod will not be enabled automatically on install" "description" : "If set to true, mod will not be enabled automatically on install"
}, },
"settings" : { "settings" : {
"type" : "object",
"description" : "List of changed game settings by mod", "description" : "List of changed game settings by mod",
"oneOf" : [
{
"type" : "object",
"$ref" : "gameSettings.json" "$ref" : "gameSettings.json"
}, },
{
"type" : "array",
"items" : { "type" : "string", "format" : "textFile" }
},
]
},
"filesystem" : { "filesystem" : {
"type" : "object", "type" : "object",
"description" : "Optional, description on how files are organized in your mod. In most cases you do not need to use this field", "description" : "Optional, description on how files are organized in your mod. In most cases you do not need to use this field",
@ -206,94 +224,76 @@
"$ref" : "#/definitions/localizable" "$ref" : "#/definitions/localizable"
}, },
"translations" : { "translations" : {
"type" : "array",
"description" : "List of files with translations for this language", "description" : "List of files with translations for this language",
"items" : { "type" : "string", "format" : "textFile" } "$ref" : "#/definitions/fileListOrObject"
}, },
"factions" : { "factions" : {
"type" : "array",
"description" : "List of configuration files for towns/factions", "description" : "List of configuration files for towns/factions",
"items" : { "type" : "string", "format" : "textFile" } "$ref" : "#/definitions/fileListOrObject"
}, },
"heroClasses" : { "heroClasses" : {
"type" : "array",
"description" : "List of configuration files for hero classes", "description" : "List of configuration files for hero classes",
"items" : { "type" : "string", "format" : "textFile" } "$ref" : "#/definitions/fileListOrObject"
}, },
"heroes" : { "heroes" : {
"type" : "array",
"description" : "List of configuration files for heroes", "description" : "List of configuration files for heroes",
"items" : { "type" : "string", "format" : "textFile" } "$ref" : "#/definitions/fileListOrObject"
}, },
"skills" : { "skills" : {
"type" : "array",
"description" : "List of configuration files for skills", "description" : "List of configuration files for skills",
"items" : { "type" : "string", "format" : "textFile" } "$ref" : "#/definitions/fileListOrObject"
}, },
"creatures" : { "creatures" : {
"type" : "array",
"description" : "List of configuration files for creatures", "description" : "List of configuration files for creatures",
"items" : { "type" : "string", "format" : "textFile" } "$ref" : "#/definitions/fileListOrObject"
}, },
"artifacts" : { "artifacts" : {
"type" : "array",
"description" : "List of configuration files for artifacts", "description" : "List of configuration files for artifacts",
"items" : { "type" : "string", "format" : "textFile" } "$ref" : "#/definitions/fileListOrObject"
}, },
"spells" : { "spells" : {
"type" : "array",
"description" : "List of configuration files for spells", "description" : "List of configuration files for spells",
"items" : { "type" : "string", "format" : "textFile" } "$ref" : "#/definitions/fileListOrObject"
}, },
"objects" : { "objects" : {
"type" : "array",
"description" : "List of configuration files for objects", "description" : "List of configuration files for objects",
"items" : { "type" : "string", "format" : "textFile" } "$ref" : "#/definitions/fileListOrObject"
}, },
"biomes" : { "biomes" : {
"type" : "array",
"description" : "List of configuration files for biomes", "description" : "List of configuration files for biomes",
"items" : { "type" : "string", "format" : "textFile" } "$ref" : "#/definitions/fileListOrObject"
}, },
"bonuses" : { "bonuses" : {
"type" : "array",
"description" : "List of configuration files for bonuses", "description" : "List of configuration files for bonuses",
"items" : { "type" : "string", "format" : "textFile" } "$ref" : "#/definitions/fileListOrObject"
}, },
"terrains" : { "terrains" : {
"type" : "array",
"description" : "List of configuration files for terrains", "description" : "List of configuration files for terrains",
"items" : { "type" : "string", "format" : "textFile" } "$ref" : "#/definitions/fileListOrObject"
}, },
"roads" : { "roads" : {
"type" : "array",
"description" : "List of configuration files for roads", "description" : "List of configuration files for roads",
"items" : { "type" : "string", "format" : "textFile" } "$ref" : "#/definitions/fileListOrObject"
}, },
"rivers" : { "rivers" : {
"type" : "array",
"description" : "List of configuration files for rivers", "description" : "List of configuration files for rivers",
"items" : { "type" : "string", "format" : "textFile" } "$ref" : "#/definitions/fileListOrObject"
}, },
"battlefields" : { "battlefields" : {
"type" : "array",
"description" : "List of configuration files for battlefields", "description" : "List of configuration files for battlefields",
"items" : { "type" : "string", "format" : "textFile" } "$ref" : "#/definitions/fileListOrObject"
}, },
"obstacles" : { "obstacles" : {
"type" : "array",
"description" : "List of configuration files for obstacles", "description" : "List of configuration files for obstacles",
"items" : { "type" : "string", "format" : "textFile" } "$ref" : "#/definitions/fileListOrObject"
}, },
"templates" : { "templates" : {
"type" : "array",
"description" : "List of configuration files for RMG templates", "description" : "List of configuration files for RMG templates",
"items" : { "type" : "string", "format" : "textFile" } "$ref" : "#/definitions/fileListOrObject"
}, },
"scripts" : { "scripts" : {
"type" : "array",
"description" : "List of configuration files for scripts", "description" : "List of configuration files for scripts",
"items" : { "type" : "string", "format" : "textFile" } "$ref" : "#/definitions/fileListOrObject"
} }
} }
} }

View File

@ -3,7 +3,7 @@
{ {
"type" : "object", "type" : "object",
"$schema" : "http://json-schema.org/draft-04/schema", "$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" : { "definitions" : {
"logLevelEnum" : { "logLevelEnum" : {
"type" : "string", "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" : { "video" : {
"type" : "object", "type" : "object",
"additionalProperties" : false, "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 // 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 // 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 // to keep one file per object (creature/hero/etc)
// with translatable strings for each type of content // 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 // list of factions/towns configuration files
"factions" : "factions" :

View File

@ -36,15 +36,15 @@ public:
virtual vstd::RNG * getRNG() = 0; 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(BattleLogMessage & pack) = 0;
virtual void apply(BattleStackMoved * pack) = 0; virtual void apply(BattleStackMoved & pack) = 0;
virtual void apply(BattleUnitsChanged * pack) = 0; virtual void apply(BattleUnitsChanged & pack) = 0;
virtual void apply(SetStackEffect * pack) = 0; virtual void apply(SetStackEffect & pack) = 0;
virtual void apply(StacksInjured * pack) = 0; virtual void apply(StacksInjured & pack) = 0;
virtual void apply(BattleObstaclesChanged * pack) = 0; virtual void apply(BattleObstaclesChanged & pack) = 0;
virtual void apply(CatapultAttack * pack) = 0; virtual void apply(CatapultAttack & pack) = 0;
}; };
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -20,8 +20,6 @@ set(launcher_SRCS
innoextract.cpp innoextract.cpp
mainwindow_moc.cpp mainwindow_moc.cpp
languages.cpp languages.cpp
launcherdirs.cpp
jsonutils.cpp
updatedialog_moc.cpp updatedialog_moc.cpp
prepare.cpp prepare.cpp
) )
@ -49,8 +47,6 @@ set(launcher_HEADERS
firstLaunch/firstlaunch_moc.h firstLaunch/firstlaunch_moc.h
mainwindow_moc.h mainwindow_moc.h
languages.h languages.h
launcherdirs.h
jsonutils.h
updatedialog_moc.h updatedialog_moc.h
main.h main.h
helper.h helper.h
@ -206,7 +202,7 @@ elseif(NOT APPLE_IOS)
target_link_libraries(vcmilauncher SDL2::SDL2) target_link_libraries(vcmilauncher SDL2::SDL2)
endif() 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 target_include_directories(vcmilauncher
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
) )

View File

@ -20,22 +20,6 @@
#include <QFile> #include <QFile>
#include <QTemporaryDir> #include <QTemporaryDir>
#include "../vcmiqt/convpathqstring.h"
VCMI_LIB_USING_NAMESPACE 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 "StdInc.h"
#include "cdownloadmanager_moc.h" #include "cdownloadmanager_moc.h"
#include "../launcherdirs.h" #include "../vcmiqt/launcherdirs.h"
#include "../../lib/CConfigHandler.h" #include "../../lib/CConfigHandler.h"

View File

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

View File

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

View File

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

View File

@ -15,7 +15,7 @@
#include "../modManager/cmodlistview_moc.h" #include "../modManager/cmodlistview_moc.h"
#include "../helper.h" #include "../helper.h"
#include "../jsonutils.h" #include "../vcmiqt/jsonutils.h"
#include "../languages.h" #include "../languages.h"
#include <QFileInfo> #include <QFileInfo>
@ -182,6 +182,13 @@ void CSettingsView::loadSettings()
else else
ui->buttonFontScalable->setChecked(true); 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(); loadToggleButtonSettings();
} }
@ -791,3 +798,21 @@ void CSettingsView::on_buttonFontOriginal_clicked(bool checked)
Settings node = settings.write["video"]["fontsType"]; Settings node = settings.write["video"]["fontsType"];
node->String() = "original"; 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_sliderToleranceDistanceController_valueChanged(int value);
void on_lineEditGameLobbyHost_textChanged(const QString &arg1); void on_lineEditGameLobbyHost_textChanged(const QString &arg1);
void on_spinBoxNetworkPortLobby_valueChanged(int arg1); void on_spinBoxNetworkPortLobby_valueChanged(int arg1);
void on_sliderControllerSticksAcceleration_valueChanged(int value); void on_sliderControllerSticksAcceleration_valueChanged(int value);
void on_sliderControllerSticksSensitivity_valueChanged(int value); void on_sliderControllerSticksSensitivity_valueChanged(int value);
//void on_buttonTtfFont_toggled(bool value);
void on_sliderScalingFont_valueChanged(int value); void on_sliderScalingFont_valueChanged(int value);
void on_buttonFontAuto_clicked(bool checked); void on_buttonFontAuto_clicked(bool checked);
void on_buttonFontScalable_clicked(bool checked); void on_buttonFontScalable_clicked(bool checked);
void on_buttonFontOriginal_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: private:
Ui::CSettingsView * ui; 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"]; const JsonNode & text = node["text"];
VLC->generaltexth->registerString(scope, art->getNameTextID(), text["name"].String()); VLC->generaltexth->registerString(scope, art->getNameTextID(), text["name"]);
VLC->generaltexth->registerString(scope, art->getDescriptionTextID(), text["description"].String()); VLC->generaltexth->registerString(scope, art->getDescriptionTextID(), text["description"]);
VLC->generaltexth->registerString(scope, art->getEventTextID(), text["event"].String()); VLC->generaltexth->registerString(scope, art->getEventTextID(), text["event"]);
const JsonNode & graphics = node["graphics"]; const JsonNode & graphics = node["graphics"];
art->image = graphics["image"].String(); art->image = graphics["image"].String();

View File

@ -200,8 +200,9 @@ ImagePath CBonusTypeHandler::bonusToGraphics(const std::shared_ptr<Bonus> & bonu
void CBonusTypeHandler::load() void CBonusTypeHandler::load()
{ {
const JsonNode gameConf(JsonPath::builtin("config/gameConfig.json")); JsonNode gameConf(JsonPath::builtin("config/gameConfig.json"));
const JsonNode config(JsonUtils::assembleFromFiles(gameConf["bonuses"].convertTo<std::vector<std::string>>())); JsonNode config(JsonUtils::assembleFromFiles(gameConf["bonuses"].convertTo<std::vector<std::string>>()));
config.setModScope("vcmi");
load(config); load(config);
} }
@ -240,8 +241,8 @@ void CBonusTypeHandler::loadItem(const JsonNode & source, CBonusType & dest, con
if (!dest.hidden) if (!dest.hidden)
{ {
VLC->generaltexth->registerString( "vcmi", dest.getNameTextID(), source["name"].String()); VLC->generaltexth->registerString( "vcmi", dest.getNameTextID(), source["name"]);
VLC->generaltexth->registerString( "vcmi", dest.getDescriptionTextID(), source["description"].String()); VLC->generaltexth->registerString( "vcmi", dest.getDescriptionTextID(), source["description"]);
} }
const JsonNode & graphics = source["graphics"]; 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"]); cre->cost = ResourceSet(node["cost"]);
VLC->generaltexth->registerString(scope, cre->getNameSingularTextID(), node["name"]["singular"].String()); VLC->generaltexth->registerString(scope, cre->getNameSingularTextID(), node["name"]["singular"]);
VLC->generaltexth->registerString(scope, cre->getNamePluralTextID(), node["name"]["plural"].String()); VLC->generaltexth->registerString(scope, cre->getNamePluralTextID(), node["name"]["plural"]);
VLC->generaltexth->registerString(scope, cre->getDescriptionTextID(), node["description"].String()); VLC->generaltexth->registerString(scope, cre->getDescriptionTextID(), node["description"]);
cre->addBonus(node["hitPoints"].Integer(), BonusType::STACK_HEALTH); cre->addBonus(node["hitPoints"].Integer(), BonusType::STACK_HEALTH);
cre->addBonus(node["speed"].Integer(), BonusType::STACKS_SPEED); cre->addBonus(node["speed"].Integer(), BonusType::STACKS_SPEED);

View File

@ -863,7 +863,7 @@ std::string CStackInstance::getName() const
ui64 CStackInstance::getPower() const ui64 CStackInstance::getPower() const
{ {
assert(type); assert(type);
return type->getAIValue() * count; return static_cast<ui64>(type->getAIValue()) * count;
} }
ui64 CStackInstance::getMarketValue() const 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->onlyOnWaterMap = node["onlyOnWaterMap"].Bool();
hero->onlyOnMapWithoutWater = node["onlyOnMapWithoutWater"].Bool(); hero->onlyOnMapWithoutWater = node["onlyOnMapWithoutWater"].Bool();
VLC->generaltexth->registerString(scope, hero->getNameTextID(), node["texts"]["name"].String()); VLC->generaltexth->registerString(scope, hero->getNameTextID(), node["texts"]["name"]);
VLC->generaltexth->registerString(scope, hero->getBiographyTextID(), node["texts"]["biography"].String()); VLC->generaltexth->registerString(scope, hero->getBiographyTextID(), node["texts"]["biography"]);
VLC->generaltexth->registerString(scope, hero->getSpecialtyNameTextID(), node["texts"]["specialty"]["name"].String()); VLC->generaltexth->registerString(scope, hero->getSpecialtyNameTextID(), node["texts"]["specialty"]["name"]);
VLC->generaltexth->registerString(scope, hero->getSpecialtyTooltipTextID(), node["texts"]["specialty"]["tooltip"].String()); VLC->generaltexth->registerString(scope, hero->getSpecialtyTooltipTextID(), node["texts"]["specialty"]["tooltip"]);
VLC->generaltexth->registerString(scope, hero->getSpecialtyDescriptionTextID(), node["texts"]["specialty"]["description"].String()); VLC->generaltexth->registerString(scope, hero->getSpecialtyDescriptionTextID(), node["texts"]["specialty"]["description"]);
hero->iconSpecSmall = node["images"]["specialtySmall"].String(); hero->iconSpecSmall = node["images"]["specialtySmall"].String();
hero->iconSpecLarge = node["images"]["specialtyLarge"].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(); 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()) switch(json["gainChance"].getType())
{ {
case JsonNode::JsonType::DATA_INTEGER: case JsonNode::JsonType::DATA_INTEGER:
@ -237,7 +237,7 @@ std::shared_ptr<CSkill> CSkillHandler::loadFromJson(const std::string & scope, c
skill->addNewBonus(bonus, level); skill->addNewBonus(bonus, level);
} }
CSkill::LevelInfo & skillAtLevel = skill->at(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.iconSmall = levelNode["images"]["small"].String();
skillAtLevel.iconMedium = levelNode["images"]["medium"].String(); skillAtLevel.iconMedium = levelNode["images"]["medium"].String();
skillAtLevel.iconLarge = levelNode["images"]["large"].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.which = BattleSetStackProperty::CASTS;
ssp.val = -spellCost; ssp.val = -spellCost;
ssp.absolute = false; ssp.absolute = false;
server->apply(&ssp); server->apply(ssp);
} }
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -101,6 +101,11 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties =
{EGameSettings::TEXTS_TERRAIN, "textData", "terrain" }, {EGameSettings::TEXTS_TERRAIN, "textData", "terrain" },
{EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP, "towns", "buildingsPerTurnCap" }, {EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP, "towns", "buildingsPerTurnCap" },
{EGameSettings::TOWNS_STARTING_DWELLING_CHANCES, "towns", "startingDwellingChances" }, {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) void GameSettings::loadBase(const JsonNode & input)

View File

@ -94,6 +94,7 @@ public:
virtual void showInfoDialog(InfoWindow * iw) = 0; virtual void showInfoDialog(InfoWindow * iw) = 0;
virtual void changeSpells(const CGHeroInstance * hero, bool give, const std::set<SpellID> &spells)=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 bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) = 0;
virtual void createBoat(const int3 & visitablePosition, BoatId type, PlayerColor initiator) = 0; virtual void createBoat(const int3 & visitablePosition, BoatId type, PlayerColor initiator) = 0;
virtual void setOwner(const CGObjectInstance * objid, PlayerColor owner)=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 setManaPoints(ObjectInstanceID hid, int val)=0;
virtual void giveHero(ObjectInstanceID id, PlayerColor player, ObjectInstanceID boatId = ObjectInstanceID()) = 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 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 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(int3 center, ui32 radius, PlayerColor player, ETileVisibility mode) = 0;
virtual void changeFogOfWar(const std::unordered_set<int3> &tiles, 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, TEXTS_TERRAIN,
TOWNS_BUILDINGS_PER_TURN_CAP, TOWNS_BUILDINGS_PER_TURN_CAP,
TOWNS_STARTING_DWELLING_CHANCES, 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_COUNT,
OPTIONS_BEGIN = BONUSES_GLOBAL OPTIONS_BEGIN = BONUSES_GLOBAL

View File

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

View File

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

View File

@ -45,7 +45,7 @@ std::shared_ptr<TerrainType> TerrainTypeHandler::loadFromJson( const std::string
info->transitionRequired = json["transitionRequired"].Bool(); info->transitionRequired = json["transitionRequired"].Bool();
info->terrainViewPatterns = json["terrainViewPatterns"].String(); 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(); const JsonVector & unblockedVec = json["minimapUnblocked"].Vector();
info->minimapUnblocked = info->minimapUnblocked =

View File

@ -927,7 +927,7 @@ bool CBattleInfoCallback::handleObstacleTriggersForUnit(SpellCastEnvironment & s
bocp.battleID = getBattle()->getBattleID(); bocp.battleID = getBattle()->getBattleID();
bocp.changes.emplace_back(spellObstacle.uniqueID, operation); bocp.changes.emplace_back(spellObstacle.uniqueID, operation);
changedObstacle.toInfo(bocp.changes.back(), operation); changedObstacle.toInfo(bocp.changes.back(), operation);
spellEnv.apply(&bocp); spellEnv.apply(bocp);
}; };
const auto side = unit.unitSide(); const auto side = unit.unitSide();
auto shouldReveal = !spellObstacle->hidden || !battleIsObstacleVisibleForSide(*obstacle, side); 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->modScope = source.getModScope();
ret->town = town; ret->town = town;
VLC->generaltexth->registerString(source.getModScope(), ret->getNameTextID(), source["name"].String()); VLC->generaltexth->registerString(source.getModScope(), ret->getNameTextID(), source["name"]);
VLC->generaltexth->registerString(source.getModScope(), ret->getDescriptionTextID(), source["description"].String()); VLC->generaltexth->registerString(source.getModScope(), ret->getDescriptionTextID(), source["description"]);
ret->subId = vstd::find_or(MappedKeys::SPECIAL_BUILDINGS, source["type"].String(), BuildingSubID::NONE); ret->subId = vstd::find_or(MappedKeys::SPECIAL_BUILDINGS, source["type"].String(), BuildingSubID::NONE);
ret->resources = TResources(source["cost"]); ret->resources = TResources(source["cost"]);
@ -603,7 +603,7 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source)
town->namesCount = 0; town->namesCount = 0;
for(const auto & name : source["names"].Vector()) 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; town->namesCount += 1;
} }
@ -718,8 +718,8 @@ std::shared_ptr<CFaction> CTownHandler::loadFromJson(const std::string & scope,
faction->modScope = scope; faction->modScope = scope;
faction->identifier = identifier; faction->identifier = identifier;
VLC->generaltexth->registerString(scope, faction->getNameTextID(), source["name"].String()); VLC->generaltexth->registerString(scope, faction->getNameTextID(), source["name"]);
VLC->generaltexth->registerString(scope, faction->getDescriptionTextID(), source["description"].String()); VLC->generaltexth->registerString(scope, faction->getDescriptionTextID(), source["description"]);
faction->creatureBg120 = ImagePath::fromJson(source["creatureBackground"]["120px"]); faction->creatureBg120 = ImagePath::fromJson(source["creatureBackground"]["120px"]);
faction->creatureBg130 = ImagePath::fromJson(source["creatureBackground"]["130px"]); faction->creatureBg130 = ImagePath::fromJson(source["creatureBackground"]["130px"]);

View File

@ -1143,9 +1143,9 @@ PlayerRelations CGameState::getPlayerRelations( PlayerColor color1, PlayerColor
return PlayerRelations::ENEMIES; 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) 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 /// picks next free hero type of the H3 hero init sequence -> chosen starting hero, then unused hero type randomly
HeroTypeID pickNextHeroType(const PlayerColor & owner); HeroTypeID pickNextHeroType(const PlayerColor & owner);
void apply(CPackForClient *pack); void apply(CPackForClient & pack);
BattleField battleGetBattlefieldType(int3 tile, vstd::RNG & rand); BattleField battleGetBattlefieldType(int3 tile, vstd::RNG & rand);
void fillUpgradeInfo(const CArmedInstance *obj, SlotID stackPos, UpgradeInfo &out) const override; 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); 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) JsonNode JsonUtils::assembleFromFiles(const std::vector<std::string> & files)
{ {
bool isValid = false; bool isValid = false;
@ -275,4 +296,28 @@ JsonNode JsonUtils::assembleFromFiles(const std::string & filename)
return result; 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 VCMI_LIB_NAMESPACE_END

View File

@ -44,6 +44,8 @@ namespace JsonUtils
* @brief generate one Json structure from multiple files * @brief generate one Json structure from multiple files
* @param files - list of filenames with parts of json structure * @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);
DLL_LINKAGE JsonNode assembleFromFiles(const std::vector<std::string> & files, bool & isValid); 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> /// 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 /// example: schema "vcmi:settings" is used to check user settings
DLL_LINKAGE const JsonNode & getSchema(const std::string & URI); 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 VCMI_LIB_NAMESPACE_END

View File

@ -28,7 +28,7 @@ void CBankInstanceConstructor::initTypeData(const JsonNode & input)
if (input.Struct().count("name") == 0) if (input.Struct().count("name") == 0)
logMod->warn("Bank %s missing name!", getJsonKey()); 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(); levels = input["levels"].Vector();
bankResetDuration = static_cast<si32>(input["resetDuration"].Float()); 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->base = json["base"];
newObject->id = index; 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); newObject->objectTypeHandlers.resize(json["lastReservedIndex"].Float() + 1);

View File

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

View File

@ -29,7 +29,7 @@ void DwellingInstanceConstructor::initTypeData(const JsonNode & input)
if (input.Struct().count("name") == 0) if (input.Struct().count("name") == 0)
logMod->warn("Dwelling %s missing name!", getJsonKey()); 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 JsonVector & levels = input["creatures"].Vector();
const auto totalLevels = levels.size(); const auto totalLevels = levels.size();

View File

@ -138,7 +138,7 @@ bool CBank::wasVisited (PlayerColor player) const
void CBank::onHeroVisit(const CGHeroInstance * h) const void CBank::onHeroVisit(const CGHeroInstance * h) const
{ {
ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_PLAYER, id, h->id); ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_PLAYER, id, h->id);
cb->sendAndApply(&cov); cb->sendAndApply(cov);
BlockingDialog bd(true, false); BlockingDialog bd(true, false);
bd.player = h->getOwner(); 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 CGCreature::getPopupText(const CGHeroInstance * hero) const
{ {
std::string hoverName; std::string hoverName;
@ -102,15 +114,13 @@ std::string CGCreature::getPopupText(const CGHeroInstance * hero) const
if (settings["general"]["enableUiEnhancements"].Bool()) if (settings["general"]["enableUiEnhancements"].Bool())
{ {
std::string monsterLevel = VLC->generaltexth->translate("vcmi.adventureMap.monsterLevel"); hoverName += getMonsterLevelText();
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 += VLC->generaltexth->translate("vcmi.adventureMap.monsterThreat.title"); hoverName += VLC->generaltexth->translate("vcmi.adventureMap.monsterThreat.title");
int choice; 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; if (ratio < 0.1) choice = 0;
else if (ratio < 0.25) choice = 1; else if (ratio < 0.25) choice = 1;
else if (ratio < 0.6) choice = 2; 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 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 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) 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; void giveReward(const CGHeroInstance * h) const;
std::string getMonsterLevelText() const;
}; };
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

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

View File

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

View File

@ -268,7 +268,10 @@ CGTownInstance::CGTownInstance(IGameCallback *cb):
built(0), built(0),
destroyed(0), destroyed(0),
identifier(0), identifier(0),
alignmentToPlayer(PlayerColor::NEUTRAL) alignmentToPlayer(PlayerColor::NEUTRAL),
spellResearchCounterDay(0),
spellResearchAcceptedCounter(0),
spellResearchAllowed(true)
{ {
this->setNodeType(CBonusSystemNode::TOWN); this->setNodeType(CBonusSystemNode::TOWN);
} }
@ -347,7 +350,7 @@ void CGTownInstance::onHeroVisit(const CGHeroInstance * h) const
scp.heroid = h->id; scp.heroid = h->id;
scp.which = SetCommanderProperty::ALIVE; scp.which = SetCommanderProperty::ALIVE;
scp.amount = 1; scp.amount = 1;
cb->sendAndApply(&scp); cb->sendAndApply(scp);
} }
cb->heroVisitCastle(this, h); cb->heroVisitCastle(this, h);
// TODO(vmarkovtsev): implement payment for rising the commander // TODO(vmarkovtsev): implement payment for rising the commander
@ -628,7 +631,7 @@ void CGTownInstance::removeCapitols(const PlayerColor & owner) const
rs.tid = id; rs.tid = id;
rs.bid.insert(BuildingID::CAPITOL); rs.bid.insert(BuildingID::CAPITOL);
rs.destroyed = destroyed; rs.destroyed = destroyed;
cb->sendAndApply(&rs); cb->sendAndApply(rs);
return; 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<std::vector<SpellID> > spells; //spells[level] -> vector of spells, first will be available in guild
std::vector<CCastleEvent> events; std::vector<CCastleEvent> events;
std::pair<si32, si32> bonusValue;//var to store town bonuses (rampart = resources from mystic pond, factory = save debts); 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) template <typename Handler> void serialize(Handler &h)
@ -93,6 +96,13 @@ public:
h & spells; h & spells;
h & events; h & events;
if (h.version >= Handler::Version::SPELL_RESEARCH)
{
h & spellResearchCounterDay;
h & spellResearchAcceptedCounter;
h & spellResearchAllowed;
}
if (h.version >= Handler::Version::NEW_TOWN_BUILDINGS) if (h.version >= Handler::Version::NEW_TOWN_BUILDINGS)
{ {
h & rewardableBuildings; h & rewardableBuildings;

View File

@ -588,7 +588,7 @@ void CGSeerHut::onHeroVisit(const CGHeroInstance * h) const
AddQuest aq; AddQuest aq;
aq.quest = QuestInfo (quest, this, visitablePos()); aq.quest = QuestInfo (quest, this, visitablePos());
aq.player = h->tempOwner; aq.player = h->tempOwner;
cb->sendAndApply(&aq); //TODO: merge with setObjProperty? cb->sendAndApply(aq); //TODO: merge with setObjProperty?
} }
if(firstVisit || failRequirements) if(firstVisit || failRequirements)
@ -811,7 +811,7 @@ void CGKeymasterTent::onHeroVisit( const CGHeroInstance * h ) const
cow.mode = ChangeObjectVisitors::VISITOR_GLOBAL; cow.mode = ChangeObjectVisitors::VISITOR_GLOBAL;
cow.hero = h->id; cow.hero = h->id;
cow.object = id; cow.object = id;
cb->sendAndApply(&cow); cb->sendAndApply(cow);
txt_id=19; txt_id=19;
} }
else else
@ -860,7 +860,7 @@ void CGBorderGuard::onHeroVisit(const CGHeroInstance * h) const
AddQuest aq; AddQuest aq;
aq.quest = QuestInfo (quest, this, visitablePos()); aq.quest = QuestInfo (quest, this, visitablePos());
aq.player = h->tempOwner; aq.player = h->tempOwner;
cb->sendAndApply (&aq); cb->sendAndApply(aq);
//TODO: add this quest only once OR check for multiple instances later //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; AddQuest aq;
aq.quest = QuestInfo (quest, this, visitablePos()); aq.quest = QuestInfo (quest, this, visitablePos());
aq.player = h->tempOwner; 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 void CRewardableObject::markAsScouted(const CGHeroInstance * hero) const
{ {
ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_PLAYER, id, hero->id); ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_PLAYER, id, hero->id);
cb->sendAndApply(&cov); cb->sendAndApply(cov);
} }
bool CRewardableObject::isGuarded() const bool CRewardableObject::isGuarded() const
@ -48,7 +48,7 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *hero) const
if(!wasScouted(hero->getOwner())) if(!wasScouted(hero->getOwner()))
{ {
ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_SCOUTED, id, hero->id); ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_SCOUTED, id, hero->id);
cb->sendAndApply(&cov); cb->sendAndApply(cov);
} }
if (isGuarded()) if (isGuarded())
@ -116,7 +116,7 @@ void CRewardableObject::markAsVisited(const CGHeroInstance * hero) const
cb->setObjPropertyValue(id, ObjProperty::REWARD_CLEARED, true); cb->setObjPropertyValue(id, ObjProperty::REWARD_CLEARED, true);
ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_HERO, id, hero->id); ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_HERO, id, hero->id);
cb->sendAndApply(&cov); cb->sendAndApply(cov);
} }
void CRewardableObject::grantReward(ui32 rewardID, const CGHeroInstance * hero) const void CRewardableObject::grantReward(ui32 rewardID, const CGHeroInstance * hero) const
@ -329,14 +329,14 @@ void CRewardableObject::newTurn(vstd::RNG & rand) const
if (configuration.resetParameters.rewards) if (configuration.resetParameters.rewards)
{ {
auto handler = std::dynamic_pointer_cast<const CRewardableConstructor>(getObjectHandler()); 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); cb->setRewardableObjectConfiguration(id, newConfiguration);
} }
if (configuration.resetParameters.visitors) if (configuration.resetParameters.visitors)
{ {
cb->setObjPropertyValue(id, ObjProperty::REWARD_CLEARED, false); cb->setObjPropertyValue(id, ObjProperty::REWARD_CLEARED, false);
ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_CLEAR, id); 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.player = getOwner();
iw.type = mode; iw.type = mode;
iw.text.appendLocalString(EMetaText::ADVOB_TXT,txtID); iw.text.appendLocalString(EMetaText::ADVOB_TXT,txtID);
cb->sendAndApply(&iw); cb->sendAndApply(iw);
} }
///IObjectInterface ///IObjectInterface

View File

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

View File

@ -189,7 +189,7 @@ void CMapHeader::registerMapStrings()
JsonUtils::mergeCopy(data, translations[language]); JsonUtils::mergeCopy(data, translations[language]);
for(auto & s : data.Struct()) 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) 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) 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; mapHeader.translations.Struct()[language].Struct()[UID.get()].String() = localized;
return UID.get(); return UID.get();
} }

View File

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

View File

@ -17,6 +17,7 @@
#include "ModIncompatibility.h" #include "ModIncompatibility.h"
#include "../CCreatureHandler.h" #include "../CCreatureHandler.h"
#include "../CConfigHandler.h"
#include "../CStopWatch.h" #include "../CStopWatch.h"
#include "../GameSettings.h" #include "../GameSettings.h"
#include "../ScriptHandler.h" #include "../ScriptHandler.h"
@ -331,10 +332,38 @@ void CModHandler::loadModFilesystems()
coreMod->updateChecksum(calculateModChecksum(ModScope::scopeBuiltin(), CResourceHandler::get(ModScope::scopeBuiltin()))); coreMod->updateChecksum(calculateModChecksum(ModScope::scopeBuiltin(), CResourceHandler::get(ModScope::scopeBuiltin())));
std::map<std::string, ISimpleResourceLoader *> modFilesystems;
for(std::string & modName : activeMods) 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]; for(std::string & leftModName : activeMods)
CResourceHandler::addFilesystem("data", modName, genModFilesystem(modName, mod.config)); {
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; 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 std::set<TModID> CModHandler::getModDependencies(const TModID & modId, bool & isModFound) const
{ {
auto it = allMods.find(modId); auto it = allMods.find(modId);
@ -384,7 +419,7 @@ std::set<TModID> CModHandler::getModDependencies(const TModID & modId, bool & is
void CModHandler::initializeConfig() void CModHandler::initializeConfig()
{ {
VLC->settingsHandler->loadBase(coreMod->config["settings"]); VLC->settingsHandler->loadBase(JsonUtils::assembleFromFiles(coreMod->config["settings"]));
for(const TModID & modName : activeMods) for(const TModID & modName : activeMods)
{ {
@ -401,33 +436,6 @@ CModVersion CModHandler::getModVersion(TModID modName) const
return {}; 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) void CModHandler::loadTranslation(const TModID & modName)
{ {
const auto & mod = allMods[modName]; const auto & mod = allMods[modName];
@ -435,14 +443,11 @@ void CModHandler::loadTranslation(const TModID & modName)
std::string preferredLanguage = VLC->generaltexth->getPreferredLanguage(); std::string preferredLanguage = VLC->generaltexth->getPreferredLanguage();
std::string modBaseLanguage = allMods[modName].baseLanguage; std::string modBaseLanguage = allMods[modName].baseLanguage;
auto baseTranslationList = mod.config["translations"].convertTo<std::vector<std::string> >(); JsonNode baseTranslation = JsonUtils::assembleFromFiles(mod.config["translations"]);
auto extraTranslationList = mod.config[preferredLanguage]["translations"].convertTo<std::vector<std::string> >(); JsonNode extraTranslation = JsonUtils::assembleFromFiles(mod.config[preferredLanguage]["translations"]);
JsonNode baseTranslation = JsonUtils::assembleFromFiles(baseTranslationList); VLC->generaltexth->loadTranslationOverrides(modName, baseTranslation);
JsonNode extraTranslation = JsonUtils::assembleFromFiles(extraTranslationList); VLC->generaltexth->loadTranslationOverrides(modName, extraTranslation);
VLC->generaltexth->loadTranslationOverrides(modBaseLanguage, modName, baseTranslation);
VLC->generaltexth->loadTranslationOverrides(preferredLanguage, modName, extraTranslation);
} }
void CModHandler::load() void CModHandler::load()
@ -480,12 +485,6 @@ void CModHandler::load()
for(const TModID & modName : activeMods) for(const TModID & modName : activeMods)
loadTranslation(modName); 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()); logMod->info("\tLoading mod data: %d ms", timer.getDiff());
VLC->creh->loadCrExpMod(); VLC->creh->loadCrExpMod();
VLC->identifiersHandler->finalize(); 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 loadOneMod(std::string modName, const std::string & parent, const JsonNode & modSettings, bool enableMods);
void loadTranslation(const TModID & modName); void loadTranslation(const TModID & modName);
bool validateTranslations(TModID modName) const;
CModVersion getModVersion(TModID modName) const; CModVersion getModVersion(TModID modName) const;
public: public:
@ -66,6 +64,7 @@ public:
std::string getModLanguage(const TModID & modId) const; 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; std::set<TModID> getModDependencies(const TModID & modId, bool & isModFound) const;
/// returns list of all (active) mods /// returns list of all (active) mods

View File

@ -17,6 +17,7 @@
#include "../BattleFieldHandler.h" #include "../BattleFieldHandler.h"
#include "../CArtHandler.h" #include "../CArtHandler.h"
#include "../CCreatureHandler.h" #include "../CCreatureHandler.h"
#include "../CConfigHandler.h"
#include "../entities/faction/CTownHandler.h" #include "../entities/faction/CTownHandler.h"
#include "../texts/CGeneralTextHandler.h" #include "../texts/CGeneralTextHandler.h"
#include "../CHeroHandler.h" #include "../CHeroHandler.h"
@ -39,9 +40,9 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, const std::string & objectName): ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, const std::string & entityName):
handler(handler), handler(handler),
objectName(objectName), entityName(entityName),
originalData(handler->loadLegacyData()) originalData(handler->loadLegacyData())
{ {
for(auto & node : originalData) 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; bool result = false;
JsonNode data = JsonUtils::assembleFromFiles(fileList, result); 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); logMod->trace("Patching object %s (%s) from %s", objectName, remoteName, modName);
JsonNode & remoteConf = modData[remoteName].patches[objectName]; 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); 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){ auto performValidate = [&,this](JsonNode & data, const std::string & name){
handler->beforeValidate(data); handler->beforeValidate(data);
if (validate) if (validate)
result &= JsonUtils::validate(data, "vcmi:" + objectName, name); result &= JsonUtils::validate(data, "vcmi:" + entityName, name);
}; };
// apply patches // 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 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 // - 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 // 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; continue;
} }
@ -158,6 +162,8 @@ void ContentTypeHandler::loadCustom()
} }
void ContentTypeHandler::afterLoadFinalization() void ContentTypeHandler::afterLoadFinalization()
{
if (settings["mods"]["validation"].String() != "off")
{ {
for (auto const & data : modData) for (auto const & data : modData)
{ {
@ -186,6 +192,43 @@ void ContentTypeHandler::afterLoadFinalization()
} }
} }
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(); handler->afterLoadFinalization();
} }
@ -216,7 +259,7 @@ bool CContentHandler::preloadModData(const std::string & modName, JsonNode modCo
bool result = true; bool result = true;
for(auto & handler : handlers) 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; return result;
} }
@ -249,7 +292,7 @@ void CContentHandler::afterLoadFinalization()
void CContentHandler::preloadData(CModInfo & mod) void CContentHandler::preloadData(CModInfo & mod)
{ {
bool validate = (mod.validation != CModInfo::PASSED); bool validate = validateMod(mod);
// print message in format [<8-symbols checksum>] <modname> // print message in format [<8-symbols checksum>] <modname>
auto & info = mod.getVerificationInfo(); auto & info = mod.getVerificationInfo();
@ -266,7 +309,7 @@ void CContentHandler::preloadData(CModInfo & mod)
void CContentHandler::load(CModInfo & mod) void CContentHandler::load(CModInfo & mod)
{ {
bool validate = (mod.validation != CModInfo::PASSED); bool validate = validateMod(mod);
if (!loadMod(mod.identifier, validate)) if (!loadMod(mod.identifier, validate))
mod.validation = CModInfo::FAILED; mod.validation = CModInfo::FAILED;
@ -287,4 +330,18 @@ const ContentTypeHandler & CContentHandler::operator[](const std::string & name)
return handlers.at(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 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) /// internal type to handle loading of one data type (e.g. artifacts, creatures)
class DLL_LINKAGE ContentTypeHandler class DLL_LINKAGE ContentTypeHandler
{ {
JsonNode conflictList;
public: public:
struct ModInfo struct ModInfo
{ {
@ -29,7 +31,7 @@ public:
}; };
/// handler to which all data will be loaded /// handler to which all data will be loaded
IHandlerBase * handler; IHandlerBase * handler;
std::string objectName; std::string entityName;
/// contains all loaded H3 data /// contains all loaded H3 data
std::vector<JsonNode> originalData; std::vector<JsonNode> originalData;
@ -39,7 +41,7 @@ public:
/// local version of methods in ContentHandler /// local version of methods in ContentHandler
/// returns true if loading was successful /// 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); bool loadMod(const std::string & modName, bool validate);
void loadCustom(); void loadCustom();
void afterLoadFinalization(); void afterLoadFinalization();
@ -56,6 +58,7 @@ class DLL_LINKAGE CContentHandler
std::map<std::string, ContentTypeHandler> handlers; std::map<std::string, ContentTypeHandler> handlers;
bool validateMod(const CModInfo & mod) const;
public: public:
void init(); void init();

View File

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

View File

@ -162,6 +162,10 @@ void ChangeSpells::visitTyped(ICPackVisitor & visitor)
visitor.visitChangeSpells(*this); visitor.visitChangeSpells(*this);
} }
void SetResearchedSpells::visitTyped(ICPackVisitor & visitor)
{
visitor.visitSetResearchedSpells(*this);
}
void SetMana::visitTyped(ICPackVisitor & visitor) void SetMana::visitTyped(ICPackVisitor & visitor)
{ {
visitor.visitSetMana(*this); visitor.visitSetMana(*this);
@ -592,6 +596,11 @@ void RazeStructure::visitTyped(ICPackVisitor & visitor)
visitor.visitRazeStructure(*this); visitor.visitRazeStructure(*this);
} }
void SpellResearch::visitTyped(ICPackVisitor & visitor)
{
visitor.visitSpellResearch(*this);
}
void RecruitCreatures::visitTyped(ICPackVisitor & visitor) void RecruitCreatures::visitTyped(ICPackVisitor & visitor)
{ {
visitor.visitRecruitCreatures(*this); visitor.visitRecruitCreatures(*this);
@ -930,6 +939,16 @@ void ChangeSpells::applyGs(CGameState *gs)
hero->removeSpellFromSpellbook(sid); 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) void SetMana::applyGs(CGameState *gs)
{ {
CGHeroInstance * hero = gs->getHero(hid); CGHeroInstance * hero = gs->getHero(hid);
@ -1715,7 +1734,7 @@ void BulkEraseArtifacts::applyGs(CGameState *gs)
for(auto & slotInfoWorn : artSet->artifactsWorn) for(auto & slotInfoWorn : artSet->artifactsWorn)
{ {
auto art = slotInfoWorn.second.artifact; 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); dis.al.slot = artSet->getArtPos(art);
break; break;
@ -1914,7 +1933,10 @@ void NewTurn::applyGs(CGameState *gs)
creatureSet.applyGs(gs); creatureSet.applyGs(gs);
for(CGTownInstance* t : gs->map->towns) for(CGTownInstance* t : gs->map->towns)
{
t->built = 0; t->built = 0;
t->spellResearchCounterDay = 0;
}
if(newRumor) if(newRumor)
gs->currentRumor = *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 struct DLL_LINKAGE SetMana : public CPackForClient
{ {
void applyGs(CGameState * gs) override; void applyGs(CGameState * gs) override;

View File

@ -306,6 +306,28 @@ struct DLL_LINKAGE RazeStructure : public BuildStructure
void visitTyped(ICPackVisitor & visitor) override; 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 struct DLL_LINKAGE RecruitCreatures : public CPackForServer
{ {
RecruitCreatures() = default; 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){ auto loadString = [&](const JsonNode & entry, const TextIdentifier & textID){
if (entry.isString() && !entry.String().empty() && entry.String()[0] != '@') 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; parameters = objectConfig;

View File

@ -68,7 +68,7 @@ CConnection::CConnection(std::weak_ptr<INetworkConnection> networkConnection)
CConnection::~CConnection() = default; CConnection::~CConnection() = default;
void CConnection::sendPack(const CPack * pack) void CConnection::sendPack(const CPack & pack)
{ {
boost::mutex::scoped_lock lock(writeMutex); 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!"); throw std::runtime_error("Attempt to send packet on a closed connection!");
packWriter->buffer.clear(); 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); connectionPtr->sendPacket(packWriter->buffer);
packWriter->buffer.clear(); packWriter->buffer.clear();
serializer->savedPointers.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->buffer = &data;
packReader->position = 0; packReader->position = 0;
@ -102,7 +102,7 @@ CPack * CConnection::retrievePack(const std::vector<std::byte> & data)
if (packReader->position != data.size()) if (packReader->position != data.size())
throw std::runtime_error("Failed to retrieve pack! Not all data has been read!"); 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->loadedPointers.clear();
deserializer->loadedSharedPointers.clear(); deserializer->loadedSharedPointers.clear();
return result; return result;

View File

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

View File

@ -61,6 +61,7 @@ enum class ESerializationVersion : int32_t
CAMPAIGN_OUTRO_SUPPORT, // 862 - support for campaign outro video CAMPAIGN_OUTRO_SUPPORT, // 862 - support for campaign outro video
REWARDABLE_BANKS, // 863 - team state contains list of scouted objects, coast visitable rewardable objects REWARDABLE_BANKS, // 863 - team state contains list of scouted objects, coast visitable rewardable objects
REGION_LABEL, // 864 - labels for campaign regions 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<LobbySetDifficulty>(238);
s.template registerType<LobbyForceSetPlayer>(239); s.template registerType<LobbyForceSetPlayer>(239);
s.template registerType<LobbySetExtraOptions>(240); s.template registerType<LobbySetExtraOptions>(240);
s.template registerType<SpellResearch>(241);
s.template registerType<SetResearchedSpells>(242);
} }
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

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

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