1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-09-16 09:26:28 +02:00

Greatly simplify town buildings logic

This commit is contained in:
Ivan Savenko
2024-08-16 12:57:38 +00:00
parent 57430c101f
commit f1e63792f0
22 changed files with 270 additions and 606 deletions

View File

@@ -781,13 +781,6 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil
enterBuilding(building);
break;
case BuildingSubID::BROTHERHOOD_OF_SWORD:
if(upgrades == BuildingID::TAVERN)
LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
else
enterBuilding(building);
break;
case BuildingSubID::CASTLE_GATE:
if (LOCPLINT->makingTurn)
enterCastleGate();
@@ -819,8 +812,11 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil
break;
default:
if(upgrades == BuildingID::TAVERN)
LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
else
enterBuilding(building);
break;
break;
}
break;

View File

@@ -4,7 +4,15 @@
"mageGuild3": { "id" : 2, "upgrades" : "mageGuild2" },
"mageGuild4": { "id" : 3, "upgrades" : "mageGuild3" },
"mageGuild5": { "id" : 4, "upgrades" : "mageGuild4" },
"tavern": { "id" : 5 },
"tavern": {
"id" : 5,
"bonuses": [
{
"type": "MORALE",
"val": 1
}
]
},
"shipyard": { "id" : 6 },
"fort": { "id" : 7 },
"citadel": { "id" : 8, "upgrades" : "fort" },
@@ -37,5 +45,159 @@
"marketplace": { "id" : 14 },
"resourceSilo": { "id" : 15, "requires" : [ "marketplace" ] },
"blacksmith": { "id" : 16 }
"blacksmith": { "id" : 16 },
// Previously hardcoded buildings that might be used by mods
// Section 1 - building with bonuses during sieges
"brotherhoodOfSword" : {
"bonuses": [
{
"type": "MORALE",
"val": 2
}
]
},
"fountainOfFortune" : {
"bonuses": [
{
"type": "LUCK",
"subtype": "primarySkill.knowledge",
"val": 2
}
]
},
"spellPowerGarrisonBonus" : {
"bonuses": [
{
"type": "PRIMARY_SKILL",
"subtype": "primarySkill.knowledge",
"val": 2
}
]
},
"attackGarrisonBonus" : {
"bonuses": [
{
"type": "PRIMARY_SKILL",
"subtype": "primarySkill.knowledge",
"val": 2
}
]
},
"defenseGarrisonBonus" : {
"bonuses": [
{
"type": "PRIMARY_SKILL",
"subtype": "primarySkill.knowledge",
"val": 2
}
]
},
"lighthouse" : {
"bonuses": [
{
"propagator": "PLAYER_PROPAGATOR",
"type": "MOVEMENT",
"subtype": "heroMovementSea",
"val": 500
}
]
},
// Section 2 - buildings that are visitable by hero
"stables": {
"configuration" : {
"visitMode" : "bonus",
"rewards" : [
{
"message" : "@core.genrltxt.580",
"movePoints" : 400,
"bonuses" : [ { "type" : "MOVEMENT", "subtype" : "heroMovementLand", "val" : 400, "valueType" : "ADDITIVE_VALUE", "duration" : "ONE_WEEK"} ]
}
]
}
},
"manaVortex": {
"configuration" : {
"resetParameters" : {
"period" : 7,
"visitors" : true
},
"visitMode" : "hero", // Should be 'once' to match (somewhat buggy) H3 logic
"rewards" : [
{
"limiter" : {
"noneOf" : [ { "manaPercentage" : 200 } ]
},
"message" : "@core.genrltxt.579",
"manaPercentage" : 200
}
]
}
},
"attackVisitingBonus": {
"configuration" : {
"visitMode" : "hero",
"rewards" : [
{
"message" : "@core.genrltxt.584",
"primary" : { "attack" : 1 }
}
]
}
},
"defenceVisitingBonus": {
"configuration" : {
"visitMode" : "hero",
"rewards" : [
{
"message" : "@core.genrltxt.585",
"primary" : { "defence" : 1 }
}
]
}
},
"spellPowerVisitingBonus": {
"configuration" : {
"visitMode" : "hero",
"rewards" : [
{
"message" : "@core.genrltxt.582",
"primary" : { "spellpower" : 1 }
}
]
}
},
"knowledgeVisitingBonus": {
"configuration" : {
"visitMode" : "hero",
"rewards" : [
{
"message" : "@core.genrltxt.581",
"primary" : { "knowledge" : 1 }
}
]
}
},
"experienceVisitingBonus": {
"configuration" : {
"visitMode" : "hero",
"rewards" : [
{
"message" : "@core.genrltxt.583",
"heroExperience" : 1000
}
]
}
}
}

View File

@@ -174,19 +174,7 @@
"horde1": { "id" : 18, "upgrades" : "dwellingLvl3" },
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl3", "requires" : [ "horde1" ], "mode" : "auto" },
"ship": { "id" : 20, "upgrades" : "shipyard" },
"special2": {
"type" : "configurable",
"requires" : [ "dwellingLvl4" ],
"configuration" : {
"visitMode" : "bonus",
"rewards" : [
{
"message" : "@core.genrltxt.580",
"movePoints" : 400,
"bonuses" : [ { "type" : "MOVEMENT", "subtype" : "heroMovementLand", "val" : 400, "valueType" : "ADDITIVE_VALUE", "duration" : "ONE_WEEK"} ]
}
]
}
"special2": { "type" : "stables", "requires" : [ "dwellingLvl4" ],
},
"special3": { "type" : "brotherhoodOfSword", "upgrades" : "tavern" },
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }, "bonuses": [ { "type": "MORALE", "val": 2, "propagator": "PLAYER_PROPAGATOR" } ] },

View File

@@ -174,39 +174,9 @@
"special1": { "type" : "artifactMerchant", "requires" : [ "marketplace" ] },
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1" },
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
"special2": {
"type" : "configurable",
"requires" : [ "mageGuild1" ],
"configuration" : {
"resetParameters" : {
"period" : 7,
"visitors" : true
},
"visitMode" : "hero", // Should be 'once' to match (somewhat buggy) H3 logic
"rewards" : [
{
"limiter" : {
"noneOf" : [ { "manaPercentage" : 200 } ]
},
"message" : "@core.genrltxt.579",
"manaPercentage" : 200
}
]
}
},
"special2": { "type" : "manaVortex", "requires" : [ "mageGuild1" ] },
"special3": { "type" : "portalOfSummoning" },
"special4": {
"type" : "configurable",
"configuration" : {
"visitMode" : "hero",
"rewards" : [
{
"message" : "@core.genrltxt.583",
"heroExperience" : 1000
}
]
}
},
"special4": { "type" : "experienceVisitingBonus" },
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 },
"bonuses": [ { "type": "PRIMARY_SKILL", "subtype": "primarySkill.spellpower", "val": 12 } ] },

View File

@@ -169,19 +169,7 @@
"resourceSilo": { "produce": { "wood": 1, "ore": 1 } },
"blacksmith": { },
"special1": {
"type" : "configurable",
"requires" : [ "allOf", [ "townHall" ], [ "special2" ] ],
"configuration" : {
"visitMode" : "hero",
"rewards" : [
{
"message" : "@core.genrltxt.585",
"primary" : { "defence" : 1 }
}
]
}
},
"special1": { "type" : "defenceVisitingBonus", "requires" : [ "allOf", [ "townHall" ], [ "special2" ] ] },
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1" },
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
"ship": { "id" : 20, "upgrades" : "shipyard" },

View File

@@ -175,19 +175,7 @@
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
"special2": { "type" : "spellPowerGarrisonBonus", "requires" : [ "fort" ] },
"special3": { "type" : "castleGate", "requires" : [ "citadel" ] },
"special4": {
"type" : "configurable",
"requires" : [ "mageGuild1" ],
"configuration" : {
"visitMode" : "hero",
"rewards" : [
{
"message" : "@core.genrltxt.582",
"primary" : { "spellpower" : 1 }
}
]
}
},
"special4": { "type" : "spellPowerVisitingBonus", "requires" : [ "mageGuild1" ] },
"horde2": { "id" : 24, "upgrades" : "dwellingLvl3" },
"horde2Upgr": { "id" : 25, "upgrades" : "dwellingUpLvl3", "requires" : [ "horde2" ], "mode" : "auto" },
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},

View File

@@ -174,19 +174,7 @@
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl2", "requires" : [ "horde1" ], "mode" : "auto" },
"special2": { "type" : "lookoutTower", "height" : "high", "requires" : [ "fort" ] },
"special3": { "type" : "library", "requires" : [ "mageGuild1" ] },
"special4": {
"type" : "configurable",
"requires" : [ "mageGuild1" ],
"configuration" : {
"visitMode" : "hero",
"rewards" : [
{
"message" : "@core.genrltxt.581",
"primary" : { "knowledge" : 1 }
}
]
}
},
"special4": { "type" : "knowledgeVisitingBonus", "requires" : [ "mageGuild1" ] },
"grail": { "height" : "skyship", "produce" : { "gold": 5000 }, "bonuses": [ { "type": "PRIMARY_SKILL", "subtype": "primarySkill.knowledge", "val": 15 } ] },
"dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] },

View File

@@ -36,7 +36,7 @@
},
"type" : {
"type" : "string",
"enum" : [ "mysticPond", "artifactMerchant", "freelancersGuild", "magicUniversity", "castleGate", "creatureTransformer", "portalOfSummoning", "ballistaYard", "lookoutTower", "library", "brotherhoodOfSword", "fountainOfFortune", "spellPowerGarrisonBonus", "attackGarrisonBonus", "defenseGarrisonBonus", "escapeTunnel", "lighthouse", "treasury", "thievesGuild", "bank", "configurable" ],
"enum" : [ "mysticPond", "artifactMerchant", "freelancersGuild", "magicUniversity", "castleGate", "creatureTransformer", "portalOfSummoning", "ballistaYard", "lookoutTower", "library", "brotherhoodOfSword", "fountainOfFortune", "spellPowerGarrisonBonus", "attackGarrisonBonus", "defenseGarrisonBonus", "escapeTunnel", "lighthouse", "treasury", "thievesGuild", "bank", "configurable", "stables", "manaVortex", "attackVisitingBonus", "defenceVisitingBonus", "spellPowerVisitingBonus", "knowledgeVisitingBonus", "experienceVisitingBonus" ],
"description" : "Subtype for some special buildings"
},
"mode" : {

View File

@@ -25,8 +25,6 @@ namespace BuildingSubID
{
DEFAULT = -50,
NONE = -1,
STABLES,
BROTHERHOOD_OF_SWORD,
CASTLE_GATE,
CREATURE_TRANSFORMER,
MYSTIC_POND,
@@ -34,26 +32,15 @@ namespace BuildingSubID
ARTIFACT_MERCHANT,
LOOKOUT_TOWER,
LIBRARY,
MANA_VORTEX,
PORTAL_OF_SUMMONING,
ESCAPE_TUNNEL,
FREELANCERS_GUILD,
BALLISTA_YARD,
ATTACK_VISITING_BONUS,
MAGIC_UNIVERSITY,
SPELL_POWER_GARRISON_BONUS,
ATTACK_GARRISON_BONUS,
DEFENSE_GARRISON_BONUS,
DEFENSE_VISITING_BONUS,
SPELL_POWER_VISITING_BONUS,
KNOWLEDGE_VISITING_BONUS,
EXPERIENCE_VISITING_BONUS,
LIGHTHOUSE,
TREASURY,
THIEVES_GUILD,
BANK,
CUSTOM_VISITING_BONUS,
CUSTOM_VISITING_REWARD
CUSTOM_VISITING_BONUS
};
}

View File

@@ -185,26 +185,13 @@ namespace MappedKeys
{ "creatureTransformer", BuildingSubID::CREATURE_TRANSFORMER },//only skeleton transformer yet
{ "portalOfSummoning", BuildingSubID::PORTAL_OF_SUMMONING },
{ "ballistaYard", BuildingSubID::BALLISTA_YARD },
{ "stables", BuildingSubID::STABLES },
{ "manaVortex", BuildingSubID::MANA_VORTEX },
{ "lookoutTower", BuildingSubID::LOOKOUT_TOWER },
{ "library", BuildingSubID::LIBRARY },
{ "brotherhoodOfSword", BuildingSubID::BROTHERHOOD_OF_SWORD },//morale garrison bonus
{ "fountainOfFortune", BuildingSubID::FOUNTAIN_OF_FORTUNE },//luck garrison bonus
{ "spellPowerGarrisonBonus", BuildingSubID::SPELL_POWER_GARRISON_BONUS },//such as 'stormclouds', but this name is not ok for good towns
{ "attackGarrisonBonus", BuildingSubID::ATTACK_GARRISON_BONUS },
{ "defenseGarrisonBonus", BuildingSubID::DEFENSE_GARRISON_BONUS },
{ "escapeTunnel", BuildingSubID::ESCAPE_TUNNEL },
{ "attackVisitingBonus", BuildingSubID::ATTACK_VISITING_BONUS },
{ "defenceVisitingBonus", BuildingSubID::DEFENSE_VISITING_BONUS },
{ "spellPowerVisitingBonus", BuildingSubID::SPELL_POWER_VISITING_BONUS },
{ "knowledgeVisitingBonus", BuildingSubID::KNOWLEDGE_VISITING_BONUS },
{ "experienceVisitingBonus", BuildingSubID::EXPERIENCE_VISITING_BONUS },
{ "lighthouse", BuildingSubID::LIGHTHOUSE },
{ "treasury", BuildingSubID::TREASURY },
{ "thievesGuild", BuildingSubID::THIEVES_GUILD },
{ "bank", BuildingSubID::BANK },
{ "configurable", BuildingSubID::CUSTOM_VISITING_REWARD}
{ "bank", BuildingSubID::BANK }
};
static const std::map<std::string, EMarketMode> MARKET_NAMES_TO_TYPES =

View File

@@ -89,23 +89,6 @@ public:
return bid == BuildingID::MARKETPLACE || subId == BuildingSubID::ARTIFACT_MERCHANT || subId == BuildingSubID::FREELANCERS_GUILD;
}
STRONG_INLINE
bool IsWeekBonus() const
{
return subId == BuildingSubID::STABLES || subId == BuildingSubID::MANA_VORTEX;
}
STRONG_INLINE
bool IsVisitingBonus() const
{
return subId == BuildingSubID::ATTACK_VISITING_BONUS ||
subId == BuildingSubID::DEFENSE_VISITING_BONUS ||
subId == BuildingSubID::SPELL_POWER_VISITING_BONUS ||
subId == BuildingSubID::KNOWLEDGE_VISITING_BONUS ||
subId == BuildingSubID::EXPERIENCE_VISITING_BONUS ||
subId == BuildingSubID::CUSTOM_VISITING_BONUS;
}
void addNewBonus(const std::shared_ptr<Bonus> & b, BonusList & bonusList) const;
friend class CTownHandler;

View File

@@ -78,14 +78,4 @@ BuildingID CTown::getBuildingType(BuildingSubID::EBuildingSubID subID) const
return building == nullptr ? BuildingID::NONE : building->bid.num;
}
std::string CTown::getGreeting(BuildingSubID::EBuildingSubID subID) const
{
return vstd::find_or(specialMessages, subID, std::string());
}
void CTown::setGreeting(BuildingSubID::EBuildingSubID subID, const std::string & message) const
{
specialMessages.insert(std::pair<BuildingSubID::EBuildingSubID, const std::string>(subID, message));
}
VCMI_LIB_NAMESPACE_END

View File

@@ -49,8 +49,6 @@ public:
std::string getBuildingScope() const;
std::set<si32> getAllBuildings() const;
const CBuilding * getSpecialBuilding(BuildingSubID::EBuildingSubID subID) const;
std::string getGreeting(BuildingSubID::EBuildingSubID subID) const;
void setGreeting(BuildingSubID::EBuildingSubID subID, const std::string & message) const; //may affect only mutable field
BuildingID getBuildingType(BuildingSubID::EBuildingSubID subID) const;
std::string getRandomNameTextID(size_t index) const;
@@ -106,10 +104,6 @@ public:
std::string towerIconLarge;
} clientInfo;
private:
///generated bonusing buildings messages for all towns of this type.
mutable std::map<BuildingSubID::EBuildingSubID, const std::string> specialMessages; //may be changed by CGTownBuilding::getVisitingBonusGreeting() const
};
VCMI_LIB_NAMESPACE_END

View File

@@ -242,43 +242,6 @@ void CTownHandler::loadBuildingRequirements(CBuilding * building, const JsonNode
bidsToLoad.push_back(hlp);
}
void CTownHandler::addBonusesForVanilaBuilding(CBuilding * building) const
{
std::shared_ptr<Bonus> b;
static const TPropagatorPtr playerPropagator = std::make_shared<CPropagatorNodeType>(CBonusSystemNode::ENodeTypes::PLAYER);
if(building->bid == BuildingID::TAVERN)
{
b = createBonus(building, BonusType::MORALE, +1);
}
switch(building->subId)
{
case BuildingSubID::BROTHERHOOD_OF_SWORD:
b = createBonus(building, BonusType::MORALE, +2);
building->overrideBids.insert(BuildingID::TAVERN);
break;
case BuildingSubID::FOUNTAIN_OF_FORTUNE:
b = createBonus(building, BonusType::LUCK, +2);
break;
case BuildingSubID::SPELL_POWER_GARRISON_BONUS:
b = createBonus(building, BonusType::PRIMARY_SKILL, +2, BonusSubtypeID(PrimarySkill::SPELL_POWER));
break;
case BuildingSubID::ATTACK_GARRISON_BONUS:
b = createBonus(building, BonusType::PRIMARY_SKILL, +2, BonusSubtypeID(PrimarySkill::ATTACK));
break;
case BuildingSubID::DEFENSE_GARRISON_BONUS:
b = createBonus(building, BonusType::PRIMARY_SKILL, +2, BonusSubtypeID(PrimarySkill::DEFENSE));
break;
case BuildingSubID::LIGHTHOUSE:
b = createBonus(building, BonusType::MOVEMENT, +500, BonusCustomSubtype::heroMovementSea, playerPropagator);
break;
}
if(b)
building->addNewBonus(b, building->buildingBonuses);
}
std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, BonusType type, int val) const
{
return createBonus(build, type, val, BonusSubtypeID(), emptyPropagator());
@@ -355,17 +318,12 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
ret->resources = TResources(source["cost"]);
ret->produce = TResources(source["produce"]);
if(ret->bid == BuildingID::TAVERN)
addBonusesForVanilaBuilding(ret);
else if(ret->bid.IsSpecialOrGrail())
if(ret->bid.IsSpecialOrGrail())
{
loadSpecialBuildingBonuses(source["bonuses"], ret->buildingBonuses, ret);
if(ret->buildingBonuses.empty())
{
ret->subId = vstd::find_or(MappedKeys::SPECIAL_BUILDINGS, source["type"].String(), BuildingSubID::NONE);
addBonusesForVanilaBuilding(ret);
}
loadSpecialBuildingBonuses(source["onVisitBonuses"], ret->onVisitBonuses, ret);
@@ -378,34 +336,24 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
bonus->sid = BonusSourceID(ret->getUniqueTypeID());
}
if(ret->subId == BuildingSubID::CUSTOM_VISITING_REWARD)
if(!source["configuration"].isNull())
ret->rewardableObjectInfo.init(source["configuration"], ret->getBaseTextID());
}
//MODS COMPATIBILITY FOR 0.96
if(!ret->produce.nonZero())
if(!ret->produce.nonZero() && ret->bid == BuildingID::RESOURCE_SILO)
{
switch (ret->bid.toEnum()) {
break; case BuildingID::VILLAGE_HALL: ret->produce[EGameResID::GOLD] = 500;
break; case BuildingID::TOWN_HALL : ret->produce[EGameResID::GOLD] = 1000;
break; case BuildingID::CITY_HALL : ret->produce[EGameResID::GOLD] = 2000;
break; case BuildingID::CAPITOL : ret->produce[EGameResID::GOLD] = 4000;
break; case BuildingID::GRAIL : ret->produce[EGameResID::GOLD] = 5000;
break; case BuildingID::RESOURCE_SILO :
{
switch (ret->town->primaryRes.toEnum())
{
case EGameResID::GOLD:
ret->produce[ret->town->primaryRes] = 500;
break;
case EGameResID::WOOD_AND_ORE:
ret->produce[EGameResID::WOOD] = 1;
ret->produce[EGameResID::ORE] = 1;
break;
default:
ret->produce[ret->town->primaryRes] = 1;
break;
}
}
switch (ret->town->primaryRes.toEnum())
{
case EGameResID::GOLD:
ret->produce[ret->town->primaryRes] = 500;
break;
case EGameResID::WOOD_AND_ORE:
ret->produce[EGameResID::WOOD] = 1;
ret->produce[EGameResID::ORE] = 1;
break;
default:
ret->produce[ret->town->primaryRes] = 1;
break;
}
}
loadBuildingRequirements(ret, source["requires"], requirementsToLoad);

View File

@@ -80,7 +80,6 @@ public:
void loadObject(std::string scope, std::string name, const JsonNode & data) override;
void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override;
void addBonusesForVanilaBuilding(CBuilding * building) const;
void loadCustom() override;
void afterLoadFinalization() override;

View File

@@ -28,9 +28,10 @@ CGTownBuilding::CGTownBuilding(IGameCallback * cb)
, town(nullptr)
{}
CGTownBuilding::CGTownBuilding(CGTownInstance * town)
CGTownBuilding::CGTownBuilding(CGTownInstance * town, const BuildingID & index)
: IObjectInterface(town->cb)
, town(town)
, bID(index)
{}
PlayerColor CGTownBuilding::getOwner() const
@@ -58,272 +59,13 @@ int3 CGTownBuilding::getPosition() const
return town->getPosition();
}
std::string CGTownBuilding::getVisitingBonusGreeting() const
{
auto bonusGreeting = town->getTown()->getGreeting(bType);
if(!bonusGreeting.empty())
return bonusGreeting;
switch(bType)
{
case BuildingSubID::MANA_VORTEX:
bonusGreeting = std::string(VLC->generaltexth->translate("vcmi.townHall.greetingManaVortex"));
break;
case BuildingSubID::KNOWLEDGE_VISITING_BONUS:
bonusGreeting = std::string(VLC->generaltexth->translate("vcmi.townHall.greetingKnowledge"));
break;
case BuildingSubID::SPELL_POWER_VISITING_BONUS:
bonusGreeting = std::string(VLC->generaltexth->translate("vcmi.townHall.greetingSpellPower"));
break;
case BuildingSubID::ATTACK_VISITING_BONUS:
bonusGreeting = std::string(VLC->generaltexth->translate("vcmi.townHall.greetingAttack"));
break;
case BuildingSubID::EXPERIENCE_VISITING_BONUS:
bonusGreeting = std::string(VLC->generaltexth->translate("vcmi.townHall.greetingExperience"));
break;
case BuildingSubID::DEFENSE_VISITING_BONUS:
bonusGreeting = std::string(VLC->generaltexth->translate("vcmi.townHall.greetingDefence"));
break;
}
auto buildingName = town->getTown()->getSpecialBuilding(bType)->getNameTranslated();
if(bonusGreeting.empty())
{
bonusGreeting = "Error: Bonus greeting for '%s' is not localized.";
logGlobal->error("'%s' building of '%s' faction has not localized bonus greeting.", buildingName, town->getTown()->faction->getNameTranslated());
}
boost::algorithm::replace_first(bonusGreeting, "%s", buildingName);
town->getTown()->setGreeting(bType, bonusGreeting);
return bonusGreeting;
}
std::string CGTownBuilding::getCustomBonusGreeting(const Bonus & bonus) const
{
if(bonus.type == BonusType::TOWN_MAGIC_WELL)
{
MetaString wellGreeting = MetaString::createFromTextID("vcmi.townHall.greetingInTownMagicWell");
wellGreeting.replaceTextID(town->getTown()->getSpecialBuilding(bType)->getNameTextID());
return wellGreeting.toString();
}
MetaString greeting = MetaString::createFromTextID("vcmi.townHall.greetingCustomBonus");
std::string paramTextID;
std::string until;
if(bonus.type == BonusType::MORALE)
paramTextID = "core.genrltxt.384"; // Morale
if(bonus.type == BonusType::LUCK)
paramTextID = "core.genrltxt.385"; // Luck
greeting.replaceTextID(town->getTown()->getSpecialBuilding(bType)->getNameTextID());
greeting.replaceNumber(bonus.val);
greeting.replaceTextID(paramTextID);
if (bonus.duration == BonusDuration::ONE_BATTLE)
greeting.replaceTextID("vcmi.townHall.greetingCustomUntil");
else
greeting.replaceRawString(".");
return greeting.toString();
}
COPWBonus::COPWBonus(IGameCallback *cb)
: CGTownBuilding(cb)
{}
COPWBonus::COPWBonus(const BuildingID & bid, BuildingSubID::EBuildingSubID subId, CGTownInstance * cgTown)
: CGTownBuilding(cgTown)
{
bID = bid;
bType = subId;
indexOnTV = static_cast<si32>(town->bonusingBuildings.size());
}
void COPWBonus::setProperty(ObjProperty what, ObjPropertyID identifier)
{
switch (what)
{
case ObjProperty::VISITORS:
visitors.insert(identifier.as<ObjectInstanceID>());
break;
case ObjProperty::STRUCTURE_CLEAR_VISITORS:
visitors.clear();
break;
}
}
void COPWBonus::onHeroVisit (const CGHeroInstance * h) const
{
ObjectInstanceID heroID = h->id;
if(town->hasBuilt(bID))
{
InfoWindow iw;
iw.player = h->tempOwner;
switch (this->bType)
{
case BuildingSubID::STABLES:
if(!h->hasBonusFrom(BonusSource::OBJECT_TYPE, BonusSourceID(Obj(Obj::STABLES)))) //does not stack with advMap Stables
{
GiveBonus gb;
gb.bonus = Bonus(BonusDuration::ONE_WEEK, BonusType::MOVEMENT, BonusSource::OBJECT_TYPE, 600, BonusSourceID(Obj(Obj::STABLES)), BonusCustomSubtype::heroMovementLand);
gb.id = heroID;
cb->giveHeroBonus(&gb);
cb->setMovePoints(heroID, 600, false);
iw.text.appendRawString(VLC->generaltexth->allTexts[580]);
cb->showInfoDialog(&iw);
}
break;
case BuildingSubID::MANA_VORTEX:
if(visitors.empty())
{
if(h->mana < h->manaLimit() * 2)
{
cb->setManaPoints (heroID, 2 * h->manaLimit());
//TODO: investigate line below
//cb->setObjProperty (town->id, ObjProperty::VISITED, true);
iw.text.appendRawString(getVisitingBonusGreeting());
cb->showInfoDialog(&iw);
town->addHeroToStructureVisitors(h, indexOnTV);
}
}
break;
}
}
}
CTownBonus::CTownBonus(IGameCallback *cb)
: CGTownBuilding(cb)
{}
CTownBonus::CTownBonus(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * cgTown)
: CGTownBuilding(cgTown)
{
bID = index;
bType = subId;
indexOnTV = static_cast<si32>(town->bonusingBuildings.size());
}
void CTownBonus::setProperty(ObjProperty what, ObjPropertyID identifier)
{
if(what == ObjProperty::VISITORS)
visitors.insert(identifier.as<ObjectInstanceID>());
}
void CTownBonus::onHeroVisit (const CGHeroInstance * h) const
{
ObjectInstanceID heroID = h->id;
if(town->hasBuilt(bID) && visitors.find(heroID) == visitors.end())
{
si64 val = 0;
InfoWindow iw;
PrimarySkill what = PrimarySkill::NONE;
switch(bType)
{
case BuildingSubID::KNOWLEDGE_VISITING_BONUS: //wall of knowledge
what = PrimarySkill::KNOWLEDGE;
val = 1;
iw.components.emplace_back(ComponentType::PRIM_SKILL, PrimarySkill::KNOWLEDGE, 1);
break;
case BuildingSubID::SPELL_POWER_VISITING_BONUS: //order of fire
what = PrimarySkill::SPELL_POWER;
val = 1;
iw.components.emplace_back(ComponentType::PRIM_SKILL, PrimarySkill::SPELL_POWER, 1);
break;
case BuildingSubID::ATTACK_VISITING_BONUS: //hall of Valhalla
what = PrimarySkill::ATTACK;
val = 1;
iw.components.emplace_back(ComponentType::PRIM_SKILL, PrimarySkill::ATTACK, 1);
break;
case BuildingSubID::EXPERIENCE_VISITING_BONUS: //academy of battle scholars
what = PrimarySkill::EXPERIENCE;
val = static_cast<int>(h->calculateXp(1000));
iw.components.emplace_back(ComponentType::EXPERIENCE, val);
break;
case BuildingSubID::DEFENSE_VISITING_BONUS: //cage of warlords
what = PrimarySkill::DEFENSE;
val = 1;
iw.components.emplace_back(ComponentType::PRIM_SKILL, PrimarySkill::DEFENSE, 1);
break;
case BuildingSubID::CUSTOM_VISITING_BONUS:
const auto building = town->getTown()->buildings.at(bID);
if(!h->hasBonusFrom(BonusSource::TOWN_STRUCTURE, BonusSourceID(building->getUniqueTypeID())))
{
const auto & bonuses = building->onVisitBonuses;
applyBonuses(const_cast<CGHeroInstance *>(h), bonuses);
}
break;
}
if(what != PrimarySkill::NONE)
{
iw.player = cb->getOwner(heroID);
iw.text.appendRawString(getVisitingBonusGreeting());
cb->showInfoDialog(&iw);
if (what == PrimarySkill::EXPERIENCE)
cb->giveExperience(cb->getHero(heroID), val);
else
cb->changePrimSkill(cb->getHero(heroID), what, val);
town->addHeroToStructureVisitors(h, indexOnTV);
}
}
}
void CTownBonus::applyBonuses(CGHeroInstance * h, const BonusList & bonuses) const
{
auto addToVisitors = false;
for(const auto & bonus : bonuses)
{
GiveBonus gb;
InfoWindow iw;
if(bonus->type == BonusType::TOWN_MAGIC_WELL)
{
if(h->mana >= h->manaLimit())
return;
cb->setManaPoints(h->id, h->manaLimit());
bonus->duration = BonusDuration::ONE_DAY;
}
gb.bonus = * bonus;
gb.id = h->id;
cb->giveHeroBonus(&gb);
if(bonus->duration == BonusDuration::PERMANENT)
addToVisitors = true;
iw.player = cb->getOwner(h->id);
iw.text.appendRawString(getCustomBonusGreeting(gb.bonus));
cb->showInfoDialog(&iw);
}
if(addToVisitors)
town->addHeroToStructureVisitors(h, indexOnTV);
}
CTownRewardableBuilding::CTownRewardableBuilding(IGameCallback *cb)
: CGTownBuilding(cb)
{}
CTownRewardableBuilding::CTownRewardableBuilding(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * cgTown, vstd::RNG & rand)
: CGTownBuilding(cgTown)
CTownRewardableBuilding::CTownRewardableBuilding(CGTownInstance * town, const BuildingID & index, vstd::RNG & rand)
: CGTownBuilding(town, index)
{
bID = index;
bType = subId;
indexOnTV = static_cast<si32>(town->bonusingBuildings.size());
initObj(rand);
}
@@ -336,7 +78,7 @@ void CTownRewardableBuilding::initObj(vstd::RNG & rand)
Rewardable::Configuration CTownRewardableBuilding::generateConfiguration(vstd::RNG & rand) const
{
Rewardable::Configuration result;
auto building = town->town->buildings.at(bID);
auto building = town->town->buildings.at(getBuildingType());
building->rewardableObjectInfo.configureObject(result, rand, cb);
for(auto & rewardInfo : result.info)
@@ -355,11 +97,11 @@ void CTownRewardableBuilding::newTurn(vstd::RNG & rand) const
if (configuration.resetParameters.period != 0 && cb->getDate(Date::DAY) > 1 && ((cb->getDate(Date::DAY)-1) % configuration.resetParameters.period) == 0)
{
auto newConfiguration = generateConfiguration(rand);
cb->setRewardableObjectConfiguration(town->id, bID, newConfiguration);
cb->setRewardableObjectConfiguration(town->id, getBuildingType(), newConfiguration);
if(configuration.resetParameters.visitors)
{
cb->setObjPropertyValue(town->id, ObjProperty::STRUCTURE_CLEAR_VISITORS, indexOnTV);
cb->setObjPropertyValue(town->id, ObjProperty::STRUCTURE_CLEAR_VISITORS, getBuildingType());
}
}
}
@@ -406,7 +148,7 @@ void CTownRewardableBuilding::blockingDialogAnswered(const CGHeroInstance *hero,
void CTownRewardableBuilding::grantReward(ui32 rewardID, const CGHeroInstance * hero) const
{
town->addHeroToStructureVisitors(hero, indexOnTV);
town->addHeroToStructureVisitors(hero, getBuildingType());
grantRewardBeforeLevelup(cb, configuration.info.at(rewardID), hero);
@@ -429,7 +171,7 @@ bool CTownRewardableBuilding::wasVisitedBefore(const CGHeroInstance * contextHer
return false; //not supported
case Rewardable::VISIT_BONUS:
{
const auto building = town->getTown()->buildings.at(bID);
const auto building = town->getTown()->buildings.at(getBuildingType());
return contextHero->hasBonusFrom(BonusSource::TOWN_STRUCTURE, BonusSourceID(building->getUniqueTypeID()));
}
case Rewardable::VISIT_HERO:
@@ -448,7 +190,7 @@ void CTownRewardableBuilding::onHeroVisit(const CGHeroInstance *h) const
auto vi = configuration.info.at(index);
logGlobal->debug("Granting reward %d. Message says: %s", index, vi.message.toString());
town->addHeroToStructureVisitors(h, indexOnTV); //adding to visitors
town->addHeroToStructureVisitors(h, getBuildingType()); //adding to visitors
InfoWindow iw;
iw.player = h->tempOwner;
@@ -476,7 +218,8 @@ void CTownRewardableBuilding::onHeroVisit(const CGHeroInstance *h) const
cb->showBlockingDialog(&sd);
};
if(!town->hasBuilt(bID))
assert(town->hasBuilt(getBuildingType()));
if(!town->hasBuilt(getBuildingType()))
return;
if(!wasVisitedBefore(h))

View File

@@ -22,31 +22,18 @@ class DLL_LINKAGE CGTownBuilding : public IObjectInterface
{
///basic class for town structures handled as map objects
public:
CGTownBuilding(CGTownInstance * town);
CGTownBuilding(CGTownInstance * town, const BuildingID & index);
CGTownBuilding(IGameCallback *cb);
si32 indexOnTV = 0; //identifies its index on towns vector
CGTownInstance * town;
STRONG_INLINE
BuildingSubID::EBuildingSubID getBuildingSubtype() const
{
return bType;
}
STRONG_INLINE
const BuildingID & getBuildingType() const
{
return bID;
}
STRONG_INLINE
void setBuildingSubtype(BuildingSubID::EBuildingSubID subId)
{
bType = subId;
}
PlayerColor getOwner() const override;
MapObjectID getObjGroupIndex() const override;
MapObjectSubID getObjTypeIndex() const override;
@@ -57,55 +44,23 @@ public:
template <typename Handler> void serialize(Handler &h)
{
h & bID;
h & indexOnTV;
h & bType;
}
if (h.version >= Handler::Version::NEW_TOWN_BUILDINGS)
{
// no-op
}
else
{
si32 indexOnTV = 0; //identifies its index on towns vector
BuildingSubID::EBuildingSubID bType = BuildingSubID::NONE;
protected:
BuildingID bID; //from buildig list
BuildingSubID::EBuildingSubID bType = BuildingSubID::NONE;
h & indexOnTV;
h & bType;
std::string getVisitingBonusGreeting() const;
std::string getCustomBonusGreeting(const Bonus & bonus) const;
};
class DLL_LINKAGE COPWBonus : public CGTownBuilding
{///used for OPW bonusing structures
public:
std::set<ObjectInstanceID> visitors;
void setProperty(ObjProperty what, ObjPropertyID identifier) override;
void onHeroVisit (const CGHeroInstance * h) const override;
COPWBonus(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * TOWN);
COPWBonus(IGameCallback *cb);
template <typename Handler> void serialize(Handler &h)
{
h & static_cast<CGTownBuilding&>(*this);
h & visitors;
}
};
class DLL_LINKAGE CTownBonus : public CGTownBuilding
{
///used for one-time bonusing structures
///feel free to merge inheritance tree
public:
std::set<ObjectInstanceID> visitors;
void setProperty(ObjProperty what, ObjPropertyID identifier) override;
void onHeroVisit (const CGHeroInstance * h) const override;
CTownBonus(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * TOWN);
CTownBonus(IGameCallback *cb);
template <typename Handler> void serialize(Handler &h)
{
h & static_cast<CGTownBuilding&>(*this);
h & visitors;
}
}
private:
void applyBonuses(CGHeroInstance * h, const BonusList & bonuses) const;
BuildingID bID; //from building list
};
class DLL_LINKAGE CTownRewardableBuilding : public CGTownBuilding, public Rewardable::Interface
@@ -135,7 +90,7 @@ public:
/// applies player selection of reward
void blockingDialogAnswered(const CGHeroInstance *hero, int32_t answer) const override;
CTownRewardableBuilding(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * town, vstd::RNG & rand);
CTownRewardableBuilding(CGTownInstance * town, const BuildingID & index, vstd::RNG & rand);
CTownRewardableBuilding(IGameCallback *cb);
template <typename Handler> void serialize(Handler &h)
@@ -146,4 +101,46 @@ public:
}
};
/// Compatibility for old code
class DLL_LINKAGE CTownCompatBuilding1 : public CTownRewardableBuilding
{
public:
using CTownRewardableBuilding::CTownRewardableBuilding;
template <typename Handler> void serialize(Handler &h)
{
if (h.version >= Handler::Version::NEW_TOWN_BUILDINGS)
{
h & static_cast<CTownRewardableBuilding&>(*this);
}
else
{
h & static_cast<CGTownBuilding&>(*this);
std::set<ObjectInstanceID> visitors;
h & visitors;
}
}
};
/// Compatibility for old code
class DLL_LINKAGE CTownCompatBuilding2 : public CTownRewardableBuilding
{
public:
using CTownRewardableBuilding::CTownRewardableBuilding;
template <typename Handler> void serialize(Handler &h)
{
if (h.version >= Handler::Version::NEW_TOWN_BUILDINGS)
{
h & static_cast<CTownRewardableBuilding&>(*this);
}
else
{
h & static_cast<CGTownBuilding&>(*this);
std::set<ObjectInstanceID> visitors;
h & visitors;
}
}
};
VCMI_LIB_NAMESPACE_END

View File

@@ -380,31 +380,12 @@ void CGTownInstance::initOverriddenBids()
}
}
bool CGTownInstance::isBonusingBuildingAdded(BuildingID bid) const
{
auto present = std::find_if(bonusingBuildings.begin(), bonusingBuildings.end(), [&](CGTownBuilding* building)
{
return building->getBuildingType() == bid;
});
return present != bonusingBuildings.end();
}
void CGTownInstance::addTownBonuses(vstd::RNG & rand)
void CGTownInstance::initializeConfigurableBuildings(vstd::RNG & rand)
{
for(const auto & kvp : town->buildings)
{
if(vstd::contains(overriddenBuildings, kvp.first))
continue;
if(kvp.second->IsVisitingBonus())
bonusingBuildings.push_back(new CTownBonus(kvp.second->bid, kvp.second->subId, this));
if(kvp.second->IsWeekBonus())
bonusingBuildings.push_back(new COPWBonus(kvp.second->bid, kvp.second->subId, this));
if(kvp.second->subId == BuildingSubID::CUSTOM_VISITING_REWARD)
bonusingBuildings.push_back(new CTownRewardableBuilding(kvp.second->bid, kvp.second->subId, this, rand));
if(!kvp.second->rewardableObjectInfo.getParameters().isNull())
bonusingBuildings.push_back(new CTownRewardableBuilding(this, kvp.second->bid, rand));
}
}
@@ -460,15 +441,7 @@ void CGTownInstance::deleteTownBonus(BuildingID bid)
if(freeIt == nullptr)
return;
auto building = town->buildings.at(bid);
auto isVisitingBonus = building->IsVisitingBonus();
auto isWeekBonus = building->IsWeekBonus();
if(!isVisitingBonus && !isWeekBonus)
return;
bonusingBuildings.erase(bonusingBuildings.begin() + i);
delete freeIt;
}
@@ -527,7 +500,7 @@ void CGTownInstance::initObj(vstd::RNG & rand) ///initialize town structures
}
}
initOverriddenBids();
addTownBonuses(rand); //add special bonuses from buildings to the bonusingBuildings vector.
initializeConfigurableBuildings(rand);
recreateBuildingsBonuses();
updateAppearance();
}
@@ -549,9 +522,6 @@ void CGTownInstance::newTurn(vstd::RNG & rand) const
cb->setObjPropertyValue(id, ObjProperty::BONUS_VALUE_FIRST, resID);
cb->setObjPropertyValue(id, ObjProperty::BONUS_VALUE_SECOND, resVal);
}
for(const auto * manaVortex : getBonusingBuildings(BuildingSubID::MANA_VORTEX))
cb->setObjPropertyValue(id, ObjProperty::STRUCTURE_CLEAR_VISITORS, manaVortex->indexOnTV); //reset visitors for Mana Vortex
if (tempOwner == PlayerColor::NEUTRAL) //garrison growth for neutral towns
{
@@ -606,7 +576,7 @@ void CGTownInstance::newTurn(vstd::RNG & rand) const
}
}
for(const auto * rewardableBuilding : getBonusingBuildings(BuildingSubID::CUSTOM_VISITING_REWARD))
for(const auto * rewardableBuilding : bonusingBuildings)
rewardableBuilding->newTurn(rand);
if(hasBuilt(BuildingSubID::BANK) && bonusValue.second > 0)
@@ -979,18 +949,6 @@ const CArmedInstance * CGTownInstance::getUpperArmy() const
return this;
}
std::vector<const CGTownBuilding *> CGTownInstance::getBonusingBuildings(BuildingSubID::EBuildingSubID subId) const
{
std::vector<const CGTownBuilding *> ret;
for(auto * const building : bonusingBuildings)
{
if(building->getBuildingSubtype() == subId)
ret.push_back(building);
}
return ret;
}
bool CGTownInstance::hasBuiltSomeTradeBuilding() const
{
for(const auto & bid : builtBuildings)

View File

@@ -165,7 +165,6 @@ public:
GrowthInfo getGrowthInfo(int level) const;
bool hasFort() const;
bool hasCapitol() const;
std::vector<const CGTownBuilding *> getBonusingBuildings(BuildingSubID::EBuildingSubID subId) const;
bool hasBuiltSomeTradeBuilding() const;
//checks if special building with type buildingID is constructed
bool hasBuilt(BuildingSubID::EBuildingSubID buildingID) const;
@@ -231,9 +230,8 @@ private:
void onTownCaptured(const PlayerColor & winner) const;
int getDwellingBonus(const std::vector<CreatureID>& creatureIds, const std::vector<ConstTransitivePtr<CGDwelling> >& dwellings) const;
bool townEnvisagesBuilding(BuildingSubID::EBuildingSubID bid) const;
bool isBonusingBuildingAdded(BuildingID bid) const;
void initOverriddenBids();
void addTownBonuses(vstd::RNG & rand);
void initializeConfigurableBuildings(vstd::RNG & rand);
};
VCMI_LIB_NAMESPACE_END

View File

@@ -95,7 +95,6 @@ public:
// class DLL_LINKAGE CGPyramid : public CBank
// EXTRA
// class DLL_LINKAGE COPWBonus : public CGTownBuilding
// class DLL_LINKAGE CTownBonus : public CGTownBuilding
// class DLL_LINKAGE CGKeys : public CGObjectInstance //Base class for Keymaster and guards
// class DLL_LINKAGE CGKeymasterTent : public CGKeys

View File

@@ -87,8 +87,8 @@ void registerTypesMapObjects(Serializer &s)
//Other object-related
s.template registerType<IObjectInterface, CGTownBuilding>();
s.template registerType<CGTownBuilding, CTownBonus>();
s.template registerType<CGTownBuilding, COPWBonus>();
s.template registerType<CGTownBuilding, CTownCompatBuilding1>();
s.template registerType<CGTownBuilding, CTownCompatBuilding2>();
s.template registerType<CGTownBuilding, CTownRewardableBuilding>();
s.template registerType<CGObjectInstance, CRewardableObject>();

View File

@@ -63,6 +63,7 @@ enum class ESerializationVersion : int32_t
STATISTICS, // 852 - removed random number generators from library classes
CAMPAIGN_REGIONS, // 853 - configurable campaign regions
EVENTS_PLAYER_SET, // 854 - map & town events use std::set instead of bitmask to store player list
NEW_TOWN_BUILDINGS, // 855 - old bonusing buildings have been removed
CURRENT = EVENTS_PLAYER_SET
CURRENT = NEW_TOWN_BUILDINGS
};