mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-24 08:32:34 +02:00
Merge pull request #672 from ShubusCorporation/do_feature_part3
Feature: Mods system improvement, Part III. Bonusing buildings customization.
This commit is contained in:
commit
925857d845
@ -83,6 +83,7 @@ size_t OptionsTab::CPlayerSettingsHelper::getImageIndex()
|
||||
TOWN_RANDOM = 38, TOWN_NONE = 39, // Special frames in ITPA
|
||||
HERO_RANDOM = 163, HERO_NONE = 164 // Special frames in PortraitsSmall
|
||||
};
|
||||
auto factionIndex = settings.castle >= CGI->townh->factions.size() ? 0 : settings.castle;
|
||||
|
||||
switch(type)
|
||||
{
|
||||
@ -94,9 +95,8 @@ size_t OptionsTab::CPlayerSettingsHelper::getImageIndex()
|
||||
case PlayerSettings::RANDOM:
|
||||
return TOWN_RANDOM;
|
||||
default:
|
||||
return CGI->townh->factions[settings.castle]->town->clientInfo.icons[true][false] + 2;
|
||||
return CGI->townh->factions[factionIndex]->town->clientInfo.icons[true][false] + 2;
|
||||
}
|
||||
|
||||
case HERO:
|
||||
switch(settings.hero)
|
||||
{
|
||||
@ -108,7 +108,8 @@ size_t OptionsTab::CPlayerSettingsHelper::getImageIndex()
|
||||
{
|
||||
if(settings.heroPortrait >= 0)
|
||||
return settings.heroPortrait;
|
||||
return CGI->heroh->heroes[settings.hero]->imageIndex;
|
||||
auto index = settings.hero >= CGI->heroh->heroes.size() ? 0 : settings.hero;
|
||||
return CGI->heroh->heroes[index]->imageIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,7 +125,7 @@ size_t OptionsTab::CPlayerSettingsHelper::getImageIndex()
|
||||
return GOLD;
|
||||
case PlayerSettings::RESOURCE:
|
||||
{
|
||||
switch(CGI->townh->factions[settings.castle]->town->primaryRes)
|
||||
switch(CGI->townh->factions[factionIndex]->town->primaryRes)
|
||||
{
|
||||
case Res::WOOD_AND_ORE:
|
||||
return WOOD_ORE;
|
||||
@ -179,7 +180,10 @@ std::string OptionsTab::CPlayerSettingsHelper::getName()
|
||||
case PlayerSettings::RANDOM:
|
||||
return CGI->generaltexth->allTexts[522];
|
||||
default:
|
||||
return CGI->townh->factions[settings.castle]->name;
|
||||
{
|
||||
auto factionIndex = settings.castle >= CGI->townh->factions.size() ? 0 : settings.castle;
|
||||
return CGI->townh->factions[factionIndex]->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
case HERO:
|
||||
@ -194,7 +198,8 @@ std::string OptionsTab::CPlayerSettingsHelper::getName()
|
||||
{
|
||||
if(!settings.heroName.empty())
|
||||
return settings.heroName;
|
||||
return CGI->heroh->heroes[settings.hero]->name;
|
||||
auto index = settings.hero >= CGI->heroh->heroes.size() ? 0 : settings.hero;
|
||||
return CGI->heroh->heroes[index]->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -240,6 +245,9 @@ std::string OptionsTab::CPlayerSettingsHelper::getTitle()
|
||||
}
|
||||
std::string OptionsTab::CPlayerSettingsHelper::getSubtitle()
|
||||
{
|
||||
auto factionIndex = settings.castle >= CGI->townh->factions.size() ? 0 : settings.castle;
|
||||
auto heroIndex = settings.hero >= CGI->heroh->heroes.size() ? 0 : settings.hero;
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case TOWN:
|
||||
@ -247,7 +255,7 @@ std::string OptionsTab::CPlayerSettingsHelper::getSubtitle()
|
||||
case HERO:
|
||||
{
|
||||
if(settings.hero >= 0)
|
||||
return getName() + " - " + CGI->heroh->heroes[settings.hero]->heroClass->name;
|
||||
return getName() + " - " + CGI->heroh->heroes[heroIndex]->heroClass->name;
|
||||
return getName();
|
||||
}
|
||||
|
||||
@ -259,7 +267,7 @@ std::string OptionsTab::CPlayerSettingsHelper::getSubtitle()
|
||||
return CGI->generaltexth->allTexts[87]; //500-1000
|
||||
case PlayerSettings::RESOURCE:
|
||||
{
|
||||
switch(CGI->townh->factions[settings.castle]->town->primaryRes)
|
||||
switch(CGI->townh->factions[factionIndex]->town->primaryRes)
|
||||
{
|
||||
case Res::MERCURY:
|
||||
return CGI->generaltexth->allTexts[694];
|
||||
@ -281,6 +289,8 @@ std::string OptionsTab::CPlayerSettingsHelper::getSubtitle()
|
||||
|
||||
std::string OptionsTab::CPlayerSettingsHelper::getDescription()
|
||||
{
|
||||
auto factionIndex = settings.castle >= CGI->townh->factions.size() ? 0 : settings.castle;
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case TOWN:
|
||||
@ -299,7 +309,7 @@ std::string OptionsTab::CPlayerSettingsHelper::getDescription()
|
||||
return CGI->generaltexth->allTexts[92]; //At the start of the game, 500-1000 gold is added to your Kingdom's resource pool
|
||||
case PlayerSettings::RESOURCE:
|
||||
{
|
||||
switch(CGI->townh->factions[settings.castle]->town->primaryRes)
|
||||
switch(CGI->townh->factions[factionIndex]->town->primaryRes)
|
||||
{
|
||||
case Res::MERCURY:
|
||||
return CGI->generaltexth->allTexts[690];
|
||||
@ -366,9 +376,10 @@ void OptionsTab::CPlayerOptionTooltipBox::genTownWindow()
|
||||
pos = Rect(0, 0, 228, 290);
|
||||
genHeader();
|
||||
labelAssociatedCreatures = std::make_shared<CLabel>(pos.w / 2 + 8, 122, FONT_MEDIUM, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[79]);
|
||||
|
||||
auto factionIndex = settings.castle >= CGI->townh->factions.size() ? 0 : settings.castle;
|
||||
std::vector<std::shared_ptr<CComponent>> components;
|
||||
const CTown * town = CGI->townh->factions[settings.castle]->town;
|
||||
const CTown * town = CGI->townh->factions[factionIndex]->town;
|
||||
|
||||
for(auto & elem : town->creatures)
|
||||
{
|
||||
if(!elem.empty())
|
||||
@ -382,9 +393,10 @@ void OptionsTab::CPlayerOptionTooltipBox::genHeroWindow()
|
||||
pos = Rect(0, 0, 292, 226);
|
||||
genHeader();
|
||||
labelHeroSpeciality = std::make_shared<CLabel>(pos.w / 2 + 4, 117, FONT_MEDIUM, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[78]);
|
||||
auto heroIndex = settings.hero >= CGI->heroh->heroes.size() ? 0 : settings.hero;
|
||||
|
||||
imageSpeciality = std::make_shared<CAnimImage>("UN44", CGI->heroh->heroes[settings.hero]->imageIndex, 0, pos.w / 2 - 22, 134);
|
||||
labelSpecialityName = std::make_shared<CLabel>(pos.w / 2, 188, FONT_SMALL, CENTER, Colors::WHITE, CGI->heroh->heroes[settings.hero]->specName);
|
||||
imageSpeciality = std::make_shared<CAnimImage>("UN44", CGI->heroh->heroes[heroIndex]->imageIndex, 0, pos.w / 2 - 22, 134);
|
||||
labelSpecialityName = std::make_shared<CLabel>(pos.w / 2, 188, FONT_SMALL, CENTER, Colors::WHITE, CGI->heroh->heroes[heroIndex]->specName);
|
||||
}
|
||||
|
||||
void OptionsTab::CPlayerOptionTooltipBox::genBonusWindow()
|
||||
|
@ -176,7 +176,7 @@
|
||||
"ship": { "id" : 20, "upgrades" : "shipyard" },
|
||||
"special2": { "type" : "stables", "requires" : [ "dwellingLvl4" ] },
|
||||
"special3": { "type" : "brotherhoodOfSword", "upgrades" : "tavern" },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }, "bonuses": [ { "type": "MORALE", "val": 2, "propagator": "PLAYER_PROPAGATOR" } ] },
|
||||
|
||||
"dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] },
|
||||
"dwellingLvl2": { "id" : 31, "requires" : [ "dwellingLvl1" ] },
|
||||
|
@ -175,7 +175,8 @@
|
||||
"special2": { "type" : "manaVortex", "requires" : [ "mageGuild1" ] },
|
||||
"special3": { "type" : "portalOfSummoning" },
|
||||
"special4": { "type" : "experienceVisitingBonus" },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 },
|
||||
"bonuses": [ { "type": "PRIMARY_SKILL", "subtype": "primSkill.spellpower", "val": 12 } ] },
|
||||
|
||||
"dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] },
|
||||
"dwellingLvl2": { "id" : 31, "requires" : [ "dwellingLvl1" ] },
|
||||
|
@ -175,9 +175,14 @@
|
||||
"ship": { "id" : 20, "upgrades" : "shipyard" },
|
||||
"special2": { "type" : "defenseGarrisonBonus", "requires" : [ "fort" ] },
|
||||
"special3": { "type" : "attackGarrisonBonus", "requires" : [ "special2" ] },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
|
||||
"extraCapitol": { "id" : 29, "requires" : [ "capitol" ], "mode" : "auto" },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 },
|
||||
"bonuses": [
|
||||
{ "type": "PRIMARY_SKILL", "subtype": "primSkill.attack", "val": 10 },
|
||||
{ "type": "PRIMARY_SKILL", "subtype": "primSkill.defence", "val": 10 }
|
||||
]
|
||||
},
|
||||
|
||||
"extraCapitol": { "id" : 29, "requires" : [ "capitol" ], "mode" : "auto" },
|
||||
"dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] },
|
||||
"dwellingLvl2": { "id" : 31, "requires" : [ "dwellingLvl1" ] },
|
||||
"dwellingLvl3": { "id" : 32, "requires" : [ "dwellingLvl1" ] },
|
||||
|
@ -175,13 +175,16 @@
|
||||
"resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce": { "ore": 1, "wood": 1 } },
|
||||
"blacksmith": { "id" : 16 },
|
||||
|
||||
"special1": { "requires" : [ "fort" ] },
|
||||
"special1": { "requires" : [ "fort" ], "bonuses": [ { "type": "DARKNESS", "val": 20 } ] },
|
||||
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1", "requires" : [ "special3" ] },
|
||||
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
|
||||
"ship": { "id" : 20, "upgrades" : "shipyard" },
|
||||
"special2": { "requires" : [ "mageGuild1" ] },
|
||||
"special2": { "requires" : [ "mageGuild1" ],
|
||||
"bonuses": [ { "type": "SECONDARY_SKILL_PREMY", "subtype": "skill.necromancy", "val": 10, "propagator": "PLAYER_PROPAGATOR" } ] },
|
||||
"special3": { "type" : "creatureTransformer", "requires" : [ "dwellingLvl1" ] },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 },
|
||||
"bonuses": [ { "type": "SECONDARY_SKILL_PREMY", "subtype": "skill.necromancy", "val": 20, "propagator": "PLAYER_PROPAGATOR" } ] },
|
||||
|
||||
"extraTownHall": { "id" : 27, "requires" : [ "townHall" ], "mode" : "auto" },
|
||||
"extraCityHall": { "id" : 28, "requires" : [ "cityHall" ], "mode" : "auto" },
|
||||
"extraCapitol": { "id" : 29, "requires" : [ "capitol" ], "mode" : "auto" },
|
||||
|
@ -181,7 +181,8 @@
|
||||
"special3": { "type" : "treasury", "requires" : [ "horde1" ] },
|
||||
"horde2": { "id" : 24, "upgrades" : "dwellingLvl5" },
|
||||
"horde2Upgr": { "id" : 25, "upgrades" : "dwellingUpLvl5", "requires" : [ "horde2" ], "mode" : "auto" },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }, "bonuses": [ { "type": "LUCK", "val": 2, "propagator": "PLAYER_PROPAGATOR" } ] },
|
||||
|
||||
"extraTownHall": { "id" : 27, "requires" : [ "townHall" ], "mode" : "auto" },
|
||||
"extraCityHall": { "id" : 28, "requires" : [ "cityHall" ], "mode" : "auto" },
|
||||
"extraCapitol": { "id" : 29, "requires" : [ "capitol" ], "mode" : "auto" },
|
||||
|
@ -172,7 +172,8 @@
|
||||
"special2": { "type" : "freelancersGuild", "requires" : [ "marketplace" ] },
|
||||
"special3": { "type" : "ballistaYard", "requires" : [ "blacksmith" ] },
|
||||
"special4": { "type" : "attackVisitingBonus", "requires" : [ "fort" ] },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 },
|
||||
"bonuses": [ { "type": "PRIMARY_SKILL", "subtype": "primSkill.attack", "val": 20 } ] },
|
||||
|
||||
"dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] },
|
||||
"dwellingLvl2": { "id" : 31, "requires" : [ "dwellingLvl1" ] },
|
||||
|
@ -175,7 +175,7 @@
|
||||
"special2": { "type" : "lookoutTower", "height" : "high", "requires" : [ "fort" ] },
|
||||
"special3": { "type" : "library", "requires" : [ "mageGuild1" ] },
|
||||
"special4": { "type" : "knowledgeVisitingBonus", "requires" : [ "mageGuild1" ] },
|
||||
"grail": { "height" : "skyship", "produce" : { "gold": 5000 } },
|
||||
"grail": { "height" : "skyship", "produce" : { "gold": 5000 }, "bonuses": [ { "type": "PRIMARY_SKILL", "subtype": "primSkill.knowledge", "val": 15 } ] },
|
||||
|
||||
"dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] },
|
||||
"dwellingLvl2": { "id" : 31, "requires" : [ "dwellingLvl1" ] },
|
||||
|
@ -85,7 +85,25 @@
|
||||
"gems": { "type":"number"},
|
||||
"gold": { "type":"number"}
|
||||
}
|
||||
},
|
||||
"overrides": {
|
||||
"type" : "array",
|
||||
"items" : [
|
||||
{
|
||||
"description" : "The buildings which bonuses should be overridden with bonuses of the current building",
|
||||
"type" : "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"bonuses": {
|
||||
"type":"array",
|
||||
"description": "Bonuses, provided by this special building on build using bonus system",
|
||||
"items": { "$ref" : "bonus.json" }
|
||||
},
|
||||
"onVisitBonuses": {
|
||||
"type":"array",
|
||||
"description": "Bonuses, provided by this special building on hero visit and applied to the visiting hero",
|
||||
"items": { "$ref" : "bonus.json" }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,9 @@
|
||||
"greetingAttack" : "Some time spent at the %s allows you to learn more effective combat skills (+1 Attack Skill).",
|
||||
"greetingDefence" : "Spending time in the %s, the experienced warriors therein teach you additional defensive skills (+1 Defense).",
|
||||
"hasNotProduced" : "The %s has not produced anything yet.",
|
||||
"hasProduced" : "The %s produced %d %s this week."
|
||||
"hasProduced" : "The %s produced %d %s this week.",
|
||||
"greetingCustomBonus" : "%s gives you +%d %s%s",
|
||||
"greetingCustomUntil" : " until next battle."
|
||||
},
|
||||
"logicalExpressions" :
|
||||
{
|
||||
|
@ -1210,6 +1210,11 @@ void CCreatureHandler::removeBonusesFromAllCreatures()
|
||||
allCreatures.removeBonuses(Selector::all);
|
||||
}
|
||||
|
||||
void CCreatureHandler::restoreAllCreaturesNodeType794()
|
||||
{
|
||||
allCreatures.setNodeType(CBonusSystemNode::ENodeTypes::ALL_CREATURES);
|
||||
}
|
||||
|
||||
void CCreatureHandler::buildBonusTreeForTiers()
|
||||
{
|
||||
for(CCreature *c : creatures)
|
||||
|
@ -249,6 +249,7 @@ public:
|
||||
void addBonusForTier(int tier, std::shared_ptr<Bonus> b); //tier must be <1-7>
|
||||
void addBonusForAllCreatures(std::shared_ptr<Bonus> b);
|
||||
void removeBonusesFromAllCreatures();
|
||||
void restoreAllCreaturesNodeType794(); //restore ALL_CREATURES node type for old saves
|
||||
|
||||
CCreatureHandler();
|
||||
~CCreatureHandler();
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "filesystem/Filesystem.h"
|
||||
#include "mapObjects/CObjectClassesHandler.h"
|
||||
#include "mapObjects/CObjectHandler.h"
|
||||
#include "HeroBonus.h"
|
||||
|
||||
const int NAMES_PER_TOWN=16; // number of town names per faction in H3 files. Json can define any number
|
||||
|
||||
@ -93,7 +94,37 @@ void CBuilding::deserializeFix()
|
||||
}
|
||||
}
|
||||
|
||||
void CBuilding::update792(const BuildingID & bid, BuildingSubID::EBuildingSubID & subId, ETowerHeight & height)
|
||||
void CBuilding::addNewBonus(std::shared_ptr<Bonus> b, BonusList & bonusList)
|
||||
{
|
||||
bonusList.push_back(b);
|
||||
}
|
||||
|
||||
const JsonNode & CBuilding::getCurrentFactionForUpdateRoutine() const
|
||||
{
|
||||
const auto & faction = town->faction->identifier;
|
||||
const auto & factionsContent = (*VLC->modh->content)["factions"];
|
||||
const auto & coreData = factionsContent.modData.at("core");
|
||||
const auto & coreFactions = coreData.modData;
|
||||
const auto & currentFaction = coreFactions[faction];
|
||||
|
||||
if(currentFaction.isNull())
|
||||
{
|
||||
const auto index = faction.find(':');
|
||||
const std::string factionDir = index == std::string::npos ? faction : faction.substr(0, index);
|
||||
const auto it = factionsContent.modData.find(factionDir);
|
||||
|
||||
if(it == factionsContent.modData.end())
|
||||
{
|
||||
logMod->warn("Warning: Update old save failed: Faction: '%s' is not found.", factionDir);
|
||||
return currentFaction;
|
||||
}
|
||||
const std::string modFaction = index == std::string::npos ? faction : faction.substr(index + 1);
|
||||
return it->second.modData[modFaction];
|
||||
}
|
||||
return currentFaction;
|
||||
}
|
||||
|
||||
void CBuilding::update792()
|
||||
{
|
||||
subId = BuildingSubID::NONE;
|
||||
height = ETowerHeight::HEIGHT_NO_TOWER;
|
||||
@ -106,37 +137,72 @@ void CBuilding::update792(const BuildingID & bid, BuildingSubID::EBuildingSubID
|
||||
if(buildingName.empty())
|
||||
return;
|
||||
|
||||
const auto & faction = town->faction->identifier;
|
||||
auto factionsContent = (*VLC->modh->content)["factions"];
|
||||
auto & coreData = factionsContent.modData.at("core");
|
||||
auto & coreFactions = coreData.modData;
|
||||
auto & currentFaction = coreFactions[faction];
|
||||
auto & currentFaction = getCurrentFactionForUpdateRoutine();
|
||||
|
||||
if (currentFaction.isNull())
|
||||
{
|
||||
const auto index = faction.find(':');
|
||||
const std::string factionDir = index == std::string::npos ? faction : faction.substr(0, index);
|
||||
const auto it = factionsContent.modData.find(factionDir);
|
||||
|
||||
if (it == factionsContent.modData.end())
|
||||
{
|
||||
logMod->warn("Warning: Update old save failed: Faction: '%s' is not found.", factionDir);
|
||||
return;
|
||||
}
|
||||
const std::string modFaction = index == std::string::npos ? faction : faction.substr(index + 1);
|
||||
currentFaction = it->second.modData[modFaction];
|
||||
}
|
||||
|
||||
if (!currentFaction.isNull() && currentFaction.getType() == JsonNode::JsonType::DATA_STRUCT)
|
||||
if(!currentFaction.isNull() && currentFaction.getType() == JsonNode::JsonType::DATA_STRUCT)
|
||||
{
|
||||
const auto & buildings = currentFaction["town"]["buildings"];
|
||||
const auto & currentBuilding = buildings[buildingName];
|
||||
|
||||
subId = CTownHandler::getMappedValue<BuildingSubID::EBuildingSubID>(currentBuilding["type"], BuildingSubID::NONE, MappedKeys::SPECIAL_BUILDINGS);
|
||||
height = CBuilding::HEIGHT_NO_TOWER;
|
||||
height = subId == BuildingSubID::LOOKOUT_TOWER || bid == BuildingID::GRAIL
|
||||
? CTownHandler::getMappedValue<CBuilding::ETowerHeight>(currentBuilding["height"], CBuilding::HEIGHT_NO_TOWER, CBuilding::TOWER_TYPES)
|
||||
: height = CBuilding::HEIGHT_NO_TOWER;
|
||||
}
|
||||
}
|
||||
|
||||
if (subId == BuildingSubID::LOOKOUT_TOWER || bid == BuildingID::GRAIL)
|
||||
height = CTownHandler::getMappedValue<CBuilding::ETowerHeight>(currentBuilding["height"], CBuilding::HEIGHT_NO_TOWER, CBuilding::TOWER_TYPES);
|
||||
void CBuilding::update794()
|
||||
{
|
||||
if(bid == BuildingID::TAVERN || subId == BuildingSubID::BROTHERHOOD_OF_SWORD)
|
||||
{
|
||||
VLC->townh->addBonusesForVanilaBuilding(this);
|
||||
return;
|
||||
}
|
||||
if(!bid.IsSpecialOrGrail())
|
||||
return;
|
||||
|
||||
VLC->townh->addBonusesForVanilaBuilding(this);
|
||||
|
||||
if(!buildingBonuses.empty() //addBonusesForVanilaBuilding has done all work
|
||||
|| town->faction == nullptr //or faction data is not valid
|
||||
|| town->faction->identifier.empty())
|
||||
return;
|
||||
|
||||
const auto buildingName = CTownHandler::getMappedValue<std::string, BuildingID>(bid, std::string(), MappedKeys::BUILDING_TYPES_TO_NAMES, false);
|
||||
|
||||
if(buildingName.empty())
|
||||
return;
|
||||
|
||||
auto & currentFaction = getCurrentFactionForUpdateRoutine();
|
||||
|
||||
if(currentFaction.isNull() || currentFaction.getType() != JsonNode::JsonType::DATA_STRUCT)
|
||||
return;
|
||||
|
||||
const auto & buildings = currentFaction["town"]["buildings"];
|
||||
const auto & currentBuilding = buildings[buildingName];
|
||||
|
||||
CTownHandler::loadSpecialBuildingBonuses(currentBuilding["bonuses"], buildingBonuses, this);
|
||||
CTownHandler::loadSpecialBuildingBonuses(currentBuilding["onVisitBonuses"], onVisitBonuses, this);
|
||||
|
||||
if(!onVisitBonuses.empty())
|
||||
{
|
||||
if(subId == BuildingSubID::NONE)
|
||||
subId = BuildingSubID::CUSTOM_VISITING_BONUS;
|
||||
|
||||
for(auto & bonus : onVisitBonuses)
|
||||
bonus->sid = Bonus::getSid32(town->faction->index, bid);
|
||||
}
|
||||
const auto & overriddenBids = currentBuilding["overrides"];
|
||||
|
||||
if(overriddenBids.isNull())
|
||||
return;
|
||||
|
||||
auto scope = town->getBuildingScope();
|
||||
|
||||
for(auto b : overriddenBids.Vector())
|
||||
{
|
||||
auto bid = BuildingID(VLC->modh->identifiers.getIdentifier(scope, b).get());
|
||||
overrideBids.insert(bid);
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,6 +324,8 @@ JsonNode readBuilding(CLegacyConfigParser & parser)
|
||||
return ret;
|
||||
}
|
||||
|
||||
TPropagatorPtr CTownHandler::emptyPropagator = std::make_shared<CPropagatorNodeType>();
|
||||
|
||||
std::vector<JsonNode> CTownHandler::loadLegacyData(size_t dataSize)
|
||||
{
|
||||
std::vector<JsonNode> dest(dataSize);
|
||||
@ -412,7 +480,7 @@ std::vector<JsonNode> CTownHandler::loadLegacyData(size_t dataSize)
|
||||
return dest;
|
||||
}
|
||||
|
||||
void CTownHandler::loadBuildingRequirements(CBuilding * building, const JsonNode & source)
|
||||
void CTownHandler::loadBuildingRequirements(CBuilding * building, const JsonNode & source, std::vector<BuildingRequirementsHelper> & bidsToLoad)
|
||||
{
|
||||
if (source.isNull())
|
||||
return;
|
||||
@ -421,7 +489,7 @@ void CTownHandler::loadBuildingRequirements(CBuilding * building, const JsonNode
|
||||
hlp.building = building;
|
||||
hlp.town = building->town;
|
||||
hlp.json = source;
|
||||
requirementsToLoad.push_back(hlp);
|
||||
bidsToLoad.push_back(hlp);
|
||||
}
|
||||
|
||||
template<typename R, typename K>
|
||||
@ -445,6 +513,100 @@ R CTownHandler::getMappedValue(const JsonNode & node, const R defval, const std:
|
||||
return defval;
|
||||
}
|
||||
|
||||
void CTownHandler::addBonusesForVanilaBuilding(CBuilding * building)
|
||||
{
|
||||
std::shared_ptr<Bonus> b;
|
||||
static TPropagatorPtr playerPropagator = std::make_shared<CPropagatorNodeType>(CBonusSystemNode::ENodeTypes::PLAYER);
|
||||
|
||||
if(building->subId == BuildingSubID::NONE)
|
||||
{
|
||||
if(building->bid == BuildingID::TAVERN)
|
||||
b = createBonus(building, Bonus::MORALE, +1);
|
||||
else if(building->bid == BuildingID::GRAIL
|
||||
&& building->town->faction != nullptr
|
||||
&& boost::algorithm::ends_with(building->town->faction->identifier, ":cove"))
|
||||
{
|
||||
static TPropagatorPtr allCreaturesPropagator(new CPropagatorNodeType(CBonusSystemNode::ENodeTypes::ALL_CREATURES));
|
||||
static auto factionLimiter = std::make_shared<CreatureFactionLimiter>(building->town->faction->index);
|
||||
b = createBonus(building, Bonus::NO_TERRAIN_PENALTY, 0, allCreaturesPropagator);
|
||||
b->addLimiter(factionLimiter);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(building->subId)
|
||||
{
|
||||
case BuildingSubID::BROTHERHOOD_OF_SWORD:
|
||||
b = createBonus(building, Bonus::MORALE, +2);
|
||||
building->overrideBids.insert(BuildingID::TAVERN);
|
||||
break;
|
||||
case BuildingSubID::FOUNTAIN_OF_FORTUNE:
|
||||
b = createBonus(building, Bonus::LUCK, +2);
|
||||
break;
|
||||
case BuildingSubID::SPELL_POWER_GARRISON_BONUS:
|
||||
b = createBonus(building, Bonus::PRIMARY_SKILL, +2, PrimarySkill::SPELL_POWER);
|
||||
break;
|
||||
case BuildingSubID::ATTACK_GARRISON_BONUS:
|
||||
b = createBonus(building, Bonus::PRIMARY_SKILL, +2, PrimarySkill::ATTACK);
|
||||
break;
|
||||
case BuildingSubID::DEFENSE_GARRISON_BONUS:
|
||||
b = createBonus(building, Bonus::PRIMARY_SKILL, +2, PrimarySkill::DEFENSE);
|
||||
break;
|
||||
case BuildingSubID::LIGHTHOUSE:
|
||||
b = createBonus(building, Bonus::SEA_MOVEMENT, +500, playerPropagator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(b)
|
||||
building->addNewBonus(b, building->buildingBonuses);
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, Bonus::BonusType type, int val, int subtype)
|
||||
{
|
||||
return createBonus(build, type, val, emptyPropagator, subtype);
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, Bonus::BonusType type, int val, TPropagatorPtr & prop, int subtype)
|
||||
{
|
||||
std::ostringstream descr;
|
||||
descr << build->name;
|
||||
return createBonusImpl(build->bid, type, val, prop, descr.str(), subtype);
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> CTownHandler::createBonusImpl(BuildingID building, Bonus::BonusType type, int val, TPropagatorPtr & prop, const std::string & description, int subtype)
|
||||
{
|
||||
auto b = std::make_shared<Bonus>(Bonus::PERMANENT, type, Bonus::TOWN_STRUCTURE, val, building, description, subtype);
|
||||
|
||||
if(prop)
|
||||
b->addPropagator(prop);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
void CTownHandler::loadSpecialBuildingBonuses(const JsonNode & source, BonusList & bonusList, CBuilding * building)
|
||||
{
|
||||
for(auto b : source.Vector())
|
||||
{
|
||||
auto bonus = JsonUtils::parseBuildingBonus(b, building->bid, building->name);
|
||||
|
||||
if(bonus == nullptr)
|
||||
continue;
|
||||
|
||||
if(bonus->limiter != nullptr)
|
||||
{
|
||||
auto limPtr = dynamic_cast<CreatureFactionLimiter*>(bonus->limiter.get());
|
||||
|
||||
if(limPtr != nullptr && limPtr->faction == (TFaction)-1)
|
||||
limPtr->faction = building->town->faction->index;
|
||||
}
|
||||
//JsonUtils::parseBuildingBonus produces UNKNOWN type propagator instead of empty.
|
||||
if(bonus->propagator != nullptr
|
||||
&& bonus->propagator->getPropagatorType() == CBonusSystemNode::ENodeTypes::UNKNOWN)
|
||||
bonus->addPropagator(emptyPropagator);
|
||||
building->addNewBonus(bonus, bonusList);
|
||||
}
|
||||
}
|
||||
|
||||
void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, const JsonNode & source)
|
||||
{
|
||||
auto ret = new CBuilding();
|
||||
@ -474,6 +636,26 @@ 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())
|
||||
{
|
||||
loadSpecialBuildingBonuses(source["bonuses"], ret->buildingBonuses, ret);
|
||||
|
||||
if(ret->buildingBonuses.empty())
|
||||
addBonusesForVanilaBuilding(ret);
|
||||
|
||||
loadSpecialBuildingBonuses(source["onVisitBonuses"], ret->onVisitBonuses, ret);
|
||||
|
||||
if(!ret->onVisitBonuses.empty())
|
||||
{
|
||||
if(ret->subId == BuildingSubID::NONE)
|
||||
ret->subId = BuildingSubID::CUSTOM_VISITING_BONUS;
|
||||
|
||||
for(auto & bonus : ret->onVisitBonuses)
|
||||
bonus->sid = Bonus::getSid32(ret->town->faction->index, ret->bid);
|
||||
}
|
||||
}
|
||||
//MODS COMPATIBILITY FOR 0.96
|
||||
if(!ret->produce.nonZero())
|
||||
{
|
||||
@ -501,8 +683,10 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
|
||||
}
|
||||
}
|
||||
}
|
||||
loadBuildingRequirements(ret, source["requires"], requirementsToLoad);
|
||||
|
||||
loadBuildingRequirements(ret, source["requires"]);
|
||||
if(ret->bid.IsSpecialOrGrail())
|
||||
loadBuildingRequirements(ret, source["overrides"], overriddenBidsToLoad);
|
||||
|
||||
if (!source["upgrades"].isNull())
|
||||
{
|
||||
@ -828,9 +1012,10 @@ ETerrainType::EETerrainType CTownHandler::getDefaultTerrainForAlignment(EAlignme
|
||||
return terrain;
|
||||
}
|
||||
|
||||
CFaction * CTownHandler::loadFromJson(const JsonNode &source, const std::string & identifier)
|
||||
CFaction * CTownHandler::loadFromJson(const JsonNode &source, const std::string & identifier, TFaction index)
|
||||
{
|
||||
auto faction = new CFaction();
|
||||
faction->index = index;
|
||||
|
||||
faction->name = source["name"].String();
|
||||
faction->identifier = identifier;
|
||||
@ -871,9 +1056,9 @@ CFaction * CTownHandler::loadFromJson(const JsonNode &source, const std::string
|
||||
|
||||
void CTownHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
|
||||
{
|
||||
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
|
||||
auto index = static_cast<TFaction>(factions.size());
|
||||
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name), index);
|
||||
|
||||
object->index = static_cast<TFaction>(factions.size());
|
||||
factions.push_back(object);
|
||||
|
||||
if (object->town)
|
||||
@ -911,8 +1096,8 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
|
||||
|
||||
void CTownHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
|
||||
{
|
||||
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
|
||||
object->index = static_cast<TFaction>(index);
|
||||
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name), static_cast<TFaction>(index));
|
||||
|
||||
if (factions.size() > index)
|
||||
assert(factions[index] == nullptr); // ensure that this id was not loaded before
|
||||
else
|
||||
@ -957,6 +1142,7 @@ void CTownHandler::loadCustom()
|
||||
void CTownHandler::afterLoadFinalization()
|
||||
{
|
||||
initializeRequirements();
|
||||
initializeOverridden();
|
||||
initializeWarMachines();
|
||||
}
|
||||
|
||||
@ -979,6 +1165,22 @@ void CTownHandler::initializeRequirements()
|
||||
requirementsToLoad.clear();
|
||||
}
|
||||
|
||||
void CTownHandler::initializeOverridden()
|
||||
{
|
||||
for(auto & bidHelper : overriddenBidsToLoad)
|
||||
{
|
||||
auto jsonNode = bidHelper.json;
|
||||
auto scope = bidHelper.town->getBuildingScope();
|
||||
|
||||
for(auto b : jsonNode.Vector())
|
||||
{
|
||||
auto bid = BuildingID(VLC->modh->identifiers.getIdentifier(scope, b).get());
|
||||
bidHelper.building->overrideBids.insert(bid);
|
||||
}
|
||||
}
|
||||
overriddenBidsToLoad.clear();
|
||||
}
|
||||
|
||||
void CTownHandler::initializeWarMachines()
|
||||
{
|
||||
// must be done separately after all objects are loaded
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "IHandlerBase.h"
|
||||
#include "LogicalExpression.h"
|
||||
#include "battle/BattleHex.h"
|
||||
#include "HeroBonus.h"
|
||||
|
||||
class CLegacyConfigParser;
|
||||
class JsonNode;
|
||||
@ -47,6 +48,9 @@ public:
|
||||
BuildingID bid; //structure ID
|
||||
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
|
||||
std::set<BuildingID> overrideBids; /// the building which bonuses should be overridden with bonuses of the current building
|
||||
BonusList buildingBonuses;
|
||||
BonusList onVisitBonuses;
|
||||
|
||||
enum EBuildMode
|
||||
{
|
||||
@ -95,14 +99,16 @@ public:
|
||||
bool IsVisitingBonus() const
|
||||
{
|
||||
return subId == BuildingSubID::ATTACK_VISITING_BONUS ||
|
||||
subId == BuildingSubID::DEFENSE_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::EXPERIENCE_VISITING_BONUS ||
|
||||
subId == BuildingSubID::CUSTOM_VISITING_BONUS;
|
||||
}
|
||||
|
||||
/// input: faction, bid; output: subId, height;
|
||||
void update792(const BuildingID & bid, BuildingSubID::EBuildingSubID & subId, ETowerHeight & height);
|
||||
void addNewBonus(std::shared_ptr<Bonus> b, BonusList & bonusList);
|
||||
void update792();
|
||||
void update794();
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
@ -123,9 +129,17 @@ public:
|
||||
h & height;
|
||||
}
|
||||
if(!h.saving && version < 793)
|
||||
update792(); //adjust height, subId
|
||||
|
||||
if(version >= 794)
|
||||
{
|
||||
update792(bid, subId, height);
|
||||
h & overrideBids;
|
||||
h & buildingBonuses;
|
||||
h & onVisitBonuses;
|
||||
}
|
||||
else if(!h.saving)
|
||||
update794(); //populate overrideBids, buildingBonuses, onVisitBonuses
|
||||
|
||||
if(!h.saving)
|
||||
deserializeFix();
|
||||
}
|
||||
@ -133,8 +147,8 @@ public:
|
||||
friend class CTownHandler;
|
||||
|
||||
private:
|
||||
|
||||
void deserializeFix();
|
||||
const JsonNode & getCurrentFactionForUpdateRoutine() const;
|
||||
};
|
||||
|
||||
/// This is structure used only by client
|
||||
@ -354,19 +368,27 @@ class DLL_LINKAGE CTownHandler : public IHandlerBase
|
||||
|
||||
std::map<CTown *, JsonNode> warMachinesToLoad;
|
||||
std::vector<BuildingRequirementsHelper> requirementsToLoad;
|
||||
std::vector<BuildingRequirementsHelper> overriddenBidsToLoad; //list of buildings, which bonuses should be overridden.
|
||||
|
||||
const static ETerrainType::EETerrainType defaultGoodTerrain = ETerrainType::EETerrainType::GRASS;
|
||||
const static ETerrainType::EETerrainType defaultEvilTerrain = ETerrainType::EETerrainType::LAVA;
|
||||
const static ETerrainType::EETerrainType defaultNeutralTerrain = ETerrainType::EETerrainType::ROUGH;
|
||||
|
||||
static TPropagatorPtr emptyPropagator;
|
||||
|
||||
void initializeRequirements();
|
||||
void initializeOverridden();
|
||||
void initializeWarMachines();
|
||||
|
||||
/// loads CBuilding's into town
|
||||
void loadBuildingRequirements(CBuilding * building, const JsonNode & source);
|
||||
void loadBuildingRequirements(CBuilding * building, const JsonNode & source, std::vector<BuildingRequirementsHelper> & bidsToLoad);
|
||||
void loadBuilding(CTown * town, const std::string & stringID, const JsonNode & source);
|
||||
void loadBuildings(CTown * town, const JsonNode & source);
|
||||
|
||||
std::shared_ptr<Bonus> createBonus(CBuilding * build, Bonus::BonusType type, int val, int subtype = -1);
|
||||
std::shared_ptr<Bonus> createBonus(CBuilding * build, Bonus::BonusType type, int val, TPropagatorPtr & prop, int subtype = -1);
|
||||
std::shared_ptr<Bonus> createBonusImpl(BuildingID building, Bonus::BonusType type, int val, TPropagatorPtr & prop, const std::string & description, int subtype = -1);
|
||||
|
||||
/// loads CStructure's into town
|
||||
void loadStructure(CTown &town, const std::string & stringID, const JsonNode & source);
|
||||
void loadStructures(CTown &town, const JsonNode & source);
|
||||
@ -383,7 +405,7 @@ class DLL_LINKAGE CTownHandler : public IHandlerBase
|
||||
|
||||
ETerrainType::EETerrainType getDefaultTerrainForAlignment(EAlignment::EAlignment aligment) const;
|
||||
|
||||
CFaction * loadFromJson(const JsonNode & data, const std::string & identifier);
|
||||
CFaction * loadFromJson(const JsonNode & data, const std::string & identifier, TFaction index);
|
||||
|
||||
void loadRandomFaction();
|
||||
|
||||
@ -404,6 +426,7 @@ 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);
|
||||
|
||||
void loadCustom() override;
|
||||
void afterLoadFinalization() override;
|
||||
@ -416,6 +439,7 @@ public:
|
||||
|
||||
//json serialization helper
|
||||
static std::string encodeFaction(const si32 index);
|
||||
static void loadSpecialBuildingBonuses(const JsonNode & source, BonusList & bonusList, CBuilding * building);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
|
@ -297,7 +297,7 @@ class TeleportChannelID : public BaseForID<TeleportChannelID, si32>
|
||||
// Enum declarations
|
||||
namespace PrimarySkill
|
||||
{
|
||||
enum PrimarySkill { ATTACK, DEFENSE, SPELL_POWER, KNOWLEDGE,
|
||||
enum PrimarySkill { NONE = -1, ATTACK, DEFENSE, SPELL_POWER, KNOWLEDGE,
|
||||
EXPERIENCE = 4}; //for some reason changePrimSkill uses it
|
||||
}
|
||||
|
||||
@ -452,7 +452,8 @@ namespace BuildingSubID
|
||||
KNOWLEDGE_VISITING_BONUS,
|
||||
EXPERIENCE_VISITING_BONUS,
|
||||
LIGHTHOUSE,
|
||||
TREASURY
|
||||
TREASURY,
|
||||
CUSTOM_VISITING_BONUS
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,8 @@ const std::map<std::string, TLimiterPtr> bonusLimiterMap =
|
||||
{"SHOOTER_ONLY", std::make_shared<HasAnotherBonusLimiter>(Bonus::SHOOTER)},
|
||||
{"DRAGON_NATURE", std::make_shared<HasAnotherBonusLimiter>(Bonus::DRAGON_NATURE)},
|
||||
{"IS_UNDEAD", std::make_shared<HasAnotherBonusLimiter>(Bonus::UNDEAD)},
|
||||
{"CREATURE_NATIVE_TERRAIN", std::make_shared<CreatureTerrainLimiter>()}
|
||||
{"CREATURE_NATIVE_TERRAIN", std::make_shared<CreatureTerrainLimiter>()},
|
||||
{"CREATURE_FACTION", std::make_shared<CreatureFactionLimiter>()}
|
||||
};
|
||||
|
||||
const std::map<std::string, TPropagatorPtr> bonusPropagatorMap =
|
||||
@ -81,7 +82,8 @@ const std::map<std::string, TPropagatorPtr> bonusPropagatorMap =
|
||||
{"PLAYER_PROPAGATOR", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::PLAYER)},
|
||||
{"HERO", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::HERO)},
|
||||
{"TEAM_PROPAGATOR", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::TEAM)}, //untested
|
||||
{"GLOBAL_EFFECT", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::GLOBAL_EFFECTS)}
|
||||
{"GLOBAL_EFFECT", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::GLOBAL_EFFECTS)},
|
||||
{"ALL_CREATURES", std::make_shared<CPropagatorNodeType>(CBonusSystemNode::ALL_CREATURES)}
|
||||
}; //untested
|
||||
|
||||
const std::map<std::string, TUpdaterPtr> bonusUpdaterMap =
|
||||
@ -1570,8 +1572,8 @@ std::string Bonus::nameForBonus() const
|
||||
}
|
||||
}
|
||||
|
||||
Bonus::Bonus(ui16 Dur, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype)
|
||||
: duration(Dur), type(Type), subtype(Subtype), source(Src), val(Val), sid(ID), description(Desc)
|
||||
Bonus::Bonus(Bonus::BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype)
|
||||
: duration((ui16)Duration), type(Type), subtype(Subtype), source(Src), val(Val), sid(ID), description(Desc)
|
||||
{
|
||||
turnsRemain = 0;
|
||||
valType = ADDITIVE_VALUE;
|
||||
@ -1579,8 +1581,8 @@ Bonus::Bonus(ui16 Dur, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::
|
||||
boost::algorithm::trim(description);
|
||||
}
|
||||
|
||||
Bonus::Bonus(ui16 Dur, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype, ValueType ValType)
|
||||
: duration(Dur), type(Type), subtype(Subtype), source(Src), val(Val), sid(ID), valType(ValType)
|
||||
Bonus::Bonus(Bonus::BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype, ValueType ValType)
|
||||
: duration((ui16)Duration), type(Type), subtype(Subtype), source(Src), val(Val), sid(ID), valType(ValType)
|
||||
{
|
||||
turnsRemain = 0;
|
||||
effectRange = NO_LIMIT;
|
||||
@ -1922,17 +1924,27 @@ bool IPropagator::shouldBeAttached(CBonusSystemNode *dest)
|
||||
return false;
|
||||
}
|
||||
|
||||
CBonusSystemNode::ENodeTypes IPropagator::getPropagatorType() const
|
||||
{
|
||||
return CBonusSystemNode::ENodeTypes::NONE;
|
||||
}
|
||||
|
||||
CPropagatorNodeType::CPropagatorNodeType()
|
||||
:nodeType(0)
|
||||
:nodeType(CBonusSystemNode::ENodeTypes::UNKNOWN)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CPropagatorNodeType::CPropagatorNodeType(int NodeType)
|
||||
CPropagatorNodeType::CPropagatorNodeType(CBonusSystemNode::ENodeTypes NodeType)
|
||||
: nodeType(NodeType)
|
||||
{
|
||||
}
|
||||
|
||||
CBonusSystemNode::ENodeTypes CPropagatorNodeType::getPropagatorType() const
|
||||
{
|
||||
return nodeType;
|
||||
}
|
||||
|
||||
bool CPropagatorNodeType::shouldBeAttached(CBonusSystemNode *dest)
|
||||
{
|
||||
return nodeType == dest->getNodeType();
|
||||
|
@ -418,8 +418,8 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
|
||||
|
||||
std::string description;
|
||||
|
||||
Bonus(ui16 Dur, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype=-1);
|
||||
Bonus(ui16 Dur, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype=-1, ValueType ValType = ADDITIVE_VALUE);
|
||||
Bonus(BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype=-1);
|
||||
Bonus(BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype=-1, ValueType ValType = ADDITIVE_VALUE);
|
||||
Bonus();
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
@ -508,6 +508,10 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
|
||||
{
|
||||
val += Val;
|
||||
}
|
||||
STRONG_INLINE static ui32 getSid32(ui32 high, ui32 low)
|
||||
{
|
||||
return (high << 16) + low;
|
||||
}
|
||||
|
||||
std::string Description() const;
|
||||
JsonNode toJsonNode() const;
|
||||
@ -639,30 +643,6 @@ inline BonusList::const_iterator range_end(BonusList const &x)
|
||||
|
||||
DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const BonusList &bonusList);
|
||||
|
||||
class DLL_LINKAGE IPropagator
|
||||
{
|
||||
public:
|
||||
virtual ~IPropagator();
|
||||
virtual bool shouldBeAttached(CBonusSystemNode *dest);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{}
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CPropagatorNodeType : public IPropagator
|
||||
{
|
||||
int nodeType; //CBonusSystemNode::ENodeTypes
|
||||
public:
|
||||
CPropagatorNodeType();
|
||||
CPropagatorNodeType(int NodeType);
|
||||
bool shouldBeAttached(CBonusSystemNode *dest) override;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & nodeType;
|
||||
}
|
||||
};
|
||||
|
||||
struct BonusLimitationContext
|
||||
{
|
||||
std::shared_ptr<const Bonus> b;
|
||||
@ -756,6 +736,7 @@ class DLL_LINKAGE CBonusSystemNode : public virtual IBonusBearer, public boost::
|
||||
public:
|
||||
enum ENodeTypes
|
||||
{
|
||||
NONE = -1,
|
||||
UNKNOWN, STACK_INSTANCE, STACK_BATTLE, SPECIALTY, ARTIFACT, CREATURE, ARTIFACT_INSTANCE, HERO, PLAYER, TEAM,
|
||||
TOWN_AND_VISITOR, BATTLE, COMMANDER, GLOBAL_EFFECTS, ALL_CREATURES
|
||||
};
|
||||
@ -857,6 +838,33 @@ public:
|
||||
friend class CBonusProxy;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE IPropagator
|
||||
{
|
||||
public:
|
||||
virtual ~IPropagator();
|
||||
virtual bool shouldBeAttached(CBonusSystemNode *dest);
|
||||
virtual CBonusSystemNode::ENodeTypes getPropagatorType() const;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{}
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CPropagatorNodeType : public IPropagator
|
||||
{
|
||||
CBonusSystemNode::ENodeTypes nodeType;
|
||||
|
||||
public:
|
||||
CPropagatorNodeType();
|
||||
CPropagatorNodeType(CBonusSystemNode::ENodeTypes NodeType);
|
||||
bool shouldBeAttached(CBonusSystemNode *dest) override;
|
||||
CBonusSystemNode::ENodeTypes getPropagatorType() const override;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & nodeType;
|
||||
}
|
||||
};
|
||||
|
||||
namespace NBonus
|
||||
{
|
||||
//set of methods that may be safely called with nullptr objs
|
||||
|
@ -697,6 +697,19 @@ std::shared_ptr<Bonus> JsonUtils::parseBonus(const JsonNode &ability)
|
||||
return b;
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> JsonUtils::parseBuildingBonus(const JsonNode &ability, BuildingID building, std::string description)
|
||||
{
|
||||
/* duration = Bonus::PERMANENT
|
||||
source = Bonus::TOWN_STRUCTURE
|
||||
bonusType, val, subtype - get from json
|
||||
*/
|
||||
auto b = std::make_shared<Bonus>(Bonus::PERMANENT, Bonus::NONE, Bonus::TOWN_STRUCTURE, 0, building, description, -1);
|
||||
|
||||
if(!parseBonus(ability, b.get()))
|
||||
return nullptr;
|
||||
return b;
|
||||
}
|
||||
|
||||
bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
||||
{
|
||||
const JsonNode *value;
|
||||
@ -726,7 +739,8 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
||||
|
||||
b->sid = static_cast<si32>(ability["sourceID"].Float());
|
||||
|
||||
b->description = ability["description"].String();
|
||||
if(!ability["description"].isNull())
|
||||
b->description = ability["description"].String();
|
||||
|
||||
value = &ability["effectRange"];
|
||||
if (!value->isNull())
|
||||
@ -738,7 +752,7 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
||||
switch (value->getType())
|
||||
{
|
||||
case JsonNode::JsonType::DATA_STRING:
|
||||
b->duration = parseByMap(bonusDurationMap, value, "duration type ");
|
||||
b->duration = (Bonus::BonusDuration)parseByMap(bonusDurationMap, value, "duration type ");
|
||||
break;
|
||||
case JsonNode::JsonType::DATA_VECTOR:
|
||||
{
|
||||
@ -747,7 +761,7 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
||||
{
|
||||
dur |= parseByMap(bonusDurationMap, &d, "duration type ");
|
||||
}
|
||||
b->duration = dur;
|
||||
b->duration = (Bonus::BonusDuration)dur;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -8,6 +8,7 @@
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
#include "GameConstants.h"
|
||||
|
||||
class JsonNode;
|
||||
typedef std::map <std::string, JsonNode> JsonMap;
|
||||
@ -168,6 +169,7 @@ namespace JsonUtils
|
||||
///
|
||||
DLL_LINKAGE std::shared_ptr<Bonus> parseBonus(const JsonVector &ability_vec);
|
||||
DLL_LINKAGE std::shared_ptr<Bonus> parseBonus(const JsonNode &ability);
|
||||
DLL_LINKAGE std::shared_ptr<Bonus> parseBuildingBonus(const JsonNode &ability, BuildingID building, std::string description);
|
||||
DLL_LINKAGE bool parseBonus(const JsonNode &ability, Bonus *placement);
|
||||
DLL_LINKAGE std::shared_ptr<ILimiter> parseLimiter(const JsonNode & limiter);
|
||||
DLL_LINKAGE void resolveIdentifier(si32 &var, const JsonNode &node, std::string name);
|
||||
|
@ -245,10 +245,21 @@ DLL_LINKAGE void GiveBonus::applyGs(CGameState *gs)
|
||||
std::string &descr = b->description;
|
||||
|
||||
if(!bdescr.message.size()
|
||||
&& bonus.source == Bonus::OBJECT
|
||||
&& (bonus.type == Bonus::LUCK || bonus.type == Bonus::MORALE))
|
||||
{
|
||||
descr = VLC->generaltexth->arraytxt[bonus.val > 0 ? 110 : 109]; //+/-%d Temporary until next battle"
|
||||
if (bonus.source == Bonus::OBJECT)
|
||||
{
|
||||
descr = VLC->generaltexth->arraytxt[bonus.val > 0 ? 110 : 109]; //+/-%d Temporary until next battle"
|
||||
}
|
||||
else if(bonus.source == Bonus::TOWN_STRUCTURE)
|
||||
{
|
||||
descr = bonus.description;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
bdescr.toString(descr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -556,16 +567,27 @@ void TryMoveHero::applyGs(CGameState *gs)
|
||||
DLL_LINKAGE void NewStructures::applyGs(CGameState *gs)
|
||||
{
|
||||
CGTownInstance *t = gs->getTown(tid);
|
||||
|
||||
for(const auto & id : bid)
|
||||
{
|
||||
assert(t->town->buildings.at(id) != nullptr);
|
||||
t->builtBuildings.insert(id);
|
||||
|
||||
t->updateAppearance();
|
||||
auto currentBuilding = t->town->buildings.at(id);
|
||||
|
||||
if(currentBuilding->overrideBids.empty())
|
||||
continue;
|
||||
|
||||
for(auto overrideBid : currentBuilding->overrideBids)
|
||||
{
|
||||
t->overriddenBuildings.insert(overrideBid);
|
||||
t->deleteTownBonus(overrideBid);
|
||||
}
|
||||
}
|
||||
t->builded = builded;
|
||||
t->recreateBuildingsBonuses();
|
||||
}
|
||||
|
||||
DLL_LINKAGE void RazeStructures::applyGs(CGameState *gs)
|
||||
{
|
||||
CGTownInstance *t = gs->getTown(tid);
|
||||
|
@ -196,3 +196,8 @@ void LibClasses::setContent(std::shared_ptr<CContentHandler> content)
|
||||
{
|
||||
modh->content = content;
|
||||
}
|
||||
|
||||
void LibClasses::restoreAllCreaturesNodeType794()
|
||||
{
|
||||
creh->restoreAllCreaturesNodeType794();
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ class DLL_LINKAGE LibClasses
|
||||
void makeNull(); //sets all handler pointers to null
|
||||
std::shared_ptr<CContentHandler> getContent() const;
|
||||
void setContent(std::shared_ptr<CContentHandler> content);
|
||||
void restoreAllCreaturesNodeType794();
|
||||
|
||||
public:
|
||||
bool IS_AI_ENABLED; //unused?
|
||||
@ -69,6 +70,9 @@ public:
|
||||
h & heroh;
|
||||
h & arth;
|
||||
h & creh;
|
||||
if(!h.saving && version < 794)
|
||||
restoreAllCreaturesNodeType794();
|
||||
|
||||
h & townh;
|
||||
h & objh;
|
||||
h & objtypeh;
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "../mapping/CMap.h"
|
||||
#include "../CPlayerState.h"
|
||||
#include "../serializer/JsonSerializeFormat.h"
|
||||
#include "../HeroBonus.h"
|
||||
|
||||
std::vector<const CArtifact *> CGTownInstance::merchantArtifacts;
|
||||
std::vector<int> CGTownInstance::universitySkills;
|
||||
@ -435,8 +436,6 @@ void CGDwelling::serializeJsonOptions(JsonSerializeFormat & handler)
|
||||
}
|
||||
}
|
||||
|
||||
TPropagatorPtr CGTownInstance::emptyPropagator = TPropagatorPtr();
|
||||
|
||||
int CGTownInstance::getSightRadius() const //returns sight distance
|
||||
{
|
||||
auto ret = CBuilding::HEIGHT_NO_TOWER;
|
||||
@ -753,6 +752,17 @@ void CGTownInstance::tryAddOnePerWeekBonus(BuildingSubID::EBuildingSubID subID)
|
||||
bonusingBuildings.push_back(new COPWBonus(bid, subID, this));
|
||||
}
|
||||
|
||||
void CGTownInstance::initOverriddenBids()
|
||||
{
|
||||
for(const auto & bid : builtBuildings)
|
||||
{
|
||||
auto & overrideThem = town->buildings.at(bid)->overrideBids;
|
||||
|
||||
for(auto & overrideIt : overrideThem)
|
||||
overriddenBuildings.insert(overrideIt);
|
||||
}
|
||||
}
|
||||
|
||||
void CGTownInstance::tryAddVisitingBonus(BuildingSubID::EBuildingSubID subID)
|
||||
{
|
||||
auto bid = town->getBuildingType(subID);
|
||||
@ -765,6 +775,9 @@ void CGTownInstance::addTownBonuses()
|
||||
{
|
||||
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));
|
||||
|
||||
@ -773,6 +786,36 @@ void CGTownInstance::addTownBonuses()
|
||||
}
|
||||
}
|
||||
|
||||
void CGTownInstance::deleteTownBonus(BuildingID::EBuildingID bid)
|
||||
{
|
||||
size_t i = 0;
|
||||
CGTownBuilding * freeIt = nullptr;
|
||||
|
||||
for(i = 0; i != bonusingBuildings.size(); i++)
|
||||
{
|
||||
if(bonusingBuildings[i]->getBuildingType() == bid)
|
||||
{
|
||||
freeIt = bonusingBuildings[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
if(isVisitingBonus)
|
||||
delete (CTownBonus *)freeIt;
|
||||
else if(isWeekBonus)
|
||||
delete (COPWBonus *)freeIt;
|
||||
}
|
||||
|
||||
void CGTownInstance::initObj(CRandomGenerator & rand) ///initialize town structures
|
||||
{
|
||||
@ -794,17 +837,18 @@ void CGTownInstance::initObj(CRandomGenerator & rand) ///initialize town structu
|
||||
creatures[level].second.push_back(town->creatures[level][upgradeNum]);
|
||||
}
|
||||
}
|
||||
initOverriddenBids();
|
||||
addTownBonuses(); //add special bonuses from buildings to the bonusingBuildings vector.
|
||||
recreateBuildingsBonuses();
|
||||
updateAppearance();
|
||||
}
|
||||
|
||||
void CGTownInstance::updateBonusingBuildings()
|
||||
void CGTownInstance::updateBonusingBuildings() //update to version 792
|
||||
{
|
||||
if (this->town->faction != nullptr)
|
||||
if(this->town->faction != nullptr)
|
||||
{
|
||||
//firstly, update subtype for the Bonusing objects, which are already stored in the bonusing list
|
||||
for (auto building : bonusingBuildings) //no garrison bonuses here, only week and visiting bonuses
|
||||
for(auto building : bonusingBuildings) //no garrison bonuses here, only week and visiting bonuses
|
||||
{
|
||||
switch (this->town->faction->index)
|
||||
{
|
||||
@ -838,7 +882,7 @@ void CGTownInstance::updateBonusingBuildings()
|
||||
}
|
||||
}
|
||||
//secondly, supplement bonusing buildings list and active bonuses; subtypes for these objects are already set in update792
|
||||
for (auto & kvp : town->buildings)
|
||||
for(auto & kvp : town->buildings)
|
||||
{
|
||||
auto & building = kvp.second;
|
||||
|
||||
@ -864,6 +908,25 @@ void CGTownInstance::updateBonusingBuildings()
|
||||
recreateBuildingsBonuses(); ///Clear all bonuses and recreate
|
||||
}
|
||||
|
||||
void CGTownInstance::updateTown794()
|
||||
{
|
||||
for(auto builtBuilding : builtBuildings)
|
||||
{
|
||||
auto building = town->buildings.at(builtBuilding);
|
||||
|
||||
for(auto overriddenBid : building->overrideBids)
|
||||
overriddenBuildings.insert(overriddenBid);
|
||||
}
|
||||
for(auto & kvp : town->buildings)
|
||||
{
|
||||
auto & building = kvp.second;
|
||||
//The building acts as a visiting bonus and it has not been overridden.
|
||||
if(building->IsVisitingBonus() && overriddenBuildings.find(kvp.first) == overriddenBuildings.end())
|
||||
tryAddVisitingBonus(building->subId);
|
||||
}
|
||||
recreateBuildingsBonuses();
|
||||
}
|
||||
|
||||
bool CGTownInstance::hasBuiltInOldWay(ETownType::ETownType type, BuildingID bid) const
|
||||
{
|
||||
return (this->town->faction != nullptr && this->town->faction->index == type && hasBuilt(bid));
|
||||
@ -1184,112 +1247,30 @@ void CGTownInstance::updateMoraleBonusFromArmy()
|
||||
|
||||
void CGTownInstance::recreateBuildingsBonuses()
|
||||
{
|
||||
static TPropagatorPtr playerProp(new CPropagatorNodeType(PLAYER));
|
||||
|
||||
BonusList bl;
|
||||
getExportedBonusList().getBonuses(bl, Selector::sourceType()(Bonus::TOWN_STRUCTURE));
|
||||
|
||||
for(auto b : bl)
|
||||
removeBonus(b);
|
||||
|
||||
//tricky! -> checks tavern only if no bratherhood of sword or not a castle
|
||||
if(!addBonusIfBuilt(BuildingSubID::BROTHERHOOD_OF_SWORD, Bonus::MORALE, +2))
|
||||
addBonusIfBuilt(BuildingID::TAVERN, Bonus::MORALE, +1);
|
||||
for(auto bid : builtBuildings)
|
||||
{
|
||||
if(vstd::contains(overriddenBuildings, bid)) //tricky! -> checks tavern only if no bratherhood of sword
|
||||
continue;
|
||||
|
||||
addBonusIfBuilt(BuildingSubID::FOUNTAIN_OF_FORTUNE, Bonus::LUCK, +2); //fountain of fortune
|
||||
addBonusIfBuilt(BuildingSubID::SPELL_POWER_GARRISON_BONUS, Bonus::PRIMARY_SKILL, +2, PrimarySkill::SPELL_POWER);//works as Brimstone Clouds
|
||||
addBonusIfBuilt(BuildingSubID::ATTACK_GARRISON_BONUS, Bonus::PRIMARY_SKILL, +2, PrimarySkill::ATTACK);//works as Blood Obelisk
|
||||
addBonusIfBuilt(BuildingSubID::DEFENSE_GARRISON_BONUS, Bonus::PRIMARY_SKILL, +2, PrimarySkill::DEFENSE);//works as Glyphs of Fear
|
||||
addBonusIfBuilt(BuildingSubID::LIGHTHOUSE, Bonus::SEA_MOVEMENT, +500, playerProp);
|
||||
auto building = town->buildings.at(bid);
|
||||
|
||||
if(subID == ETownType::CASTLE) //castle
|
||||
{
|
||||
addBonusIfBuilt(BuildingID::GRAIL, Bonus::MORALE, +2, playerProp); //colossus
|
||||
}
|
||||
else if(subID == ETownType::RAMPART) //rampart
|
||||
{
|
||||
addBonusIfBuilt(BuildingID::GRAIL, Bonus::LUCK, +2, playerProp); //guardian spirit
|
||||
}
|
||||
else if(subID == ETownType::TOWER) //tower
|
||||
{
|
||||
addBonusIfBuilt(BuildingID::GRAIL, Bonus::PRIMARY_SKILL, +15, PrimarySkill::KNOWLEDGE); //grail
|
||||
}
|
||||
else if(subID == ETownType::NECROPOLIS) //necropolis
|
||||
{
|
||||
addBonusIfBuilt(BuildingID::COVER_OF_DARKNESS, Bonus::DARKNESS, +20);
|
||||
addBonusIfBuilt(BuildingID::NECROMANCY_AMPLIFIER, Bonus::SECONDARY_SKILL_PREMY, +10, playerProp, SecondarySkill::NECROMANCY); //necromancy amplifier
|
||||
addBonusIfBuilt(BuildingID::GRAIL, Bonus::SECONDARY_SKILL_PREMY, +20, playerProp, SecondarySkill::NECROMANCY); //Soul prison
|
||||
}
|
||||
else if(subID == ETownType::DUNGEON) //Dungeon
|
||||
{
|
||||
addBonusIfBuilt(BuildingID::GRAIL, Bonus::PRIMARY_SKILL, +12, PrimarySkill::SPELL_POWER); //grail
|
||||
}
|
||||
else if(subID == ETownType::STRONGHOLD) //Stronghold
|
||||
{
|
||||
addBonusIfBuilt(BuildingID::GRAIL, Bonus::PRIMARY_SKILL, +20, PrimarySkill::ATTACK); //grail
|
||||
}
|
||||
else if(subID == ETownType::FORTRESS) //Fortress
|
||||
{
|
||||
addBonusIfBuilt(BuildingID::GRAIL, Bonus::PRIMARY_SKILL, +10, PrimarySkill::ATTACK); //grail
|
||||
addBonusIfBuilt(BuildingID::GRAIL, Bonus::PRIMARY_SKILL, +10, PrimarySkill::DEFENSE); //grail
|
||||
}
|
||||
else if(boost::algorithm::ends_with(this->town->faction->identifier, ":cove") && hasBuilt(BuildingID::GRAIL))
|
||||
{
|
||||
static TPropagatorPtr lsProp(new CPropagatorNodeType(ALL_CREATURES));
|
||||
static auto factionLimiter = std::make_shared<CreatureFactionLimiter>(this->town->faction->index);
|
||||
auto b = std::make_shared<Bonus>(Bonus::PERMANENT, Bonus::NO_TERRAIN_PENALTY, Bonus::TOWN_STRUCTURE, 0, BuildingID::GRAIL, "", -1);
|
||||
if(building->buildingBonuses.empty())
|
||||
continue;
|
||||
|
||||
b->addLimiter(factionLimiter);
|
||||
b->addPropagator(lsProp);
|
||||
VLC->creh->addBonusForAllCreatures(b);
|
||||
}
|
||||
}
|
||||
|
||||
bool CGTownInstance::addBonusIfBuilt(BuildingSubID::EBuildingSubID subId, Bonus::BonusType type, int val, TPropagatorPtr & prop, int subtype)
|
||||
{
|
||||
BuildingID currentBid = BuildingID::NONE;
|
||||
std::ostringstream descr;
|
||||
|
||||
for(const auto & bid : builtBuildings)
|
||||
{
|
||||
if(town->buildings.at(bid)->subId == subId)
|
||||
for(auto bonus : building->buildingBonuses)
|
||||
{
|
||||
descr << town->buildings.at(bid)->Name();
|
||||
currentBid = bid;
|
||||
break;
|
||||
if(bonus->propagator != nullptr && bonus->propagator->getPropagatorType() == ALL_CREATURES)
|
||||
VLC->creh->addBonusForAllCreatures(bonus);
|
||||
else
|
||||
addNewBonus(bonus);
|
||||
}
|
||||
}
|
||||
return currentBid == BuildingID::NONE ? false
|
||||
: addBonusImpl(currentBid, type, val, prop, descr.str(), subtype);
|
||||
}
|
||||
|
||||
bool CGTownInstance::addBonusIfBuilt(BuildingSubID::EBuildingSubID subId, Bonus::BonusType type, int val, int subtype)
|
||||
{
|
||||
return addBonusIfBuilt(subId, type, val, emptyPropagator, subtype);
|
||||
}
|
||||
|
||||
bool CGTownInstance::addBonusIfBuilt(BuildingID building, Bonus::BonusType type, int val, TPropagatorPtr & prop, int subtype)
|
||||
{
|
||||
if(!hasBuilt(building))
|
||||
return false;
|
||||
|
||||
std::ostringstream descr;
|
||||
descr << town->buildings.at(building)->Name();
|
||||
return addBonusImpl(building, type, val, prop, descr.str(), subtype);
|
||||
}
|
||||
|
||||
bool CGTownInstance::addBonusIfBuilt(BuildingID building, Bonus::BonusType type, int val, int subtype)
|
||||
{
|
||||
return addBonusIfBuilt(building, type, val, emptyPropagator, subtype);
|
||||
}
|
||||
|
||||
bool CGTownInstance::addBonusImpl(BuildingID building, Bonus::BonusType type, int val, TPropagatorPtr & prop, const std::string & description, int subtype)
|
||||
{
|
||||
auto b = std::make_shared<Bonus>(Bonus::PERMANENT, type, Bonus::TOWN_STRUCTURE, val, building, description, subtype);
|
||||
|
||||
if(prop)
|
||||
b->addPropagator(prop);
|
||||
addNewBonus(b);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGTownInstance::setVisitingHero(CGHeroInstance *h)
|
||||
@ -1738,11 +1719,11 @@ void CTownBonus::setProperty (ui8 what, ui32 val)
|
||||
void CTownBonus::onHeroVisit(const CGHeroInstance * h) const
|
||||
{
|
||||
ObjectInstanceID heroID = h->id;
|
||||
if (town->hasBuilt(bID) && visitors.find(heroID) == visitors.end())
|
||||
if(town->hasBuilt(bID) && visitors.find(heroID) == visitors.end())
|
||||
{
|
||||
si64 val = 0;
|
||||
InfoWindow iw;
|
||||
PrimarySkill::PrimarySkill what = PrimarySkill::ATTACK;
|
||||
PrimarySkill::PrimarySkill what = PrimarySkill::NONE;
|
||||
|
||||
switch (bType)
|
||||
{
|
||||
@ -1775,15 +1756,51 @@ void CTownBonus::onHeroVisit(const CGHeroInstance * h) const
|
||||
val = 1;
|
||||
iw.components.push_back(Component(Component::PRIM_SKILL, 1, 1, 0));
|
||||
break;
|
||||
|
||||
case BuildingSubID::CUSTOM_VISITING_BONUS:
|
||||
const auto building = town->town->buildings.at(bID);
|
||||
if(!h->hasBonusFrom(Bonus::TOWN_STRUCTURE, Bonus::getSid32(building->town->faction->index, building->bid)))
|
||||
{
|
||||
const auto & bonuses = building->onVisitBonuses;
|
||||
applyBonuses(const_cast<CGHeroInstance *>(h), bonuses);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(what != PrimarySkill::NONE)
|
||||
{
|
||||
iw.player = cb->getOwner(heroID);
|
||||
iw.text << getVisitingBonusGreeting();
|
||||
cb->showInfoDialog(&iw);
|
||||
cb->changePrimSkill(cb->getHero(heroID), what, val);
|
||||
town->addHeroToStructureVisitors(h, indexOnTV);
|
||||
}
|
||||
iw.player = cb->getOwner(heroID);
|
||||
iw.text << getVisitingBonusGreeting();
|
||||
cb->showInfoDialog(&iw);
|
||||
cb->changePrimSkill(cb->getHero(heroID), what, val);
|
||||
town->addHeroToStructureVisitors(h, indexOnTV);
|
||||
}
|
||||
}
|
||||
|
||||
void CTownBonus::applyBonuses(CGHeroInstance * h, const BonusList & bonuses) const
|
||||
{
|
||||
auto addToVisitors = false;
|
||||
|
||||
for(auto bonus : bonuses)
|
||||
{
|
||||
GiveBonus gb;
|
||||
InfoWindow iw;
|
||||
|
||||
gb.bonus = * bonus;
|
||||
gb.id = h->id.getNum();
|
||||
cb->giveHeroBonus(&gb);
|
||||
|
||||
if(bonus->duration == Bonus::PERMANENT)
|
||||
addToVisitors = true;
|
||||
|
||||
iw.player = cb->getOwner(h->id);
|
||||
iw.text << getCustomBonusGreeting(gb.bonus);
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
if(addToVisitors)
|
||||
town->addHeroToStructureVisitors(h, indexOnTV);
|
||||
}
|
||||
|
||||
GrowthInfo::Entry::Entry(const std::string &format, int _count)
|
||||
: count(_count)
|
||||
{
|
||||
@ -1851,3 +1868,22 @@ const std::string CGTownBuilding::getVisitingBonusGreeting() const
|
||||
town->town->setGreeting(bType, bonusGreeting);
|
||||
return bonusGreeting;
|
||||
}
|
||||
|
||||
const std::string CGTownBuilding::getCustomBonusGreeting(const Bonus & bonus)
|
||||
{
|
||||
auto bonusGreeting = std::string(VLC->generaltexth->localizedTexts["townHall"]["greetingCustomBonus"].String()); //"%s gives you +%d %s%s"
|
||||
std::string param = "";
|
||||
std::string until = "";
|
||||
|
||||
if(bonus.type == Bonus::MORALE)
|
||||
param = VLC->generaltexth->allTexts[384];
|
||||
else if(bonus.type == Bonus::LUCK)
|
||||
param = VLC->generaltexth->allTexts[385];
|
||||
|
||||
until = bonus.duration == (ui16)Bonus::ONE_BATTLE
|
||||
? VLC->generaltexth->localizedTexts["townHall"]["greetingCustomUntil"].String() : ".";
|
||||
|
||||
boost::format fmt = boost::format(bonusGreeting) % bonus.description % bonus.val % param % until;
|
||||
std::string greeting = fmt.str();
|
||||
return greeting;
|
||||
}
|
||||
|
@ -133,6 +133,7 @@ protected:
|
||||
BuildingSubID::EBuildingSubID bType;
|
||||
|
||||
const std::string getVisitingBonusGreeting() const;
|
||||
static const std::string getCustomBonusGreeting(const Bonus & bonus);
|
||||
};
|
||||
|
||||
class DLL_LINKAGE COPWBonus : public CGTownBuilding
|
||||
@ -169,6 +170,9 @@ public:
|
||||
h & static_cast<CGTownBuilding&>(*this);
|
||||
h & visitors;
|
||||
}
|
||||
|
||||
private:
|
||||
void applyBonuses(CGHeroInstance * h, const BonusList & bonuses) const;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CTownAndVisitingHero : public CBonusSystemNode
|
||||
@ -205,7 +209,9 @@ public:
|
||||
ConstTransitivePtr<CGHeroInstance> garrisonHero, visitingHero;
|
||||
ui32 identifier; //special identifier from h3m (only > RoE maps)
|
||||
si32 alignment;
|
||||
std::set<BuildingID> forbiddenBuildings, builtBuildings;
|
||||
std::set<BuildingID> forbiddenBuildings;
|
||||
std::set<BuildingID> builtBuildings;
|
||||
std::set<BuildingID> overriddenBuildings; ///buildings which bonuses are overridden and should not be applied
|
||||
std::vector<CGTownBuilding*> bonusingBuildings;
|
||||
std::vector<SpellID> possibleSpells, obligatorySpells;
|
||||
std::vector<std::vector<SpellID> > spells; //spells[level] -> vector of spells, first will be available in guild
|
||||
@ -237,8 +243,8 @@ public:
|
||||
h & events;
|
||||
h & bonusingBuildings;
|
||||
|
||||
for (std::vector<CGTownBuilding*>::iterator i = bonusingBuildings.begin(); i!=bonusingBuildings.end(); i++)
|
||||
(*i)->town = this;
|
||||
for(auto * bonusingBuilding : bonusingBuildings)
|
||||
bonusingBuilding->town = this;
|
||||
|
||||
h & town;
|
||||
h & townAndVis;
|
||||
@ -256,6 +262,11 @@ public:
|
||||
|
||||
if(!h.saving && version < 793)
|
||||
updateBonusingBuildings();
|
||||
|
||||
if(version >= 794)
|
||||
h & overriddenBuildings;
|
||||
else if(!h.saving)
|
||||
updateTown794();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -264,11 +275,6 @@ public:
|
||||
void updateMoraleBonusFromArmy() override;
|
||||
void deserializationFix();
|
||||
void recreateBuildingsBonuses();
|
||||
///bid: param to bind a building with a bonus, subId: param to check if already built
|
||||
bool addBonusIfBuilt(BuildingSubID::EBuildingSubID subId, Bonus::BonusType type, int val, TPropagatorPtr & prop, int subtype = -1);
|
||||
bool addBonusIfBuilt(BuildingSubID::EBuildingSubID subId, Bonus::BonusType type, int val, int subtype = -1);
|
||||
bool addBonusIfBuilt(BuildingID building, Bonus::BonusType type, int val, TPropagatorPtr &prop, int subtype = -1); //returns true if building is built and bonus has been added
|
||||
bool addBonusIfBuilt(BuildingID building, Bonus::BonusType type, int val, int subtype = -1); //convienence version of above
|
||||
void setVisitingHero(CGHeroInstance *h);
|
||||
void setGarrisonedHero(CGHeroInstance *h);
|
||||
const CArmedInstance *getUpperArmy() const; //garrisoned hero if present or the town itself
|
||||
@ -318,6 +324,7 @@ public:
|
||||
void removeCapitols (PlayerColor owner) const;
|
||||
void clearArmy() const;
|
||||
void addHeroToStructureVisitors(const CGHeroInstance *h, si64 structureInstanceID) const; //hero must be visiting or garrisoned in town
|
||||
void deleteTownBonus(BuildingID::EBuildingID bid);
|
||||
|
||||
const CTown * getTown() const ;
|
||||
|
||||
@ -336,7 +343,6 @@ public:
|
||||
static void reset();
|
||||
|
||||
protected:
|
||||
static TPropagatorPtr emptyPropagator;
|
||||
void setPropertyDer(ui8 what, ui32 val) override;
|
||||
void serializeJsonOptions(JsonSerializeFormat & handler) override;
|
||||
|
||||
@ -344,9 +350,10 @@ private:
|
||||
int getDwellingBonus(const std::vector<CreatureID>& creatureIds, const std::vector<ConstTransitivePtr<CGDwelling> >& dwellings) const;
|
||||
void updateBonusingBuildings();
|
||||
bool hasBuiltInOldWay(ETownType::ETownType type, BuildingID bid) const;
|
||||
bool addBonusImpl(BuildingID building, Bonus::BonusType type, int val, TPropagatorPtr & prop, const std::string & description, int subtype = -1);
|
||||
bool townEnvisagesBuilding(BuildingSubID::EBuildingSubID bid) const;
|
||||
void tryAddOnePerWeekBonus(BuildingSubID::EBuildingSubID subID);
|
||||
void tryAddVisitingBonus(BuildingSubID::EBuildingSubID subID);
|
||||
void initOverriddenBids();
|
||||
void addTownBonuses();
|
||||
void updateTown794(); //populate overriddenBuildings and vanila bonuses for old saves
|
||||
};
|
||||
|
@ -260,7 +260,7 @@ void CGObjectInstance::giveDummyBonus(ObjectInstanceID heroID, ui8 duration) con
|
||||
GiveBonus gbonus;
|
||||
gbonus.bonus.type = Bonus::NONE;
|
||||
gbonus.id = heroID.getNum();
|
||||
gbonus.bonus.duration = duration;
|
||||
gbonus.bonus.duration = (Bonus::BonusDuration)duration;
|
||||
gbonus.bonus.source = Bonus::OBJECT;
|
||||
gbonus.bonus.sid = ID;
|
||||
cb->giveHeroBonus(&gbonus);
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "../ConstTransitivePtr.h"
|
||||
#include "../GameConstants.h"
|
||||
|
||||
const ui32 SERIALIZATION_VERSION = 793;
|
||||
const ui32 SERIALIZATION_VERSION = 794;
|
||||
const ui32 MINIMAL_SERIALIZATION_VERSION = 753;
|
||||
const std::string SAVEGAME_MAGIC = "VCMISVG";
|
||||
|
||||
|
@ -3147,16 +3147,14 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
|
||||
processBeforeBuiltStructure(builtID);
|
||||
|
||||
//Take cost
|
||||
if (!force)
|
||||
{
|
||||
if(!force)
|
||||
giveResources(t->tempOwner, -requestedBuilding->resources);
|
||||
}
|
||||
|
||||
//We know what has been built, appluy changes. Do this as final step to properly update town window
|
||||
//We know what has been built, apply changes. Do this as final step to properly update town window
|
||||
sendAndApply(&ns);
|
||||
|
||||
//Other post-built events. To some logic like giving spells to work gamestate changes for new building must be already in place!
|
||||
for (auto builtID : ns.bid)
|
||||
for(auto builtID : ns.bid)
|
||||
processAfterBuiltStructure(builtID);
|
||||
|
||||
// now when everything is built - reveal tiles for lookout tower
|
||||
@ -3166,14 +3164,15 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
|
||||
getTilesInRange(fw.tiles, t->getSightCenter(), t->getSightRadius(), t->tempOwner, 1);
|
||||
sendAndApply(&fw);
|
||||
|
||||
if (t->visitingHero)
|
||||
if(t->visitingHero)
|
||||
visitCastleObjects(t, t->visitingHero);
|
||||
if (t->garrisonHero)
|
||||
if(t->garrisonHero)
|
||||
visitCastleObjects(t, t->garrisonHero);
|
||||
|
||||
checkVictoryLossConditionsForPlayer(t->tempOwner);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CGameHandler::razeStructure (ObjectInstanceID tid, BuildingID bid)
|
||||
{
|
||||
///incomplete, simply erases target building
|
||||
|
Loading…
Reference in New Issue
Block a user