mirror of
https://github.com/vcmi/vcmi.git
synced 2025-03-17 20:58:07 +02:00
Merge remote-tracking branch 'upstream/develop' into develop
This commit is contained in:
commit
9a40577994
@ -394,7 +394,7 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector
|
||||
{
|
||||
std::set<BattleHex> obstacleHexes;
|
||||
|
||||
auto insertAffected = [](const CObstacleInstance & spellObst, std::set<BattleHex> obstacleHexes) {
|
||||
auto insertAffected = [](const CObstacleInstance & spellObst, std::set<BattleHex> & obstacleHexes) {
|
||||
auto affectedHexes = spellObst.getAffectedTiles();
|
||||
obstacleHexes.insert(affectedHexes.cbegin(), affectedHexes.cend());
|
||||
};
|
||||
|
@ -531,44 +531,44 @@ vstd::RNG * HypotheticBattle::HypotheticServerCallback::getRNG()
|
||||
return &rngStub;
|
||||
}
|
||||
|
||||
void HypotheticBattle::HypotheticServerCallback::apply(CPackForClient * pack)
|
||||
void HypotheticBattle::HypotheticServerCallback::apply(CPackForClient & pack)
|
||||
{
|
||||
logAi->error("Package of type %s is not allowed in battle evaluation", typeid(pack).name());
|
||||
}
|
||||
|
||||
void HypotheticBattle::HypotheticServerCallback::apply(BattleLogMessage * pack)
|
||||
void HypotheticBattle::HypotheticServerCallback::apply(BattleLogMessage & pack)
|
||||
{
|
||||
pack->applyBattle(owner);
|
||||
pack.applyBattle(owner);
|
||||
}
|
||||
|
||||
void HypotheticBattle::HypotheticServerCallback::apply(BattleStackMoved * pack)
|
||||
void HypotheticBattle::HypotheticServerCallback::apply(BattleStackMoved & pack)
|
||||
{
|
||||
pack->applyBattle(owner);
|
||||
pack.applyBattle(owner);
|
||||
}
|
||||
|
||||
void HypotheticBattle::HypotheticServerCallback::apply(BattleUnitsChanged * pack)
|
||||
void HypotheticBattle::HypotheticServerCallback::apply(BattleUnitsChanged & pack)
|
||||
{
|
||||
pack->applyBattle(owner);
|
||||
pack.applyBattle(owner);
|
||||
}
|
||||
|
||||
void HypotheticBattle::HypotheticServerCallback::apply(SetStackEffect * pack)
|
||||
void HypotheticBattle::HypotheticServerCallback::apply(SetStackEffect & pack)
|
||||
{
|
||||
pack->applyBattle(owner);
|
||||
pack.applyBattle(owner);
|
||||
}
|
||||
|
||||
void HypotheticBattle::HypotheticServerCallback::apply(StacksInjured * pack)
|
||||
void HypotheticBattle::HypotheticServerCallback::apply(StacksInjured & pack)
|
||||
{
|
||||
pack->applyBattle(owner);
|
||||
pack.applyBattle(owner);
|
||||
}
|
||||
|
||||
void HypotheticBattle::HypotheticServerCallback::apply(BattleObstaclesChanged * pack)
|
||||
void HypotheticBattle::HypotheticServerCallback::apply(BattleObstaclesChanged & pack)
|
||||
{
|
||||
pack->applyBattle(owner);
|
||||
pack.applyBattle(owner);
|
||||
}
|
||||
|
||||
void HypotheticBattle::HypotheticServerCallback::apply(CatapultAttack * pack)
|
||||
void HypotheticBattle::HypotheticServerCallback::apply(CatapultAttack & pack)
|
||||
{
|
||||
pack->applyBattle(owner);
|
||||
pack.applyBattle(owner);
|
||||
}
|
||||
|
||||
HypotheticBattle::HypotheticEnvironment::HypotheticEnvironment(HypotheticBattle * owner_, const Environment * upperEnvironment)
|
||||
|
@ -189,15 +189,15 @@ private:
|
||||
|
||||
vstd::RNG * getRNG() override;
|
||||
|
||||
void apply(CPackForClient * pack) override;
|
||||
void apply(CPackForClient & pack) override;
|
||||
|
||||
void apply(BattleLogMessage * pack) override;
|
||||
void apply(BattleStackMoved * pack) override;
|
||||
void apply(BattleUnitsChanged * pack) override;
|
||||
void apply(SetStackEffect * pack) override;
|
||||
void apply(StacksInjured * pack) override;
|
||||
void apply(BattleObstaclesChanged * pack) override;
|
||||
void apply(CatapultAttack * pack) override;
|
||||
void apply(BattleLogMessage & pack) override;
|
||||
void apply(BattleStackMoved & pack) override;
|
||||
void apply(BattleUnitsChanged & pack) override;
|
||||
void apply(SetStackEffect & pack) override;
|
||||
void apply(StacksInjured & pack) override;
|
||||
void apply(BattleObstaclesChanged & pack) override;
|
||||
void apply(CatapultAttack & pack) override;
|
||||
private:
|
||||
HypotheticBattle * owner;
|
||||
RNGStub rngStub;
|
||||
|
100
CCallback.cpp
100
CCallback.cpp
@ -29,20 +29,20 @@
|
||||
bool CCallback::teleportHero(const CGHeroInstance *who, const CGTownInstance *where)
|
||||
{
|
||||
CastleTeleportHero pack(who->id, where->id, 1);
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CCallback::moveHero(const CGHeroInstance *h, const int3 & destination, bool transit)
|
||||
{
|
||||
MoveHero pack({destination}, h->id, transit);
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
}
|
||||
|
||||
void CCallback::moveHero(const CGHeroInstance *h, const std::vector<int3> & path, bool transit)
|
||||
{
|
||||
MoveHero pack(path, h->id, transit);
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
}
|
||||
|
||||
int CCallback::selectionMade(int selection, QueryID queryID)
|
||||
@ -61,7 +61,7 @@ int CCallback::sendQueryReply(std::optional<int32_t> reply, QueryID queryID)
|
||||
|
||||
QueryReply pack(queryID, reply);
|
||||
pack.player = *player;
|
||||
return sendRequest(&pack);
|
||||
return sendRequest(pack);
|
||||
}
|
||||
|
||||
void CCallback::recruitCreatures(const CGDwelling * obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level)
|
||||
@ -71,7 +71,7 @@ void CCallback::recruitCreatures(const CGDwelling * obj, const CArmedInstance *
|
||||
return;
|
||||
|
||||
RecruitCreatures pack(obj->id, dst->id, ID, amount, level);
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
}
|
||||
|
||||
bool CCallback::dismissCreature(const CArmedInstance *obj, SlotID stackPos)
|
||||
@ -80,14 +80,14 @@ bool CCallback::dismissCreature(const CArmedInstance *obj, SlotID stackPos)
|
||||
return false;
|
||||
|
||||
DisbandCreature pack(stackPos,obj->id);
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCallback::upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID)
|
||||
{
|
||||
UpgradeCreature pack(stackPos,obj->id,newID);
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -95,54 +95,54 @@ void CCallback::endTurn()
|
||||
{
|
||||
logGlobal->trace("Player %d ended his turn.", player->getNum());
|
||||
EndTurn pack;
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
}
|
||||
int CCallback::swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
|
||||
{
|
||||
ArrangeStacks pack(1,p1,p2,s1->id,s2->id,0);
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CCallback::mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
|
||||
{
|
||||
ArrangeStacks pack(2,p1,p2,s1->id,s2->id,0);
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CCallback::splitStack(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2, int val)
|
||||
{
|
||||
ArrangeStacks pack(3,p1,p2,s1->id,s2->id,val);
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CCallback::bulkMoveArmy(ObjectInstanceID srcArmy, ObjectInstanceID destArmy, SlotID srcSlot)
|
||||
{
|
||||
BulkMoveArmy pack(srcArmy, destArmy, srcSlot);
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CCallback::bulkSplitStack(ObjectInstanceID armyId, SlotID srcSlot, int howMany)
|
||||
{
|
||||
BulkSplitStack pack(armyId, srcSlot, howMany);
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CCallback::bulkSmartSplitStack(ObjectInstanceID armyId, SlotID srcSlot)
|
||||
{
|
||||
BulkSmartSplitStack pack(armyId, srcSlot);
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CCallback::bulkMergeStacks(ObjectInstanceID armyId, SlotID srcSlot)
|
||||
{
|
||||
BulkMergeStacks pack(armyId, srcSlot);
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -151,7 +151,7 @@ bool CCallback::dismissHero(const CGHeroInstance *hero)
|
||||
if(player!=hero->tempOwner) return false;
|
||||
|
||||
DismissHero pack(hero->id);
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -160,7 +160,7 @@ bool CCallback::swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation
|
||||
ExchangeArtifacts ea;
|
||||
ea.src = l1;
|
||||
ea.dst = l2;
|
||||
sendRequest(&ea);
|
||||
sendRequest(ea);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -175,13 +175,13 @@ bool CCallback::swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation
|
||||
void CCallback::assembleArtifacts(const ObjectInstanceID & heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)
|
||||
{
|
||||
AssembleArtifacts aa(heroID, artifactSlot, assemble, assembleTo);
|
||||
sendRequest(&aa);
|
||||
sendRequest(aa);
|
||||
}
|
||||
|
||||
void CCallback::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped, bool backpack)
|
||||
{
|
||||
BulkExchangeArtifacts bma(srcHero, dstHero, swap, equipped, backpack);
|
||||
sendRequest(&bma);
|
||||
sendRequest(bma);
|
||||
}
|
||||
|
||||
void CCallback::scrollBackpackArtifacts(ObjectInstanceID hero, bool left)
|
||||
@ -189,19 +189,37 @@ void CCallback::scrollBackpackArtifacts(ObjectInstanceID hero, bool left)
|
||||
ManageBackpackArtifacts mba(hero, ManageBackpackArtifacts::ManageCmd::SCROLL_RIGHT);
|
||||
if(left)
|
||||
mba.cmd = ManageBackpackArtifacts::ManageCmd::SCROLL_LEFT;
|
||||
sendRequest(&mba);
|
||||
sendRequest(mba);
|
||||
}
|
||||
|
||||
void CCallback::sortBackpackArtifactsBySlot(const ObjectInstanceID hero)
|
||||
{
|
||||
ManageBackpackArtifacts mba(hero, ManageBackpackArtifacts::ManageCmd::SORT_BY_SLOT);
|
||||
sendRequest(mba);
|
||||
}
|
||||
|
||||
void CCallback::sortBackpackArtifactsByCost(const ObjectInstanceID hero)
|
||||
{
|
||||
ManageBackpackArtifacts mba(hero, ManageBackpackArtifacts::ManageCmd::SORT_BY_COST);
|
||||
sendRequest(mba);
|
||||
}
|
||||
|
||||
void CCallback::sortBackpackArtifactsByClass(const ObjectInstanceID hero)
|
||||
{
|
||||
ManageBackpackArtifacts mba(hero, ManageBackpackArtifacts::ManageCmd::SORT_BY_CLASS);
|
||||
sendRequest(mba);
|
||||
}
|
||||
|
||||
void CCallback::manageHeroCostume(ObjectInstanceID hero, size_t costumeIndex, bool saveCostume)
|
||||
{
|
||||
ManageEquippedArtifacts mea(hero, costumeIndex, saveCostume);
|
||||
sendRequest(&mea);
|
||||
sendRequest(mea);
|
||||
}
|
||||
|
||||
void CCallback::eraseArtifactByClient(const ArtifactLocation & al)
|
||||
{
|
||||
EraseArtifactByClient ea(al);
|
||||
sendRequest(&ea);
|
||||
sendRequest(ea);
|
||||
}
|
||||
|
||||
bool CCallback::buildBuilding(const CGTownInstance *town, BuildingID buildingID)
|
||||
@ -213,7 +231,7 @@ bool CCallback::buildBuilding(const CGTownInstance *town, BuildingID buildingID)
|
||||
return false;
|
||||
|
||||
BuildStructure pack(town->id,buildingID);
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -223,7 +241,7 @@ bool CCallback::visitTownBuilding(const CGTownInstance *town, BuildingID buildin
|
||||
return false;
|
||||
|
||||
VisitTownBuilding pack(town->id, buildingID);
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -232,10 +250,10 @@ void CBattleCallback::battleMakeSpellAction(const BattleID & battleID, const Bat
|
||||
assert(action.actionType == EActionType::HERO_SPELL);
|
||||
MakeAction mca(action);
|
||||
mca.battleID = battleID;
|
||||
sendRequest(&mca);
|
||||
sendRequest(mca);
|
||||
}
|
||||
|
||||
int CBattleCallback::sendRequest(const CPackForServer * request)
|
||||
int CBattleCallback::sendRequest(const CPackForServer & request)
|
||||
{
|
||||
int requestID = cl->sendRequest(request, *getPlayerID());
|
||||
if(waitTillRealize)
|
||||
@ -249,12 +267,18 @@ int CBattleCallback::sendRequest(const CPackForServer * request)
|
||||
return requestID;
|
||||
}
|
||||
|
||||
void CCallback::spellResearch( const CGTownInstance *town, SpellID spellAtSlot, bool accepted )
|
||||
{
|
||||
SpellResearch pack(town->id, spellAtSlot, accepted);
|
||||
sendRequest(pack);
|
||||
}
|
||||
|
||||
void CCallback::swapGarrisonHero( const CGTownInstance *town )
|
||||
{
|
||||
if(town->tempOwner == *player || (town->garrisonHero && town->garrisonHero->tempOwner == *player ))
|
||||
{
|
||||
GarrisonHeroSwap pack(town->id);
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
}
|
||||
}
|
||||
|
||||
@ -263,7 +287,7 @@ void CCallback::buyArtifact(const CGHeroInstance *hero, ArtifactID aid)
|
||||
if(hero->tempOwner != *player) return;
|
||||
|
||||
BuyArtifact pack(hero->id,aid);
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
}
|
||||
|
||||
void CCallback::trade(const ObjectInstanceID marketId, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero)
|
||||
@ -280,13 +304,13 @@ void CCallback::trade(const ObjectInstanceID marketId, EMarketMode mode, const s
|
||||
pack.r1 = id1;
|
||||
pack.r2 = id2;
|
||||
pack.val = val1;
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
}
|
||||
|
||||
void CCallback::setFormation(const CGHeroInstance * hero, EArmyFormation mode)
|
||||
{
|
||||
SetFormation pack(hero->id, mode);
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
}
|
||||
|
||||
void CCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero, const HeroTypeID & nextHero)
|
||||
@ -296,7 +320,7 @@ void CCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroIn
|
||||
|
||||
HireHero pack(hero->getHeroType(), townOrTavern->id, nextHero);
|
||||
pack.player = *player;
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
}
|
||||
|
||||
void CCallback::save( const std::string &fname )
|
||||
@ -310,7 +334,7 @@ void CCallback::gamePause(bool pause)
|
||||
{
|
||||
GamePause pack;
|
||||
pack.player = *player;
|
||||
sendRequest(&pack);
|
||||
sendRequest(pack);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -324,14 +348,14 @@ void CCallback::sendMessage(const std::string &mess, const CGObjectInstance * cu
|
||||
PlayerMessage pm(mess, currentObject? currentObject->id : ObjectInstanceID(-1));
|
||||
if(player)
|
||||
pm.player = *player;
|
||||
sendRequest(&pm);
|
||||
sendRequest(pm);
|
||||
}
|
||||
|
||||
void CCallback::buildBoat( const IShipyard *obj )
|
||||
{
|
||||
BuildBoat bb;
|
||||
bb.objid = dynamic_cast<const CGObjectInstance*>(obj)->id;
|
||||
sendRequest(&bb);
|
||||
sendRequest(bb);
|
||||
}
|
||||
|
||||
CCallback::CCallback(CGameState * GS, std::optional<PlayerColor> Player, CClient * C)
|
||||
@ -373,7 +397,7 @@ void CCallback::dig( const CGObjectInstance *hero )
|
||||
{
|
||||
DigWithHero dwh;
|
||||
dwh.id = hero->id;
|
||||
sendRequest(&dwh);
|
||||
sendRequest(dwh);
|
||||
}
|
||||
|
||||
void CCallback::castSpell(const CGHeroInstance *hero, SpellID spellID, const int3 &pos)
|
||||
@ -382,7 +406,7 @@ void CCallback::castSpell(const CGHeroInstance *hero, SpellID spellID, const int
|
||||
cas.hid = hero->id;
|
||||
cas.sid = spellID;
|
||||
cas.pos = pos;
|
||||
sendRequest(&cas);
|
||||
sendRequest(cas);
|
||||
}
|
||||
|
||||
int CCallback::mergeOrSwapStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
|
||||
@ -415,7 +439,7 @@ void CBattleCallback::battleMakeUnitAction(const BattleID & battleID, const Batt
|
||||
MakeAction ma;
|
||||
ma.ba = action;
|
||||
ma.battleID = battleID;
|
||||
sendRequest(&ma);
|
||||
sendRequest(ma);
|
||||
}
|
||||
|
||||
void CBattleCallback::battleMakeTacticAction(const BattleID & battleID, const BattleAction & action )
|
||||
@ -424,7 +448,7 @@ void CBattleCallback::battleMakeTacticAction(const BattleID & battleID, const Ba
|
||||
MakeAction ma;
|
||||
ma.ba = action;
|
||||
ma.battleID = battleID;
|
||||
sendRequest(&ma);
|
||||
sendRequest(ma);
|
||||
}
|
||||
|
||||
std::optional<BattleAction> CBattleCallback::makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState)
|
||||
|
10
CCallback.h
10
CCallback.h
@ -78,6 +78,7 @@ public:
|
||||
virtual bool visitTownBuilding(const CGTownInstance *town, BuildingID buildingID)=0;
|
||||
virtual void recruitCreatures(const CGDwelling *obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level=-1)=0;
|
||||
virtual bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE)=0; //if newID==-1 then best possible upgrade will be made
|
||||
virtual void spellResearch(const CGTownInstance *town, SpellID spellAtSlot, bool accepted)=0;
|
||||
virtual void swapGarrisonHero(const CGTownInstance *town)=0;
|
||||
|
||||
virtual void trade(const ObjectInstanceID marketId, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero)=0; //mode==0: sell val1 units of id1 resource for id2 resiurce
|
||||
@ -92,6 +93,9 @@ public:
|
||||
//virtual bool swapArtifacts(const CGHeroInstance * hero1, ui16 pos1, const CGHeroInstance * hero2, ui16 pos2)=0; //swaps artifacts between two given heroes
|
||||
virtual bool swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation &l2)=0;
|
||||
virtual void scrollBackpackArtifacts(ObjectInstanceID hero, bool left) = 0;
|
||||
virtual void sortBackpackArtifactsBySlot(const ObjectInstanceID hero) = 0;
|
||||
virtual void sortBackpackArtifactsByCost(const ObjectInstanceID hero) = 0;
|
||||
virtual void sortBackpackArtifactsByClass(const ObjectInstanceID hero) = 0;
|
||||
virtual void manageHeroCostume(ObjectInstanceID hero, size_t costumeIndex, bool saveCostume) = 0;
|
||||
virtual void assembleArtifacts(const ObjectInstanceID & heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)=0;
|
||||
virtual void eraseArtifactByClient(const ArtifactLocation & al)=0;
|
||||
@ -123,7 +127,7 @@ class CBattleCallback : public IBattleCallback
|
||||
std::optional<PlayerColor> player;
|
||||
|
||||
protected:
|
||||
int sendRequest(const CPackForServer * request); //returns requestID (that'll be matched to requestID in PackageApplied)
|
||||
int sendRequest(const CPackForServer & request); //returns requestID (that'll be matched to requestID in PackageApplied)
|
||||
CClient *cl;
|
||||
|
||||
public:
|
||||
@ -179,6 +183,9 @@ public:
|
||||
void assembleArtifacts(const ObjectInstanceID & heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo) override;
|
||||
void bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped = true, bool backpack = true) override;
|
||||
void scrollBackpackArtifacts(ObjectInstanceID hero, bool left) override;
|
||||
void sortBackpackArtifactsBySlot(const ObjectInstanceID hero) override;
|
||||
void sortBackpackArtifactsByCost(const ObjectInstanceID hero) override;
|
||||
void sortBackpackArtifactsByClass(const ObjectInstanceID hero) override;
|
||||
void manageHeroCostume(ObjectInstanceID hero, size_t costumeIdx, bool saveCostume) override;
|
||||
void eraseArtifactByClient(const ArtifactLocation & al) override;
|
||||
bool buildBuilding(const CGTownInstance *town, BuildingID buildingID) override;
|
||||
@ -187,6 +194,7 @@ public:
|
||||
bool dismissCreature(const CArmedInstance *obj, SlotID stackPos) override;
|
||||
bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE) override;
|
||||
void endTurn() override;
|
||||
void spellResearch(const CGTownInstance *town, SpellID spellAtSlot, bool accepted) override;
|
||||
void swapGarrisonHero(const CGTownInstance *town) override;
|
||||
void buyArtifact(const CGHeroInstance *hero, ArtifactID aid) override;
|
||||
void trade(const ObjectInstanceID marketId, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero = nullptr) override;
|
||||
|
@ -486,11 +486,7 @@ if(NOT FORCE_BUNDLED_MINIZIP)
|
||||
endif()
|
||||
|
||||
if (ENABLE_CLIENT)
|
||||
set(FFMPEG_COMPONENTS avutil swscale avformat avcodec)
|
||||
if(APPLE_IOS AND NOT USING_CONAN)
|
||||
list(APPEND FFMPEG_COMPONENTS swresample)
|
||||
endif()
|
||||
find_package(ffmpeg COMPONENTS ${FFMPEG_COMPONENTS})
|
||||
find_package(ffmpeg COMPONENTS avutil swscale avformat avcodec swresample)
|
||||
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(SDL2_image REQUIRED)
|
||||
@ -666,6 +662,10 @@ if(NOT TARGET minizip::minizip)
|
||||
add_library(minizip::minizip ALIAS minizip)
|
||||
endif()
|
||||
|
||||
if(ENABLE_LAUNCHER OR ENABLE_EDITOR)
|
||||
add_subdirectory(vcmiqt)
|
||||
endif()
|
||||
|
||||
if(ENABLE_LAUNCHER)
|
||||
add_subdirectory(launcher)
|
||||
endif()
|
||||
|
BIN
Mods/vcmi/Data/spellResearch/accept.png
Normal file
BIN
Mods/vcmi/Data/spellResearch/accept.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 931 B |
BIN
Mods/vcmi/Data/spellResearch/close.png
Normal file
BIN
Mods/vcmi/Data/spellResearch/close.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 675 B |
BIN
Mods/vcmi/Data/spellResearch/reroll.png
Normal file
BIN
Mods/vcmi/Data/spellResearch/reroll.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
@ -12,7 +12,9 @@
|
||||
"vcmi.adventureMap.monsterThreat.levels.9" : "压倒性的",
|
||||
"vcmi.adventureMap.monsterThreat.levels.10" : "致命的",
|
||||
"vcmi.adventureMap.monsterThreat.levels.11" : "无法取胜",
|
||||
"vcmi.adventureMap.monsterLevel" : "\n\n%TOWN%LEVEL级生物",
|
||||
"vcmi.adventureMap.monsterLevel" : "\n\n%TOWN%LEVEL级%ATTACK_TYPE生物",
|
||||
"vcmi.adventureMap.monsterMeleeType" : "近战",
|
||||
"vcmi.adventureMap.monsterRangedType" : "远程",
|
||||
|
||||
"vcmi.adventureMap.confirmRestartGame" : "你想要重新开始游戏吗?",
|
||||
"vcmi.adventureMap.noTownWithMarket" : "没有足够的市场。",
|
||||
|
@ -12,7 +12,9 @@
|
||||
"vcmi.adventureMap.monsterThreat.levels.9" : "Overpowering",
|
||||
"vcmi.adventureMap.monsterThreat.levels.10" : "Deadly",
|
||||
"vcmi.adventureMap.monsterThreat.levels.11" : "Impossible",
|
||||
"vcmi.adventureMap.monsterLevel" : "\n\nLevel %LEVEL %TOWN unit",
|
||||
"vcmi.adventureMap.monsterLevel" : "\n\nLevel %LEVEL %TOWN %ATTACK_TYPE unit",
|
||||
"vcmi.adventureMap.monsterMeleeType" : "melee",
|
||||
"vcmi.adventureMap.monsterRangedType" : "ranged",
|
||||
|
||||
"vcmi.adventureMap.confirmRestartGame" : "Are you sure you want to restart the game?",
|
||||
"vcmi.adventureMap.noTownWithMarket" : "There are no available marketplaces!",
|
||||
@ -59,6 +61,13 @@
|
||||
|
||||
"vcmi.spellBook.search" : "search...",
|
||||
|
||||
"vcmi.spellResearch.canNotAfford" : "You can't afford to replace {%SPELL1} with {%SPELL2}. But you can still discard this spell and continue spell research.",
|
||||
"vcmi.spellResearch.comeAgain" : "Research has already been done today. Come back tomorrow.",
|
||||
"vcmi.spellResearch.pay" : "Would you like to replace {%SPELL1} with {%SPELL2}? Or discard this spell and continue spell research?",
|
||||
"vcmi.spellResearch.research" : "Research this Spell",
|
||||
"vcmi.spellResearch.skip" : "Skip this Spell",
|
||||
"vcmi.spellResearch.abort" : "Abort",
|
||||
|
||||
"vcmi.mainMenu.serverConnecting" : "Connecting...",
|
||||
"vcmi.mainMenu.serverAddressEnter" : "Enter address:",
|
||||
"vcmi.mainMenu.serverConnectionFailed" : "Failed to connect",
|
||||
@ -143,6 +152,7 @@
|
||||
"vcmi.client.errors.invalidMap" : "{Invalid map or campaign}\n\nFailed to start game! Selected map or campaign might be invalid or corrupted. Reason:\n%s",
|
||||
"vcmi.client.errors.missingCampaigns" : "{Missing data files}\n\nCampaigns data files were not found! You may be using incomplete or corrupted Heroes 3 data files. Please reinstall game data.",
|
||||
"vcmi.server.errors.disconnected" : "{Network Error}\n\nConnection to game server has been lost!",
|
||||
"vcmi.server.errors.playerLeft" : "{Player Left}\n\n%s player have disconnected from the game!", //%s -> player color
|
||||
"vcmi.server.errors.existingProcess" : "Another VCMI server process is running. Please terminate it before starting a new game.",
|
||||
"vcmi.server.errors.modsToEnable" : "{Following mods are required}",
|
||||
"vcmi.server.errors.modsToDisable" : "{Following mods must be disabled}",
|
||||
@ -340,6 +350,12 @@
|
||||
"vcmi.heroWindow.openCommander.help" : "Shows details about the commander of this hero.",
|
||||
"vcmi.heroWindow.openBackpack.hover" : "Open artifact backpack window",
|
||||
"vcmi.heroWindow.openBackpack.help" : "Opens window that allows easier artifact backpack management.",
|
||||
"vcmi.heroWindow.sortBackpackByCost.hover" : "Sort by cost",
|
||||
"vcmi.heroWindow.sortBackpackByCost.help" : "Sort artifacts in backpack by cost.",
|
||||
"vcmi.heroWindow.sortBackpackBySlot.hover" : "Sort by slot",
|
||||
"vcmi.heroWindow.sortBackpackBySlot.help" : "Sort artifacts in backpack by equipped slot.",
|
||||
"vcmi.heroWindow.sortBackpackByClass.hover" : "Sort by class",
|
||||
"vcmi.heroWindow.sortBackpackByClass.help" : "Sort artifacts in backpack by artifact class. Treasure, Minor, Major, Relic",
|
||||
|
||||
"vcmi.tavernWindow.inviteHero" : "Invite hero",
|
||||
|
||||
|
@ -12,7 +12,9 @@
|
||||
"vcmi.adventureMap.monsterThreat.levels.9" : "Überwältigend",
|
||||
"vcmi.adventureMap.monsterThreat.levels.10" : "Tödlich",
|
||||
"vcmi.adventureMap.monsterThreat.levels.11" : "Unmöglich",
|
||||
"vcmi.adventureMap.monsterLevel" : "\n\nStufe %LEVEL %TOWN-Einheit",
|
||||
"vcmi.adventureMap.monsterLevel" : "\n\nStufe %LEVEL %TOWN-Einheit (%ATTACK_TYPE)",
|
||||
"vcmi.adventureMap.monsterMeleeType" : "Nahkampf",
|
||||
"vcmi.adventureMap.monsterRangedType" : "Fernkampf",
|
||||
|
||||
"vcmi.adventureMap.confirmRestartGame" : "Seid Ihr sicher, dass Ihr das Spiel neu starten wollt?",
|
||||
"vcmi.adventureMap.noTownWithMarket" : "Kein Marktplatz verfügbar!",
|
||||
@ -58,6 +60,13 @@
|
||||
|
||||
"vcmi.spellBook.search" : "suchen...",
|
||||
|
||||
"vcmi.spellResearch.canNotAfford" : "Ihr könnt es Euch nicht leisten, {%SPELL1} durch {%SPELL2} zu ersetzen. Aber Ihr könnt diesen Zauberspruch trotzdem verwerfen und die Zauberspruchforschung fortsetzen.",
|
||||
"vcmi.spellResearch.comeAgain" : "Die Forschung wurde heute bereits abgeschlossen. Kommt morgen wieder.",
|
||||
"vcmi.spellResearch.pay" : "Möchtet Ihr {%SPELL1} durch {%SPELL2} ersetzen? Oder diesen Zauberspruch verwerfen und die Zauberspruchforschung fortsetzen?",
|
||||
"vcmi.spellResearch.research" : "Erforsche diesen Zauberspruch",
|
||||
"vcmi.spellResearch.skip" : "Überspringe diesen Zauberspruch",
|
||||
"vcmi.spellResearch.abort" : "Abbruch",
|
||||
|
||||
"vcmi.mainMenu.serverConnecting" : "Verbinde...",
|
||||
"vcmi.mainMenu.serverAddressEnter" : "Addresse eingeben:",
|
||||
"vcmi.mainMenu.serverConnectionFailed" : "Verbindung fehlgeschlagen",
|
||||
|
@ -141,7 +141,12 @@ void CServerHandler::resetStateForLobby(EStartMode mode, ESelectionScreen screen
|
||||
if(!playerNames.empty()) //if have custom set of player names - use it
|
||||
localPlayerNames = playerNames;
|
||||
else
|
||||
localPlayerNames.push_back(settings["general"]["playerName"].String());
|
||||
{
|
||||
std::string playerName = settings["general"]["playerName"].String();
|
||||
if(playerName == "Player")
|
||||
playerName = CGI->generaltexth->translate("core.genrltxt.434");
|
||||
localPlayerNames.push_back(playerName);
|
||||
}
|
||||
|
||||
gameChat->resetMatchState();
|
||||
lobbyClient->resetMatchState();
|
||||
@ -853,7 +858,7 @@ void CServerHandler::onPacketReceived(const std::shared_ptr<INetworkConnection>
|
||||
if(getState() == EClientState::DISCONNECTING)
|
||||
return;
|
||||
|
||||
CPack * pack = logicConnection->retrievePack(message);
|
||||
auto pack = logicConnection->retrievePack(message);
|
||||
ServerHandlerCPackVisitor visitor(*this);
|
||||
pack->visit(visitor);
|
||||
}
|
||||
@ -938,14 +943,14 @@ void CServerHandler::visitForLobby(CPackForLobby & lobbyPack)
|
||||
|
||||
void CServerHandler::visitForClient(CPackForClient & clientPack)
|
||||
{
|
||||
client->handlePack(&clientPack);
|
||||
client->handlePack(clientPack);
|
||||
}
|
||||
|
||||
|
||||
void CServerHandler::sendLobbyPack(const CPackForLobby & pack) const
|
||||
{
|
||||
if(getState() != EClientState::STARTING)
|
||||
logicConnection->sendPack(&pack);
|
||||
logicConnection->sendPack(pack);
|
||||
}
|
||||
|
||||
bool CServerHandler::inLobbyRoom() const
|
||||
|
@ -25,7 +25,6 @@ struct TurnTimerInfo;
|
||||
class CMapInfo;
|
||||
class CGameState;
|
||||
struct ClientPlayer;
|
||||
struct CPack;
|
||||
struct CPackForLobby;
|
||||
struct CPackForClient;
|
||||
|
||||
|
@ -163,7 +163,7 @@ void CClient::save(const std::string & fname)
|
||||
}
|
||||
|
||||
SaveGame save_game(fname);
|
||||
sendRequest(&save_game, PlayerColor::NEUTRAL);
|
||||
sendRequest(save_game, PlayerColor::NEUTRAL);
|
||||
}
|
||||
|
||||
void CClient::endNetwork()
|
||||
@ -348,37 +348,35 @@ void CClient::installNewBattleInterface(std::shared_ptr<CBattleGameInterface> ba
|
||||
}
|
||||
}
|
||||
|
||||
void CClient::handlePack(CPackForClient * pack)
|
||||
void CClient::handlePack(CPackForClient & pack)
|
||||
{
|
||||
ApplyClientNetPackVisitor afterVisitor(*this, *gameState());
|
||||
ApplyFirstClientNetPackVisitor beforeVisitor(*this, *gameState());
|
||||
|
||||
pack->visit(beforeVisitor);
|
||||
logNetwork->trace("\tMade first apply on cl: %s", typeid(*pack).name());
|
||||
pack.visit(beforeVisitor);
|
||||
logNetwork->trace("\tMade first apply on cl: %s", typeid(pack).name());
|
||||
{
|
||||
boost::unique_lock lock(CGameState::mutex);
|
||||
gs->apply(pack);
|
||||
}
|
||||
logNetwork->trace("\tApplied on gs: %s", typeid(*pack).name());
|
||||
pack->visit(afterVisitor);
|
||||
logNetwork->trace("\tMade second apply on cl: %s", typeid(*pack).name());
|
||||
|
||||
delete pack;
|
||||
logNetwork->trace("\tApplied on gs: %s", typeid(pack).name());
|
||||
pack.visit(afterVisitor);
|
||||
logNetwork->trace("\tMade second apply on cl: %s", typeid(pack).name());
|
||||
}
|
||||
|
||||
int CClient::sendRequest(const CPackForServer * request, PlayerColor player)
|
||||
int CClient::sendRequest(const CPackForServer & request, PlayerColor player)
|
||||
{
|
||||
static ui32 requestCounter = 1;
|
||||
|
||||
ui32 requestID = requestCounter++;
|
||||
logNetwork->trace("Sending a request \"%s\". It'll have an ID=%d.", typeid(*request).name(), requestID);
|
||||
logNetwork->trace("Sending a request \"%s\". It'll have an ID=%d.", typeid(request).name(), requestID);
|
||||
|
||||
waitingRequest.pushBack(requestID);
|
||||
request->requestID = requestID;
|
||||
request->player = player;
|
||||
request.requestID = requestID;
|
||||
request.player = player;
|
||||
CSH->logicConnection->sendPack(request);
|
||||
if(vstd::contains(playerint, player))
|
||||
playerint[player]->requestSent(request, requestID);
|
||||
playerint[player]->requestSent(&request, requestID);
|
||||
|
||||
return requestID;
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
struct CPack;
|
||||
struct CPackForServer;
|
||||
class IBattleEventsReceiver;
|
||||
class CBattleGameInterface;
|
||||
@ -143,8 +142,8 @@ public:
|
||||
|
||||
static ThreadSafeVector<int> waitingRequest; //FIXME: make this normal field (need to join all threads before client destruction)
|
||||
|
||||
void handlePack(CPackForClient * pack); //applies the given pack and deletes it
|
||||
int sendRequest(const CPackForServer * request, PlayerColor player); //returns ID given to that request
|
||||
void handlePack(CPackForClient & pack); //applies the given pack and deletes it
|
||||
int sendRequest(const CPackForServer & request, PlayerColor player); //returns ID given to that request
|
||||
|
||||
void battleStarted(const BattleInfo * info);
|
||||
void battleFinished(const BattleID & battleID);
|
||||
@ -159,6 +158,7 @@ public:
|
||||
friend class CBattleCallback; //handling players actions
|
||||
|
||||
void changeSpells(const CGHeroInstance * hero, bool give, const std::set<SpellID> & spells) override {};
|
||||
void setResearchedSpells(const CGTownInstance * town, int level, const std::vector<SpellID> spells, bool accepted) override {};
|
||||
bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) override {return false;};
|
||||
void createBoat(const int3 & visitablePosition, BoatId type, PlayerColor initiator) override {};
|
||||
void setOwner(const CGObjectInstance * obj, PlayerColor owner) override {};
|
||||
@ -204,7 +204,7 @@ public:
|
||||
void setManaPoints(ObjectInstanceID hid, int val) override {};
|
||||
void giveHero(ObjectInstanceID id, PlayerColor player, ObjectInstanceID boatId = ObjectInstanceID()) override {};
|
||||
void changeObjPos(ObjectInstanceID objid, int3 newPos, const PlayerColor & initiator) override {};
|
||||
void sendAndApply(CPackForClient * pack) override {};
|
||||
void sendAndApply(CPackForClient & pack) override {};
|
||||
void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2) override {};
|
||||
void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) override {};
|
||||
|
||||
|
@ -37,6 +37,7 @@ public:
|
||||
void visitHeroVisitCastle(HeroVisitCastle & pack) override;
|
||||
void visitSetMana(SetMana & pack) override;
|
||||
void visitSetMovePoints(SetMovePoints & pack) override;
|
||||
void visitSetResearchedSpells(SetResearchedSpells & pack) override;
|
||||
void visitFoWChange(FoWChange & pack) override;
|
||||
void visitChangeStackCount(ChangeStackCount & pack) override;
|
||||
void visitSetStackType(SetStackType & pack) override;
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "CPlayerInterface.h"
|
||||
#include "CGameInfo.h"
|
||||
#include "windows/GUIClasses.h"
|
||||
#include "windows/CCastleInterface.h"
|
||||
#include "mapView/mapHandler.h"
|
||||
#include "adventureMap/AdventureMapInterface.h"
|
||||
#include "adventureMap/CInGameConsole.h"
|
||||
@ -172,6 +173,12 @@ void ApplyClientNetPackVisitor::visitSetMovePoints(SetMovePoints & pack)
|
||||
callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroMovePointsChanged, h);
|
||||
}
|
||||
|
||||
void ApplyClientNetPackVisitor::visitSetResearchedSpells(SetResearchedSpells & pack)
|
||||
{
|
||||
for(const auto & win : GH.windows().findWindows<CMageGuildScreen>())
|
||||
win->updateSpells(pack.tid);
|
||||
}
|
||||
|
||||
void ApplyClientNetPackVisitor::visitFoWChange(FoWChange & pack)
|
||||
{
|
||||
for(auto &i : cl.playerint)
|
||||
|
@ -835,9 +835,9 @@ OptionsTab::HandicapWindow::HandicapWindow()
|
||||
if(i == 0)
|
||||
{
|
||||
if(isIncome)
|
||||
labels.push_back(std::make_shared<CLabel>(xPos, 35, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->translate("core.jktext.32")));
|
||||
labels.push_back(std::make_shared<CLabel>(xPos, 38, FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->translate("core.jktext.32")));
|
||||
else if(isGrowth)
|
||||
labels.push_back(std::make_shared<CLabel>(xPos, 35, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->translate("core.genrltxt.194")));
|
||||
labels.push_back(std::make_shared<CLabel>(xPos, 38, FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->translate("core.genrltxt.194")));
|
||||
else
|
||||
anim.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SMALRES"), GameResID(resource), 0, 15 + xPos + (j == 0 ? 10 : 0), 35));
|
||||
}
|
||||
@ -1035,14 +1035,13 @@ OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry(const PlayerSettings & S, con
|
||||
labelPlayerNameEdit = std::make_shared<CTextInput>(Rect(6, 3, 95, 15), EFonts::FONT_SMALL, ETextAlignment::CENTER, false);
|
||||
labelPlayerNameEdit->setText(name);
|
||||
}
|
||||
const auto & font = GH.renderHandler().loadFont(FONT_SMALL);
|
||||
|
||||
labelWhoCanPlay = std::make_shared<CMultiLineLabel>(Rect(6, 23, 45, font->getLineHeight()*2), EFonts::FONT_TINY, ETextAlignment::TOPCENTER, Colors::WHITE, CGI->generaltexth->arraytxt[206 + whoCanPlay]);
|
||||
labelWhoCanPlay = std::make_shared<CMultiLineLabel>(Rect(6, 21, 45, 26), EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->arraytxt[206 + whoCanPlay]);
|
||||
|
||||
auto hasHandicap = [this](){ return s->handicap.startBonus.empty() && s->handicap.percentIncome == 100 && s->handicap.percentGrowth == 100; };
|
||||
std::string labelHandicapText = hasHandicap() ? CGI->generaltexth->arraytxt[210] : MetaString::createFromTextID("vcmi.lobby.handicap").toString();
|
||||
labelHandicap = std::make_shared<CMultiLineLabel>(Rect(57, 24, 47, font->getLineHeight()*2), EFonts::FONT_TINY, ETextAlignment::TOPCENTER, Colors::WHITE, labelHandicapText);
|
||||
handicap = std::make_shared<LRClickableArea>(Rect(56, 24, 49, font->getLineHeight()*2), [](){
|
||||
labelHandicap = std::make_shared<CMultiLineLabel>(Rect(55, 23, 46, 24), EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, labelHandicapText);
|
||||
handicap = std::make_shared<LRClickableArea>(Rect(53, 23, 50, 24), [](){
|
||||
if(!CSH->isHost())
|
||||
return;
|
||||
|
||||
|
@ -33,7 +33,9 @@ extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libswresample/swresample.h>
|
||||
}
|
||||
|
||||
// Define a set of functions to read data
|
||||
@ -501,32 +503,71 @@ std::pair<std::unique_ptr<ui8 []>, si64> CAudioInstance::extractAudio(const Vide
|
||||
int numChannels = codecpar->ch_layout.nb_channels;
|
||||
#endif
|
||||
|
||||
samples.reserve(44100 * 5); // arbitrary 5-second buffer
|
||||
samples.reserve(44100 * 5); // arbitrary 5-second buffer to reduce reallocations
|
||||
|
||||
for (;;)
|
||||
if (formatProperties.isPlanar && numChannels > 1)
|
||||
{
|
||||
decodeNextFrame();
|
||||
const AVFrame * frame = getCurrentFrame();
|
||||
// Format is 'planar', which is not supported by wav / SDL
|
||||
// Use swresample part of ffmpeg to deplanarize audio into format supported by wav / SDL
|
||||
|
||||
if (!frame)
|
||||
break;
|
||||
auto sourceFormat = static_cast<AVSampleFormat>(codecpar->format);
|
||||
auto targetFormat = av_get_alt_sample_fmt(sourceFormat, false);
|
||||
|
||||
int samplesToRead = frame->nb_samples * numChannels;
|
||||
int bytesToRead = samplesToRead * formatProperties.sampleSizeBytes;
|
||||
SwrContext * swr_ctx = swr_alloc();
|
||||
|
||||
if (formatProperties.isPlanar && numChannels > 1)
|
||||
#if (LIBAVUTIL_VERSION_MAJOR < 58)
|
||||
av_opt_set_channel_layout(swr_ctx, "in_chlayout", codecpar->channel_layout, 0);
|
||||
av_opt_set_channel_layout(swr_ctx, "out_chlayout", codecpar->channel_layout, 0);
|
||||
#else
|
||||
av_opt_set_chlayout(swr_ctx, "in_chlayout", &codecpar->ch_layout, 0);
|
||||
av_opt_set_chlayout(swr_ctx, "out_chlayout", &codecpar->ch_layout, 0);
|
||||
#endif
|
||||
av_opt_set_int(swr_ctx, "in_sample_rate", codecpar->sample_rate, 0);
|
||||
av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", sourceFormat, 0);
|
||||
av_opt_set_int(swr_ctx, "out_sample_rate", codecpar->sample_rate, 0);
|
||||
av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", targetFormat, 0);
|
||||
|
||||
int initResult = swr_init(swr_ctx);
|
||||
if (initResult < 0)
|
||||
throwFFmpegError(initResult);
|
||||
|
||||
std::vector<uint8_t> frameSamplesBuffer;
|
||||
for (;;)
|
||||
{
|
||||
// Workaround for lack of resampler
|
||||
// Currently, ffmpeg on conan systems is built without sws resampler
|
||||
// Because of that, and because wav format does not supports 'planar' formats from ffmpeg
|
||||
// we need to de-planarize it and convert to "normal" (non-planar / interleaved) stream
|
||||
samples.reserve(samples.size() + bytesToRead);
|
||||
for (int sm = 0; sm < frame->nb_samples; ++sm)
|
||||
for (int ch = 0; ch < numChannels; ++ch)
|
||||
samples.insert(samples.end(), frame->data[ch] + sm * formatProperties.sampleSizeBytes, frame->data[ch] + (sm+1) * formatProperties.sampleSizeBytes );
|
||||
decodeNextFrame();
|
||||
const AVFrame * frame = getCurrentFrame();
|
||||
|
||||
if (!frame)
|
||||
break;
|
||||
|
||||
size_t samplesToRead = frame->nb_samples * numChannels;
|
||||
size_t bytesToRead = samplesToRead * formatProperties.sampleSizeBytes;
|
||||
frameSamplesBuffer.resize(std::max(frameSamplesBuffer.size(), bytesToRead));
|
||||
uint8_t * frameSamplesPtr = frameSamplesBuffer.data();
|
||||
|
||||
int result = swr_convert(swr_ctx, &frameSamplesPtr, frame->nb_samples, (const uint8_t **)frame->data, frame->nb_samples);
|
||||
|
||||
if (result < 0)
|
||||
throwFFmpegError(result);
|
||||
|
||||
size_t samplesToCopy = result * numChannels;
|
||||
size_t bytesToCopy = samplesToCopy * formatProperties.sampleSizeBytes;
|
||||
samples.insert(samples.end(), frameSamplesBuffer.begin(), frameSamplesBuffer.begin() + bytesToCopy);
|
||||
}
|
||||
else
|
||||
swr_free(&swr_ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
decodeNextFrame();
|
||||
const AVFrame * frame = getCurrentFrame();
|
||||
|
||||
if (!frame)
|
||||
break;
|
||||
|
||||
size_t samplesToRead = frame->nb_samples * numChannels;
|
||||
size_t bytesToRead = samplesToRead * formatProperties.sampleSizeBytes;
|
||||
samples.insert(samples.end(), frame->data[0], frame->data[0] + bytesToRead);
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,10 @@
|
||||
#include "../render/IRenderHandler.h"
|
||||
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/GameSettings.h"
|
||||
#include "../lib/IGameSettings.h"
|
||||
#include "../lib/json/JsonNode.h"
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
|
||||
void AssetGenerator::generateAll()
|
||||
{
|
||||
@ -138,16 +142,17 @@ void AssetGenerator::createPlayerColoredBackground(const PlayerColor & player)
|
||||
|
||||
std::shared_ptr<IImage> texture = GH.renderHandler().loadImage(locator, EImageBlitMode::OPAQUE);
|
||||
|
||||
// Color transform to make color of brown DIBOX.PCX texture match color of specified player
|
||||
// transform to make color of brown DIBOX.PCX texture match color of specified player
|
||||
auto filterSettings = VLC->settingsHandler->getFullConfig()["interface"]["playerColoredBackground"];
|
||||
static const std::array<ColorFilter, PlayerColor::PLAYER_LIMIT_I> filters = {
|
||||
ColorFilter::genRangeShifter( 0.25, 0, 0, 1.25, 0.00, 0.00 ), // red
|
||||
ColorFilter::genRangeShifter( 0, 0, 0, 0.45, 1.20, 4.50 ), // blue
|
||||
ColorFilter::genRangeShifter( 0.40, 0.27, 0.23, 1.10, 1.20, 1.15 ), // tan
|
||||
ColorFilter::genRangeShifter( -0.27, 0.10, -0.27, 0.70, 1.70, 0.70 ), // green
|
||||
ColorFilter::genRangeShifter( 0.47, 0.17, -0.27, 1.60, 1.20, 0.70 ), // orange
|
||||
ColorFilter::genRangeShifter( 0.12, -0.1, 0.25, 1.15, 1.20, 2.20 ), // purple
|
||||
ColorFilter::genRangeShifter( -0.13, 0.23, 0.23, 0.90, 1.20, 2.20 ), // teal
|
||||
ColorFilter::genRangeShifter( 0.44, 0.15, 0.25, 1.00, 1.00, 1.75 ) // pink
|
||||
ColorFilter::genRangeShifter( filterSettings["red" ].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["blue" ].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["tan" ].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["green" ].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["orange"].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["purple"].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["teal" ].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["pink" ].convertTo<std::vector<float>>() )
|
||||
};
|
||||
|
||||
assert(player.isValidPlayer());
|
||||
|
@ -70,6 +70,13 @@ ColorFilter ColorFilter::genRangeShifter( float minR, float minG, float minB, fl
|
||||
1.f);
|
||||
}
|
||||
|
||||
ColorFilter ColorFilter::genRangeShifter( std::vector<float> parameters )
|
||||
{
|
||||
assert(std::size(parameters) == 6);
|
||||
|
||||
return genRangeShifter(parameters[0], parameters[1], parameters[2], parameters[3], parameters[4], parameters[5]);
|
||||
}
|
||||
|
||||
ColorFilter ColorFilter::genMuxerShifter( ChannelMuxer r, ChannelMuxer g, ChannelMuxer b, float a )
|
||||
{
|
||||
return ColorFilter(r, g, b, a);
|
||||
|
@ -44,6 +44,7 @@ public:
|
||||
|
||||
/// Generates object that transforms each channel independently
|
||||
static ColorFilter genRangeShifter( float minR, float minG, float minB, float maxR, float maxG, float maxB );
|
||||
static ColorFilter genRangeShifter( std::vector<float> parameters );
|
||||
|
||||
/// Generates object that performs arbitrary mixing between any channels
|
||||
static ColorFilter genMuxerShifter( ChannelMuxer r, ChannelMuxer g, ChannelMuxer b, float a );
|
||||
|
@ -70,6 +70,7 @@ void CComponent::init(ComponentType Type, ComponentSubType Subtype, std::optiona
|
||||
customSubtitle = ValText;
|
||||
size = imageSize;
|
||||
font = fnt;
|
||||
newLine = false;
|
||||
|
||||
assert(size < sizeInvalid);
|
||||
|
||||
@ -471,7 +472,8 @@ void CComponentBox::placeComponents(bool selectable)
|
||||
|
||||
//start next row
|
||||
if ((pos.w != 0 && rows.back().width + comp->pos.w + distance > pos.w) // row is full
|
||||
|| rows.back().comps >= componentsInRow)
|
||||
|| rows.back().comps >= componentsInRow
|
||||
|| (prevComp && prevComp->newLine))
|
||||
{
|
||||
prevComp = nullptr;
|
||||
rows.push_back (RowData (0,0,0));
|
||||
|
@ -52,6 +52,7 @@ public:
|
||||
std::string customSubtitle;
|
||||
ESize size; //component size.
|
||||
EFonts font; //Font size of label
|
||||
bool newLine; //Line break after component
|
||||
|
||||
std::string getDescription() const;
|
||||
std::string getSubtitle() const;
|
||||
|
@ -1782,6 +1782,7 @@ CFortScreen::CFortScreen(const CGTownInstance * town):
|
||||
ui32 fortSize = static_cast<ui32>(town->creatures.size());
|
||||
if(fortSize > town->town->creatures.size() && town->creatures.back().second.empty())
|
||||
fortSize--;
|
||||
fortSize = std::min(fortSize, static_cast<ui32>(GameConstants::CREATURES_PER_TOWN)); // for 8 creatures + portal of summoning
|
||||
|
||||
const CBuilding * fortBuilding = town->town->buildings.at(BuildingID(town->fortLevel()+6));
|
||||
title = std::make_shared<CLabel>(400, 12, FONT_BIG, ETextAlignment::CENTER, Colors::WHITE, fortBuilding->getNameTranslated());
|
||||
@ -1840,6 +1841,7 @@ ImagePath CFortScreen::getBgName(const CGTownInstance * town)
|
||||
ui32 fortSize = static_cast<ui32>(town->creatures.size());
|
||||
if(fortSize > town->town->creatures.size() && town->creatures.back().second.empty())
|
||||
fortSize--;
|
||||
fortSize = std::min(fortSize, static_cast<ui32>(GameConstants::CREATURES_PER_TOWN)); // for 8 creatures + portal of summoning
|
||||
|
||||
if(fortSize == GameConstants::CREATURES_PER_TOWN)
|
||||
return ImagePath::builtin("TPCASTL8");
|
||||
@ -1966,7 +1968,7 @@ void CFortScreen::RecruitArea::showPopupWindow(const Point & cursorPosition)
|
||||
}
|
||||
|
||||
CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner, const ImagePath & imagename)
|
||||
: CWindowObject(BORDERED, imagename)
|
||||
: CWindowObject(BORDERED, imagename), townId(owner->town->id)
|
||||
{
|
||||
OBJECT_CONSTRUCTION;
|
||||
|
||||
@ -1982,6 +1984,15 @@ CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner, const ImagePath & i
|
||||
|
||||
exit = std::make_shared<CButton>(Point(748, 556), AnimationPath::builtin("TPMAGE1.DEF"), CButton::tooltip(CGI->generaltexth->allTexts[593]), [&](){ close(); }, EShortcut::GLOBAL_RETURN);
|
||||
|
||||
updateSpells(townId);
|
||||
}
|
||||
|
||||
void CMageGuildScreen::updateSpells(ObjectInstanceID tID)
|
||||
{
|
||||
if(tID != townId)
|
||||
return;
|
||||
|
||||
OBJECT_CONSTRUCTION;
|
||||
static const std::vector<std::vector<Point> > positions =
|
||||
{
|
||||
{Point(222,445), Point(312,445), Point(402,445), Point(520,445), Point(610,445), Point(700,445)},
|
||||
@ -1991,21 +2002,28 @@ CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner, const ImagePath & i
|
||||
{Point(491,325), Point(591,325)}
|
||||
};
|
||||
|
||||
for(size_t i=0; i<owner->town->town->mageLevel; i++)
|
||||
spells.clear();
|
||||
emptyScrolls.clear();
|
||||
|
||||
const CGTownInstance * town = LOCPLINT->cb->getTown(townId);
|
||||
|
||||
for(size_t i=0; i<town->town->mageLevel; i++)
|
||||
{
|
||||
size_t spellCount = owner->town->spellsAtLevel((int)i+1,false); //spell at level with -1 hmmm?
|
||||
size_t spellCount = town->spellsAtLevel((int)i+1,false); //spell at level with -1 hmmm?
|
||||
for(size_t j=0; j<spellCount; j++)
|
||||
{
|
||||
if(i<owner->town->mageGuildLevel() && owner->town->spells[i].size()>j)
|
||||
spells.push_back(std::make_shared<Scroll>(positions[i][j], owner->town->spells[i][j].toSpell()));
|
||||
if(i<town->mageGuildLevel() && town->spells[i].size()>j)
|
||||
spells.push_back(std::make_shared<Scroll>(positions[i][j], town->spells[i][j].toSpell(), townId));
|
||||
else
|
||||
emptyScrolls.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("TPMAGES.DEF"), 1, 0, positions[i][j].x, positions[i][j].y));
|
||||
}
|
||||
}
|
||||
|
||||
redraw();
|
||||
}
|
||||
|
||||
CMageGuildScreen::Scroll::Scroll(Point position, const CSpell *Spell)
|
||||
: spell(Spell)
|
||||
CMageGuildScreen::Scroll::Scroll(Point position, const CSpell *Spell, ObjectInstanceID townId)
|
||||
: spell(Spell), townId(townId)
|
||||
{
|
||||
OBJECT_CONSTRUCTION;
|
||||
|
||||
@ -2017,7 +2035,61 @@ CMageGuildScreen::Scroll::Scroll(Point position, const CSpell *Spell)
|
||||
|
||||
void CMageGuildScreen::Scroll::clickPressed(const Point & cursorPosition)
|
||||
{
|
||||
LOCPLINT->showInfoDialog(spell->getDescriptionTranslated(0), std::make_shared<CComponent>(ComponentType::SPELL, spell->id));
|
||||
const CGTownInstance * town = LOCPLINT->cb->getTown(townId);
|
||||
if(LOCPLINT->cb->getSettings().getBoolean(EGameSettings::TOWNS_SPELL_RESEARCH) && town->spellResearchAllowed)
|
||||
{
|
||||
int level = -1;
|
||||
for(int i = 0; i < town->spells.size(); i++)
|
||||
if(vstd::find_pos(town->spells[i], spell->id) != -1)
|
||||
level = i;
|
||||
|
||||
if(town->spellResearchCounterDay >= LOCPLINT->cb->getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_PER_DAY).Vector()[level].Float())
|
||||
{
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.spellResearch.comeAgain"));
|
||||
return;
|
||||
}
|
||||
|
||||
auto costBase = TResources(LOCPLINT->cb->getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST).Vector()[level]);
|
||||
auto costExponent = LOCPLINT->cb->getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST_EXPONENT_PER_RESEARCH).Vector()[level].Float();
|
||||
auto cost = costBase * std::pow(town->spellResearchAcceptedCounter + 1, costExponent);
|
||||
|
||||
std::vector<std::shared_ptr<CComponent>> resComps;
|
||||
auto newSpell = town->spells[level].at(town->spellsAtLevel(level, false));
|
||||
resComps.push_back(std::make_shared<CComponent>(ComponentType::SPELL, spell->id));
|
||||
resComps.push_back(std::make_shared<CComponent>(ComponentType::SPELL, newSpell));
|
||||
resComps.back()->newLine = true;
|
||||
for(TResources::nziterator i(cost); i.valid(); i++)
|
||||
{
|
||||
resComps.push_back(std::make_shared<CComponent>(ComponentType::RESOURCE, i->resType, i->resVal, CComponent::ESize::medium));
|
||||
}
|
||||
|
||||
auto showSpellResearchDialog = [this, resComps, town, cost, newSpell](){
|
||||
std::vector<std::pair<AnimationPath, CFunctionList<void()>>> pom;
|
||||
for(int i = 0; i < 3; i++)
|
||||
pom.emplace_back(AnimationPath::builtin("settingsWindow/button80"), nullptr);
|
||||
|
||||
auto text = CGI->generaltexth->translate(LOCPLINT->cb->getResourceAmount().canAfford(cost) ? "vcmi.spellResearch.pay" : "vcmi.spellResearch.canNotAfford");
|
||||
boost::replace_first(text, "%SPELL1", spell->id.toSpell()->getNameTranslated());
|
||||
boost::replace_first(text, "%SPELL2", newSpell.toSpell()->getNameTranslated());
|
||||
auto temp = std::make_shared<CInfoWindow>(text, LOCPLINT->playerID, resComps, pom);
|
||||
|
||||
temp->buttons[0]->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("spellResearch/accept")));
|
||||
temp->buttons[0]->addCallback([this, town](){ LOCPLINT->cb->spellResearch(town, spell->id, true); });
|
||||
temp->buttons[0]->addPopupCallback([](){ CRClickPopup::createAndPush(CGI->generaltexth->translate("vcmi.spellResearch.research")); });
|
||||
temp->buttons[0]->setEnabled(LOCPLINT->cb->getResourceAmount().canAfford(cost));
|
||||
temp->buttons[1]->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("spellResearch/reroll")));
|
||||
temp->buttons[1]->addCallback([this, town](){ LOCPLINT->cb->spellResearch(town, spell->id, false); });
|
||||
temp->buttons[1]->addPopupCallback([](){ CRClickPopup::createAndPush(CGI->generaltexth->translate("vcmi.spellResearch.skip")); });
|
||||
temp->buttons[2]->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("spellResearch/close")));
|
||||
temp->buttons[2]->addPopupCallback([](){ CRClickPopup::createAndPush(CGI->generaltexth->translate("vcmi.spellResearch.abort")); });
|
||||
|
||||
GH.windows().pushWindow(temp);
|
||||
};
|
||||
|
||||
showSpellResearchDialog();
|
||||
}
|
||||
else
|
||||
LOCPLINT->showInfoDialog(spell->getDescriptionTranslated(0), std::make_shared<CComponent>(ComponentType::SPELL, spell->id));
|
||||
}
|
||||
|
||||
void CMageGuildScreen::Scroll::showPopupWindow(const Point & cursorPosition)
|
||||
|
@ -379,9 +379,10 @@ class CMageGuildScreen : public CStatusbarWindow
|
||||
{
|
||||
const CSpell * spell;
|
||||
std::shared_ptr<CAnimImage> image;
|
||||
ObjectInstanceID townId;
|
||||
|
||||
public:
|
||||
Scroll(Point position, const CSpell *Spell);
|
||||
Scroll(Point position, const CSpell *Spell, ObjectInstanceID townId);
|
||||
void clickPressed(const Point & cursorPosition) override;
|
||||
void showPopupWindow(const Point & cursorPosition) override;
|
||||
void hover(bool on) override;
|
||||
@ -393,8 +394,11 @@ class CMageGuildScreen : public CStatusbarWindow
|
||||
|
||||
std::shared_ptr<CMinorResDataBar> resdatabar;
|
||||
|
||||
ObjectInstanceID townId;
|
||||
|
||||
public:
|
||||
CMageGuildScreen(CCastleInterface * owner, const ImagePath & image);
|
||||
void updateSpells(ObjectInstanceID tID);
|
||||
};
|
||||
|
||||
/// The blacksmith window where you can buy available in town war machine
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include "render/Canvas.h"
|
||||
#include "CPlayerInterface.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../lib/networkPacks/ArtifactLocation.h"
|
||||
|
||||
@ -41,17 +43,38 @@ CHeroBackpackWindow::CHeroBackpackWindow(const CGHeroInstance * hero, const std:
|
||||
};
|
||||
addSet(arts);
|
||||
arts->setHero(hero);
|
||||
quitButton = std::make_shared<CButton>(Point(), AnimationPath::builtin("IOKAY32.def"), CButton::tooltip(""),
|
||||
[this]() { WindowBase::close(); }, EShortcut::GLOBAL_RETURN);
|
||||
|
||||
buttons.emplace_back(std::make_unique<CButton>(Point(), AnimationPath::builtin("ALTFILL.DEF"),
|
||||
CButton::tooltipLocalized("vcmi.heroWindow.sortBackpackByCost"),
|
||||
[hero]() { LOCPLINT->cb->sortBackpackArtifactsByCost(hero->id); }));
|
||||
buttons.emplace_back(std::make_unique<CButton>(Point(), AnimationPath::builtin("ALTFILL.DEF"),
|
||||
CButton::tooltipLocalized("vcmi.heroWindow.sortBackpackBySlot"),
|
||||
[hero]() { LOCPLINT->cb->sortBackpackArtifactsBySlot(hero->id); }));
|
||||
buttons.emplace_back(std::make_unique<CButton>(Point(), AnimationPath::builtin("ALTFILL.DEF"),
|
||||
CButton::tooltipLocalized("vcmi.heroWindow.sortBackpackByClass"),
|
||||
[hero]() { LOCPLINT->cb->sortBackpackArtifactsByClass(hero->id); }));
|
||||
|
||||
pos.w = stretchedBackground->pos.w = arts->pos.w + 2 * windowMargin;
|
||||
pos.h = stretchedBackground->pos.h = arts->pos.h + quitButton->pos.h + 3 * windowMargin;
|
||||
quitButton->moveTo(Point(pos.x + pos.w / 2 - quitButton->pos.w / 2, pos.y + arts->pos.h + 2 * windowMargin));
|
||||
pos.h = stretchedBackground->pos.h = arts->pos.h + buttons.back()->pos.h + 3 * windowMargin;
|
||||
|
||||
auto buttonPos = Point(pos.x + windowMargin, pos.y + arts->pos.h + 2 * windowMargin);
|
||||
for(const auto & button : buttons)
|
||||
{
|
||||
button->moveTo(buttonPos);
|
||||
buttonPos += Point(button->pos.w + 10, 0);
|
||||
}
|
||||
|
||||
statusbar = CGStatusBar::create(0, pos.h, ImagePath::builtin("ADROLLVR.bmp"), pos.w);
|
||||
pos.h += statusbar->pos.h;
|
||||
|
||||
addUsedEvents(LCLICK);
|
||||
center();
|
||||
}
|
||||
|
||||
void CHeroBackpackWindow::notFocusedClick()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void CHeroBackpackWindow::showAll(Canvas & to)
|
||||
{
|
||||
CIntObject::showAll(to);
|
||||
|
@ -17,10 +17,11 @@ class CHeroBackpackWindow : public CStatusbarWindow, public CWindowWithArtifacts
|
||||
{
|
||||
public:
|
||||
CHeroBackpackWindow(const CGHeroInstance * hero, const std::vector<CArtifactsOfHeroPtr> & artsSets);
|
||||
void notFocusedClick() override;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<CArtifactsOfHeroBackpack> arts;
|
||||
std::shared_ptr<CButton> quitButton;
|
||||
std::vector<std::unique_ptr<CButton>> buttons;
|
||||
std::shared_ptr<CFilledTexture> stretchedBackground;
|
||||
const int windowMargin = 5;
|
||||
|
||||
|
@ -473,7 +473,7 @@ CKingdomInterface::CKingdomInterface()
|
||||
generateButtons();
|
||||
|
||||
statusbar = CGStatusBar::create(std::make_shared<CPicture>(ImagePath::builtin("KSTATBAR"), 10,pos.h - 45));
|
||||
resdatabar = std::make_shared<CResDataBar>(ImagePath::builtin("KRESBAR"), 7, 111+footerPos, 29, 5, 76, 81);
|
||||
resdatabar = std::make_shared<CResDataBar>(ImagePath::builtin("KRESBAR"), 7, 111+footerPos, 29, 3, 76, 81);
|
||||
|
||||
activateTab(persistentStorage["gui"]["lastKindomInterface"].Integer());
|
||||
}
|
||||
@ -805,7 +805,7 @@ CTownItem::CTownItem(const CGTownInstance * Town)
|
||||
picture = std::make_shared<CAnimImage>(AnimationPath::builtin("ITPT"), iconIndex, 0, 5, 6);
|
||||
openTown = std::make_shared<LRClickableAreaOpenTown>(Rect(5, 6, 58, 64), town);
|
||||
|
||||
for(size_t i=0; i<town->creatures.size(); i++)
|
||||
for(size_t i=0; i<town->creatures.size() && i<GameConstants::CREATURES_PER_TOWN; i++)
|
||||
{
|
||||
growth.push_back(std::make_shared<CCreaInfo>(Point(401+37*(int)i, 78), town, (int)i, true, true));
|
||||
available.push_back(std::make_shared<CCreaInfo>(Point(48+37*(int)i, 78), town, (int)i, true, false));
|
||||
@ -866,7 +866,7 @@ void CTownItem::update()
|
||||
|
||||
heroes->update();
|
||||
|
||||
for (size_t i=0; i<town->creatures.size(); i++)
|
||||
for (size_t i=0; i<std::min(static_cast<int>(town->creatures.size()), GameConstants::CREATURES_PER_TOWN); i++)
|
||||
{
|
||||
growth[i]->update();
|
||||
available[i]->update();
|
||||
|
@ -129,7 +129,7 @@ std::vector<std::string> CMessage::breakText(std::string text, size_t maxLineWid
|
||||
if(wordBreak != ui32(-1))
|
||||
{
|
||||
currPos = wordBreak;
|
||||
if(text.substr(0, currPos).find('{') == std::string::npos)
|
||||
if(boost::count(text.substr(0, currPos), '{') == boost::count(text.substr(0, currPos), '}'))
|
||||
{
|
||||
opened = false;
|
||||
color = "";
|
||||
|
@ -548,11 +548,12 @@ void CTavernWindow::addInvite()
|
||||
|
||||
if(!inviteableHeroes.empty())
|
||||
{
|
||||
int imageIndex = heroToInvite ? (*CGI->heroh)[heroToInvite->getHeroType()]->imageIndex : 156; // 156 => special id for random
|
||||
if(!heroToInvite)
|
||||
heroToInvite = (*RandomGeneratorUtil::nextItem(inviteableHeroes, CRandomGenerator::getDefault())).second;
|
||||
|
||||
inviteHero = std::make_shared<CLabel>(170, 444, EFonts::FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->translate("vcmi.tavernWindow.inviteHero"));
|
||||
inviteHeroImage = std::make_shared<CAnimImage>(AnimationPath::builtin("PortraitsSmall"), (*CGI->heroh)[heroToInvite->getHeroType()]->imageIndex, 0, 245, 428);
|
||||
inviteHeroImage = std::make_shared<CAnimImage>(AnimationPath::builtin("PortraitsSmall"), imageIndex, 0, 245, 428);
|
||||
inviteHeroImageArea = std::make_shared<LRClickableArea>(Rect(245, 428, 48, 32), [this](){ GH.windows().createAndPushWindow<HeroSelector>(inviteableHeroes, [this](CGHeroInstance* h){ heroToInvite = h; addInvite(); }); }, [this](){ GH.windows().createAndPushWindow<CRClickPopupInt>(std::make_shared<CHeroWindow>(heroToInvite)); });
|
||||
}
|
||||
}
|
||||
|
@ -166,4 +166,6 @@ QuickRecruitmentWindow::QuickRecruitmentWindow(const CGTownInstance * townd, Rec
|
||||
setButtons();
|
||||
setCreaturePurchaseCards();
|
||||
maxAllCards(cards);
|
||||
|
||||
center();
|
||||
}
|
||||
|
@ -302,7 +302,7 @@
|
||||
"backpackSize" : -1,
|
||||
// if heroes are invitable in tavern
|
||||
"tavernInvite" : false,
|
||||
// minimai primary skills for heroes
|
||||
// minimal primary skills for heroes
|
||||
"minimalPrimarySkills": [ 0, 0, 1, 1]
|
||||
},
|
||||
|
||||
@ -311,7 +311,21 @@
|
||||
// How many new building can be built in a town per day
|
||||
"buildingsPerTurnCap" : 1,
|
||||
// Chances for a town with default buildings to receive corresponding dwelling level built in start
|
||||
"startingDwellingChances": [100, 50]
|
||||
"startingDwellingChances": [100, 50],
|
||||
// Enable spell research in mage guild
|
||||
"spellResearch": false,
|
||||
// Cost for an spell research (array index is spell tier)
|
||||
"spellResearchCost": [
|
||||
{ "gold": 1000, "wood" : 2, "mercury": 2, "ore": 2, "sulfur": 2, "crystal": 2, "gems": 2 },
|
||||
{ "gold": 1000, "wood" : 4, "mercury": 4, "ore": 4, "sulfur": 4, "crystal": 4, "gems": 4 },
|
||||
{ "gold": 1000, "wood" : 6, "mercury": 6, "ore": 6, "sulfur": 6, "crystal": 6, "gems": 6 },
|
||||
{ "gold": 1000, "wood" : 8, "mercury": 8, "ore": 8, "sulfur": 8, "crystal": 8, "gems": 8 },
|
||||
{ "gold": 1000, "wood" : 10, "mercury": 10, "ore": 10, "sulfur": 10, "crystal": 10, "gems": 10 }
|
||||
],
|
||||
// How much researchs/skips per day are possible? (array index is spell tier)
|
||||
"spellResearchPerDay": [ 2, 2, 2, 2, 1 ],
|
||||
// Exponent for increasing cost for each research (factor 1 disables this; array index is spell tier)
|
||||
"spellResearchCostExponentPerResearch": [ 1.25, 1.25, 1.25, 1.25, 1.25 ]
|
||||
},
|
||||
|
||||
"combat":
|
||||
@ -555,6 +569,22 @@
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"interface" :
|
||||
{
|
||||
// Color transform to make color of brown DIBOX.PCX texture match color of specified player
|
||||
"playerColoredBackground" :
|
||||
{
|
||||
"red" : [ 0.25, 0, 0, 1.25, 0.00, 0.00 ],
|
||||
"blue" : [ 0, 0, 0, 0.45, 1.20, 4.50 ],
|
||||
"tan" : [ 0.40, 0.27, 0.23, 1.10, 1.20, 1.15 ],
|
||||
"green" : [ -0.27, 0.10, -0.27, 0.70, 1.70, 0.70 ],
|
||||
"orange" : [ 0.47, 0.17, -0.27, 1.60, 1.20, 0.70 ],
|
||||
"purple" : [ 0.12, -0.1, 0.25, 1.15, 1.20, 2.20 ],
|
||||
"teal" : [ -0.13, 0.23, 0.23, 0.90, 1.20, 2.20 ],
|
||||
"pink" : [ 0.44, 0.15, 0.25, 1.00, 1.00, 1.75 ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,8 +51,12 @@
|
||||
"type" : "object",
|
||||
"additionalProperties" : false,
|
||||
"properties" : {
|
||||
"buildingsPerTurnCap" : { "type" : "number" },
|
||||
"startingDwellingChances" : { "type" : "array" }
|
||||
"buildingsPerTurnCap" : { "type" : "number" },
|
||||
"startingDwellingChances" : { "type" : "array" },
|
||||
"spellResearch" : { "type" : "boolean" },
|
||||
"spellResearchCost" : { "type" : "array" },
|
||||
"spellResearchPerDay" : { "type" : "array" },
|
||||
"spellResearchCostExponentPerResearch" : { "type" : "array" }
|
||||
}
|
||||
},
|
||||
"combat": {
|
||||
@ -148,5 +152,12 @@
|
||||
"perHero" : { "type" : "object" }
|
||||
}
|
||||
},
|
||||
"interface": {
|
||||
"type" : "object",
|
||||
"additionalProperties" : false,
|
||||
"properties" : {
|
||||
"playerColoredBackground" : { "type" : "object" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,17 @@
|
||||
"description" : "Format used to define main mod file (mod.json) in VCMI",
|
||||
"required" : [ "name", "description", "modType", "version", "author", "contact" ],
|
||||
"definitions" : {
|
||||
"fileListOrObject" : {
|
||||
"oneOf" : [
|
||||
{
|
||||
"type" : "array",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
},
|
||||
{
|
||||
"type" : "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"localizable" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : false,
|
||||
@ -35,9 +46,8 @@
|
||||
"description" : "If set to true, vcmi will skip validation of current translation json files"
|
||||
},
|
||||
"translations" : {
|
||||
"type" : "array",
|
||||
"description" : "List of files with translations for this language",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
"$ref" : "#/definitions/fileListOrObject"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -122,9 +132,17 @@
|
||||
"description" : "If set to true, mod will not be enabled automatically on install"
|
||||
},
|
||||
"settings" : {
|
||||
"type" : "object",
|
||||
"description" : "List of changed game settings by mod",
|
||||
"$ref" : "gameSettings.json"
|
||||
"oneOf" : [
|
||||
{
|
||||
"type" : "object",
|
||||
"$ref" : "gameSettings.json"
|
||||
},
|
||||
{
|
||||
"type" : "array",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
},
|
||||
]
|
||||
},
|
||||
"filesystem" : {
|
||||
"type" : "object",
|
||||
@ -206,94 +224,76 @@
|
||||
"$ref" : "#/definitions/localizable"
|
||||
},
|
||||
"translations" : {
|
||||
"type" : "array",
|
||||
"description" : "List of files with translations for this language",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
"$ref" : "#/definitions/fileListOrObject"
|
||||
},
|
||||
"factions" : {
|
||||
"type" : "array",
|
||||
"description" : "List of configuration files for towns/factions",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
"$ref" : "#/definitions/fileListOrObject"
|
||||
},
|
||||
"heroClasses" : {
|
||||
"type" : "array",
|
||||
"description" : "List of configuration files for hero classes",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
"$ref" : "#/definitions/fileListOrObject"
|
||||
},
|
||||
"heroes" : {
|
||||
"type" : "array",
|
||||
"description" : "List of configuration files for heroes",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
"$ref" : "#/definitions/fileListOrObject"
|
||||
},
|
||||
"skills" : {
|
||||
"type" : "array",
|
||||
"description" : "List of configuration files for skills",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
"$ref" : "#/definitions/fileListOrObject"
|
||||
},
|
||||
"creatures" : {
|
||||
"type" : "array",
|
||||
"description" : "List of configuration files for creatures",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
"$ref" : "#/definitions/fileListOrObject"
|
||||
},
|
||||
"artifacts" : {
|
||||
"type" : "array",
|
||||
"description" : "List of configuration files for artifacts",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
"$ref" : "#/definitions/fileListOrObject"
|
||||
},
|
||||
"spells" : {
|
||||
"type" : "array",
|
||||
"description" : "List of configuration files for spells",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
"$ref" : "#/definitions/fileListOrObject"
|
||||
},
|
||||
"objects" : {
|
||||
"type" : "array",
|
||||
"description" : "List of configuration files for objects",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
"$ref" : "#/definitions/fileListOrObject"
|
||||
},
|
||||
"biomes" : {
|
||||
"type" : "array",
|
||||
"description" : "List of configuration files for biomes",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
"$ref" : "#/definitions/fileListOrObject"
|
||||
},
|
||||
"bonuses" : {
|
||||
"type" : "array",
|
||||
"description" : "List of configuration files for bonuses",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
"$ref" : "#/definitions/fileListOrObject"
|
||||
},
|
||||
"terrains" : {
|
||||
"type" : "array",
|
||||
"description" : "List of configuration files for terrains",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
"$ref" : "#/definitions/fileListOrObject"
|
||||
},
|
||||
"roads" : {
|
||||
"type" : "array",
|
||||
"description" : "List of configuration files for roads",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
"$ref" : "#/definitions/fileListOrObject"
|
||||
},
|
||||
"rivers" : {
|
||||
"type" : "array",
|
||||
"description" : "List of configuration files for rivers",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
"$ref" : "#/definitions/fileListOrObject"
|
||||
},
|
||||
"battlefields" : {
|
||||
"type" : "array",
|
||||
"description" : "List of configuration files for battlefields",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
"$ref" : "#/definitions/fileListOrObject"
|
||||
},
|
||||
"obstacles" : {
|
||||
"type" : "array",
|
||||
"description" : "List of configuration files for obstacles",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
"$ref" : "#/definitions/fileListOrObject"
|
||||
},
|
||||
"templates" : {
|
||||
"type" : "array",
|
||||
"description" : "List of configuration files for RMG templates",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
"$ref" : "#/definitions/fileListOrObject"
|
||||
},
|
||||
"scripts" : {
|
||||
"type" : "array",
|
||||
"description" : "List of configuration files for scripts",
|
||||
"items" : { "type" : "string", "format" : "textFile" }
|
||||
"$ref" : "#/definitions/fileListOrObject"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
{
|
||||
"type" : "object",
|
||||
"$schema" : "http://json-schema.org/draft-04/schema",
|
||||
"required" : [ "general", "video", "adventure", "battle", "input", "server", "logging", "launcher", "lobby", "gameTweaks" ],
|
||||
"required" : [ "general", "video", "adventure", "battle", "input", "server", "logging", "launcher", "lobby", "gameTweaks", "mods" ],
|
||||
"definitions" : {
|
||||
"logLevelEnum" : {
|
||||
"type" : "string",
|
||||
@ -149,6 +149,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"mods" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : false,
|
||||
"default" : {},
|
||||
"required" : [
|
||||
"validation"
|
||||
],
|
||||
"properties" : {
|
||||
"validation" : {
|
||||
"type" : "string",
|
||||
"enum" : [ "off", "basic", "full" ],
|
||||
"default" : "basic"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"video" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : false,
|
||||
|
@ -90,8 +90,9 @@ These are fields that are present only in local mod.json file
|
||||
{
|
||||
// Following section describes configuration files with content added by mod
|
||||
// It can be split into several files in any way you want but recommended organization is
|
||||
// to keep one file per object (creature/hero/etc) and, if applicable, add separate file
|
||||
// with translatable strings for each type of content
|
||||
// to keep one file per object (creature/hero/etc)
|
||||
// Alternatively, for small changes you can embed changes to content directly in here, e.g.
|
||||
// "creatures" : { "core:imp" : { "health" : 5 }}
|
||||
|
||||
// list of factions/towns configuration files
|
||||
"factions" :
|
||||
|
@ -36,15 +36,15 @@ public:
|
||||
|
||||
virtual vstd::RNG * getRNG() = 0;
|
||||
|
||||
virtual void apply(CPackForClient * pack) = 0;
|
||||
virtual void apply(CPackForClient & pack) = 0;
|
||||
|
||||
virtual void apply(BattleLogMessage * pack) = 0;
|
||||
virtual void apply(BattleStackMoved * pack) = 0;
|
||||
virtual void apply(BattleUnitsChanged * pack) = 0;
|
||||
virtual void apply(SetStackEffect * pack) = 0;
|
||||
virtual void apply(StacksInjured * pack) = 0;
|
||||
virtual void apply(BattleObstaclesChanged * pack) = 0;
|
||||
virtual void apply(CatapultAttack * pack) = 0;
|
||||
virtual void apply(BattleLogMessage & pack) = 0;
|
||||
virtual void apply(BattleStackMoved & pack) = 0;
|
||||
virtual void apply(BattleUnitsChanged & pack) = 0;
|
||||
virtual void apply(SetStackEffect & pack) = 0;
|
||||
virtual void apply(StacksInjured & pack) = 0;
|
||||
virtual void apply(BattleObstaclesChanged & pack) = 0;
|
||||
virtual void apply(CatapultAttack & pack) = 0;
|
||||
};
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -20,8 +20,6 @@ set(launcher_SRCS
|
||||
innoextract.cpp
|
||||
mainwindow_moc.cpp
|
||||
languages.cpp
|
||||
launcherdirs.cpp
|
||||
jsonutils.cpp
|
||||
updatedialog_moc.cpp
|
||||
prepare.cpp
|
||||
)
|
||||
@ -49,8 +47,6 @@ set(launcher_HEADERS
|
||||
firstLaunch/firstlaunch_moc.h
|
||||
mainwindow_moc.h
|
||||
languages.h
|
||||
launcherdirs.h
|
||||
jsonutils.h
|
||||
updatedialog_moc.h
|
||||
main.h
|
||||
helper.h
|
||||
@ -206,7 +202,7 @@ elseif(NOT APPLE_IOS)
|
||||
target_link_libraries(vcmilauncher SDL2::SDL2)
|
||||
endif()
|
||||
|
||||
target_link_libraries(vcmilauncher vcmi Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network)
|
||||
target_link_libraries(vcmilauncher vcmi vcmiqt Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network)
|
||||
target_include_directories(vcmilauncher
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
@ -18,24 +18,8 @@
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
#include <QTemporaryDir>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
#include "../vcmiqt/convpathqstring.h"
|
||||
|
||||
VCMI_LIB_USING_NAMESPACE
|
||||
|
||||
inline QString pathToQString(const boost::filesystem::path & path)
|
||||
{
|
||||
#ifdef VCMI_WINDOWS
|
||||
return QString::fromStdWString(path.wstring());
|
||||
#else
|
||||
return QString::fromStdString(path.string());
|
||||
#endif
|
||||
}
|
||||
|
||||
inline boost::filesystem::path qstringToPath(const QString & path)
|
||||
{
|
||||
#ifdef VCMI_WINDOWS
|
||||
return boost::filesystem::path(path.toStdWString());
|
||||
#else
|
||||
return boost::filesystem::path(path.toUtf8().data());
|
||||
#endif
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "cdownloadmanager_moc.h"
|
||||
|
||||
#include "../launcherdirs.h"
|
||||
#include "../vcmiqt/launcherdirs.h"
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
|
||||
|
@ -22,8 +22,8 @@
|
||||
#include "cdownloadmanager_moc.h"
|
||||
#include "chroniclesextractor.h"
|
||||
#include "../settingsView/csettingsview_moc.h"
|
||||
#include "../launcherdirs.h"
|
||||
#include "../jsonutils.h"
|
||||
#include "../vcmiqt/launcherdirs.h"
|
||||
#include "../vcmiqt/jsonutils.h"
|
||||
#include "../helper.h"
|
||||
|
||||
#include "../../lib/VCMIDirs.h"
|
||||
|
@ -17,8 +17,8 @@
|
||||
#include "../../lib/modding/CModInfo.h"
|
||||
#include "../../lib/modding/IdentifierStorage.h"
|
||||
|
||||
#include "../jsonutils.h"
|
||||
#include "../launcherdirs.h"
|
||||
#include "../vcmiqt/jsonutils.h"
|
||||
#include "../vcmiqt/launcherdirs.h"
|
||||
|
||||
#include <future>
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "prepare.h"
|
||||
#include "launcherdirs.h"
|
||||
#include "../vcmiqt/launcherdirs.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
#include "../modManager/cmodlistview_moc.h"
|
||||
#include "../helper.h"
|
||||
#include "../jsonutils.h"
|
||||
#include "../vcmiqt/jsonutils.h"
|
||||
#include "../languages.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
@ -182,6 +182,13 @@ void CSettingsView::loadSettings()
|
||||
else
|
||||
ui->buttonFontScalable->setChecked(true);
|
||||
|
||||
if (settings["mods"]["validation"].String() == "off")
|
||||
ui->buttonValidationOff->setChecked(true);
|
||||
else if (settings["mods"]["validation"].String() == "basic")
|
||||
ui->buttonValidationBasic->setChecked(true);
|
||||
else
|
||||
ui->buttonValidationFull->setChecked(true);
|
||||
|
||||
loadToggleButtonSettings();
|
||||
}
|
||||
|
||||
@ -791,3 +798,21 @@ void CSettingsView::on_buttonFontOriginal_clicked(bool checked)
|
||||
Settings node = settings.write["video"]["fontsType"];
|
||||
node->String() = "original";
|
||||
}
|
||||
|
||||
void CSettingsView::on_buttonValidationOff_clicked(bool checked)
|
||||
{
|
||||
Settings node = settings.write["mods"]["validation"];
|
||||
node->String() = "off";
|
||||
}
|
||||
|
||||
void CSettingsView::on_buttonValidationBasic_clicked(bool checked)
|
||||
{
|
||||
Settings node = settings.write["mods"]["validation"];
|
||||
node->String() = "basic";
|
||||
}
|
||||
|
||||
void CSettingsView::on_buttonValidationFull_clicked(bool checked)
|
||||
{
|
||||
Settings node = settings.write["mods"]["validation"];
|
||||
node->String() = "full";
|
||||
}
|
||||
|
@ -83,19 +83,20 @@ private slots:
|
||||
void on_sliderToleranceDistanceController_valueChanged(int value);
|
||||
void on_lineEditGameLobbyHost_textChanged(const QString &arg1);
|
||||
void on_spinBoxNetworkPortLobby_valueChanged(int arg1);
|
||||
|
||||
void on_sliderControllerSticksAcceleration_valueChanged(int value);
|
||||
|
||||
void on_sliderControllerSticksSensitivity_valueChanged(int value);
|
||||
|
||||
//void on_buttonTtfFont_toggled(bool value);
|
||||
|
||||
void on_sliderScalingFont_valueChanged(int value);
|
||||
|
||||
void on_buttonFontAuto_clicked(bool checked);
|
||||
void on_buttonFontScalable_clicked(bool checked);
|
||||
void on_buttonFontOriginal_clicked(bool checked);
|
||||
|
||||
|
||||
void on_buttonValidationOff_clicked(bool checked);
|
||||
|
||||
void on_buttonValidationBasic_clicked(bool checked);
|
||||
|
||||
void on_buttonValidationFull_clicked(bool checked);
|
||||
|
||||
private:
|
||||
Ui::CSettingsView * ui;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -431,9 +431,9 @@ std::shared_ptr<CArtifact> CArtHandler::loadFromJson(const std::string & scope,
|
||||
|
||||
const JsonNode & text = node["text"];
|
||||
|
||||
VLC->generaltexth->registerString(scope, art->getNameTextID(), text["name"].String());
|
||||
VLC->generaltexth->registerString(scope, art->getDescriptionTextID(), text["description"].String());
|
||||
VLC->generaltexth->registerString(scope, art->getEventTextID(), text["event"].String());
|
||||
VLC->generaltexth->registerString(scope, art->getNameTextID(), text["name"]);
|
||||
VLC->generaltexth->registerString(scope, art->getDescriptionTextID(), text["description"]);
|
||||
VLC->generaltexth->registerString(scope, art->getEventTextID(), text["event"]);
|
||||
|
||||
const JsonNode & graphics = node["graphics"];
|
||||
art->image = graphics["image"].String();
|
||||
|
@ -200,8 +200,9 @@ ImagePath CBonusTypeHandler::bonusToGraphics(const std::shared_ptr<Bonus> & bonu
|
||||
|
||||
void CBonusTypeHandler::load()
|
||||
{
|
||||
const JsonNode gameConf(JsonPath::builtin("config/gameConfig.json"));
|
||||
const JsonNode config(JsonUtils::assembleFromFiles(gameConf["bonuses"].convertTo<std::vector<std::string>>()));
|
||||
JsonNode gameConf(JsonPath::builtin("config/gameConfig.json"));
|
||||
JsonNode config(JsonUtils::assembleFromFiles(gameConf["bonuses"].convertTo<std::vector<std::string>>()));
|
||||
config.setModScope("vcmi");
|
||||
load(config);
|
||||
}
|
||||
|
||||
@ -240,8 +241,8 @@ void CBonusTypeHandler::loadItem(const JsonNode & source, CBonusType & dest, con
|
||||
|
||||
if (!dest.hidden)
|
||||
{
|
||||
VLC->generaltexth->registerString( "vcmi", dest.getNameTextID(), source["name"].String());
|
||||
VLC->generaltexth->registerString( "vcmi", dest.getDescriptionTextID(), source["description"].String());
|
||||
VLC->generaltexth->registerString( "vcmi", dest.getNameTextID(), source["name"]);
|
||||
VLC->generaltexth->registerString( "vcmi", dest.getDescriptionTextID(), source["description"]);
|
||||
}
|
||||
|
||||
const JsonNode & graphics = source["graphics"];
|
||||
|
@ -617,9 +617,9 @@ std::shared_ptr<CCreature> CCreatureHandler::loadFromJson(const std::string & sc
|
||||
|
||||
cre->cost = ResourceSet(node["cost"]);
|
||||
|
||||
VLC->generaltexth->registerString(scope, cre->getNameSingularTextID(), node["name"]["singular"].String());
|
||||
VLC->generaltexth->registerString(scope, cre->getNamePluralTextID(), node["name"]["plural"].String());
|
||||
VLC->generaltexth->registerString(scope, cre->getDescriptionTextID(), node["description"].String());
|
||||
VLC->generaltexth->registerString(scope, cre->getNameSingularTextID(), node["name"]["singular"]);
|
||||
VLC->generaltexth->registerString(scope, cre->getNamePluralTextID(), node["name"]["plural"]);
|
||||
VLC->generaltexth->registerString(scope, cre->getDescriptionTextID(), node["description"]);
|
||||
|
||||
cre->addBonus(node["hitPoints"].Integer(), BonusType::STACK_HEALTH);
|
||||
cre->addBonus(node["speed"].Integer(), BonusType::STACKS_SPEED);
|
||||
|
@ -863,7 +863,7 @@ std::string CStackInstance::getName() const
|
||||
ui64 CStackInstance::getPower() const
|
||||
{
|
||||
assert(type);
|
||||
return type->getAIValue() * count;
|
||||
return static_cast<ui64>(type->getAIValue()) * count;
|
||||
}
|
||||
|
||||
ui64 CStackInstance::getMarketValue() const
|
||||
|
@ -459,11 +459,11 @@ std::shared_ptr<CHero> CHeroHandler::loadFromJson(const std::string & scope, con
|
||||
hero->onlyOnWaterMap = node["onlyOnWaterMap"].Bool();
|
||||
hero->onlyOnMapWithoutWater = node["onlyOnMapWithoutWater"].Bool();
|
||||
|
||||
VLC->generaltexth->registerString(scope, hero->getNameTextID(), node["texts"]["name"].String());
|
||||
VLC->generaltexth->registerString(scope, hero->getBiographyTextID(), node["texts"]["biography"].String());
|
||||
VLC->generaltexth->registerString(scope, hero->getSpecialtyNameTextID(), node["texts"]["specialty"]["name"].String());
|
||||
VLC->generaltexth->registerString(scope, hero->getSpecialtyTooltipTextID(), node["texts"]["specialty"]["tooltip"].String());
|
||||
VLC->generaltexth->registerString(scope, hero->getSpecialtyDescriptionTextID(), node["texts"]["specialty"]["description"].String());
|
||||
VLC->generaltexth->registerString(scope, hero->getNameTextID(), node["texts"]["name"]);
|
||||
VLC->generaltexth->registerString(scope, hero->getBiographyTextID(), node["texts"]["biography"]);
|
||||
VLC->generaltexth->registerString(scope, hero->getSpecialtyNameTextID(), node["texts"]["specialty"]["name"]);
|
||||
VLC->generaltexth->registerString(scope, hero->getSpecialtyTooltipTextID(), node["texts"]["specialty"]["tooltip"]);
|
||||
VLC->generaltexth->registerString(scope, hero->getSpecialtyDescriptionTextID(), node["texts"]["specialty"]["description"]);
|
||||
|
||||
hero->iconSpecSmall = node["images"]["specialtySmall"].String();
|
||||
hero->iconSpecLarge = node["images"]["specialtyLarge"].String();
|
||||
|
@ -212,7 +212,7 @@ std::shared_ptr<CSkill> CSkillHandler::loadFromJson(const std::string & scope, c
|
||||
|
||||
skill->onlyOnWaterMap = json["onlyOnWaterMap"].Bool();
|
||||
|
||||
VLC->generaltexth->registerString(scope, skill->getNameTextID(), json["name"].String());
|
||||
VLC->generaltexth->registerString(scope, skill->getNameTextID(), json["name"]);
|
||||
switch(json["gainChance"].getType())
|
||||
{
|
||||
case JsonNode::JsonType::DATA_INTEGER:
|
||||
@ -237,7 +237,7 @@ std::shared_ptr<CSkill> CSkillHandler::loadFromJson(const std::string & scope, c
|
||||
skill->addNewBonus(bonus, level);
|
||||
}
|
||||
CSkill::LevelInfo & skillAtLevel = skill->at(level);
|
||||
VLC->generaltexth->registerString(scope, skill->getDescriptionTextID(level), levelNode["description"].String());
|
||||
VLC->generaltexth->registerString(scope, skill->getDescriptionTextID(level), levelNode["description"]);
|
||||
skillAtLevel.iconSmall = levelNode["images"]["small"].String();
|
||||
skillAtLevel.iconMedium = levelNode["images"]["medium"].String();
|
||||
skillAtLevel.iconLarge = levelNode["images"]["large"].String();
|
||||
|
@ -401,7 +401,7 @@ void CStack::spendMana(ServerCallback * server, const int spellCost) const
|
||||
ssp.which = BattleSetStackProperty::CASTS;
|
||||
ssp.val = -spellCost;
|
||||
ssp.absolute = false;
|
||||
server->apply(&ssp);
|
||||
server->apply(ssp);
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -37,70 +37,75 @@ GameSettings::GameSettings() = default;
|
||||
GameSettings::~GameSettings() = default;
|
||||
|
||||
const std::vector<GameSettings::SettingOption> GameSettings::settingProperties = {
|
||||
{EGameSettings::BANKS_SHOW_GUARDS_COMPOSITION, "banks", "showGuardsComposition" },
|
||||
{EGameSettings::BONUSES_GLOBAL, "bonuses", "global" },
|
||||
{EGameSettings::BONUSES_PER_HERO, "bonuses", "perHero" },
|
||||
{EGameSettings::COMBAT_AREA_SHOT_CAN_TARGET_EMPTY_HEX, "combat", "areaShotCanTargetEmptyHex" },
|
||||
{EGameSettings::COMBAT_ATTACK_POINT_DAMAGE_FACTOR, "combat", "attackPointDamageFactor" },
|
||||
{EGameSettings::COMBAT_ATTACK_POINT_DAMAGE_FACTOR_CAP, "combat", "attackPointDamageFactorCap" },
|
||||
{EGameSettings::COMBAT_BAD_LUCK_DICE, "combat", "badLuckDice" },
|
||||
{EGameSettings::COMBAT_BAD_MORALE_DICE, "combat", "badMoraleDice" },
|
||||
{EGameSettings::COMBAT_DEFENSE_POINT_DAMAGE_FACTOR, "combat", "defensePointDamageFactor" },
|
||||
{EGameSettings::COMBAT_DEFENSE_POINT_DAMAGE_FACTOR_CAP, "combat", "defensePointDamageFactorCap" },
|
||||
{EGameSettings::COMBAT_GOOD_LUCK_DICE, "combat", "goodLuckDice" },
|
||||
{EGameSettings::COMBAT_GOOD_MORALE_DICE, "combat", "goodMoraleDice" },
|
||||
{EGameSettings::COMBAT_LAYOUTS, "combat", "layouts" },
|
||||
{EGameSettings::COMBAT_ONE_HEX_TRIGGERS_OBSTACLES, "combat", "oneHexTriggersObstacles" },
|
||||
{EGameSettings::CREATURES_ALLOW_ALL_FOR_DOUBLE_MONTH, "creatures", "allowAllForDoubleMonth" },
|
||||
{EGameSettings::CREATURES_ALLOW_RANDOM_SPECIAL_WEEKS, "creatures", "allowRandomSpecialWeeks" },
|
||||
{EGameSettings::CREATURES_DAILY_STACK_EXPERIENCE, "creatures", "dailyStackExperience" },
|
||||
{EGameSettings::CREATURES_WEEKLY_GROWTH_CAP, "creatures", "weeklyGrowthCap" },
|
||||
{EGameSettings::CREATURES_WEEKLY_GROWTH_PERCENT, "creatures", "weeklyGrowthPercent" },
|
||||
{EGameSettings::DIMENSION_DOOR_EXPOSES_TERRAIN_TYPE, "spells", "dimensionDoorExposesTerrainType" },
|
||||
{EGameSettings::DIMENSION_DOOR_FAILURE_SPENDS_POINTS, "spells", "dimensionDoorFailureSpendsPoints" },
|
||||
{EGameSettings::DIMENSION_DOOR_ONLY_TO_UNCOVERED_TILES, "spells", "dimensionDoorOnlyToUncoveredTiles"},
|
||||
{EGameSettings::DIMENSION_DOOR_TOURNAMENT_RULES_LIMIT, "spells", "dimensionDoorTournamentRulesLimit"},
|
||||
{EGameSettings::DIMENSION_DOOR_TRIGGERS_GUARDS, "spells", "dimensionDoorTriggersGuards" },
|
||||
{EGameSettings::DWELLINGS_ACCUMULATE_WHEN_NEUTRAL, "dwellings", "accumulateWhenNeutral" },
|
||||
{EGameSettings::DWELLINGS_ACCUMULATE_WHEN_OWNED, "dwellings", "accumulateWhenOwned" },
|
||||
{EGameSettings::DWELLINGS_MERGE_ON_RECRUIT, "dwellings", "mergeOnRecruit" },
|
||||
{EGameSettings::HEROES_BACKPACK_CAP, "heroes", "backpackSize" },
|
||||
{EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS, "heroes", "minimalPrimarySkills" },
|
||||
{EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP, "heroes", "perPlayerOnMapCap" },
|
||||
{EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP, "heroes", "perPlayerTotalCap" },
|
||||
{EGameSettings::HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS, "heroes", "retreatOnWinWithoutTroops" },
|
||||
{EGameSettings::HEROES_STARTING_STACKS_CHANCES, "heroes", "startingStackChances" },
|
||||
{EGameSettings::HEROES_TAVERN_INVITE, "heroes", "tavernInvite" },
|
||||
{EGameSettings::MAP_FORMAT_ARMAGEDDONS_BLADE, "mapFormat", "armageddonsBlade" },
|
||||
{EGameSettings::MAP_FORMAT_CHRONICLES, "mapFormat", "chronicles" },
|
||||
{EGameSettings::MAP_FORMAT_HORN_OF_THE_ABYSS, "mapFormat", "hornOfTheAbyss" },
|
||||
{EGameSettings::MAP_FORMAT_IN_THE_WAKE_OF_GODS, "mapFormat", "inTheWakeOfGods" },
|
||||
{EGameSettings::MAP_FORMAT_JSON_VCMI, "mapFormat", "jsonVCMI" },
|
||||
{EGameSettings::MAP_FORMAT_RESTORATION_OF_ERATHIA, "mapFormat", "restorationOfErathia" },
|
||||
{EGameSettings::MAP_FORMAT_SHADOW_OF_DEATH, "mapFormat", "shadowOfDeath" },
|
||||
{EGameSettings::MARKETS_BLACK_MARKET_RESTOCK_PERIOD, "markets", "blackMarketRestockPeriod" },
|
||||
{EGameSettings::MODULE_COMMANDERS, "modules", "commanders" },
|
||||
{EGameSettings::MODULE_STACK_ARTIFACT, "modules", "stackArtifact" },
|
||||
{EGameSettings::MODULE_STACK_EXPERIENCE, "modules", "stackExperience" },
|
||||
{EGameSettings::PATHFINDER_IGNORE_GUARDS, "pathfinder", "ignoreGuards" },
|
||||
{EGameSettings::PATHFINDER_ORIGINAL_FLY_RULES, "pathfinder", "originalFlyRules" },
|
||||
{EGameSettings::PATHFINDER_USE_BOAT, "pathfinder", "useBoat" },
|
||||
{EGameSettings::PATHFINDER_USE_MONOLITH_ONE_WAY_RANDOM, "pathfinder", "useMonolithOneWayRandom" },
|
||||
{EGameSettings::PATHFINDER_USE_MONOLITH_ONE_WAY_UNIQUE, "pathfinder", "useMonolithOneWayUnique" },
|
||||
{EGameSettings::PATHFINDER_USE_MONOLITH_TWO_WAY, "pathfinder", "useMonolithTwoWay" },
|
||||
{EGameSettings::PATHFINDER_USE_WHIRLPOOL, "pathfinder", "useWhirlpool" },
|
||||
{EGameSettings::TEXTS_ARTIFACT, "textData", "artifact" },
|
||||
{EGameSettings::TEXTS_CREATURE, "textData", "creature" },
|
||||
{EGameSettings::TEXTS_FACTION, "textData", "faction" },
|
||||
{EGameSettings::TEXTS_HERO, "textData", "hero" },
|
||||
{EGameSettings::TEXTS_HERO_CLASS, "textData", "heroClass" },
|
||||
{EGameSettings::TEXTS_OBJECT, "textData", "object" },
|
||||
{EGameSettings::TEXTS_RIVER, "textData", "river" },
|
||||
{EGameSettings::TEXTS_ROAD, "textData", "road" },
|
||||
{EGameSettings::TEXTS_SPELL, "textData", "spell" },
|
||||
{EGameSettings::TEXTS_TERRAIN, "textData", "terrain" },
|
||||
{EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP, "towns", "buildingsPerTurnCap" },
|
||||
{EGameSettings::TOWNS_STARTING_DWELLING_CHANCES, "towns", "startingDwellingChances" },
|
||||
{EGameSettings::BANKS_SHOW_GUARDS_COMPOSITION, "banks", "showGuardsComposition" },
|
||||
{EGameSettings::BONUSES_GLOBAL, "bonuses", "global" },
|
||||
{EGameSettings::BONUSES_PER_HERO, "bonuses", "perHero" },
|
||||
{EGameSettings::COMBAT_AREA_SHOT_CAN_TARGET_EMPTY_HEX, "combat", "areaShotCanTargetEmptyHex" },
|
||||
{EGameSettings::COMBAT_ATTACK_POINT_DAMAGE_FACTOR, "combat", "attackPointDamageFactor" },
|
||||
{EGameSettings::COMBAT_ATTACK_POINT_DAMAGE_FACTOR_CAP, "combat", "attackPointDamageFactorCap" },
|
||||
{EGameSettings::COMBAT_BAD_LUCK_DICE, "combat", "badLuckDice" },
|
||||
{EGameSettings::COMBAT_BAD_MORALE_DICE, "combat", "badMoraleDice" },
|
||||
{EGameSettings::COMBAT_DEFENSE_POINT_DAMAGE_FACTOR, "combat", "defensePointDamageFactor" },
|
||||
{EGameSettings::COMBAT_DEFENSE_POINT_DAMAGE_FACTOR_CAP, "combat", "defensePointDamageFactorCap" },
|
||||
{EGameSettings::COMBAT_GOOD_LUCK_DICE, "combat", "goodLuckDice" },
|
||||
{EGameSettings::COMBAT_GOOD_MORALE_DICE, "combat", "goodMoraleDice" },
|
||||
{EGameSettings::COMBAT_LAYOUTS, "combat", "layouts" },
|
||||
{EGameSettings::COMBAT_ONE_HEX_TRIGGERS_OBSTACLES, "combat", "oneHexTriggersObstacles" },
|
||||
{EGameSettings::CREATURES_ALLOW_ALL_FOR_DOUBLE_MONTH, "creatures", "allowAllForDoubleMonth" },
|
||||
{EGameSettings::CREATURES_ALLOW_RANDOM_SPECIAL_WEEKS, "creatures", "allowRandomSpecialWeeks" },
|
||||
{EGameSettings::CREATURES_DAILY_STACK_EXPERIENCE, "creatures", "dailyStackExperience" },
|
||||
{EGameSettings::CREATURES_WEEKLY_GROWTH_CAP, "creatures", "weeklyGrowthCap" },
|
||||
{EGameSettings::CREATURES_WEEKLY_GROWTH_PERCENT, "creatures", "weeklyGrowthPercent" },
|
||||
{EGameSettings::DIMENSION_DOOR_EXPOSES_TERRAIN_TYPE, "spells", "dimensionDoorExposesTerrainType" },
|
||||
{EGameSettings::DIMENSION_DOOR_FAILURE_SPENDS_POINTS, "spells", "dimensionDoorFailureSpendsPoints" },
|
||||
{EGameSettings::DIMENSION_DOOR_ONLY_TO_UNCOVERED_TILES, "spells", "dimensionDoorOnlyToUncoveredTiles" },
|
||||
{EGameSettings::DIMENSION_DOOR_TOURNAMENT_RULES_LIMIT, "spells", "dimensionDoorTournamentRulesLimit" },
|
||||
{EGameSettings::DIMENSION_DOOR_TRIGGERS_GUARDS, "spells", "dimensionDoorTriggersGuards" },
|
||||
{EGameSettings::DWELLINGS_ACCUMULATE_WHEN_NEUTRAL, "dwellings", "accumulateWhenNeutral" },
|
||||
{EGameSettings::DWELLINGS_ACCUMULATE_WHEN_OWNED, "dwellings", "accumulateWhenOwned" },
|
||||
{EGameSettings::DWELLINGS_MERGE_ON_RECRUIT, "dwellings", "mergeOnRecruit" },
|
||||
{EGameSettings::HEROES_BACKPACK_CAP, "heroes", "backpackSize" },
|
||||
{EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS, "heroes", "minimalPrimarySkills" },
|
||||
{EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP, "heroes", "perPlayerOnMapCap" },
|
||||
{EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP, "heroes", "perPlayerTotalCap" },
|
||||
{EGameSettings::HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS, "heroes", "retreatOnWinWithoutTroops" },
|
||||
{EGameSettings::HEROES_STARTING_STACKS_CHANCES, "heroes", "startingStackChances" },
|
||||
{EGameSettings::HEROES_TAVERN_INVITE, "heroes", "tavernInvite" },
|
||||
{EGameSettings::MAP_FORMAT_ARMAGEDDONS_BLADE, "mapFormat", "armageddonsBlade" },
|
||||
{EGameSettings::MAP_FORMAT_CHRONICLES, "mapFormat", "chronicles" },
|
||||
{EGameSettings::MAP_FORMAT_HORN_OF_THE_ABYSS, "mapFormat", "hornOfTheAbyss" },
|
||||
{EGameSettings::MAP_FORMAT_IN_THE_WAKE_OF_GODS, "mapFormat", "inTheWakeOfGods" },
|
||||
{EGameSettings::MAP_FORMAT_JSON_VCMI, "mapFormat", "jsonVCMI" },
|
||||
{EGameSettings::MAP_FORMAT_RESTORATION_OF_ERATHIA, "mapFormat", "restorationOfErathia" },
|
||||
{EGameSettings::MAP_FORMAT_SHADOW_OF_DEATH, "mapFormat", "shadowOfDeath" },
|
||||
{EGameSettings::MARKETS_BLACK_MARKET_RESTOCK_PERIOD, "markets", "blackMarketRestockPeriod" },
|
||||
{EGameSettings::MODULE_COMMANDERS, "modules", "commanders" },
|
||||
{EGameSettings::MODULE_STACK_ARTIFACT, "modules", "stackArtifact" },
|
||||
{EGameSettings::MODULE_STACK_EXPERIENCE, "modules", "stackExperience" },
|
||||
{EGameSettings::PATHFINDER_IGNORE_GUARDS, "pathfinder", "ignoreGuards" },
|
||||
{EGameSettings::PATHFINDER_ORIGINAL_FLY_RULES, "pathfinder", "originalFlyRules" },
|
||||
{EGameSettings::PATHFINDER_USE_BOAT, "pathfinder", "useBoat" },
|
||||
{EGameSettings::PATHFINDER_USE_MONOLITH_ONE_WAY_RANDOM, "pathfinder", "useMonolithOneWayRandom" },
|
||||
{EGameSettings::PATHFINDER_USE_MONOLITH_ONE_WAY_UNIQUE, "pathfinder", "useMonolithOneWayUnique" },
|
||||
{EGameSettings::PATHFINDER_USE_MONOLITH_TWO_WAY, "pathfinder", "useMonolithTwoWay" },
|
||||
{EGameSettings::PATHFINDER_USE_WHIRLPOOL, "pathfinder", "useWhirlpool" },
|
||||
{EGameSettings::TEXTS_ARTIFACT, "textData", "artifact" },
|
||||
{EGameSettings::TEXTS_CREATURE, "textData", "creature" },
|
||||
{EGameSettings::TEXTS_FACTION, "textData", "faction" },
|
||||
{EGameSettings::TEXTS_HERO, "textData", "hero" },
|
||||
{EGameSettings::TEXTS_HERO_CLASS, "textData", "heroClass" },
|
||||
{EGameSettings::TEXTS_OBJECT, "textData", "object" },
|
||||
{EGameSettings::TEXTS_RIVER, "textData", "river" },
|
||||
{EGameSettings::TEXTS_ROAD, "textData", "road" },
|
||||
{EGameSettings::TEXTS_SPELL, "textData", "spell" },
|
||||
{EGameSettings::TEXTS_TERRAIN, "textData", "terrain" },
|
||||
{EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP, "towns", "buildingsPerTurnCap" },
|
||||
{EGameSettings::TOWNS_STARTING_DWELLING_CHANCES, "towns", "startingDwellingChances" },
|
||||
{EGameSettings::TOWNS_SPELL_RESEARCH, "towns", "spellResearch" },
|
||||
{EGameSettings::TOWNS_SPELL_RESEARCH_COST, "towns", "spellResearchCost" },
|
||||
{EGameSettings::TOWNS_SPELL_RESEARCH_PER_DAY, "towns", "spellResearchPerDay" },
|
||||
{EGameSettings::TOWNS_SPELL_RESEARCH_COST_EXPONENT_PER_RESEARCH, "towns", "spellResearchCostExponentPerResearch" },
|
||||
{EGameSettings::INTERFACE_PLAYER_COLORED_BACKGROUND, "interface", "playerColoredBackground" },
|
||||
};
|
||||
|
||||
void GameSettings::loadBase(const JsonNode & input)
|
||||
|
@ -94,6 +94,7 @@ public:
|
||||
virtual void showInfoDialog(InfoWindow * iw) = 0;
|
||||
|
||||
virtual void changeSpells(const CGHeroInstance * hero, bool give, const std::set<SpellID> &spells)=0;
|
||||
virtual void setResearchedSpells(const CGTownInstance * town, int level, const std::vector<SpellID> spells, bool accepted)=0;
|
||||
virtual bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) = 0;
|
||||
virtual void createBoat(const int3 & visitablePosition, BoatId type, PlayerColor initiator) = 0;
|
||||
virtual void setOwner(const CGObjectInstance * objid, PlayerColor owner)=0;
|
||||
@ -139,7 +140,7 @@ public:
|
||||
virtual void setManaPoints(ObjectInstanceID hid, int val)=0;
|
||||
virtual void giveHero(ObjectInstanceID id, PlayerColor player, ObjectInstanceID boatId = ObjectInstanceID()) = 0;
|
||||
virtual void changeObjPos(ObjectInstanceID objid, int3 newPos, const PlayerColor & initiator)=0;
|
||||
virtual void sendAndApply(CPackForClient * pack) = 0;
|
||||
virtual void sendAndApply(CPackForClient & pack) = 0;
|
||||
virtual void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2)=0; //when two heroes meet on adventure map
|
||||
virtual void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, ETileVisibility mode) = 0;
|
||||
virtual void changeFogOfWar(const std::unordered_set<int3> &tiles, PlayerColor player, ETileVisibility mode) = 0;
|
||||
|
@ -79,6 +79,11 @@ enum class EGameSettings
|
||||
TEXTS_TERRAIN,
|
||||
TOWNS_BUILDINGS_PER_TURN_CAP,
|
||||
TOWNS_STARTING_DWELLING_CHANCES,
|
||||
INTERFACE_PLAYER_COLORED_BACKGROUND,
|
||||
TOWNS_SPELL_RESEARCH,
|
||||
TOWNS_SPELL_RESEARCH_COST,
|
||||
TOWNS_SPELL_RESEARCH_PER_DAY,
|
||||
TOWNS_SPELL_RESEARCH_COST_EXPONENT_PER_RESEARCH,
|
||||
|
||||
OPTIONS_COUNT,
|
||||
OPTIONS_BEGIN = BONUSES_GLOBAL
|
||||
|
@ -50,7 +50,7 @@ std::shared_ptr<RiverType> RiverTypeHandler::loadFromJson(
|
||||
info->paletteAnimation.push_back(element);
|
||||
}
|
||||
|
||||
VLC->generaltexth->registerString(scope, info->getNameTextID(), json["text"].String());
|
||||
VLC->generaltexth->registerString(scope, info->getNameTextID(), json["text"]);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ std::shared_ptr<RoadType> RoadTypeHandler::loadFromJson(
|
||||
info->shortIdentifier = json["shortIdentifier"].String();
|
||||
info->movementCost = json["moveCost"].Integer();
|
||||
|
||||
VLC->generaltexth->registerString(scope,info->getNameTextID(), json["text"].String());
|
||||
VLC->generaltexth->registerString(scope,info->getNameTextID(), json["text"]);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ std::shared_ptr<TerrainType> TerrainTypeHandler::loadFromJson( const std::string
|
||||
info->transitionRequired = json["transitionRequired"].Bool();
|
||||
info->terrainViewPatterns = json["terrainViewPatterns"].String();
|
||||
|
||||
VLC->generaltexth->registerString(scope, info->getNameTextID(), json["text"].String());
|
||||
VLC->generaltexth->registerString(scope, info->getNameTextID(), json["text"]);
|
||||
|
||||
const JsonVector & unblockedVec = json["minimapUnblocked"].Vector();
|
||||
info->minimapUnblocked =
|
||||
|
@ -927,7 +927,7 @@ bool CBattleInfoCallback::handleObstacleTriggersForUnit(SpellCastEnvironment & s
|
||||
bocp.battleID = getBattle()->getBattleID();
|
||||
bocp.changes.emplace_back(spellObstacle.uniqueID, operation);
|
||||
changedObstacle.toInfo(bocp.changes.back(), operation);
|
||||
spellEnv.apply(&bocp);
|
||||
spellEnv.apply(bocp);
|
||||
};
|
||||
const auto side = unit.unitSide();
|
||||
auto shouldReveal = !spellObstacle->hidden || !battleIsObstacleVisibleForSide(*obstacle, side);
|
||||
|
@ -292,8 +292,8 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
|
||||
ret->modScope = source.getModScope();
|
||||
ret->town = town;
|
||||
|
||||
VLC->generaltexth->registerString(source.getModScope(), ret->getNameTextID(), source["name"].String());
|
||||
VLC->generaltexth->registerString(source.getModScope(), ret->getDescriptionTextID(), source["description"].String());
|
||||
VLC->generaltexth->registerString(source.getModScope(), ret->getNameTextID(), source["name"]);
|
||||
VLC->generaltexth->registerString(source.getModScope(), ret->getDescriptionTextID(), source["description"]);
|
||||
|
||||
ret->subId = vstd::find_or(MappedKeys::SPECIAL_BUILDINGS, source["type"].String(), BuildingSubID::NONE);
|
||||
ret->resources = TResources(source["cost"]);
|
||||
@ -603,7 +603,7 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source)
|
||||
town->namesCount = 0;
|
||||
for(const auto & name : source["names"].Vector())
|
||||
{
|
||||
VLC->generaltexth->registerString(town->faction->modScope, town->getRandomNameTextID(town->namesCount), name.String());
|
||||
VLC->generaltexth->registerString(town->faction->modScope, town->getRandomNameTextID(town->namesCount), name);
|
||||
town->namesCount += 1;
|
||||
}
|
||||
|
||||
@ -718,8 +718,8 @@ std::shared_ptr<CFaction> CTownHandler::loadFromJson(const std::string & scope,
|
||||
faction->modScope = scope;
|
||||
faction->identifier = identifier;
|
||||
|
||||
VLC->generaltexth->registerString(scope, faction->getNameTextID(), source["name"].String());
|
||||
VLC->generaltexth->registerString(scope, faction->getDescriptionTextID(), source["description"].String());
|
||||
VLC->generaltexth->registerString(scope, faction->getNameTextID(), source["name"]);
|
||||
VLC->generaltexth->registerString(scope, faction->getDescriptionTextID(), source["description"]);
|
||||
|
||||
faction->creatureBg120 = ImagePath::fromJson(source["creatureBackground"]["120px"]);
|
||||
faction->creatureBg130 = ImagePath::fromJson(source["creatureBackground"]["130px"]);
|
||||
|
@ -1143,9 +1143,9 @@ PlayerRelations CGameState::getPlayerRelations( PlayerColor color1, PlayerColor
|
||||
return PlayerRelations::ENEMIES;
|
||||
}
|
||||
|
||||
void CGameState::apply(CPackForClient *pack)
|
||||
void CGameState::apply(CPackForClient & pack)
|
||||
{
|
||||
pack->applyGs(this);
|
||||
pack.applyGs(this);
|
||||
}
|
||||
|
||||
void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out)
|
||||
|
@ -98,7 +98,7 @@ public:
|
||||
/// picks next free hero type of the H3 hero init sequence -> chosen starting hero, then unused hero type randomly
|
||||
HeroTypeID pickNextHeroType(const PlayerColor & owner);
|
||||
|
||||
void apply(CPackForClient *pack);
|
||||
void apply(CPackForClient & pack);
|
||||
BattleField battleGetBattlefieldType(int3 tile, vstd::RNG & rand);
|
||||
|
||||
void fillUpgradeInfo(const CArmedInstance *obj, SlotID stackPos, UpgradeInfo &out) const override;
|
||||
|
@ -230,6 +230,27 @@ void JsonUtils::inherit(JsonNode & descendant, const JsonNode & base)
|
||||
std::swap(descendant, inheritedNode);
|
||||
}
|
||||
|
||||
JsonNode JsonUtils::assembleFromFiles(const JsonNode & files, bool & isValid)
|
||||
{
|
||||
if (files.isVector())
|
||||
{
|
||||
auto configList = files.convertTo<std::vector<std::string> >();
|
||||
JsonNode result = JsonUtils::assembleFromFiles(configList, isValid);
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return files;
|
||||
}
|
||||
}
|
||||
|
||||
JsonNode JsonUtils::assembleFromFiles(const JsonNode & files)
|
||||
{
|
||||
bool isValid = false;
|
||||
return assembleFromFiles(files, isValid);
|
||||
}
|
||||
|
||||
JsonNode JsonUtils::assembleFromFiles(const std::vector<std::string> & files)
|
||||
{
|
||||
bool isValid = false;
|
||||
@ -275,4 +296,28 @@ JsonNode JsonUtils::assembleFromFiles(const std::string & filename)
|
||||
return result;
|
||||
}
|
||||
|
||||
void JsonUtils::detectConflicts(JsonNode & result, const JsonNode & left, const JsonNode & right, const std::string & keyName)
|
||||
{
|
||||
switch (left.getType())
|
||||
{
|
||||
case JsonNode::JsonType::DATA_NULL:
|
||||
case JsonNode::JsonType::DATA_BOOL:
|
||||
case JsonNode::JsonType::DATA_FLOAT:
|
||||
case JsonNode::JsonType::DATA_INTEGER:
|
||||
case JsonNode::JsonType::DATA_STRING:
|
||||
case JsonNode::JsonType::DATA_VECTOR: // NOTE: comparing vectors as whole - since merge will overwrite it in its entirety
|
||||
{
|
||||
result[keyName][left.getModScope()] = left;
|
||||
result[keyName][right.getModScope()] = right;
|
||||
return;
|
||||
}
|
||||
case JsonNode::JsonType::DATA_STRUCT:
|
||||
{
|
||||
for(const auto & node : left.Struct())
|
||||
if (!right[node.first].isNull())
|
||||
detectConflicts(result, node.second, right[node.first], keyName + "/" + node.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -44,6 +44,8 @@ namespace JsonUtils
|
||||
* @brief generate one Json structure from multiple files
|
||||
* @param files - list of filenames with parts of json structure
|
||||
*/
|
||||
DLL_LINKAGE JsonNode assembleFromFiles(const JsonNode & files);
|
||||
DLL_LINKAGE JsonNode assembleFromFiles(const JsonNode & files, bool & isValid);
|
||||
DLL_LINKAGE JsonNode assembleFromFiles(const std::vector<std::string> & files);
|
||||
DLL_LINKAGE JsonNode assembleFromFiles(const std::vector<std::string> & files, bool & isValid);
|
||||
|
||||
@ -72,6 +74,12 @@ namespace JsonUtils
|
||||
/// get schema by json URI: vcmi:<name of file in schemas directory>#<entry in file, optional>
|
||||
/// example: schema "vcmi:settings" is used to check user settings
|
||||
DLL_LINKAGE const JsonNode & getSchema(const std::string & URI);
|
||||
|
||||
/// detects potential conflicts - json entries present in both nodes
|
||||
/// returns JsonNode that contains list of conflicting keys
|
||||
/// For each conflict - list of conflicting mods and list of conflicting json values
|
||||
/// result[pathToKey][modID] -> node that was conflicting
|
||||
DLL_LINKAGE void detectConflicts(JsonNode & result, const JsonNode & left, const JsonNode & right, const std::string & keyName);
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -28,7 +28,7 @@ void CBankInstanceConstructor::initTypeData(const JsonNode & input)
|
||||
if (input.Struct().count("name") == 0)
|
||||
logMod->warn("Bank %s missing name!", getJsonKey());
|
||||
|
||||
VLC->generaltexth->registerString(input.getModScope(), getNameTextID(), input["name"].String());
|
||||
VLC->generaltexth->registerString(input.getModScope(), getNameTextID(), input["name"]);
|
||||
|
||||
levels = input["levels"].Vector();
|
||||
bankResetDuration = static_cast<si32>(input["resetDuration"].Float());
|
||||
|
@ -278,7 +278,7 @@ std::unique_ptr<ObjectClass> CObjectClassesHandler::loadFromJson(const std::stri
|
||||
newObject->base = json["base"];
|
||||
newObject->id = index;
|
||||
|
||||
VLC->generaltexth->registerString(scope, newObject->getNameTextID(), json["name"].String());
|
||||
VLC->generaltexth->registerString(scope, newObject->getNameTextID(), json["name"]);
|
||||
|
||||
newObject->objectTypeHandlers.resize(json["lastReservedIndex"].Float() + 1);
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "../mapObjects/CRewardableObject.h"
|
||||
#include "../texts/CGeneralTextHandler.h"
|
||||
#include "../IGameCallback.h"
|
||||
#include "../CConfigHandler.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
@ -23,9 +24,10 @@ void CRewardableConstructor::initTypeData(const JsonNode & config)
|
||||
blockVisit = config["blockedVisitable"].Bool();
|
||||
|
||||
if (!config["name"].isNull())
|
||||
VLC->generaltexth->registerString( config.getModScope(), getNameTextID(), config["name"].String());
|
||||
VLC->generaltexth->registerString( config.getModScope(), getNameTextID(), config["name"]);
|
||||
|
||||
JsonUtils::validate(config, "vcmi:rewardable", getJsonKey());
|
||||
if (settings["mods"]["validation"].String() != "off")
|
||||
JsonUtils::validate(config, "vcmi:rewardable", getJsonKey());
|
||||
|
||||
}
|
||||
|
||||
@ -43,9 +45,10 @@ CGObjectInstance * CRewardableConstructor::create(IGameCallback * cb, std::share
|
||||
return ret;
|
||||
}
|
||||
|
||||
Rewardable::Configuration CRewardableConstructor::generateConfiguration(IGameCallback * cb, vstd::RNG & rand, MapObjectID objectID) const
|
||||
Rewardable::Configuration CRewardableConstructor::generateConfiguration(IGameCallback * cb, vstd::RNG & rand, MapObjectID objectID, const std::map<std::string, JsonNode> & presetVariables) const
|
||||
{
|
||||
Rewardable::Configuration result;
|
||||
result.variables.preset = presetVariables;
|
||||
objectInfo.configureObject(result, rand, cb);
|
||||
|
||||
for(auto & rewardInfo : result.info)
|
||||
@ -67,7 +70,7 @@ void CRewardableConstructor::configureObject(CGObjectInstance * object, vstd::RN
|
||||
if (!rewardableObject)
|
||||
throw std::runtime_error("Object " + std::to_string(object->getObjGroupIndex()) + ", " + std::to_string(object->getObjTypeIndex()) + " is not a rewardable object!" );
|
||||
|
||||
rewardableObject->configuration = generateConfiguration(object->cb, rng, object->ID);
|
||||
rewardableObject->configuration = generateConfiguration(object->cb, rng, object->ID, rewardableObject->configuration.variables.preset);
|
||||
rewardableObject->initializeGuards();
|
||||
|
||||
if (rewardableObject->configuration.info.empty())
|
||||
|
@ -31,7 +31,7 @@ public:
|
||||
|
||||
std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const override;
|
||||
|
||||
Rewardable::Configuration generateConfiguration(IGameCallback * cb, vstd::RNG & rand, MapObjectID objectID) const;
|
||||
Rewardable::Configuration generateConfiguration(IGameCallback * cb, vstd::RNG & rand, MapObjectID objectID, const std::map<std::string, JsonNode> & presetVariables) const;
|
||||
};
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -29,7 +29,7 @@ void DwellingInstanceConstructor::initTypeData(const JsonNode & input)
|
||||
if (input.Struct().count("name") == 0)
|
||||
logMod->warn("Dwelling %s missing name!", getJsonKey());
|
||||
|
||||
VLC->generaltexth->registerString( input.getModScope(), getNameTextID(), input["name"].String());
|
||||
VLC->generaltexth->registerString( input.getModScope(), getNameTextID(), input["name"]);
|
||||
|
||||
const JsonVector & levels = input["creatures"].Vector();
|
||||
const auto totalLevels = levels.size();
|
||||
|
@ -138,7 +138,7 @@ bool CBank::wasVisited (PlayerColor player) const
|
||||
void CBank::onHeroVisit(const CGHeroInstance * h) const
|
||||
{
|
||||
ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_PLAYER, id, h->id);
|
||||
cb->sendAndApply(&cov);
|
||||
cb->sendAndApply(cov);
|
||||
|
||||
BlockingDialog bd(true, false);
|
||||
bd.player = h->getOwner();
|
||||
|
@ -66,6 +66,18 @@ std::string CGCreature::getHoverText(const CGHeroInstance * hero) const
|
||||
}
|
||||
}
|
||||
|
||||
std::string CGCreature::getMonsterLevelText() const
|
||||
{
|
||||
std::string monsterLevel = VLC->generaltexth->translate("vcmi.adventureMap.monsterLevel");
|
||||
bool isRanged = VLC->creatures()->getById(getCreature())->getBonusBearer()->hasBonusOfType(BonusType::SHOOTER);
|
||||
std::string attackTypeKey = isRanged ? "vcmi.adventureMap.monsterRangedType" : "vcmi.adventureMap.monsterMeleeType";
|
||||
std::string attackType = VLC->generaltexth->translate(attackTypeKey);
|
||||
boost::replace_first(monsterLevel, "%TOWN", (*VLC->townh)[VLC->creatures()->getById(getCreature())->getFaction()]->getNameTranslated());
|
||||
boost::replace_first(monsterLevel, "%LEVEL", std::to_string(VLC->creatures()->getById(getCreature())->getLevel()));
|
||||
boost::replace_first(monsterLevel, "%ATTACK_TYPE", attackType);
|
||||
return monsterLevel;
|
||||
}
|
||||
|
||||
std::string CGCreature::getPopupText(const CGHeroInstance * hero) const
|
||||
{
|
||||
std::string hoverName;
|
||||
@ -102,15 +114,13 @@ std::string CGCreature::getPopupText(const CGHeroInstance * hero) const
|
||||
|
||||
if (settings["general"]["enableUiEnhancements"].Bool())
|
||||
{
|
||||
std::string monsterLevel = VLC->generaltexth->translate("vcmi.adventureMap.monsterLevel");
|
||||
boost::replace_first(monsterLevel, "%TOWN", (*VLC->townh)[VLC->creatures()->getById(getCreature())->getFaction()]->getNameTranslated());
|
||||
boost::replace_first(monsterLevel, "%LEVEL", std::to_string(VLC->creatures()->getById(getCreature())->getLevel()));
|
||||
hoverName += monsterLevel;
|
||||
|
||||
hoverName += getMonsterLevelText();
|
||||
hoverName += VLC->generaltexth->translate("vcmi.adventureMap.monsterThreat.title");
|
||||
|
||||
int choice;
|
||||
double ratio = (static_cast<double>(getArmyStrength()) / hero->getTotalStrength());
|
||||
uint64_t armyStrength = getArmyStrength();
|
||||
uint64_t heroStrength = hero->getTotalStrength();
|
||||
double ratio = static_cast<double>(armyStrength) / heroStrength;
|
||||
if (ratio < 0.1) choice = 0;
|
||||
else if (ratio < 0.25) choice = 1;
|
||||
else if (ratio < 0.6) choice = 2;
|
||||
@ -131,7 +141,10 @@ std::string CGCreature::getPopupText(const CGHeroInstance * hero) const
|
||||
|
||||
std::string CGCreature::getPopupText(PlayerColor player) const
|
||||
{
|
||||
return getHoverText(player);
|
||||
std::string hoverName = getHoverText(player);
|
||||
if (settings["general"]["enableUiEnhancements"].Bool())
|
||||
hoverName += getMonsterLevelText();
|
||||
return hoverName;
|
||||
}
|
||||
|
||||
std::vector<Component> CGCreature::getPopupComponents(PlayerColor player) const
|
||||
|
@ -81,7 +81,7 @@ private:
|
||||
|
||||
int takenAction(const CGHeroInstance *h, bool allowJoin=true) const; //action on confrontation: -2 - fight, -1 - flee, >=0 - will join for given value of gold (may be 0)
|
||||
void giveReward(const CGHeroInstance * h) const;
|
||||
|
||||
std::string getMonsterLevelText() const;
|
||||
};
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -224,7 +224,7 @@ void CGDwelling::onHeroVisit( const CGHeroInstance * h ) const
|
||||
iw.player = h->tempOwner;
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 44); //{%s} \n\n The camp is deserted. Perhaps you should try next week.
|
||||
iw.text.replaceName(ID);
|
||||
cb->sendAndApply(&iw);
|
||||
cb->sendAndApply(iw);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -324,7 +324,7 @@ void CGDwelling::newTurn(vstd::RNG & rand) const
|
||||
}
|
||||
|
||||
if(change)
|
||||
cb->sendAndApply(&sac);
|
||||
cb->sendAndApply(sac);
|
||||
|
||||
updateGuards();
|
||||
}
|
||||
@ -392,7 +392,7 @@ void CGDwelling::updateGuards() const
|
||||
csc.slot = slot;
|
||||
csc.count = crea->getGrowth() * 3;
|
||||
csc.absoluteValue = true;
|
||||
cb->sendAndApply(&csc);
|
||||
cb->sendAndApply(csc);
|
||||
}
|
||||
else //slot is empty, create whole new stack
|
||||
{
|
||||
@ -401,7 +401,7 @@ void CGDwelling::updateGuards() const
|
||||
ns.slot = slot;
|
||||
ns.type = crea->getId();
|
||||
ns.count = crea->getGrowth() * 3;
|
||||
cb->sendAndApply(&ns);
|
||||
cb->sendAndApply(ns);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -458,7 +458,7 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const
|
||||
iw.text.replaceNamePlural(crid);
|
||||
|
||||
cb->showInfoDialog(&iw);
|
||||
cb->sendAndApply(&sac);
|
||||
cb->sendAndApply(sac);
|
||||
cb->addToSlot(StackLocation(h, slot), crs, count);
|
||||
}
|
||||
}
|
||||
@ -469,7 +469,7 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 422); //There are no %s here to recruit.
|
||||
iw.text.replaceNamePlural(crid);
|
||||
iw.player = h->tempOwner;
|
||||
cb->sendAndApply(&iw);
|
||||
cb->sendAndApply(iw);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -483,7 +483,7 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const
|
||||
sac.creatures[0].first = !h->getArt(ArtifactPosition::MACH1); //ballista
|
||||
sac.creatures[1].first = !h->getArt(ArtifactPosition::MACH3); //first aid tent
|
||||
sac.creatures[2].first = !h->getArt(ArtifactPosition::MACH2); //ammo cart
|
||||
cb->sendAndApply(&sac);
|
||||
cb->sendAndApply(sac);
|
||||
}
|
||||
|
||||
auto windowMode = (ID == Obj::CREATURE_GENERATOR1 || ID == Obj::REFUGEE_CAMP) ? EOpenWindowMode::RECRUITMENT_FIRST : EOpenWindowMode::RECRUITMENT_ALL;
|
||||
|
@ -827,7 +827,7 @@ void CGHeroInstance::spendMana(ServerCallback * server, const int spellCost) con
|
||||
sm.hid = id;
|
||||
sm.val = -spellCost;
|
||||
|
||||
server->apply(&sm);
|
||||
server->apply(sm);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ void CGBlackMarket::newTurn(vstd::RNG & rand) const
|
||||
SetAvailableArtifacts saa;
|
||||
saa.id = id;
|
||||
cb->pickAllowedArtsSet(saa.arts, rand);
|
||||
cb->sendAndApply(&saa);
|
||||
cb->sendAndApply(saa);
|
||||
}
|
||||
|
||||
std::vector<TradeItemBuy> CGUniversity::availableItemsIds(EMarketMode mode) const
|
||||
|
@ -268,7 +268,10 @@ CGTownInstance::CGTownInstance(IGameCallback *cb):
|
||||
built(0),
|
||||
destroyed(0),
|
||||
identifier(0),
|
||||
alignmentToPlayer(PlayerColor::NEUTRAL)
|
||||
alignmentToPlayer(PlayerColor::NEUTRAL),
|
||||
spellResearchCounterDay(0),
|
||||
spellResearchAcceptedCounter(0),
|
||||
spellResearchAllowed(true)
|
||||
{
|
||||
this->setNodeType(CBonusSystemNode::TOWN);
|
||||
}
|
||||
@ -347,7 +350,7 @@ void CGTownInstance::onHeroVisit(const CGHeroInstance * h) const
|
||||
scp.heroid = h->id;
|
||||
scp.which = SetCommanderProperty::ALIVE;
|
||||
scp.amount = 1;
|
||||
cb->sendAndApply(&scp);
|
||||
cb->sendAndApply(scp);
|
||||
}
|
||||
cb->heroVisitCastle(this, h);
|
||||
// TODO(vmarkovtsev): implement payment for rising the commander
|
||||
@ -628,7 +631,7 @@ void CGTownInstance::removeCapitols(const PlayerColor & owner) const
|
||||
rs.tid = id;
|
||||
rs.bid.insert(BuildingID::CAPITOL);
|
||||
rs.destroyed = destroyed;
|
||||
cb->sendAndApply(&rs);
|
||||
cb->sendAndApply(rs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +73,9 @@ public:
|
||||
std::vector<std::vector<SpellID> > spells; //spells[level] -> vector of spells, first will be available in guild
|
||||
std::vector<CCastleEvent> events;
|
||||
std::pair<si32, si32> bonusValue;//var to store town bonuses (rampart = resources from mystic pond, factory = save debts);
|
||||
int spellResearchCounterDay;
|
||||
int spellResearchAcceptedCounter;
|
||||
bool spellResearchAllowed;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename Handler> void serialize(Handler &h)
|
||||
@ -93,6 +96,13 @@ public:
|
||||
h & spells;
|
||||
h & events;
|
||||
|
||||
if (h.version >= Handler::Version::SPELL_RESEARCH)
|
||||
{
|
||||
h & spellResearchCounterDay;
|
||||
h & spellResearchAcceptedCounter;
|
||||
h & spellResearchAllowed;
|
||||
}
|
||||
|
||||
if (h.version >= Handler::Version::NEW_TOWN_BUILDINGS)
|
||||
{
|
||||
h & rewardableBuildings;
|
||||
|
@ -588,7 +588,7 @@ void CGSeerHut::onHeroVisit(const CGHeroInstance * h) const
|
||||
AddQuest aq;
|
||||
aq.quest = QuestInfo (quest, this, visitablePos());
|
||||
aq.player = h->tempOwner;
|
||||
cb->sendAndApply(&aq); //TODO: merge with setObjProperty?
|
||||
cb->sendAndApply(aq); //TODO: merge with setObjProperty?
|
||||
}
|
||||
|
||||
if(firstVisit || failRequirements)
|
||||
@ -811,7 +811,7 @@ void CGKeymasterTent::onHeroVisit( const CGHeroInstance * h ) const
|
||||
cow.mode = ChangeObjectVisitors::VISITOR_GLOBAL;
|
||||
cow.hero = h->id;
|
||||
cow.object = id;
|
||||
cb->sendAndApply(&cow);
|
||||
cb->sendAndApply(cow);
|
||||
txt_id=19;
|
||||
}
|
||||
else
|
||||
@ -860,7 +860,7 @@ void CGBorderGuard::onHeroVisit(const CGHeroInstance * h) const
|
||||
AddQuest aq;
|
||||
aq.quest = QuestInfo (quest, this, visitablePos());
|
||||
aq.player = h->tempOwner;
|
||||
cb->sendAndApply (&aq);
|
||||
cb->sendAndApply(aq);
|
||||
//TODO: add this quest only once OR check for multiple instances later
|
||||
}
|
||||
}
|
||||
@ -885,7 +885,7 @@ void CGBorderGate::onHeroVisit(const CGHeroInstance * h) const //TODO: passabili
|
||||
AddQuest aq;
|
||||
aq.quest = QuestInfo (quest, this, visitablePos());
|
||||
aq.player = h->tempOwner;
|
||||
cb->sendAndApply (&aq);
|
||||
cb->sendAndApply(aq);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ const IObjectInterface * CRewardableObject::getObject() const
|
||||
void CRewardableObject::markAsScouted(const CGHeroInstance * hero) const
|
||||
{
|
||||
ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_PLAYER, id, hero->id);
|
||||
cb->sendAndApply(&cov);
|
||||
cb->sendAndApply(cov);
|
||||
}
|
||||
|
||||
bool CRewardableObject::isGuarded() const
|
||||
@ -48,7 +48,7 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *hero) const
|
||||
if(!wasScouted(hero->getOwner()))
|
||||
{
|
||||
ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_SCOUTED, id, hero->id);
|
||||
cb->sendAndApply(&cov);
|
||||
cb->sendAndApply(cov);
|
||||
}
|
||||
|
||||
if (isGuarded())
|
||||
@ -116,7 +116,7 @@ void CRewardableObject::markAsVisited(const CGHeroInstance * hero) const
|
||||
cb->setObjPropertyValue(id, ObjProperty::REWARD_CLEARED, true);
|
||||
|
||||
ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_HERO, id, hero->id);
|
||||
cb->sendAndApply(&cov);
|
||||
cb->sendAndApply(cov);
|
||||
}
|
||||
|
||||
void CRewardableObject::grantReward(ui32 rewardID, const CGHeroInstance * hero) const
|
||||
@ -329,14 +329,14 @@ void CRewardableObject::newTurn(vstd::RNG & rand) const
|
||||
if (configuration.resetParameters.rewards)
|
||||
{
|
||||
auto handler = std::dynamic_pointer_cast<const CRewardableConstructor>(getObjectHandler());
|
||||
auto newConfiguration = handler->generateConfiguration(cb, rand, ID);
|
||||
auto newConfiguration = handler->generateConfiguration(cb, rand, ID, configuration.variables.preset);
|
||||
cb->setRewardableObjectConfiguration(id, newConfiguration);
|
||||
}
|
||||
if (configuration.resetParameters.visitors)
|
||||
{
|
||||
cb->setObjPropertyValue(id, ObjProperty::REWARD_CLEARED, false);
|
||||
ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_CLEAR, id);
|
||||
cb->sendAndApply(&cov);
|
||||
cb->sendAndApply(cov);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ void IObjectInterface::showInfoDialog(const ui32 txtID, const ui16 soundID, EInf
|
||||
iw.player = getOwner();
|
||||
iw.type = mode;
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT,txtID);
|
||||
cb->sendAndApply(&iw);
|
||||
cb->sendAndApply(iw);
|
||||
}
|
||||
|
||||
///IObjectInterface
|
||||
|
@ -1087,14 +1087,14 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const
|
||||
for(const auto & eye : eyes)
|
||||
{
|
||||
cb->getTilesInRange (fw.tiles, eye->pos, 10, ETileVisibility::HIDDEN, h->tempOwner);
|
||||
cb->sendAndApply(&fw);
|
||||
cb->sendAndApply(fw);
|
||||
cv.pos = eye->pos;
|
||||
|
||||
cb->sendAndApply(&cv);
|
||||
cb->sendAndApply(cv);
|
||||
}
|
||||
cv.pos = h->visitablePos();
|
||||
cv.focusTime = 0;
|
||||
cb->sendAndApply(&cv);
|
||||
cb->sendAndApply(cv);
|
||||
}
|
||||
}
|
||||
else if (ID == Obj::EYE_OF_MAGI)
|
||||
@ -1258,7 +1258,7 @@ void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const
|
||||
if(!wasVisited(team))
|
||||
{
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 96);
|
||||
cb->sendAndApply(&iw);
|
||||
cb->sendAndApply(iw);
|
||||
|
||||
// increment general visited obelisks counter
|
||||
cb->setObjPropertyID(id, ObjProperty::OBELISK_VISITED, team);
|
||||
@ -1273,7 +1273,7 @@ void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const
|
||||
else
|
||||
{
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 97);
|
||||
cb->sendAndApply(&iw);
|
||||
cb->sendAndApply(iw);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1341,7 +1341,7 @@ void CGLighthouse::onHeroVisit( const CGHeroInstance * h ) const
|
||||
rb.whoID = oldOwner;
|
||||
rb.source = BonusSource::OBJECT_INSTANCE;
|
||||
rb.id = BonusSourceID(id);
|
||||
cb->sendAndApply(&rb);
|
||||
cb->sendAndApply(rb);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1372,7 +1372,7 @@ void CGLighthouse::giveBonusTo(const PlayerColor & player, bool onInit) const
|
||||
if(onInit)
|
||||
gb.applyGs(cb->gameState());
|
||||
else
|
||||
cb->sendAndApply(&gb);
|
||||
cb->sendAndApply(gb);
|
||||
}
|
||||
|
||||
void CGLighthouse::serializeJsonOptions(JsonSerializeFormat& handler)
|
||||
|
@ -189,7 +189,7 @@ void CMapHeader::registerMapStrings()
|
||||
JsonUtils::mergeCopy(data, translations[language]);
|
||||
|
||||
for(auto & s : data.Struct())
|
||||
texts.registerString("map", TextIdentifier(s.first), s.second.String(), language);
|
||||
texts.registerString("map", TextIdentifier(s.first), s.second.String());
|
||||
}
|
||||
|
||||
std::string mapRegisterLocalizedString(const std::string & modContext, CMapHeader & mapHeader, const TextIdentifier & UID, const std::string & localized)
|
||||
@ -199,7 +199,7 @@ std::string mapRegisterLocalizedString(const std::string & modContext, CMapHeade
|
||||
|
||||
std::string mapRegisterLocalizedString(const std::string & modContext, CMapHeader & mapHeader, const TextIdentifier & UID, const std::string & localized, const std::string & language)
|
||||
{
|
||||
mapHeader.texts.registerString(modContext, UID, localized, language);
|
||||
mapHeader.texts.registerString(modContext, UID, localized);
|
||||
mapHeader.translations.Struct()[language].Struct()[UID.get()].String() = localized;
|
||||
return UID.get();
|
||||
}
|
||||
|
@ -2235,10 +2235,7 @@ CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_pt
|
||||
}
|
||||
|
||||
if(features.levelHOTA1)
|
||||
{
|
||||
// TODO: HOTA support
|
||||
[[maybe_unused]] bool spellResearchAvailable = reader->readBool();
|
||||
}
|
||||
object->spellResearchAllowed = reader->readBool();
|
||||
|
||||
// Read castle events
|
||||
uint32_t eventsCount = reader->readUInt32();
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "ModIncompatibility.h"
|
||||
|
||||
#include "../CCreatureHandler.h"
|
||||
#include "../CConfigHandler.h"
|
||||
#include "../CStopWatch.h"
|
||||
#include "../GameSettings.h"
|
||||
#include "../ScriptHandler.h"
|
||||
@ -331,10 +332,38 @@ void CModHandler::loadModFilesystems()
|
||||
|
||||
coreMod->updateChecksum(calculateModChecksum(ModScope::scopeBuiltin(), CResourceHandler::get(ModScope::scopeBuiltin())));
|
||||
|
||||
std::map<std::string, ISimpleResourceLoader *> modFilesystems;
|
||||
|
||||
for(std::string & modName : activeMods)
|
||||
modFilesystems[modName] = genModFilesystem(modName, allMods[modName].config);
|
||||
|
||||
for(std::string & modName : activeMods)
|
||||
CResourceHandler::addFilesystem("data", modName, modFilesystems[modName]);
|
||||
|
||||
if (settings["mods"]["validation"].String() == "full")
|
||||
{
|
||||
CModInfo & mod = allMods[modName];
|
||||
CResourceHandler::addFilesystem("data", modName, genModFilesystem(modName, mod.config));
|
||||
for(std::string & leftModName : activeMods)
|
||||
{
|
||||
for(std::string & rightModName : activeMods)
|
||||
{
|
||||
if (leftModName == rightModName)
|
||||
continue;
|
||||
|
||||
if (getModDependencies(leftModName).count(rightModName) || getModDependencies(rightModName).count(leftModName))
|
||||
continue;
|
||||
|
||||
const auto & filter = [](const ResourcePath &path){return path.getType() != EResType::DIRECTORY;};
|
||||
|
||||
std::unordered_set<ResourcePath> leftResources = modFilesystems[leftModName]->getFilteredFiles(filter);
|
||||
std::unordered_set<ResourcePath> rightResources = modFilesystems[rightModName]->getFilteredFiles(filter);
|
||||
|
||||
for (auto const & leftFile : leftResources)
|
||||
{
|
||||
if (rightResources.count(leftFile))
|
||||
logMod->warn("Potential confict detected between '%s' and '%s': both mods add file '%s'", leftModName, rightModName, leftFile.getOriginalName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,6 +399,12 @@ std::string CModHandler::getModLanguage(const TModID& modId) const
|
||||
return allMods.at(modId).baseLanguage;
|
||||
}
|
||||
|
||||
std::set<TModID> CModHandler::getModDependencies(const TModID & modId) const
|
||||
{
|
||||
bool isModFound;
|
||||
return getModDependencies(modId, isModFound);
|
||||
}
|
||||
|
||||
std::set<TModID> CModHandler::getModDependencies(const TModID & modId, bool & isModFound) const
|
||||
{
|
||||
auto it = allMods.find(modId);
|
||||
@ -384,7 +419,7 @@ std::set<TModID> CModHandler::getModDependencies(const TModID & modId, bool & is
|
||||
|
||||
void CModHandler::initializeConfig()
|
||||
{
|
||||
VLC->settingsHandler->loadBase(coreMod->config["settings"]);
|
||||
VLC->settingsHandler->loadBase(JsonUtils::assembleFromFiles(coreMod->config["settings"]));
|
||||
|
||||
for(const TModID & modName : activeMods)
|
||||
{
|
||||
@ -401,33 +436,6 @@ CModVersion CModHandler::getModVersion(TModID modName) const
|
||||
return {};
|
||||
}
|
||||
|
||||
bool CModHandler::validateTranslations(TModID modName) const
|
||||
{
|
||||
bool result = true;
|
||||
const auto & mod = allMods.at(modName);
|
||||
|
||||
{
|
||||
auto fileList = mod.config["translations"].convertTo<std::vector<std::string> >();
|
||||
JsonNode json = JsonUtils::assembleFromFiles(fileList);
|
||||
result |= VLC->generaltexth->validateTranslation(mod.baseLanguage, modName, json);
|
||||
}
|
||||
|
||||
for(const auto & language : Languages::getLanguageList())
|
||||
{
|
||||
if (mod.config[language.identifier].isNull())
|
||||
continue;
|
||||
|
||||
if (mod.config[language.identifier]["skipValidation"].Bool())
|
||||
continue;
|
||||
|
||||
auto fileList = mod.config[language.identifier]["translations"].convertTo<std::vector<std::string> >();
|
||||
JsonNode json = JsonUtils::assembleFromFiles(fileList);
|
||||
result |= VLC->generaltexth->validateTranslation(language.identifier, modName, json);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void CModHandler::loadTranslation(const TModID & modName)
|
||||
{
|
||||
const auto & mod = allMods[modName];
|
||||
@ -435,14 +443,11 @@ void CModHandler::loadTranslation(const TModID & modName)
|
||||
std::string preferredLanguage = VLC->generaltexth->getPreferredLanguage();
|
||||
std::string modBaseLanguage = allMods[modName].baseLanguage;
|
||||
|
||||
auto baseTranslationList = mod.config["translations"].convertTo<std::vector<std::string> >();
|
||||
auto extraTranslationList = mod.config[preferredLanguage]["translations"].convertTo<std::vector<std::string> >();
|
||||
JsonNode baseTranslation = JsonUtils::assembleFromFiles(mod.config["translations"]);
|
||||
JsonNode extraTranslation = JsonUtils::assembleFromFiles(mod.config[preferredLanguage]["translations"]);
|
||||
|
||||
JsonNode baseTranslation = JsonUtils::assembleFromFiles(baseTranslationList);
|
||||
JsonNode extraTranslation = JsonUtils::assembleFromFiles(extraTranslationList);
|
||||
|
||||
VLC->generaltexth->loadTranslationOverrides(modBaseLanguage, modName, baseTranslation);
|
||||
VLC->generaltexth->loadTranslationOverrides(preferredLanguage, modName, extraTranslation);
|
||||
VLC->generaltexth->loadTranslationOverrides(modName, baseTranslation);
|
||||
VLC->generaltexth->loadTranslationOverrides(modName, extraTranslation);
|
||||
}
|
||||
|
||||
void CModHandler::load()
|
||||
@ -480,12 +485,6 @@ void CModHandler::load()
|
||||
for(const TModID & modName : activeMods)
|
||||
loadTranslation(modName);
|
||||
|
||||
#if 0
|
||||
for(const TModID & modName : activeMods)
|
||||
if (!validateTranslations(modName))
|
||||
allMods[modName].validation = CModInfo::FAILED;
|
||||
#endif
|
||||
|
||||
logMod->info("\tLoading mod data: %d ms", timer.getDiff());
|
||||
VLC->creh->loadCrExpMod();
|
||||
VLC->identifiersHandler->finalize();
|
||||
|
@ -49,8 +49,6 @@ class DLL_LINKAGE CModHandler final : boost::noncopyable
|
||||
void loadOneMod(std::string modName, const std::string & parent, const JsonNode & modSettings, bool enableMods);
|
||||
void loadTranslation(const TModID & modName);
|
||||
|
||||
bool validateTranslations(TModID modName) const;
|
||||
|
||||
CModVersion getModVersion(TModID modName) const;
|
||||
|
||||
public:
|
||||
@ -66,6 +64,7 @@ public:
|
||||
|
||||
std::string getModLanguage(const TModID & modId) const;
|
||||
|
||||
std::set<TModID> getModDependencies(const TModID & modId) const;
|
||||
std::set<TModID> getModDependencies(const TModID & modId, bool & isModFound) const;
|
||||
|
||||
/// returns list of all (active) mods
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "../BattleFieldHandler.h"
|
||||
#include "../CArtHandler.h"
|
||||
#include "../CCreatureHandler.h"
|
||||
#include "../CConfigHandler.h"
|
||||
#include "../entities/faction/CTownHandler.h"
|
||||
#include "../texts/CGeneralTextHandler.h"
|
||||
#include "../CHeroHandler.h"
|
||||
@ -39,9 +40,9 @@
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, const std::string & objectName):
|
||||
ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, const std::string & entityName):
|
||||
handler(handler),
|
||||
objectName(objectName),
|
||||
entityName(entityName),
|
||||
originalData(handler->loadLegacyData())
|
||||
{
|
||||
for(auto & node : originalData)
|
||||
@ -50,7 +51,7 @@ ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, const std::string
|
||||
}
|
||||
}
|
||||
|
||||
bool ContentTypeHandler::preloadModData(const std::string & modName, const std::vector<std::string> & fileList, bool validate)
|
||||
bool ContentTypeHandler::preloadModData(const std::string & modName, const JsonNode & fileList, bool validate)
|
||||
{
|
||||
bool result = false;
|
||||
JsonNode data = JsonUtils::assembleFromFiles(fileList, result);
|
||||
@ -79,6 +80,9 @@ bool ContentTypeHandler::preloadModData(const std::string & modName, const std::
|
||||
logMod->trace("Patching object %s (%s) from %s", objectName, remoteName, modName);
|
||||
JsonNode & remoteConf = modData[remoteName].patches[objectName];
|
||||
|
||||
if (!remoteConf.isNull() && settings["mods"]["validation"].String() != "off")
|
||||
JsonUtils::detectConflicts(conflictList, remoteConf, entry.second, objectName);
|
||||
|
||||
JsonUtils::merge(remoteConf, entry.second);
|
||||
}
|
||||
}
|
||||
@ -93,7 +97,7 @@ bool ContentTypeHandler::loadMod(const std::string & modName, bool validate)
|
||||
auto performValidate = [&,this](JsonNode & data, const std::string & name){
|
||||
handler->beforeValidate(data);
|
||||
if (validate)
|
||||
result &= JsonUtils::validate(data, "vcmi:" + objectName, name);
|
||||
result &= JsonUtils::validate(data, "vcmi:" + entityName, name);
|
||||
};
|
||||
|
||||
// apply patches
|
||||
@ -113,7 +117,7 @@ bool ContentTypeHandler::loadMod(const std::string & modName, bool validate)
|
||||
// - another mod attempts to add object into this mod (technically can be supported, but might lead to weird edge cases)
|
||||
// - another mod attempts to edit object from this mod that no longer exist - DANGER since such patch likely has very incomplete data
|
||||
// so emit warning and skip such case
|
||||
logMod->warn("Mod '%s' attempts to edit object '%s' of type '%s' from mod '%s' but no such object exist!", data.getModScope(), name, objectName, modName);
|
||||
logMod->warn("Mod '%s' attempts to edit object '%s' of type '%s' from mod '%s' but no such object exist!", data.getModScope(), name, entityName, modName);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -159,31 +163,70 @@ void ContentTypeHandler::loadCustom()
|
||||
|
||||
void ContentTypeHandler::afterLoadFinalization()
|
||||
{
|
||||
for (auto const & data : modData)
|
||||
if (settings["mods"]["validation"].String() != "off")
|
||||
{
|
||||
if (data.second.modData.isNull())
|
||||
for (auto const & data : modData)
|
||||
{
|
||||
for (auto node : data.second.patches.Struct())
|
||||
logMod->warn("Mod '%s' have added patch for object '%s' from mod '%s', but this mod was not loaded or has no new objects.", node.second.getModScope(), node.first, data.first);
|
||||
}
|
||||
|
||||
for(auto & otherMod : modData)
|
||||
{
|
||||
if (otherMod.first == data.first)
|
||||
continue;
|
||||
|
||||
if (otherMod.second.modData.isNull())
|
||||
continue;
|
||||
|
||||
for(auto & otherObject : otherMod.second.modData.Struct())
|
||||
if (data.second.modData.isNull())
|
||||
{
|
||||
if (data.second.modData.Struct().count(otherObject.first))
|
||||
for (auto node : data.second.patches.Struct())
|
||||
logMod->warn("Mod '%s' have added patch for object '%s' from mod '%s', but this mod was not loaded or has no new objects.", node.second.getModScope(), node.first, data.first);
|
||||
}
|
||||
|
||||
for(auto & otherMod : modData)
|
||||
{
|
||||
if (otherMod.first == data.first)
|
||||
continue;
|
||||
|
||||
if (otherMod.second.modData.isNull())
|
||||
continue;
|
||||
|
||||
for(auto & otherObject : otherMod.second.modData.Struct())
|
||||
{
|
||||
logMod->warn("Mod '%s' have added object with name '%s' that is also available in mod '%s'", data.first, otherObject.first, otherMod.first);
|
||||
logMod->warn("Two objects with same name were loaded. Please use form '%s:%s' if mod '%s' needs to modify this object instead", otherMod.first, otherObject.first, data.first);
|
||||
if (data.second.modData.Struct().count(otherObject.first))
|
||||
{
|
||||
logMod->warn("Mod '%s' have added object with name '%s' that is also available in mod '%s'", data.first, otherObject.first, otherMod.first);
|
||||
logMod->warn("Two objects with same name were loaded. Please use form '%s:%s' if mod '%s' needs to modify this object instead", otherMod.first, otherObject.first, data.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [conflictPath, conflictModData] : conflictList.Struct())
|
||||
{
|
||||
std::set<std::string> conflictingMods;
|
||||
std::set<std::string> resolvedConflicts;
|
||||
|
||||
for (auto const & conflictModData : conflictModData.Struct())
|
||||
conflictingMods.insert(conflictModData.first);
|
||||
|
||||
for (auto const & modID : conflictingMods)
|
||||
resolvedConflicts.merge(VLC->modh->getModDependencies(modID));
|
||||
|
||||
vstd::erase_if(conflictingMods, [&resolvedConflicts](const std::string & entry){ return resolvedConflicts.count(entry);});
|
||||
|
||||
if (conflictingMods.size() < 2)
|
||||
continue; // all conflicts were resolved - either via compatibility patch (mod that depends on 2 conflicting mods) or simple mod that depends on another one
|
||||
|
||||
bool allEqual = true;
|
||||
|
||||
for (auto const & modID : conflictingMods)
|
||||
{
|
||||
if (conflictModData[modID] != conflictModData[*conflictingMods.begin()])
|
||||
{
|
||||
allEqual = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allEqual)
|
||||
continue; // conflict still present, but all mods use the same value for conflicting entry - permit it
|
||||
|
||||
logMod->warn("Potential confict in '%s'", conflictPath);
|
||||
|
||||
for (auto const & modID : conflictingMods)
|
||||
logMod->warn("Mod '%s' - value set to %s", modID, conflictModData[modID].toCompactString());
|
||||
}
|
||||
}
|
||||
|
||||
handler->afterLoadFinalization();
|
||||
@ -216,7 +259,7 @@ bool CContentHandler::preloadModData(const std::string & modName, JsonNode modCo
|
||||
bool result = true;
|
||||
for(auto & handler : handlers)
|
||||
{
|
||||
result &= handler.second.preloadModData(modName, modConfig[handler.first].convertTo<std::vector<std::string> >(), validate);
|
||||
result &= handler.second.preloadModData(modName, modConfig[handler.first], validate);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -249,7 +292,7 @@ void CContentHandler::afterLoadFinalization()
|
||||
|
||||
void CContentHandler::preloadData(CModInfo & mod)
|
||||
{
|
||||
bool validate = (mod.validation != CModInfo::PASSED);
|
||||
bool validate = validateMod(mod);
|
||||
|
||||
// print message in format [<8-symbols checksum>] <modname>
|
||||
auto & info = mod.getVerificationInfo();
|
||||
@ -266,7 +309,7 @@ void CContentHandler::preloadData(CModInfo & mod)
|
||||
|
||||
void CContentHandler::load(CModInfo & mod)
|
||||
{
|
||||
bool validate = (mod.validation != CModInfo::PASSED);
|
||||
bool validate = validateMod(mod);
|
||||
|
||||
if (!loadMod(mod.identifier, validate))
|
||||
mod.validation = CModInfo::FAILED;
|
||||
@ -287,4 +330,18 @@ const ContentTypeHandler & CContentHandler::operator[](const std::string & name)
|
||||
return handlers.at(name);
|
||||
}
|
||||
|
||||
bool CContentHandler::validateMod(const CModInfo & mod) const
|
||||
{
|
||||
if (settings["mods"]["validation"].String() == "full")
|
||||
return true;
|
||||
|
||||
if (mod.validation == CModInfo::PASSED)
|
||||
return false;
|
||||
|
||||
if (settings["mods"]["validation"].String() == "off")
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -19,6 +19,8 @@ class CModInfo;
|
||||
/// internal type to handle loading of one data type (e.g. artifacts, creatures)
|
||||
class DLL_LINKAGE ContentTypeHandler
|
||||
{
|
||||
JsonNode conflictList;
|
||||
|
||||
public:
|
||||
struct ModInfo
|
||||
{
|
||||
@ -29,7 +31,7 @@ public:
|
||||
};
|
||||
/// handler to which all data will be loaded
|
||||
IHandlerBase * handler;
|
||||
std::string objectName;
|
||||
std::string entityName;
|
||||
|
||||
/// contains all loaded H3 data
|
||||
std::vector<JsonNode> originalData;
|
||||
@ -39,7 +41,7 @@ public:
|
||||
|
||||
/// local version of methods in ContentHandler
|
||||
/// returns true if loading was successful
|
||||
bool preloadModData(const std::string & modName, const std::vector<std::string> & fileList, bool validate);
|
||||
bool preloadModData(const std::string & modName, const JsonNode & fileList, bool validate);
|
||||
bool loadMod(const std::string & modName, bool validate);
|
||||
void loadCustom();
|
||||
void afterLoadFinalization();
|
||||
@ -56,6 +58,7 @@ class DLL_LINKAGE CContentHandler
|
||||
|
||||
std::map<std::string, ContentTypeHandler> handlers;
|
||||
|
||||
bool validateMod(const CModInfo & mod) const;
|
||||
public:
|
||||
void init();
|
||||
|
||||
|
@ -42,6 +42,7 @@ public:
|
||||
virtual void visitSetSecSkill(SetSecSkill & pack) {}
|
||||
virtual void visitHeroVisitCastle(HeroVisitCastle & pack) {}
|
||||
virtual void visitChangeSpells(ChangeSpells & pack) {}
|
||||
virtual void visitSetResearchedSpells(SetResearchedSpells & pack) {}
|
||||
virtual void visitSetMana(SetMana & pack) {}
|
||||
virtual void visitSetMovePoints(SetMovePoints & pack) {}
|
||||
virtual void visitFoWChange(FoWChange & pack) {}
|
||||
@ -128,6 +129,7 @@ public:
|
||||
virtual void visitBuildStructure(BuildStructure & pack) {}
|
||||
virtual void visitVisitTownBuilding(VisitTownBuilding & pack) {}
|
||||
virtual void visitRazeStructure(RazeStructure & pack) {}
|
||||
virtual void visitSpellResearch(SpellResearch & pack) {}
|
||||
virtual void visitRecruitCreatures(RecruitCreatures & pack) {}
|
||||
virtual void visitUpgradeCreature(UpgradeCreature & pack) {}
|
||||
virtual void visitGarrisonHeroSwap(GarrisonHeroSwap & pack) {}
|
||||
|
@ -162,6 +162,10 @@ void ChangeSpells::visitTyped(ICPackVisitor & visitor)
|
||||
visitor.visitChangeSpells(*this);
|
||||
}
|
||||
|
||||
void SetResearchedSpells::visitTyped(ICPackVisitor & visitor)
|
||||
{
|
||||
visitor.visitSetResearchedSpells(*this);
|
||||
}
|
||||
void SetMana::visitTyped(ICPackVisitor & visitor)
|
||||
{
|
||||
visitor.visitSetMana(*this);
|
||||
@ -592,6 +596,11 @@ void RazeStructure::visitTyped(ICPackVisitor & visitor)
|
||||
visitor.visitRazeStructure(*this);
|
||||
}
|
||||
|
||||
void SpellResearch::visitTyped(ICPackVisitor & visitor)
|
||||
{
|
||||
visitor.visitSpellResearch(*this);
|
||||
}
|
||||
|
||||
void RecruitCreatures::visitTyped(ICPackVisitor & visitor)
|
||||
{
|
||||
visitor.visitRecruitCreatures(*this);
|
||||
@ -930,6 +939,16 @@ void ChangeSpells::applyGs(CGameState *gs)
|
||||
hero->removeSpellFromSpellbook(sid);
|
||||
}
|
||||
|
||||
void SetResearchedSpells::applyGs(CGameState *gs)
|
||||
{
|
||||
CGTownInstance *town = gs->getTown(tid);
|
||||
|
||||
town->spells[level] = spells;
|
||||
town->spellResearchCounterDay++;
|
||||
if(accepted)
|
||||
town->spellResearchAcceptedCounter++;
|
||||
}
|
||||
|
||||
void SetMana::applyGs(CGameState *gs)
|
||||
{
|
||||
CGHeroInstance * hero = gs->getHero(hid);
|
||||
@ -1715,7 +1734,7 @@ void BulkEraseArtifacts::applyGs(CGameState *gs)
|
||||
for(auto & slotInfoWorn : artSet->artifactsWorn)
|
||||
{
|
||||
auto art = slotInfoWorn.second.artifact;
|
||||
if(art->isCombined() && art->isPart(slotInfo->getArt()))
|
||||
if(art->isCombined() && art->isPart(slotInfo->artifact))
|
||||
{
|
||||
dis.al.slot = artSet->getArtPos(art);
|
||||
break;
|
||||
@ -1914,7 +1933,10 @@ void NewTurn::applyGs(CGameState *gs)
|
||||
creatureSet.applyGs(gs);
|
||||
|
||||
for(CGTownInstance* t : gs->map->towns)
|
||||
{
|
||||
t->built = 0;
|
||||
t->spellResearchCounterDay = 0;
|
||||
}
|
||||
|
||||
if(newRumor)
|
||||
gs->currentRumor = *newRumor;
|
||||
|
@ -288,6 +288,26 @@ struct DLL_LINKAGE ChangeSpells : public CPackForClient
|
||||
}
|
||||
};
|
||||
|
||||
struct DLL_LINKAGE SetResearchedSpells : public CPackForClient
|
||||
{
|
||||
void applyGs(CGameState * gs) override;
|
||||
|
||||
void visitTyped(ICPackVisitor & visitor) override;
|
||||
|
||||
ui8 level = 0;
|
||||
ObjectInstanceID tid;
|
||||
std::vector<SpellID> spells;
|
||||
bool accepted;
|
||||
|
||||
template <typename Handler> void serialize(Handler & h)
|
||||
{
|
||||
h & level;
|
||||
h & tid;
|
||||
h & spells;
|
||||
h & accepted;
|
||||
}
|
||||
};
|
||||
|
||||
struct DLL_LINKAGE SetMana : public CPackForClient
|
||||
{
|
||||
void applyGs(CGameState * gs) override;
|
||||
|
@ -306,6 +306,28 @@ struct DLL_LINKAGE RazeStructure : public BuildStructure
|
||||
void visitTyped(ICPackVisitor & visitor) override;
|
||||
};
|
||||
|
||||
struct DLL_LINKAGE SpellResearch : public CPackForServer
|
||||
{
|
||||
SpellResearch() = default;
|
||||
SpellResearch(const ObjectInstanceID & TID, SpellID spellAtSlot, bool accepted)
|
||||
: tid(TID), spellAtSlot(spellAtSlot), accepted(accepted)
|
||||
{
|
||||
}
|
||||
ObjectInstanceID tid;
|
||||
SpellID spellAtSlot;
|
||||
bool accepted;
|
||||
|
||||
void visitTyped(ICPackVisitor & visitor) override;
|
||||
|
||||
template <typename Handler> void serialize(Handler & h)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & tid;
|
||||
h & spellAtSlot;
|
||||
h & accepted;
|
||||
}
|
||||
};
|
||||
|
||||
struct DLL_LINKAGE RecruitCreatures : public CPackForServer
|
||||
{
|
||||
RecruitCreatures() = default;
|
||||
|
@ -76,7 +76,7 @@ void Rewardable::Info::init(const JsonNode & objectConfig, const std::string & o
|
||||
|
||||
auto loadString = [&](const JsonNode & entry, const TextIdentifier & textID){
|
||||
if (entry.isString() && !entry.String().empty() && entry.String()[0] != '@')
|
||||
VLC->generaltexth->registerString(entry.getModScope(), textID, entry.String());
|
||||
VLC->generaltexth->registerString(entry.getModScope(), textID, entry);
|
||||
};
|
||||
|
||||
parameters = objectConfig;
|
||||
|
@ -68,7 +68,7 @@ CConnection::CConnection(std::weak_ptr<INetworkConnection> networkConnection)
|
||||
|
||||
CConnection::~CConnection() = default;
|
||||
|
||||
void CConnection::sendPack(const CPack * pack)
|
||||
void CConnection::sendPack(const CPack & pack)
|
||||
{
|
||||
boost::mutex::scoped_lock lock(writeMutex);
|
||||
|
||||
@ -78,18 +78,18 @@ void CConnection::sendPack(const CPack * pack)
|
||||
throw std::runtime_error("Attempt to send packet on a closed connection!");
|
||||
|
||||
packWriter->buffer.clear();
|
||||
*serializer & pack;
|
||||
(*serializer) & (&pack);
|
||||
|
||||
logNetwork->trace("Sending a pack of type %s", typeid(*pack).name());
|
||||
logNetwork->trace("Sending a pack of type %s", typeid(pack).name());
|
||||
|
||||
connectionPtr->sendPacket(packWriter->buffer);
|
||||
packWriter->buffer.clear();
|
||||
serializer->savedPointers.clear();
|
||||
}
|
||||
|
||||
CPack * CConnection::retrievePack(const std::vector<std::byte> & data)
|
||||
std::unique_ptr<CPack> CConnection::retrievePack(const std::vector<std::byte> & data)
|
||||
{
|
||||
CPack * result;
|
||||
std::unique_ptr<CPack> result;
|
||||
|
||||
packReader->buffer = &data;
|
||||
packReader->position = 0;
|
||||
@ -102,7 +102,7 @@ CPack * CConnection::retrievePack(const std::vector<std::byte> & data)
|
||||
if (packReader->position != data.size())
|
||||
throw std::runtime_error("Failed to retrieve pack! Not all data has been read!");
|
||||
|
||||
logNetwork->trace("Received CPack of type %s", typeid(*result).name());
|
||||
logNetwork->trace("Received CPack of type %s", typeid(result.get()).name());
|
||||
deserializer->loadedPointers.clear();
|
||||
deserializer->loadedSharedPointers.clear();
|
||||
return result;
|
||||
|
@ -51,8 +51,8 @@ public:
|
||||
explicit CConnection(std::weak_ptr<INetworkConnection> networkConnection);
|
||||
~CConnection();
|
||||
|
||||
void sendPack(const CPack * pack);
|
||||
CPack * retrievePack(const std::vector<std::byte> & data);
|
||||
void sendPack(const CPack & pack);
|
||||
std::unique_ptr<CPack> retrievePack(const std::vector<std::byte> & data);
|
||||
|
||||
void enterLobbyConnectionMode();
|
||||
void setCallback(IGameCallback * cb);
|
||||
|
@ -61,6 +61,7 @@ enum class ESerializationVersion : int32_t
|
||||
CAMPAIGN_OUTRO_SUPPORT, // 862 - support for campaign outro video
|
||||
REWARDABLE_BANKS, // 863 - team state contains list of scouted objects, coast visitable rewardable objects
|
||||
REGION_LABEL, // 864 - labels for campaign regions
|
||||
SPELL_RESEARCH, // 865 - spell research
|
||||
|
||||
CURRENT = REGION_LABEL
|
||||
CURRENT = SPELL_RESEARCH
|
||||
};
|
||||
|
@ -288,6 +288,8 @@ void registerTypes(Serializer &s)
|
||||
s.template registerType<LobbySetDifficulty>(238);
|
||||
s.template registerType<LobbyForceSetPlayer>(239);
|
||||
s.template registerType<LobbySetExtraOptions>(240);
|
||||
s.template registerType<SpellResearch>(241);
|
||||
s.template registerType<SetResearchedSpells>(242);
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -105,7 +105,7 @@ ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(SpellCastEnviron
|
||||
GiveBonus gb;
|
||||
gb.id = ObjectInstanceID(parameters.caster->getCasterUnitId());
|
||||
gb.bonus = b;
|
||||
env->apply(&gb);
|
||||
env->apply(gb);
|
||||
}
|
||||
|
||||
return ESpellCastResult::OK;
|
||||
@ -136,7 +136,7 @@ void AdventureSpellMechanics::performCast(SpellCastEnvironment * env, const Adve
|
||||
AdvmapSpellCast asc;
|
||||
asc.casterID = ObjectInstanceID(parameters.caster->getCasterUnitId());
|
||||
asc.spellID = owner->id;
|
||||
env->apply(&asc);
|
||||
env->apply(asc);
|
||||
|
||||
ESpellCastResult result = applyAdventureEffects(env, parameters);
|
||||
|
||||
@ -194,7 +194,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment
|
||||
iw.player = parameters.caster->getCasterOwner();
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 336); //%s tried to summon a boat, but failed.
|
||||
parameters.caster->getCasterName(iw.text);
|
||||
env->apply(&iw);
|
||||
env->apply(iw);
|
||||
return ESpellCastResult::OK;
|
||||
}
|
||||
|
||||
@ -226,14 +226,14 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment
|
||||
cop.objid = nearest->id;
|
||||
cop.nPos = summonPos;
|
||||
cop.initiator = parameters.caster->getCasterOwner();
|
||||
env->apply(&cop);
|
||||
env->apply(cop);
|
||||
}
|
||||
else if(schoolLevel < 2) //none or basic level -> cannot create boat :(
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->getCasterOwner();
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 335); //There are no boats to summon.
|
||||
env->apply(&iw);
|
||||
env->apply(iw);
|
||||
return ESpellCastResult::ERROR;
|
||||
}
|
||||
else //create boat
|
||||
@ -282,7 +282,7 @@ ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(SpellCastEnvironmen
|
||||
iw.player = parameters.caster->getCasterOwner();
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 337); //%s tried to scuttle the boat, but failed
|
||||
parameters.caster->getCasterName(iw.text);
|
||||
env->apply(&iw);
|
||||
env->apply(iw);
|
||||
return ESpellCastResult::OK;
|
||||
}
|
||||
|
||||
@ -291,7 +291,7 @@ ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(SpellCastEnvironmen
|
||||
RemoveObject ro;
|
||||
ro.initiator = parameters.caster->getCasterOwner();
|
||||
ro.objectID = t.visitableObjects.back()->id;
|
||||
env->apply(&ro);
|
||||
env->apply(ro);
|
||||
return ESpellCastResult::OK;
|
||||
}
|
||||
|
||||
@ -400,14 +400,14 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironm
|
||||
{
|
||||
// SOD: DD to such "wrong" terrain results in mana and move points spending, but fails to move hero
|
||||
iw.text = MetaString::createFromTextID("core.genrltxt.70"); // Dimension Door failed!
|
||||
env->apply(&iw);
|
||||
env->apply(iw);
|
||||
// no return - resources will be spent
|
||||
}
|
||||
else
|
||||
{
|
||||
// HotA: game will show error message without taking mana or move points, even when DD into terra incognita
|
||||
iw.text = MetaString::createFromTextID("vcmi.dimensionDoor.seaToLandError");
|
||||
env->apply(&iw);
|
||||
env->apply(iw);
|
||||
return ESpellCastResult::CANCEL;
|
||||
}
|
||||
}
|
||||
@ -415,7 +415,7 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironm
|
||||
GiveBonus gb;
|
||||
gb.id = ObjectInstanceID(parameters.caster->getCasterUnitId());
|
||||
gb.bonus = Bonus(BonusDuration::ONE_DAY, BonusType::NONE, BonusSource::SPELL_EFFECT, 0, BonusSourceID(owner->id));
|
||||
env->apply(&gb);
|
||||
env->apply(gb);
|
||||
|
||||
SetMovePoints smp;
|
||||
smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId());
|
||||
@ -423,7 +423,7 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironm
|
||||
smp.val = parameters.caster->getHeroCaster()->movementPointsRemaining() - movementCost;
|
||||
else
|
||||
smp.val = 0;
|
||||
env->apply(&smp);
|
||||
env->apply(smp);
|
||||
|
||||
return ESpellCastResult::OK;
|
||||
}
|
||||
@ -471,7 +471,7 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->getCasterOwner();
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 123);
|
||||
env->apply(&iw);
|
||||
env->apply(iw);
|
||||
return ESpellCastResult::CANCEL;
|
||||
}
|
||||
}
|
||||
@ -539,7 +539,7 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->getCasterOwner();
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 135);
|
||||
env->apply(&iw);
|
||||
env->apply(iw);
|
||||
return ESpellCastResult::ERROR;
|
||||
}
|
||||
|
||||
@ -568,7 +568,7 @@ void TownPortalMechanics::endCast(SpellCastEnvironment * env, const AdventureSpe
|
||||
SetMovePoints smp;
|
||||
smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId());
|
||||
smp.val = std::max<ui32>(0, parameters.caster->getHeroCaster()->movementPointsRemaining() - moveCost);
|
||||
env->apply(&smp);
|
||||
env->apply(smp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -587,7 +587,7 @@ ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, cons
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->getCasterOwner();
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 124);
|
||||
env->apply(&iw);
|
||||
env->apply(iw);
|
||||
return ESpellCastResult::CANCEL;
|
||||
}
|
||||
|
||||
@ -598,7 +598,7 @@ ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, cons
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->getCasterOwner();
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 125);
|
||||
env->apply(&iw);
|
||||
env->apply(iw);
|
||||
return ESpellCastResult::CANCEL;
|
||||
}
|
||||
|
||||
@ -643,7 +643,7 @@ ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, cons
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->getCasterOwner();
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 124);
|
||||
env->apply(&iw);
|
||||
env->apply(iw);
|
||||
return ESpellCastResult::CANCEL;
|
||||
}
|
||||
|
||||
@ -737,7 +737,7 @@ ESpellCastResult ViewMechanics::applyAdventureEffects(SpellCastEnvironment * env
|
||||
}
|
||||
pack.showTerrain = showTerrain(spellLevel);
|
||||
|
||||
env->apply(&pack);
|
||||
env->apply(pack);
|
||||
|
||||
return ESpellCastResult::OK;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user