mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-20 20:23:03 +02:00
Implemented explicitly visitable town buildings, e.g. hota mana vortex
Added flag `manualHeroVisit` flag to town building. If this flag is set, then building will only be activated on click and will not give its effect on hero recrutiment, hero visit, or new day. This allows implementing changes to Mana Vortex from HotA
This commit is contained in:
parent
ee64928454
commit
327ff01471
@ -217,12 +217,12 @@ bool CCallback::buildBuilding(const CGTownInstance *town, BuildingID buildingID)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCallback::triggerTownSpecialBuildingAction(const CGTownInstance *town, BuildingSubID::EBuildingSubID subBuildingID)
|
||||
bool CCallback::visitTownBuilding(const CGTownInstance *town, BuildingID buildingID)
|
||||
{
|
||||
if(town->tempOwner!=player)
|
||||
return false;
|
||||
|
||||
TriggerTownSpecialBuildingAction pack(town->id, subBuildingID);
|
||||
VisitTownBuilding pack(town->id, buildingID);
|
||||
sendRequest(&pack);
|
||||
return true;
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ public:
|
||||
//town
|
||||
virtual void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero, const HeroTypeID & nextHero=HeroTypeID::NONE)=0;
|
||||
virtual bool buildBuilding(const CGTownInstance *town, BuildingID buildingID)=0;
|
||||
virtual bool triggerTownSpecialBuildingAction(const CGTownInstance *town, BuildingSubID::EBuildingSubID subBuildingID)=0;
|
||||
virtual bool visitTownBuilding(const CGTownInstance *town, BuildingID buildingID)=0;
|
||||
virtual void recruitCreatures(const CGDwelling *obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level=-1)=0;
|
||||
virtual bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE)=0; //if newID==-1 then best possible upgrade will be made
|
||||
virtual void swapGarrisonHero(const CGTownInstance *town)=0;
|
||||
@ -182,7 +182,7 @@ public:
|
||||
void manageHeroCostume(ObjectInstanceID hero, size_t costumeIdx, bool saveCostume) override;
|
||||
void eraseArtifactByClient(const ArtifactLocation & al) override;
|
||||
bool buildBuilding(const CGTownInstance *town, BuildingID buildingID) override;
|
||||
bool triggerTownSpecialBuildingAction(const CGTownInstance *town, BuildingSubID::EBuildingSubID subBuildingID) override;
|
||||
bool visitTownBuilding(const CGTownInstance *town, BuildingID buildingID) override;
|
||||
void recruitCreatures(const CGDwelling * obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level=-1) override;
|
||||
bool dismissCreature(const CArmedInstance *obj, SlotID stackPos) override;
|
||||
bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE) override;
|
||||
|
@ -743,6 +743,12 @@ bool CCastleBuildings::buildingTryActivateCustomUI(BuildingID buildingToTest, Bu
|
||||
}
|
||||
}
|
||||
|
||||
if (town->rewardableBuildings.count(buildingToTest) && town->town->buildings.at(buildingToTest)->manualHeroVisit)
|
||||
{
|
||||
enterRewardable(buildingToTest);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buildingToTest >= BuildingID::DWELL_FIRST)
|
||||
{
|
||||
enterDwelling((BuildingID::getLevelFromDwelling(buildingToTest)));
|
||||
@ -820,7 +826,7 @@ bool CCastleBuildings::buildingTryActivateCustomUI(BuildingID buildingToTest, Bu
|
||||
return true;
|
||||
|
||||
case BuildingSubID::BANK:
|
||||
enterBank();
|
||||
enterBank(buildingTarget);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -837,6 +843,11 @@ bool CCastleBuildings::buildingTryActivateCustomUI(BuildingID buildingToTest, Bu
|
||||
return false;
|
||||
}
|
||||
|
||||
void CCastleBuildings::enterRewardable(BuildingID building)
|
||||
{
|
||||
LOCPLINT->cb->visitTownBuilding(town, building);
|
||||
}
|
||||
|
||||
void CCastleBuildings::enterBlacksmith(BuildingID building, ArtifactID artifactID)
|
||||
{
|
||||
const CGHeroInstance *hero = town->visitingHero;
|
||||
@ -1053,7 +1064,7 @@ void CCastleBuildings::enterAnyThievesGuild()
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithTavern"));
|
||||
}
|
||||
|
||||
void CCastleBuildings::enterBank()
|
||||
void CCastleBuildings::enterBank(BuildingID building)
|
||||
{
|
||||
std::vector<std::shared_ptr<CComponent>> components;
|
||||
if(town->bonusValue.second > 0)
|
||||
@ -1064,7 +1075,7 @@ void CCastleBuildings::enterBank()
|
||||
else{
|
||||
|
||||
components.push_back(std::make_shared<CComponent>(ComponentType::RESOURCE, GameResID(GameResID::GOLD), 2500));
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->translate("vcmi.townStructure.bank.borrow"), [this](){ LOCPLINT->cb->triggerTownSpecialBuildingAction(town, BuildingSubID::BANK); }, nullptr, components);
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->translate("vcmi.townStructure.bank.borrow"), [this, building](){ LOCPLINT->cb->visitTownBuilding(town, building); }, nullptr, components);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,10 +167,11 @@ public:
|
||||
|
||||
void enterDwelling(int level);
|
||||
void enterTownHall();
|
||||
void enterRewardable(BuildingID building);
|
||||
void enterMagesGuild();
|
||||
void enterAnyMarket();
|
||||
void enterAnyThievesGuild();
|
||||
void enterBank();
|
||||
void enterBank(BuildingID building);
|
||||
void enterToTheQuickRecruitmentWindow();
|
||||
|
||||
bool buildingTryActivateCustomUI(BuildingID buildingToTest, BuildingID buildingTarget);
|
||||
|
@ -180,6 +180,7 @@
|
||||
"visitors" : true
|
||||
},
|
||||
"visitMode" : "hero", // Should be 'once' to match (somewhat buggy) H3 logic
|
||||
"visitMode" : "once",
|
||||
"rewards" : [
|
||||
{
|
||||
"limiter" : {
|
||||
|
@ -61,11 +61,15 @@
|
||||
"description" : "If set to true, this building will replace all bonuses from base building, leaving only bonuses defined by this building",
|
||||
"type" : "boolean"
|
||||
},
|
||||
"manualHeroVisit" : {
|
||||
"description" : "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",
|
||||
"type" : "boolean"
|
||||
},
|
||||
"configuration" : {
|
||||
"description" : "Optional, configuration of building that can be activated by visiting hero",
|
||||
"$ref" : "rewardable.json"
|
||||
},
|
||||
"firtufications" : {
|
||||
"fortifications" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : false,
|
||||
"description" : "Fortifications provided by this buildings, if any",
|
||||
|
@ -196,8 +196,12 @@ These are just a couple of examples of what can be done in VCMI. See vcmi config
|
||||
// Bonuses provided by this special building if this building or any of its upgrades are constructed in town
|
||||
"bonuses" : [ BONUS_FORMAT ]
|
||||
|
||||
// If set to true, this building will replace all bonuses from base building, leaving only bonuses defined by this building"
|
||||
"upgradeReplacesBonuses" : false,
|
||||
// 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
|
||||
"manualHeroVisit" : false,
|
||||
|
||||
// Bonuses provided by this special building if this building or any of its upgrades are constructed in town
|
||||
"bonuses" : [ BONUS_FORMAT ]
|
||||
|
||||
|
||||
// If the building is a market, it requires market mode.
|
||||
"marketModes" : [ "resource-resource", "resource-player" ],
|
||||
|
@ -44,6 +44,7 @@ public:
|
||||
BuildingID upgrade; /// indicates that building "upgrade" can be improved by this, -1 = empty
|
||||
BuildingSubID::EBuildingSubID subId; /// subtype for special buildings, -1 = the building is not special
|
||||
bool upgradeReplacesBonuses = false;
|
||||
bool manualHeroVisit = false;
|
||||
BonusList buildingBonuses;
|
||||
|
||||
Rewardable::Info rewardableObjectInfo; ///configurable rewards for special buildings
|
||||
|
@ -299,6 +299,9 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
|
||||
ret->resources = TResources(source["cost"]);
|
||||
ret->produce = TResources(source["produce"]);
|
||||
|
||||
ret->manualHeroVisit = source["manualHeroVisit"].Bool();
|
||||
ret->upgradeReplacesBonuses = source["upgradeReplacesBonuses"].Bool();
|
||||
|
||||
const JsonNode & fortifications = source["fortifications"];
|
||||
if (!fortifications.isNull())
|
||||
{
|
||||
|
@ -126,7 +126,7 @@ public:
|
||||
virtual void visitBulkSmartSplitStack(BulkSmartSplitStack & pack) {}
|
||||
virtual void visitDisbandCreature(DisbandCreature & pack) {}
|
||||
virtual void visitBuildStructure(BuildStructure & pack) {}
|
||||
virtual void visitTriggerTownSpecialBuildingAction(TriggerTownSpecialBuildingAction & pack) {}
|
||||
virtual void visitVisitTownBuilding(VisitTownBuilding & pack) {}
|
||||
virtual void visitRazeStructure(RazeStructure & pack) {}
|
||||
virtual void visitRecruitCreatures(RecruitCreatures & pack) {}
|
||||
virtual void visitUpgradeCreature(UpgradeCreature & pack) {}
|
||||
|
@ -582,9 +582,9 @@ void BuildStructure::visitTyped(ICPackVisitor & visitor)
|
||||
visitor.visitBuildStructure(*this);
|
||||
}
|
||||
|
||||
void TriggerTownSpecialBuildingAction::visitTyped(ICPackVisitor & visitor)
|
||||
void VisitTownBuilding::visitTyped(ICPackVisitor & visitor)
|
||||
{
|
||||
visitor.visitTriggerTownSpecialBuildingAction(*this);
|
||||
visitor.visitVisitTownBuilding(*this);
|
||||
}
|
||||
|
||||
void RazeStructure::visitTyped(ICPackVisitor & visitor)
|
||||
|
@ -280,16 +280,16 @@ struct DLL_LINKAGE BuildStructure : public CPackForServer
|
||||
}
|
||||
};
|
||||
|
||||
struct DLL_LINKAGE TriggerTownSpecialBuildingAction : public CPackForServer
|
||||
struct DLL_LINKAGE VisitTownBuilding : public CPackForServer
|
||||
{
|
||||
TriggerTownSpecialBuildingAction() = default;
|
||||
TriggerTownSpecialBuildingAction(const ObjectInstanceID & TID, const BuildingSubID::EBuildingSubID SID)
|
||||
VisitTownBuilding() = default;
|
||||
VisitTownBuilding(const ObjectInstanceID & TID, const BuildingID BID)
|
||||
: tid(TID)
|
||||
, sid(SID)
|
||||
, bid(BID)
|
||||
{
|
||||
}
|
||||
ObjectInstanceID tid;
|
||||
BuildingSubID::EBuildingSubID sid;
|
||||
BuildingID bid;
|
||||
|
||||
void visitTyped(ICPackVisitor & visitor) override;
|
||||
|
||||
@ -297,7 +297,7 @@ struct DLL_LINKAGE TriggerTownSpecialBuildingAction : public CPackForServer
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & tid;
|
||||
h & sid;
|
||||
h & bid;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -239,7 +239,7 @@ void registerTypes(Serializer &s)
|
||||
s.template registerType<ArrangeStacks>(183);
|
||||
s.template registerType<DisbandCreature>(184);
|
||||
s.template registerType<BuildStructure>(185);
|
||||
s.template registerType<TriggerTownSpecialBuildingAction>(186);
|
||||
s.template registerType<VisitTownBuilding>(186);
|
||||
s.template registerType<RecruitCreatures>(187);
|
||||
s.template registerType<UpgradeCreature>(188);
|
||||
s.template registerType<GarrisonHeroSwap>(189);
|
||||
|
@ -1181,8 +1181,11 @@ void CGameHandler::heroVisitCastle(const CGTownInstance * obj, const CGHeroInsta
|
||||
void CGameHandler::visitCastleObjects(const CGTownInstance * t, const CGHeroInstance * h)
|
||||
{
|
||||
for (auto & building : t->rewardableBuildings)
|
||||
{
|
||||
if (!t->town->buildings.at(building.first)->manualHeroVisit)
|
||||
building.second->onHeroVisit(h);
|
||||
}
|
||||
}
|
||||
|
||||
void CGameHandler::stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero)
|
||||
{
|
||||
@ -2148,20 +2151,39 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CGameHandler::triggerTownSpecialBuildingAction(ObjectInstanceID tid, BuildingSubID::EBuildingSubID sid)
|
||||
bool CGameHandler::visitTownBuilding(ObjectInstanceID tid, BuildingID bid)
|
||||
{
|
||||
const CGTownInstance * t = getTown(tid);
|
||||
|
||||
if(t->town->getBuildingType(sid) == BuildingID::NONE)
|
||||
if(!t->hasBuilt(bid))
|
||||
return false;
|
||||
|
||||
if(sid == BuildingSubID::EBuildingSubID::BANK)
|
||||
auto subID = t->town->buildings.at(bid)->subId;
|
||||
|
||||
if(subID == BuildingSubID::EBuildingSubID::BANK)
|
||||
{
|
||||
TResources res;
|
||||
res[EGameResID::GOLD] = 2500;
|
||||
giveResources(t->getOwner(), res);
|
||||
|
||||
setObjPropertyValue(t->id, ObjProperty::BONUS_VALUE_SECOND, 2500);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (t->rewardableBuildings.count(bid))
|
||||
{
|
||||
auto & hero = t->garrisonHero ? t->garrisonHero : t->visitingHero;
|
||||
auto * building = t->rewardableBuildings.at(bid);
|
||||
|
||||
if (hero && t->town->buildings.at(bid)->manualHeroVisit)
|
||||
{
|
||||
// FIXME: query might produce unintended side effects, double check
|
||||
auto visitQuery = std::make_shared<CObjectVisitQuery>(this, t, hero, t->visitablePos());
|
||||
queries->addQuery(visitQuery);
|
||||
building->onHeroVisit(hero);
|
||||
queries->popIfTop(visitQuery);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -214,7 +214,7 @@ public:
|
||||
bool upgradeCreature( ObjectInstanceID objid, SlotID pos, CreatureID upgID );
|
||||
bool recruitCreatures(ObjectInstanceID objid, ObjectInstanceID dst, CreatureID crid, ui32 cram, si32 level, PlayerColor player);
|
||||
bool buildStructure(ObjectInstanceID tid, BuildingID bid, bool force=false);//force - for events: no cost, no checkings
|
||||
bool triggerTownSpecialBuildingAction(ObjectInstanceID tid, BuildingSubID::EBuildingSubID sid);
|
||||
bool visitTownBuilding(ObjectInstanceID tid, BuildingID bid);
|
||||
bool razeStructure(ObjectInstanceID tid, BuildingID bid);
|
||||
bool disbandCreature( ObjectInstanceID id, SlotID pos );
|
||||
bool arrangeStacks( ObjectInstanceID id1, ObjectInstanceID id2, ui8 what, SlotID p1, SlotID p2, si32 val, PlayerColor player);
|
||||
|
@ -139,12 +139,12 @@ void ApplyGhNetPackVisitor::visitBuildStructure(BuildStructure & pack)
|
||||
result = gh.buildStructure(pack.tid, pack.bid);
|
||||
}
|
||||
|
||||
void ApplyGhNetPackVisitor::visitTriggerTownSpecialBuildingAction(TriggerTownSpecialBuildingAction & pack)
|
||||
void ApplyGhNetPackVisitor::visitVisitTownBuilding(VisitTownBuilding & pack)
|
||||
{
|
||||
gh.throwIfWrongOwner(&pack, pack.tid);
|
||||
gh.throwIfPlayerNotActive(&pack);
|
||||
|
||||
result = gh.triggerTownSpecialBuildingAction(pack.tid, pack.sid);
|
||||
result = gh.visitTownBuilding(pack.tid, pack.bid);
|
||||
}
|
||||
|
||||
void ApplyGhNetPackVisitor::visitRecruitCreatures(RecruitCreatures & pack)
|
||||
|
@ -41,7 +41,7 @@ public:
|
||||
void visitBulkSmartSplitStack(BulkSmartSplitStack & pack) override;
|
||||
void visitDisbandCreature(DisbandCreature & pack) override;
|
||||
void visitBuildStructure(BuildStructure & pack) override;
|
||||
void visitTriggerTownSpecialBuildingAction(TriggerTownSpecialBuildingAction & pack) override;
|
||||
void visitVisitTownBuilding(VisitTownBuilding & pack) override;
|
||||
void visitRecruitCreatures(RecruitCreatures & pack) override;
|
||||
void visitUpgradeCreature(UpgradeCreature & pack) override;
|
||||
void visitGarrisonHeroSwap(GarrisonHeroSwap & pack) override;
|
||||
|
Loading…
Reference in New Issue
Block a user