1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-15 00:05:02 +02:00

Completely remove IGameCallback class

- CClient now inherits directly from CPrivilegedInfoCallback, like
IGameCallback did before. However CClient no longer needs dummy
implementation of IGameEventCallback
- CGObjectInstance hierarchy now uses CPrivilegedInfoCallback for
callback. Actual events can only be emitted in calls that receive
IGameEventCallback pointer, e.g. heroVisit
- CGameHandler now inherits directly from both CPrivilegedInfoCallback
and IGameEventCallback as it did before via IGameCallback
This commit is contained in:
Ivan Savenko
2025-05-13 15:24:45 +03:00
parent f7d08a7288
commit 716da918f8
109 changed files with 854 additions and 933 deletions

View File

@ -12,7 +12,8 @@
#include "MiscObjects.h"
#include "../bonuses/Propagators.h"
#include "../callback/IGameCallback.h"
#include "../callback/CPrivilegedInfoCallback.h"
#include "../callback/IGameEventCallback.h"
#include "../constants/StringConstants.h"
#include "../entities/artifact/ArtifactUtils.h"
#include "../entities/artifact/CArtifact.h"
@ -73,13 +74,13 @@ bool CTeamVisited::wasVisited(const TeamID & team) const
}
//CGMine
void CGMine::onHeroVisit( const CGHeroInstance * h ) const
void CGMine::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
{
auto relations = cb->gameState().getPlayerRelations(h->tempOwner, tempOwner);
if(relations == PlayerRelations::SAME_PLAYER) //we're visiting our mine
{
cb->showGarrisonDialog(id,h->id,true);
gameEvents.showGarrisonDialog(id,h->id,true);
return;
}
else if (relations == PlayerRelations::ALLIES)//ally
@ -90,11 +91,11 @@ void CGMine::onHeroVisit( const CGHeroInstance * h ) const
BlockingDialog ynd(true,false);
ynd.player = h->tempOwner;
ynd.text.appendLocalString(EMetaText::ADVOB_TXT, isAbandoned() ? 84 : 187);
cb->showBlockingDialog(this, &ynd);
gameEvents.showBlockingDialog(this, &ynd);
return;
}
flagMine(h->tempOwner);
flagMine(gameEvents, h->tempOwner);
}
void CGMine::initObj(vstd::RNG & rand)
@ -172,17 +173,17 @@ std::string CGMine::getHoverText(PlayerColor player) const
return hoverName;
}
void CGMine::flagMine(const PlayerColor & player) const
void CGMine::flagMine(IGameEventCallback & gameEvents, const PlayerColor & player) const
{
assert(tempOwner != player);
cb->setOwner(this, player); //not ours? flag it!
gameEvents.setOwner(this, player); //not ours? flag it!
InfoWindow iw;
iw.type = EInfoWindowMode::AUTO;
iw.text.appendTextID(TextIdentifier("core.mineevnt", producedResource.getNum()).get()); //not use subID, abandoned mines uses default mine texts
iw.player = player;
iw.components.emplace_back(ComponentType::RESOURCE_PER_DAY, producedResource, getProducedQuantity());
cb->showInfoDialog(&iw);
gameEvents.showInfoDialog(&iw);
}
ui32 CGMine::defaultResProduction() const
@ -206,22 +207,22 @@ ui32 CGMine::getProducedQuantity() const
return vstd::divideAndCeil(producedQuantity * playerSettings->handicap.percentIncome, 100);
}
void CGMine::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
void CGMine::battleFinished(IGameEventCallback & gameEvents, const CGHeroInstance *hero, const BattleResult &result) const
{
if(result.winner == BattleSide::ATTACKER) //attacker won
{
if(isAbandoned())
{
hero->showInfoDialog(85);
hero->showInfoDialog(gameEvents, 85);
}
flagMine(hero->tempOwner);
flagMine(gameEvents, hero->tempOwner);
}
}
void CGMine::blockingDialogAnswered(const CGHeroInstance *hero, int32_t answer) const
void CGMine::blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const
{
if(answer)
cb->startBattle(hero, this);
gameEvents.startBattle(hero, this);
}
void CGMine::serializeJsonOptions(JsonSerializeFormat & handler)
@ -295,11 +296,11 @@ std::vector<ObjectInstanceID> CGTeleport::getAllExits(bool excludeCurrent) const
return ret;
}
ObjectInstanceID CGTeleport::getRandomExit(const CGHeroInstance * h) const
ObjectInstanceID CGTeleport::getRandomExit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
{
auto passableExits = getPassableExits(cb->gameState(), h, getAllExits(true));
if(!passableExits.empty())
return *RandomGeneratorUtil::nextItem(passableExits, cb->gameState().getRandomGenerator());
return *RandomGeneratorUtil::nextItem(passableExits, gameEvents.getRandomGenerator());
return ObjectInstanceID();
}
@ -385,7 +386,7 @@ TeleportChannelID CGMonolith::findMeChannel(const std::vector<Obj> & IDs, MapObj
return TeleportChannelID();
}
void CGMonolith::onHeroVisit( const CGHeroInstance * h ) const
void CGMonolith::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
{
TeleportDialog td(h->id, channel);
if(isEntrance())
@ -404,19 +405,19 @@ void CGMonolith::onHeroVisit( const CGHeroInstance * h ) const
logGlobal->debug("Cannot find corresponding exit monolith for %d at %s", id.getNum(), anchorPos().toString());
td.impassable = true;
}
else if(getRandomExit(h) == ObjectInstanceID())
else if(getRandomExit(gameEvents, h) == ObjectInstanceID())
logGlobal->debug("All exits blocked for monolith %d at %s", id.getNum(), anchorPos().toString());
}
else
h->showInfoDialog(70);
h->showInfoDialog(gameEvents, 70);
cb->showTeleportDialog(&td);
gameEvents.showTeleportDialog(&td);
}
void CGMonolith::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const
void CGMonolith::teleportDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const
{
int3 dPos;
auto randomExit = getRandomExit(hero);
auto randomExit = getRandomExit(gameEvents, hero);
auto realExits = getAllExits(true);
if(!isEntrance() // Do nothing if hero visited exit only object
|| (exits.empty() && realExits.empty()) // Do nothing if there no exits on this channel
@ -429,7 +430,7 @@ void CGMonolith::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer,
else
dPos = cb->getObj(randomExit)->visitablePos();
cb->moveHero(hero->id, hero->convertFromVisitablePos(dPos), EMovementMode::MONOLITH);
gameEvents.moveHero(hero->id, hero->convertFromVisitablePos(dPos), EMovementMode::MONOLITH);
}
void CGMonolith::initObj(vstd::RNG & rand)
@ -459,22 +460,22 @@ void CGMonolith::initObj(vstd::RNG & rand)
addToChannel(cb->gameState().getMap().teleportChannels, this);
}
void CGSubterraneanGate::onHeroVisit( const CGHeroInstance * h ) const
void CGSubterraneanGate::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
{
TeleportDialog td(h->id, channel);
if(cb->isTeleportChannelImpassable(channel))
{
h->showInfoDialog(153);//Just inside the entrance you find a large pile of rubble blocking the tunnel. You leave discouraged.
h->showInfoDialog(gameEvents, 153);//Just inside the entrance you find a large pile of rubble blocking the tunnel. You leave discouraged.
logGlobal->debug("Cannot find exit subterranean gate for %d at %s", id.getNum(), anchorPos().toString());
td.impassable = true;
}
else
{
auto exit = getRandomExit(h);
auto exit = getRandomExit(gameEvents, h);
td.exits.push_back(std::make_pair(exit, cb->getObj(exit)->visitablePos()));
}
cb->showTeleportDialog(&td);
gameEvents.showTeleportDialog(&td);
}
void CGSubterraneanGate::initObj(vstd::RNG & rand)
@ -482,7 +483,7 @@ void CGSubterraneanGate::initObj(vstd::RNG & rand)
type = BOTH;
}
void CGSubterraneanGate::postInit(IGameCallback * cb) //matches subterranean gates into pairs
void CGSubterraneanGate::postInit(CPrivilegedInfoCallback * cb) //matches subterranean gates into pairs
{
//split on underground and surface gates
std::vector<CGSubterraneanGate *> gatesSplit[2]; //surface and underground gates
@ -538,7 +539,7 @@ void CGSubterraneanGate::postInit(IGameCallback * cb) //matches subterranean gat
assignToChannel(i);
}
void CGWhirlpool::onHeroVisit( const CGHeroInstance * h ) const
void CGWhirlpool::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
{
TeleportDialog td(h->id, channel);
if(cb->isTeleportChannelImpassable(channel))
@ -546,7 +547,7 @@ void CGWhirlpool::onHeroVisit( const CGHeroInstance * h ) const
logGlobal->debug("Cannot find exit whirlpool for %d at %s", id.getNum(), anchorPos().toString());
td.impassable = true;
}
else if(getRandomExit(h) == ObjectInstanceID())
else if(getRandomExit(gameEvents, h) == ObjectInstanceID())
logGlobal->debug("All exits are blocked for whirlpool %d at %s", id.getNum(), anchorPos().toString());
if(!isProtected(h))
@ -566,8 +567,8 @@ void CGWhirlpool::onHeroVisit( const CGHeroInstance * h ) const
iw.player = h->tempOwner;
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 168);
iw.components.emplace_back(ComponentType::CREATURE, h->getCreature(targetstack)->getId(), -countToTake);
cb->showInfoDialog(&iw);
cb->changeStackCount(StackLocation(h->id, targetstack), -countToTake);
gameEvents.showInfoDialog(&iw);
gameEvents.changeStackCount(StackLocation(h->id, targetstack), -countToTake);
}
else
{
@ -580,10 +581,10 @@ void CGWhirlpool::onHeroVisit( const CGHeroInstance * h ) const
}
}
cb->showTeleportDialog(&td);
gameEvents.showTeleportDialog(&td);
}
void CGWhirlpool::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const
void CGWhirlpool::teleportDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const
{
int3 dPos;
auto realExits = getAllExits();
@ -593,17 +594,17 @@ void CGWhirlpool::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer
dPos = exits[answer].second;
else
{
auto exit = getRandomExit(hero);
auto exit = getRandomExit(gameEvents, hero);
if(exit == ObjectInstanceID())
return;
const auto * obj = cb->getObj(exit);
std::set<int3> tiles = obj->getBlockedPos();
dPos = *RandomGeneratorUtil::nextItem(tiles, cb->gameState().getRandomGenerator());
dPos = *RandomGeneratorUtil::nextItem(tiles, gameEvents.getRandomGenerator());
}
cb->moveHero(hero->id, hero->convertFromVisitablePos(dPos), EMovementMode::MONOLITH);
gameEvents.moveHero(hero->id, hero->convertFromVisitablePos(dPos), EMovementMode::MONOLITH);
}
bool CGWhirlpool::isProtected(const CGHeroInstance * h)
@ -708,7 +709,7 @@ std::vector<Component> CGArtifact::getPopupComponents(PlayerColor player) const
};
}
void CGArtifact::onHeroVisit(const CGHeroInstance * h) const
void CGArtifact::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
{
if(!stacksCount())
{
@ -748,8 +749,8 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const
{
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 2);
}
cb->showInfoDialog(&iw);
pick(h);
gameEvents.showInfoDialog(&iw);
pick(gameEvents, h);
}
else
{
@ -769,7 +770,7 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const
ynd.text.replaceRawString(getArmyDescription());
ynd.text.replaceLocalString(EMetaText::GENERAL_TXT, 43); // creatures
}
cb->showBlockingDialog(this, &ynd);
gameEvents.showBlockingDialog(this, &ynd);
}
break;
case Obj::SPELL_SCROLL:
@ -779,20 +780,20 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const
BlockingDialog ynd(true,false);
ynd.player = h->getOwner();
ynd.text = message;
cb->showBlockingDialog(this, &ynd);
gameEvents.showBlockingDialog(this, &ynd);
}
else
blockingDialogAnswered(h, true);
blockingDialogAnswered(gameEvents, h, true);
}
break;
}
}
}
void CGArtifact::pick(const CGHeroInstance * h) const
void CGArtifact::pick(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
{
if(cb->putArtifact(ArtifactLocation(h->id, ArtifactPosition::FIRST_AVAILABLE), getArtifactInstance()->getId()))
cb->removeObject(this, h->getOwner());
if(gameEvents.putArtifact(ArtifactLocation(h->id, ArtifactPosition::FIRST_AVAILABLE), getArtifactInstance()->getId()))
gameEvents.removeObject(this, h->getOwner());
}
BattleField CGArtifact::getBattlefield() const
@ -800,16 +801,16 @@ BattleField CGArtifact::getBattlefield() const
return BattleField::NONE;
}
void CGArtifact::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
void CGArtifact::battleFinished(IGameEventCallback & gameEvents, const CGHeroInstance *hero, const BattleResult &result) const
{
if(result.winner == BattleSide::ATTACKER) //attacker won
pick(hero);
pick(gameEvents, hero);
}
void CGArtifact::blockingDialogAnswered(const CGHeroInstance *hero, int32_t answer) const
void CGArtifact::blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const
{
if(answer)
cb->startBattle(hero, this);
gameEvents.startBattle(hero, this);
}
void CGArtifact::serializeJsonOptions(JsonSerializeFormat& handler)
@ -844,15 +845,15 @@ void CGSignBottle::initObj(vstd::RNG & rand)
}
}
void CGSignBottle::onHeroVisit( const CGHeroInstance * h ) const
void CGSignBottle::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
{
InfoWindow iw;
iw.player = h->getOwner();
iw.text = message;
cb->showInfoDialog(&iw);
gameEvents.showInfoDialog(&iw);
if(ID == Obj::OCEAN_BOTTLE)
cb->removeObject(this, h->getOwner());
gameEvents.removeObject(this, h->getOwner());
}
void CGSignBottle::serializeJsonOptions(JsonSerializeFormat& handler)
@ -875,20 +876,20 @@ std::vector<CreatureID> CGGarrison::providedCreatures() const
return {};
}
void CGGarrison::onHeroVisit (const CGHeroInstance *h) const
void CGGarrison::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
{
auto relations = cb->gameState().getPlayerRelations(h->tempOwner, tempOwner);
if (relations == PlayerRelations::ENEMIES && stacksCount() > 0) {
//TODO: Find a way to apply magic garrison effects in battle.
cb->startBattle(h, this);
gameEvents.startBattle(h, this);
return;
}
//New owner.
if (relations == PlayerRelations::ENEMIES)
cb->setOwner(this, h->tempOwner);
gameEvents.setOwner(this, h->tempOwner);
cb->showGarrisonDialog(id, h->id, removableUnits);
gameEvents.showGarrisonDialog(id, h->id, removableUnits);
}
bool CGGarrison::passableFor(PlayerColor player) const
@ -905,10 +906,10 @@ bool CGGarrison::passableFor(PlayerColor player) const
return false;
}
void CGGarrison::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
void CGGarrison::battleFinished(IGameEventCallback & gameEvents, const CGHeroInstance *hero, const BattleResult &result) const
{
if (result.winner == BattleSide::ATTACKER)
onHeroVisit(hero);
onHeroVisit(gameEvents, hero);
}
void CGGarrison::serializeJsonOptions(JsonSerializeFormat& handler)
@ -941,11 +942,11 @@ void CGMagi::initObj(vstd::RNG & rand)
blockVisit = true;
}
void CGMagi::onHeroVisit(const CGHeroInstance * h) const
void CGMagi::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
{
if (ID == Obj::HUT_OF_MAGI)
{
h->showInfoDialog(61);
h->showInfoDialog(gameEvents, 61);
std::vector<const CGObjectInstance *> eyes;
@ -969,23 +970,23 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const
for(const auto & eye : eyes)
{
cb->getTilesInRange (fw.tiles, eye->visitablePos(), 10, ETileVisibility::HIDDEN, h->tempOwner);
cb->sendAndApply(fw);
gameEvents.sendAndApply(fw);
cv.pos = eye->visitablePos();
cb->sendAndApply(cv);
gameEvents.sendAndApply(cv);
}
cv.pos = h->visitablePos();
cv.focusTime = 0;
cb->sendAndApply(cv);
gameEvents.sendAndApply(cv);
}
}
else if (ID == Obj::EYE_OF_MAGI)
{
h->showInfoDialog(48);
h->showInfoDialog(gameEvents, 48);
}
}
CGBoat::CGBoat(IGameCallback * cb)
CGBoat::CGBoat(CPrivilegedInfoCallback * cb)
: CGObjectInstance(cb)
{
direction = 4;
@ -1023,7 +1024,7 @@ std::string CGSirens::getHoverText(const CGHeroInstance * hero) const
return getObjectName() + " " + visitedTxt(hero->hasBonusFrom(BonusSource::OBJECT_TYPE, BonusSourceID(ID)));
}
void CGSirens::onHeroVisit( const CGHeroInstance * h ) const
void CGSirens::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
{
InfoWindow iw;
iw.player = h->tempOwner;
@ -1034,7 +1035,7 @@ void CGSirens::onHeroVisit( const CGHeroInstance * h ) const
}
else
{
giveDummyBonus(h->id, BonusDuration::ONE_BATTLE);
giveDummyBonus(gameEvents, h->id, BonusDuration::ONE_BATTLE);
TExpType xp = 0;
for (auto i = h->Slots().begin(); i != h->Slots().end(); i++)
@ -1048,7 +1049,7 @@ void CGSirens::onHeroVisit( const CGHeroInstance * h ) const
if(drown)
{
cb->changeStackCount(StackLocation(h->id, i->first), -drown);
gameEvents.changeStackCount(StackLocation(h->id, i->first), -drown);
xp += drown * i->second->getType()->getMaxHealth();
}
}
@ -1058,14 +1059,14 @@ void CGSirens::onHeroVisit( const CGHeroInstance * h ) const
xp = h->calculateXp(static_cast<int>(xp));
iw.text.appendLocalString(EMetaText::ADVOB_TXT,132);
iw.text.replaceNumber(static_cast<int>(xp));
cb->giveExperience(h, xp);
gameEvents.giveExperience(h, xp);
}
else
{
iw.text.appendLocalString(EMetaText::ADVOB_TXT,134);
}
}
cb->showInfoDialog(&iw);
gameEvents.showInfoDialog(&iw);
}
void CGShipyard::getOutOffsets( std::vector<int3> &offsets ) const
@ -1094,10 +1095,10 @@ const IObjectInterface * CGShipyard::getObject() const
return this;
}
void CGShipyard::onHeroVisit( const CGHeroInstance * h ) const
void CGShipyard::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
{
if(cb->gameState().getPlayerRelations(tempOwner, h->tempOwner) == PlayerRelations::ENEMIES)
cb->setOwner(this, h->tempOwner);
gameEvents.setOwner(this, h->tempOwner);
if(shipyardStatus() != IBoatGenerator::GOOD)
{
@ -1105,11 +1106,11 @@ void CGShipyard::onHeroVisit( const CGHeroInstance * h ) const
iw.type = EInfoWindowMode::AUTO;
iw.player = tempOwner;
getProblemText(iw.text, h);
cb->showInfoDialog(&iw);
gameEvents.showInfoDialog(&iw);
}
else
{
cb->showObjectWindow(this, EOpenWindowMode::SHIPYARD_WINDOW, h, false);
gameEvents.showObjectWindow(this, EOpenWindowMode::SHIPYARD_WINDOW, h, false);
}
}
@ -1138,12 +1139,12 @@ std::vector<CreatureID> CGShipyard::providedCreatures() const
return {};
}
void CGDenOfthieves::onHeroVisit (const CGHeroInstance * h) const
void CGDenOfthieves::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
{
cb->showObjectWindow(this, EOpenWindowMode::THIEVES_GUILD, h, false);
gameEvents.showObjectWindow(this, EOpenWindowMode::THIEVES_GUILD, h, false);
}
void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const
void CGObelisk::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
{
InfoWindow iw;
iw.type = EInfoWindowMode::AUTO;
@ -1155,22 +1156,22 @@ void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const
if(!wasVisited(team))
{
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 96);
cb->sendAndApply(iw);
gameEvents.sendAndApply(iw);
// increment general visited obelisks counter
cb->setObjPropertyID(id, ObjProperty::OBELISK_VISITED, team);
cb->showObjectWindow(this, EOpenWindowMode::PUZZLE_MAP, h, false);
gameEvents.setObjPropertyID(id, ObjProperty::OBELISK_VISITED, team);
gameEvents.showObjectWindow(this, EOpenWindowMode::PUZZLE_MAP, h, false);
// mark that particular obelisk as visited for all players in the team
for(const auto & color : ts->players)
{
cb->setObjPropertyID(id, ObjProperty::VISITED, color);
gameEvents.setObjPropertyID(id, ObjProperty::VISITED, color);
}
}
else
{
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 97);
cb->sendAndApply(iw);
gameEvents.sendAndApply(iw);
}
}
@ -1213,9 +1214,9 @@ void CGObelisk::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
}
}
void HillFort::onHeroVisit(const CGHeroInstance * h) const
void HillFort::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
{
cb->showObjectWindow(this, EOpenWindowMode::HILL_FORT_WINDOW, h, false);
gameEvents.showObjectWindow(this, EOpenWindowMode::HILL_FORT_WINDOW, h, false);
}
void HillFort::fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const