1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-24 08:32:34 +02:00

Merge pull request #4609 from IvanSavenko/building_fixes

Building fixes
This commit is contained in:
Ivan Savenko 2024-09-17 17:50:49 +03:00 committed by GitHub
commit d0ac6458b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 110 additions and 35 deletions

View File

@ -54,6 +54,7 @@
#include "../../lib/entities/building/CBuilding.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/mapObjects/TownBuildingInstance.h"
static bool useCompactCreatureBox()
@ -845,7 +846,21 @@ bool CCastleBuildings::buildingTryActivateCustomUI(BuildingID buildingToTest, Bu
void CCastleBuildings::enterRewardable(BuildingID building)
{
LOCPLINT->cb->visitTownBuilding(town, building);
if (town->visitingHero == nullptr)
{
MetaString message;
message.appendTextID("core.genrltxt.273"); // only visiting heroes may visit %s
message.replaceTextID(town->town->buildings.at(building)->getNameTextID());
LOCPLINT->showInfoDialog(message.toString());
}
else
{
if (town->rewardableBuildings.at(building)->wasVisited(town->visitingHero))
enterBuilding(building);
else
LOCPLINT->cb->visitTownBuilding(town, building);
}
}
void CCastleBuildings::enterBlacksmith(BuildingID building, ArtifactID artifactID)

View File

@ -197,6 +197,7 @@ These are just a couple of examples of what can be done in VCMI. See vcmi config
"bonuses" : [ BONUS_FORMAT ]
// If set to true, this building will not automatically activate on new day or on entering town and needs to be activated manually on click
// Note that such building can only be activated by visiting hero, and not by garrisoned hero.
"manualHeroVisit" : false,
// Bonuses provided by this special building if this building or any of its upgrades are constructed in town

View File

@ -246,6 +246,9 @@ void CRewardableObject::blockingDialogAnswered(const CGHeroInstance * hero, int3
}
else
{
if (answer == 0)
return; //Player refused
if(answer > 0 && answer - 1 < configuration.info.size())
{
auto list = getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT);

View File

@ -165,6 +165,11 @@ void TownRewardableBuildingInstance::grantReward(ui32 rewardID, const CGHeroInst
}
}
bool TownRewardableBuildingInstance::wasVisited(const CGHeroInstance * contextHero) const
{
return wasVisitedBefore(contextHero);
}
bool TownRewardableBuildingInstance::wasVisitedBefore(const CGHeroInstance * contextHero) const
{
switch (configuration.visitMode)

View File

@ -70,6 +70,7 @@ class DLL_LINKAGE TownRewardableBuildingInstance : public TownBuildingInstance,
public:
void setProperty(ObjProperty what, ObjPropertyID identifier) override;
void onHeroVisit(const CGHeroInstance * h) const override;
bool wasVisited(const CGHeroInstance * contextHero) const override;
void newTurn(vstd::RNG & rand) const override;

View File

@ -1171,7 +1171,6 @@ void CGameHandler::heroVisitCastle(const CGTownInstance * obj, const CGHeroInsta
sendAndApply(&vc);
}
visitCastleObjects(obj, hero);
giveSpells (obj, hero);
if (obj->visitingHero && obj->garrisonHero)
useScholarSkill(obj->visitingHero->id, obj->garrisonHero->id);
@ -1180,10 +1179,27 @@ void CGameHandler::heroVisitCastle(const CGTownInstance * obj, const CGHeroInsta
void CGameHandler::visitCastleObjects(const CGTownInstance * t, const CGHeroInstance * h)
{
std::vector<const CGHeroInstance * > visitors;
visitors.push_back(h);
visitCastleObjects(t, visitors);
}
void CGameHandler::visitCastleObjects(const CGTownInstance * t, std::vector<const CGHeroInstance * > visitors)
{
std::vector<BuildingID> buildingsToVisit;
for (auto const & hero : visitors)
giveSpells (t, hero);
for (auto & building : t->rewardableBuildings)
{
if (!t->town->buildings.at(building.first)->manualHeroVisit)
building.second->onHeroVisit(h);
buildingsToVisit.push_back(building.first);
}
if (!buildingsToVisit.empty())
{
auto visitQuery = std::make_shared<TownBuildingVisitQuery>(this, t, visitors, buildingsToVisit);
queries->addQuery(visitQuery);
}
}
@ -2144,10 +2160,15 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
if (!force)
{
if(t->garrisonHero) //garrison hero first - consistent with original H3 Mana Vortex and Battle Scholar Academy levelup windows order
objectVisited(t, t->garrisonHero);
if(t->visitingHero)
objectVisited(t, t->visitingHero);
//garrison hero first - consistent with original H3 Mana Vortex and Battle Scholar Academy levelup windows order
std::vector<const CGHeroInstance *> visitors;
if (t->garrisonHero)
visitors.push_back(t->garrisonHero);
if (t->visitingHero)
visitors.push_back(t->visitingHero);
if (!visitors.empty())
visitCastleObjects(t, visitors);
}
checkVictoryLossConditionsForPlayer(t->tempOwner);
@ -2173,19 +2194,15 @@ bool CGameHandler::visitTownBuilding(ObjectInstanceID tid, BuildingID bid)
return true;
}
if (t->rewardableBuildings.count(bid))
if (t->rewardableBuildings.count(bid) && t->visitingHero && t->town->buildings.at(bid)->manualHeroVisit)
{
auto & hero = t->garrisonHero ? t->garrisonHero : t->visitingHero;
auto * building = t->rewardableBuildings.at(bid);
if (hero && t->town->buildings.at(bid)->manualHeroVisit)
{
auto visitQuery = std::make_shared<TownBuildingVisitQuery>(this, t, hero, bid);
queries->addQuery(visitQuery);
building->onHeroVisit(hero);
queries->popIfTop(visitQuery);
return true;
}
std::vector<BuildingID> buildingsToVisit;
std::vector<const CGHeroInstance*> visitors;
buildingsToVisit.push_back(bid);
visitors.push_back(t->visitingHero);
auto visitQuery = std::make_shared<TownBuildingVisitQuery>(this, t, visitors, buildingsToVisit);
queries->addQuery(visitQuery);
return true;
}
return true;

View File

@ -182,6 +182,7 @@ public:
void visitObjectOnTile(const TerrainTile &t, const CGHeroInstance * h);
bool teleportHero(ObjectInstanceID hid, ObjectInstanceID dstid, ui8 source, PlayerColor asker = PlayerColor::NEUTRAL);
void visitCastleObjects(const CGTownInstance * obj, const CGHeroInstance * hero) override;
void visitCastleObjects(const CGTownInstance * obj, std::vector<const CGHeroInstance * > visitors);
void levelUpHero(const CGHeroInstance * hero, SecondarySkill skill);//handle client respond and send one more request if needed
void levelUpHero(const CGHeroInstance * hero);//initial call - check if hero have remaining levelups & handle them
void levelUpCommander (const CCommanderInstance * c, int skill); //secondary skill 1 to 6, special skill : skill - 100

View File

@ -11,6 +11,8 @@
#include "VisitQueries.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/mapObjects/TownBuildingInstance.h"
#include "../CGameHandler.h"
#include "QueriesProcessor.h"
@ -29,7 +31,7 @@ bool VisitQuery::blocksPack(const CPack * pack) const
return true;
}
void VisitQuery::onExposure(QueryPtr topQuery)
void MapObjectVisitQuery::onExposure(QueryPtr topQuery)
{
//Object may have been removed and deleted.
if(gh->isValidObject(visitedObject))
@ -54,13 +56,31 @@ void MapObjectVisitQuery::onRemoval(PlayerColor color)
gh->removeObject(visitedObject, color);
}
TownBuildingVisitQuery::TownBuildingVisitQuery(CGameHandler * owner, const CGObjectInstance * Obj, const CGHeroInstance * Hero, BuildingID buildingToVisit)
: VisitQuery(owner, Obj, Hero)
, visitedBuilding(buildingToVisit)
TownBuildingVisitQuery::TownBuildingVisitQuery(CGameHandler * owner, const CGTownInstance * Obj, std::vector<const CGHeroInstance *> heroes, std::vector<BuildingID> buildingToVisit)
: VisitQuery(owner, Obj, heroes.front())
, visitedTown(Obj)
{
// generate in reverse order - first building-hero pair to handle must be in the end of vector
for (auto const * hero : boost::adaptors::reverse(heroes))
for (auto const & building : boost::adaptors::reverse(buildingToVisit))
visitedBuilding.push_back({ hero, building});
}
void TownBuildingVisitQuery::onRemoval(PlayerColor color)
void TownBuildingVisitQuery::onExposure(QueryPtr topQuery)
{
onAdded(players.front());
}
void TownBuildingVisitQuery::onAdded(PlayerColor color)
{
while (!visitedBuilding.empty() && owner->topQuery(color).get() == this)
{
visitingHero = visitedBuilding.back().hero;
auto * building = visitedTown->rewardableBuildings.at(visitedBuilding.back().building);
building->onHeroVisit(visitingHero);
visitedBuilding.pop_back();
}
if (visitedBuilding.empty() && owner->topQuery(color).get() == this)
owner->popIfTop(*this);
}

View File

@ -11,19 +11,22 @@
#include "CQuery.h"
VCMI_LIB_NAMESPACE_BEGIN
class CGTownInstance;
VCMI_LIB_NAMESPACE_END
//Created when hero visits object.
//Removed when query above is resolved (or immediately after visit if no queries were created)
class VisitQuery : public CQuery
{
protected:
VisitQuery(CGameHandler * owner, const CGObjectInstance *Obj, const CGHeroInstance *Hero);
VisitQuery(CGameHandler * owner, const CGObjectInstance * Obj, const CGHeroInstance * Hero);
public:
const CGObjectInstance *visitedObject;
const CGHeroInstance *visitingHero;
const CGObjectInstance * visitedObject;
const CGHeroInstance * visitingHero;
bool blocksPack(const CPack *pack) const final;
void onExposure(QueryPtr topQuery) final;
bool blocksPack(const CPack * pack) const final;
};
class MapObjectVisitQuery final : public VisitQuery
@ -31,17 +34,26 @@ class MapObjectVisitQuery final : public VisitQuery
public:
bool removeObjectAfterVisit;
MapObjectVisitQuery(CGameHandler * owner, const CGObjectInstance *Obj, const CGHeroInstance *Hero);
MapObjectVisitQuery(CGameHandler * owner, const CGObjectInstance * Obj, const CGHeroInstance * Hero);
void onRemoval(PlayerColor color) final;
void onExposure(QueryPtr topQuery) final;
};
class TownBuildingVisitQuery final : public VisitQuery
{
struct BuildingVisit
{
const CGHeroInstance * hero;
BuildingID building;
};
const CGTownInstance * visitedTown;
std::vector<BuildingVisit> visitedBuilding;
public:
BuildingID visitedBuilding;
TownBuildingVisitQuery(CGameHandler * owner, const CGTownInstance * Obj, std::vector<const CGHeroInstance *> heroes, std::vector<BuildingID> buildingToVisit);
TownBuildingVisitQuery(CGameHandler * owner, const CGObjectInstance *Obj, const CGHeroInstance *Hero, BuildingID buildingToVisit);
void onRemoval(PlayerColor color) final;
void onAdded(PlayerColor color) final;
void onExposure(QueryPtr topQuery) final;
};