mirror of
https://github.com/vcmi/vcmi.git
synced 2026-06-19 22:57:37 +02:00
Merge pull request #652 from ShubusCorporation/shc_special_buildings_support
Mod system improvement Part I : Special buildings should work in modders towns
This commit is contained in:
+1
-1
@@ -698,7 +698,7 @@ void processCommand(const std::string &message)
|
||||
|
||||
for(auto contentName : contentNames)
|
||||
{
|
||||
auto & content = VLC->modh->content[contentName];
|
||||
auto & content = (*VLC->modh->content)[contentName];
|
||||
|
||||
auto contentOutPath = outPath / contentName;
|
||||
bfs::create_directories(contentOutPath);
|
||||
|
||||
@@ -112,8 +112,11 @@ void CBuildingRect::hover(bool on)
|
||||
void CBuildingRect::clickLeft(tribool down, bool previousState)
|
||||
{
|
||||
if( previousState && getBuilding() && area && !down && (parent->selectedBuilding==this))
|
||||
if (!CSDL_Ext::isTransparent(area, GH.current->motion.x-pos.x, GH.current->motion.y-pos.y) ) //inside building image
|
||||
parent->buildingClicked(getBuilding()->bid);
|
||||
if (!CSDL_Ext::isTransparent(area, GH.current->motion.x - pos.x, GH.current->motion.y - pos.y)) //inside building image
|
||||
{
|
||||
auto building = getBuilding();
|
||||
parent->buildingClicked(building->bid, building->subId);
|
||||
}
|
||||
}
|
||||
|
||||
void CBuildingRect::clickRight(tribool down, bool previousState)
|
||||
@@ -650,7 +653,7 @@ const CGHeroInstance * CCastleBuildings::getHero()
|
||||
return town->garrisonHero;
|
||||
}
|
||||
|
||||
void CCastleBuildings::buildingClicked(BuildingID building)
|
||||
void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuildingSubID subID)
|
||||
{
|
||||
logGlobal->trace("You've clicked on %d", (int)building.toEnum());
|
||||
const CBuilding *b = town->town->buildings.find(building)->second;
|
||||
@@ -703,82 +706,67 @@ void CCastleBuildings::buildingClicked(BuildingID building)
|
||||
enterBlacksmith(town->town->warMachine);
|
||||
break;
|
||||
|
||||
case BuildingID::SHIP:
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[51]); //Cannot build another boat
|
||||
break;
|
||||
|
||||
case BuildingID::SPECIAL_1:
|
||||
switch(town->subID)
|
||||
case BuildingID::SPECIAL_2:
|
||||
case BuildingID::SPECIAL_3:
|
||||
switch(subID)
|
||||
{
|
||||
case ETownType::RAMPART://Mystic Pond
|
||||
case BuildingSubID::NONE:
|
||||
break;
|
||||
|
||||
case BuildingSubID::MYSTIC_POND:
|
||||
enterFountain(building);
|
||||
break;
|
||||
|
||||
case ETownType::TOWER:
|
||||
case ETownType::DUNGEON://Artifact Merchant
|
||||
case ETownType::CONFLUX:
|
||||
case BuildingSubID::ARTIFACT_MERCHANT:
|
||||
if(town->visitingHero)
|
||||
GH.pushIntT<CMarketplaceWindow>(town, town->visitingHero, EMarketMode::RESOURCE_ARTIFACT);
|
||||
else
|
||||
LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->Name())); //Only visiting heroes may use the %s.
|
||||
break;
|
||||
|
||||
default:
|
||||
enterBuilding(building);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case BuildingID::SHIP:
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[51]); //Cannot build another boat
|
||||
break;
|
||||
|
||||
case BuildingID::SPECIAL_2:
|
||||
switch(town->subID)
|
||||
{
|
||||
case ETownType::RAMPART: //Fountain of Fortune
|
||||
case BuildingSubID::FOUNTAIN_OF_FORTUNE:
|
||||
enterFountain(building);
|
||||
break;
|
||||
|
||||
case ETownType::STRONGHOLD: //Freelancer's Guild
|
||||
case BuildingSubID::FREELANCERS_GUILD:
|
||||
if(getHero())
|
||||
GH.pushIntT<CMarketplaceWindow>(town, getHero(), EMarketMode::CREATURE_RESOURCE);
|
||||
else
|
||||
LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->Name())); //Only visiting heroes may use the %s.
|
||||
break;
|
||||
|
||||
case ETownType::CONFLUX: //Magic University
|
||||
case BuildingSubID::MAGIC_UNIVERSITY:
|
||||
if (getHero())
|
||||
GH.pushIntT<CUniversityWindow>(getHero(), town);
|
||||
else
|
||||
enterBuilding(building);
|
||||
break;
|
||||
|
||||
default:
|
||||
enterBuilding(building);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case BuildingID::SPECIAL_3:
|
||||
switch(town->subID)
|
||||
{
|
||||
case ETownType::CASTLE: //Brotherhood of sword
|
||||
case BuildingSubID::BROTHERHOOD_OF_SWORD:
|
||||
LOCPLINT->showTavernWindow(town);
|
||||
break;
|
||||
|
||||
case ETownType::INFERNO: //Castle Gate
|
||||
case BuildingSubID::CASTLE_GATE:
|
||||
enterCastleGate();
|
||||
break;
|
||||
|
||||
case ETownType::NECROPOLIS: //Skeleton Transformer
|
||||
case BuildingSubID::CREATURE_TRANSFORMER: //Skeleton Transformer
|
||||
GH.pushIntT<CTransformerWindow>(getHero(), town);
|
||||
break;
|
||||
|
||||
case ETownType::DUNGEON: //Portal of Summoning
|
||||
case BuildingSubID::PORTAL_OF_SUMMONING:
|
||||
if (town->creatures[GameConstants::CREATURES_PER_TOWN].second.empty())//No creatures
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->tcommands[30]);
|
||||
else
|
||||
enterDwelling(GameConstants::CREATURES_PER_TOWN);
|
||||
break;
|
||||
|
||||
case ETownType::STRONGHOLD: //Ballista Yard
|
||||
case BuildingSubID::BALLISTA_YARD:
|
||||
enterBlacksmith(ArtifactID::BALLISTA);
|
||||
break;
|
||||
|
||||
@@ -828,7 +816,8 @@ void CCastleBuildings::enterCastleGate()
|
||||
{
|
||||
const CGTownInstance *t = Town;
|
||||
if (t->id != this->town->id && t->visitingHero == nullptr && //another town, empty and this is
|
||||
t->hasBuilt(BuildingID::CASTLE_GATE, ETownType::INFERNO))
|
||||
t->town->faction->index == town->town->faction->index && //the town of the same faction
|
||||
t->hasBuilt(BuildingSubID::CASTLE_GATE)) //and the town has a castle gate
|
||||
{
|
||||
availableTowns.push_back(t->id.getNum());//add to the list
|
||||
}
|
||||
@@ -1173,6 +1162,7 @@ void CCastleInterface::close()
|
||||
void CCastleInterface::castleTeleport(int where)
|
||||
{
|
||||
const CGTownInstance * dest = LOCPLINT->cb->getTown(ObjectInstanceID(where));
|
||||
adventureInt->select(town->visitingHero);//according to assert(ho == adventureInt->selection) in the eraseCurrentPathOf
|
||||
LOCPLINT->cb->teleportHero(town->visitingHero, dest);
|
||||
LOCPLINT->eraseCurrentPathOf(town->visitingHero, false);
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ public:
|
||||
void enterDwelling(int level);
|
||||
void enterToTheQuickRecruitmentWindow();
|
||||
|
||||
void buildingClicked(BuildingID building);
|
||||
void buildingClicked(BuildingID building, BuildingSubID::EBuildingSubID subID = BuildingSubID::NONE);
|
||||
void addBuilding(BuildingID building);
|
||||
void removeBuilding(BuildingID building);//FIXME: not tested!!!
|
||||
};
|
||||
|
||||
@@ -1781,9 +1781,6 @@ void CObjectListWindow::init(std::shared_ptr<CIntObject> titleWidget_, std::stri
|
||||
|
||||
title = std::make_shared<CLabel>(152, 27, FONT_BIG, CENTER, Colors::YELLOW, _title);
|
||||
descr = std::make_shared<CLabel>(145, 133, FONT_SMALL, CENTER, Colors::WHITE, _descr);
|
||||
|
||||
ok = std::make_shared<CButton>(Point(15, 402), "IOKAY.DEF", CButton::tooltip(), std::bind(&CObjectListWindow::elementSelected, this), SDLK_RETURN);
|
||||
ok->block(true);
|
||||
exit = std::make_shared<CButton>( Point(228, 402), "ICANCEL.DEF", CButton::tooltip(), std::bind(&CObjectListWindow::exitPressed, this), SDLK_ESCAPE);
|
||||
|
||||
if(titleWidget)
|
||||
@@ -1796,6 +1793,9 @@ void CObjectListWindow::init(std::shared_ptr<CIntObject> titleWidget_, std::stri
|
||||
list = std::make_shared<CListBox>(std::bind(&CObjectListWindow::genItem, this, _1),
|
||||
Point(14, 151), Point(0, 25), 9, items.size(), 0, 1, Rect(262, -32, 256, 256) );
|
||||
list->type |= REDRAW_PARENT;
|
||||
|
||||
ok = std::make_shared<CButton>(Point(15, 402), "IOKAY.DEF", CButton::tooltip(), std::bind(&CObjectListWindow::elementSelected, this), SDLK_RETURN);
|
||||
ok->block(!list->size());
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> CObjectListWindow::genItem(size_t index)
|
||||
|
||||
@@ -170,12 +170,12 @@
|
||||
"resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce": { "ore": 1, "wood": 1 } },
|
||||
"blacksmith": { "id" : 16 },
|
||||
|
||||
"special1": { "id" : 17, "requires" : [ "shipyard" ] },
|
||||
"special1": { "requires" : [ "shipyard" ] },
|
||||
"horde1": { "id" : 18, "upgrades" : "dwellingLvl3" },
|
||||
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl3", "requires" : [ "horde1" ], "mode" : "auto" },
|
||||
"ship": { "id" : 20, "upgrades" : "shipyard" },
|
||||
"special2": { "id" : 21, "requires" : [ "dwellingLvl4" ] },
|
||||
"special3": { "id" : 22, "upgrades" : "tavern" },
|
||||
"special2": { "type" : "stables", "requires" : [ "dwellingLvl4" ] },
|
||||
"special3": { "type" : "brotherhoodOfSword", "upgrades" : "tavern" },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
|
||||
|
||||
"dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] },
|
||||
|
||||
@@ -175,11 +175,11 @@
|
||||
"resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce": { "mercury": 1 } },
|
||||
"blacksmith": { "id" : 16 },
|
||||
|
||||
"special1": { "id" : 17, "requires" : [ "marketplace" ] },
|
||||
"special1": { "type" : "artifactMerchant", "requires" : [ "marketplace" ] },
|
||||
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1" },
|
||||
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
|
||||
"ship": { "id" : 20, "upgrades" : "shipyard" },
|
||||
"special2": { "id" : 21, "requires" : [ "mageGuild1" ] },
|
||||
"special2": { "type" : "magicUniversity", "requires" : [ "mageGuild1" ] },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
|
||||
"extraTownHall": { "id" : 27, "requires" : [ "townHall" ], "mode" : "auto" },
|
||||
"extraCityHall": { "id" : 28, "requires" : [ "cityHall" ], "mode" : "auto" },
|
||||
|
||||
@@ -169,12 +169,12 @@
|
||||
"resourceSilo": { "id" : 15, "requires" : [ "marketplace" ] },
|
||||
"blacksmith": { "id" : 16 },
|
||||
|
||||
"special1": { "id" : 17, "requires" : [ "marketplace" ] },
|
||||
"special1": { "type" : "artifactMerchant", "requires" : [ "marketplace" ] },
|
||||
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1" },
|
||||
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
|
||||
"special2": { "id" : 21, "requires" : [ "mageGuild1" ] },
|
||||
"special3": { "id" : 22 },
|
||||
"special4": { "id" : 23 },
|
||||
"special2": { "type" : "manaVortex", "requires" : [ "mageGuild1" ] },
|
||||
"special3": { "type" : "portalOfSummoning" },
|
||||
"special4": { },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
|
||||
|
||||
"dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] },
|
||||
|
||||
@@ -169,12 +169,12 @@
|
||||
"resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce": { "wood": 1, "ore": 1 } },
|
||||
"blacksmith": { "id" : 16 },
|
||||
|
||||
"special1": { "id" : 17, "requires" : [ "allOf", [ "townHall" ], [ "special2" ] ] },
|
||||
"special1": { "requires" : [ "allOf", [ "townHall" ], [ "special2" ] ] },
|
||||
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1" },
|
||||
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
|
||||
"ship": { "id" : 20, "upgrades" : "shipyard" },
|
||||
"special2": { "id" : 21, "requires" : [ "fort" ] },
|
||||
"special3": { "id" : 22, "requires" : [ "special2" ] },
|
||||
"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" },
|
||||
|
||||
|
||||
@@ -172,9 +172,9 @@
|
||||
|
||||
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1" },
|
||||
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
|
||||
"special2": { "id" : 21, "requires" : [ "fort" ] },
|
||||
"special3": { "id" : 22, "requires" : [ "citadel" ] },
|
||||
"special4": { "id" : 23, "requires" : [ "mageGuild1" ] },
|
||||
"special2": { "type" : "spellPowerGarrisonBonus", "requires" : [ "fort" ] },
|
||||
"special3": { "type" : "castleGate", "requires" : [ "citadel" ] },
|
||||
"special4": { "requires" : [ "mageGuild1" ] },
|
||||
"horde2": { "id" : 24, "upgrades" : "dwellingLvl3" },
|
||||
"horde2Upgr": { "id" : 25, "upgrades" : "dwellingUpLvl3", "requires" : [ "horde2" ], "mode" : "auto" },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
|
||||
|
||||
@@ -175,12 +175,12 @@
|
||||
"resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce": { "ore": 1, "wood": 1 } },
|
||||
"blacksmith": { "id" : 16 },
|
||||
|
||||
"special1": { "id" : 17, "requires" : [ "fort" ] },
|
||||
"special1": { "requires" : [ "fort" ] },
|
||||
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1", "requires" : [ "special3" ] },
|
||||
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
|
||||
"ship": { "id" : 20, "upgrades" : "shipyard" },
|
||||
"special2": { "id" : 21, "requires" : [ "mageGuild1" ] },
|
||||
"special3": { "id" : 22, "requires" : [ "dwellingLvl1" ] },
|
||||
"special2": { "requires" : [ "mageGuild1" ] },
|
||||
"special3": { "type" : "creatureTransformer", "requires" : [ "dwellingLvl1" ] },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
|
||||
"extraTownHall": { "id" : 27, "requires" : [ "townHall" ], "mode" : "auto" },
|
||||
"extraCityHall": { "id" : 28, "requires" : [ "cityHall" ], "mode" : "auto" },
|
||||
|
||||
@@ -174,11 +174,11 @@
|
||||
"resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce": { "crystal": 1 } },
|
||||
"blacksmith": { "id" : 16 },
|
||||
|
||||
"special1": { "id" : 17 },
|
||||
"special1": { "type" : "mysticPond" },
|
||||
"horde1": { "id" : 18, "upgrades" : "dwellingLvl2" },
|
||||
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl2", "requires" : [ "horde1" ], "mode" : "auto" },
|
||||
"special2": { "id" : 21, "requires" : [ "special1" ] },
|
||||
"special3": { "id" : 22, "requires" : [ "horde1" ] },
|
||||
"special2": { "type" : "fountainOfFortune", "requires" : [ "special1" ] },
|
||||
"special3": { "requires" : [ "horde1" ] },
|
||||
"horde2": { "id" : 24, "upgrades" : "dwellingLvl5" },
|
||||
"horde2Upgr": { "id" : 25, "upgrades" : "dwellingUpLvl5", "requires" : [ "horde2" ], "mode" : "auto" },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
|
||||
|
||||
@@ -166,12 +166,12 @@
|
||||
"resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce": { "ore": 1, "wood": 1 } },
|
||||
"blacksmith": { "id" : 16 },
|
||||
|
||||
"special1": { "id" : 17, "requires" : [ "fort" ] },
|
||||
"special1": { "type" : "escapeTunnel", "requires" : [ "fort" ] },
|
||||
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1" },
|
||||
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
|
||||
"special2": { "id" : 21, "requires" : [ "marketplace" ] },
|
||||
"special3": { "id" : 22, "requires" : [ "blacksmith" ] },
|
||||
"special4": { "id" : 23, "requires" : [ "fort" ] },
|
||||
"special2": { "type" : "freelancersGuild", "requires" : [ "marketplace" ] },
|
||||
"special3": { "type" : "ballistaYard", "requires" : [ "blacksmith" ] },
|
||||
"special4": { "requires" : [ "fort" ] },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
|
||||
|
||||
"dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] },
|
||||
|
||||
@@ -169,13 +169,13 @@
|
||||
"resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce" : { "gems": 1 } },
|
||||
"blacksmith": { "id" : 16 },
|
||||
|
||||
"special1": { "id" : 17, "requires" : [ "marketplace" ] },
|
||||
"special1": { "type" : "artifactMerchant", "requires" : [ "marketplace" ] },
|
||||
"horde1": { "id" : 18, "upgrades" : "dwellingLvl2" },
|
||||
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl2", "requires" : [ "horde1" ], "mode" : "auto" },
|
||||
"special2": { "id" : 21, "requires" : [ "fort" ] },
|
||||
"special3": { "id" : 22, "requires" : [ "mageGuild1" ] },
|
||||
"special4": { "id" : 23, "requires" : [ "mageGuild1" ] },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce" : { "gold": 5000 } },
|
||||
"special2": { "type" : "lookoutTower", "height" : "high", "requires" : [ "fort" ] },
|
||||
"special3": { "type" : "library", "requires" : [ "mageGuild1" ] },
|
||||
"special4": { "requires" : [ "mageGuild1" ] },
|
||||
"grail": { "height" : "skyship", "produce" : { "gold": 5000 } },
|
||||
|
||||
"dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] },
|
||||
"dwellingLvl2": { "id" : 31, "requires" : [ "dwellingLvl1" ] },
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
"$schema": "http://json-schema.org/draft-04/schema",
|
||||
"title" : "VCMI town building format",
|
||||
"description" : "Format used to define town buildings in VCMI",
|
||||
"required": [ "id" ],
|
||||
|
||||
"definitions" :
|
||||
{
|
||||
@@ -29,6 +28,15 @@
|
||||
"type":"number",
|
||||
"description" : "Numeric identifier of this building"
|
||||
},
|
||||
"type": {
|
||||
"type":"string",
|
||||
"description" : "Subtype for some special buildings"
|
||||
},
|
||||
"height": {
|
||||
"type":"string",
|
||||
"enum" : [ "skyship", "high", "average", "low"],
|
||||
"description" : "Height for lookout towers and some grails"
|
||||
},
|
||||
"mode": {
|
||||
"type":"string",
|
||||
"enum" : [ "normal", "auto", "special", "grail" ],
|
||||
|
||||
+8
-8
@@ -610,7 +610,7 @@ void CModInfo::loadLocalData(const JsonNode & data)
|
||||
validation = validated ? PASSED : FAILED;
|
||||
}
|
||||
|
||||
CModHandler::CModHandler()
|
||||
CModHandler::CModHandler() : content(std::make_shared<CContentHandler>())
|
||||
{
|
||||
modules.COMMANDERS = false;
|
||||
modules.STACK_ARTIFACT = false;
|
||||
@@ -955,7 +955,7 @@ void CModHandler::load()
|
||||
|
||||
logMod->info("\tInitializing content handler: %d ms", timer.getDiff());
|
||||
|
||||
content.init();
|
||||
content->init();
|
||||
|
||||
for(const TModID & modName : activeMods)
|
||||
{
|
||||
@@ -965,16 +965,16 @@ void CModHandler::load()
|
||||
|
||||
// first - load virtual "core" mod that contains all data
|
||||
// TODO? move all data into real mods? RoE, AB, SoD, WoG
|
||||
content.preloadData(coreMod);
|
||||
content->preloadData(coreMod);
|
||||
for(const TModID & modName : activeMods)
|
||||
content.preloadData(allMods[modName]);
|
||||
content->preloadData(allMods[modName]);
|
||||
logMod->info("\tParsing mod data: %d ms", timer.getDiff());
|
||||
|
||||
content.load(coreMod);
|
||||
content->load(coreMod);
|
||||
for(const TModID & modName : activeMods)
|
||||
content.load(allMods[modName]);
|
||||
content->load(allMods[modName]);
|
||||
|
||||
content.loadCustom();
|
||||
content->loadCustom();
|
||||
|
||||
logMod->info("\tLoading mod data: %d ms", timer.getDiff());
|
||||
|
||||
@@ -984,7 +984,7 @@ void CModHandler::load()
|
||||
identifiers.finalize();
|
||||
logMod->info("\tResolving identifiers: %d ms", timer.getDiff());
|
||||
|
||||
content.afterLoadFinalization();
|
||||
content->afterLoadFinalization();
|
||||
logMod->info("\tHandlers post-load finalization: %d ms ", timer.getDiff());
|
||||
logMod->info("\tAll game content loaded in %d ms", totalTime.getDiff());
|
||||
}
|
||||
|
||||
+1
-1
@@ -253,7 +253,7 @@ public:
|
||||
|
||||
CIdentifierStorage identifiers;
|
||||
|
||||
CContentHandler content; //(!)Do not serialize
|
||||
std::shared_ptr<CContentHandler> content; //(!)Do not serialize
|
||||
|
||||
/// receives list of available mods and trying to load mod.json from all of them
|
||||
void initializeConfig();
|
||||
|
||||
+96
-17
@@ -25,11 +25,21 @@
|
||||
|
||||
const int NAMES_PER_TOWN=16; // number of town names per faction in H3 files. Json can define any number
|
||||
|
||||
CBuilding::CBuilding():
|
||||
town(nullptr),mode(BUILD_NORMAL)
|
||||
const std::map<std::string, CBuilding::EBuildMode> CBuilding::MODES =
|
||||
{
|
||||
{ "normal", CBuilding::BUILD_NORMAL },
|
||||
{ "auto", CBuilding::BUILD_AUTO },
|
||||
{ "special", CBuilding::BUILD_SPECIAL },
|
||||
{ "grail", CBuilding::BUILD_GRAIL }
|
||||
};
|
||||
|
||||
}
|
||||
const std::map<std::string, CBuilding::ETowerHeight> CBuilding::TOWER_TYPES =
|
||||
{
|
||||
{ "low", CBuilding::HEIGHT_LOW },
|
||||
{ "average", CBuilding::HEIGHT_AVERAGE },
|
||||
{ "high", CBuilding::HEIGHT_HIGH },
|
||||
{ "skyship", CBuilding::HEIGHT_SKYSHIP }
|
||||
};
|
||||
|
||||
const std::string & CBuilding::Name() const
|
||||
{
|
||||
@@ -83,6 +93,52 @@ void CBuilding::deserializeFix()
|
||||
}
|
||||
}
|
||||
|
||||
void CBuilding::update792(const BuildingID & bid, BuildingSubID::EBuildingSubID & subId, ETowerHeight & height)
|
||||
{
|
||||
subId = BuildingSubID::NONE;
|
||||
height = ETowerHeight::HEIGHT_NO_TOWER;
|
||||
|
||||
if(!bid.IsSpecialOrGrail() || town == nullptr || town->faction == nullptr || town->faction->identifier.empty())
|
||||
return;
|
||||
|
||||
const auto buildingName = CTownHandler::getMappedValue<std::string, BuildingID>(bid, std::string(), MappedKeys::BUILDING_TYPES_TO_NAMES);
|
||||
|
||||
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];
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
if (subId == BuildingSubID::LOOKOUT_TOWER || bid == BuildingID::GRAIL)
|
||||
height = CTownHandler::getMappedValue<CBuilding::ETowerHeight>(currentBuilding["height"], CBuilding::HEIGHT_NO_TOWER, CBuilding::TOWER_TYPES);
|
||||
}
|
||||
}
|
||||
|
||||
CFaction::CFaction()
|
||||
{
|
||||
@@ -343,28 +399,51 @@ void CTownHandler::loadBuildingRequirements(CBuilding * building, const JsonNode
|
||||
requirementsToLoad.push_back(hlp);
|
||||
}
|
||||
|
||||
template<typename R, typename K>
|
||||
R CTownHandler::getMappedValue(const K key, const R defval, const std::map<K, R> & map, bool required)
|
||||
{
|
||||
auto it = map.find(key);
|
||||
|
||||
if(it != map.end())
|
||||
return it->second;
|
||||
|
||||
if(required)
|
||||
logMod->warn("Warning: Property: '%s' is unknown. Correct the typo or update VCMI.", key);
|
||||
return defval;
|
||||
}
|
||||
|
||||
template<typename R>
|
||||
R CTownHandler::getMappedValue(const JsonNode & node, const R defval, const std::map<std::string, R> & map, bool required)
|
||||
{
|
||||
if(!node.isNull() && node.getType() == JsonNode::JsonType::DATA_STRING)
|
||||
return getMappedValue<R, std::string>(node.String(), defval, map, required);
|
||||
return defval;
|
||||
}
|
||||
|
||||
void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, const JsonNode & source)
|
||||
{
|
||||
auto ret = new CBuilding();
|
||||
ret->bid = getMappedValue<BuildingID, std::string>(stringID, BuildingID::NONE, MappedKeys::BUILDING_NAMES_TO_TYPES, false);
|
||||
|
||||
static const std::vector<std::string> MODES =
|
||||
{
|
||||
"normal", "auto", "special", "grail"
|
||||
};
|
||||
if(ret->bid == BuildingID::NONE)
|
||||
ret->bid = source["id"].isNull() ? BuildingID(BuildingID::NONE) : BuildingID(source["id"].Float());
|
||||
|
||||
ret->mode = CBuilding::BUILD_NORMAL;
|
||||
{
|
||||
if(source["mode"].getType() == JsonNode::JsonType::DATA_STRING)
|
||||
{
|
||||
auto rawMode = vstd::find_pos(MODES, source["mode"].String());
|
||||
if(rawMode > 0)
|
||||
ret->mode = static_cast<CBuilding::EBuildMode>(rawMode);
|
||||
}
|
||||
}
|
||||
if (ret->bid == BuildingID::NONE)
|
||||
logMod->error("Error: Building '%s' has not internal ID and won't work properly. Correct the typo or update VCMI.", stringID);
|
||||
|
||||
ret->mode = ret->bid == BuildingID::GRAIL
|
||||
? CBuilding::BUILD_GRAIL
|
||||
: getMappedValue<CBuilding::EBuildMode>(source["mode"], CBuilding::BUILD_NORMAL, CBuilding::MODES);
|
||||
|
||||
ret->subId = getMappedValue<BuildingSubID::EBuildingSubID>(source["type"], BuildingSubID::NONE, MappedKeys::SPECIAL_BUILDINGS);
|
||||
ret->height = CBuilding::HEIGHT_NO_TOWER;
|
||||
|
||||
if(ret->subId == BuildingSubID::LOOKOUT_TOWER
|
||||
|| ret->bid == BuildingID::GRAIL)
|
||||
ret->height = getMappedValue<CBuilding::ETowerHeight>(source["height"], CBuilding::HEIGHT_NO_TOWER, CBuilding::TOWER_TYPES);
|
||||
|
||||
ret->identifier = stringID;
|
||||
ret->town = town;
|
||||
ret->bid = BuildingID((si32)source["id"].Float());
|
||||
ret->name = source["name"].String();
|
||||
ret->description = source["description"].String();
|
||||
ret->resources = TResources(source["cost"]);
|
||||
|
||||
+32
-1
@@ -46,6 +46,7 @@ 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
|
||||
|
||||
enum EBuildMode
|
||||
{
|
||||
@@ -55,7 +56,19 @@ public:
|
||||
BUILD_GRAIL // 3 - grail - building reqires grail to be built
|
||||
} mode;
|
||||
|
||||
CBuilding();
|
||||
enum ETowerHeight // for lookup towers and some grails
|
||||
{
|
||||
HEIGHT_NO_TOWER = 5, // building has not 'lookout tower' ability
|
||||
HEIGHT_LOW = 10, // low lookout tower, but castle without lookout tower gives radius 5
|
||||
HEIGHT_AVERAGE = 15,
|
||||
HEIGHT_HIGH = 20, // such tower is in the Tower town
|
||||
HEIGHT_SKYSHIP = std::numeric_limits<int>::max() // grail, open entire map
|
||||
} height;
|
||||
|
||||
static const std::map<std::string, CBuilding::EBuildMode> MODES;
|
||||
static const std::map<std::string, CBuilding::ETowerHeight> TOWER_TYPES;
|
||||
|
||||
CBuilding() : town(nullptr), mode(BUILD_NORMAL) {};
|
||||
|
||||
const std::string &Name() const;
|
||||
const std::string &Description() const;
|
||||
@@ -65,6 +78,8 @@ public:
|
||||
|
||||
// returns how many times build has to be upgraded to become build
|
||||
si32 getDistance(BuildingID build) const;
|
||||
/// input: faction, bid; output: subId, height;
|
||||
void update792(const BuildingID & bid, BuildingSubID::EBuildingSubID & subId, ETowerHeight & height);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
@@ -78,6 +93,16 @@ public:
|
||||
h & requirements;
|
||||
h & upgrade;
|
||||
h & mode;
|
||||
|
||||
if(version >= 792)
|
||||
{
|
||||
h & subId;
|
||||
h & height;
|
||||
}
|
||||
else if(!h.saving)
|
||||
{
|
||||
update792(bid, subId, height);
|
||||
}
|
||||
if(!h.saving)
|
||||
deserializeFix();
|
||||
}
|
||||
@@ -330,7 +355,13 @@ class DLL_LINKAGE CTownHandler : public IHandlerBase
|
||||
CFaction * loadFromJson(const JsonNode & data, const std::string & identifier);
|
||||
|
||||
void loadRandomFaction();
|
||||
|
||||
public:
|
||||
template<typename R, typename K>
|
||||
static R getMappedValue(const K key, const R defval, const std::map<K, R> & map, bool required = true);
|
||||
template<typename R>
|
||||
static R getMappedValue(const JsonNode & node, const R defval, const std::map<std::string, R> & map, bool required = true);
|
||||
|
||||
std::vector<ConstTransitivePtr<CFaction> > factions;
|
||||
|
||||
CTown * randomTown;
|
||||
|
||||
@@ -409,6 +409,12 @@ public:
|
||||
BuildingID(EBuildingID _num = NONE) : num(_num)
|
||||
{}
|
||||
|
||||
STRONG_INLINE
|
||||
bool IsSpecialOrGrail() const
|
||||
{
|
||||
return num == SPECIAL_1 || num == SPECIAL_2 || num == SPECIAL_3 || num == SPECIAL_4 || num == GRAIL;
|
||||
}
|
||||
|
||||
ID_LIKE_CLASS_COMMON(BuildingID, EBuildingID)
|
||||
|
||||
EBuildingID num;
|
||||
@@ -416,6 +422,78 @@ public:
|
||||
|
||||
ID_LIKE_OPERATORS(BuildingID, BuildingID::EBuildingID)
|
||||
|
||||
namespace BuildingSubID
|
||||
{
|
||||
enum EBuildingSubID
|
||||
{
|
||||
DEFAULT = -50,
|
||||
NONE = -1,
|
||||
STABLES,
|
||||
BROTHERHOOD_OF_SWORD,
|
||||
CASTLE_GATE,
|
||||
CREATURE_TRANSFORMER,
|
||||
MYSTIC_POND,
|
||||
FOUNTAIN_OF_FORTUNE,
|
||||
ARTIFACT_MERCHANT,
|
||||
LOOKOUT_TOWER,
|
||||
LIBRARY,
|
||||
MANA_VORTEX,
|
||||
PORTAL_OF_SUMMONING,
|
||||
ESCAPE_TUNNEL,
|
||||
FREELANCERS_GUILD,
|
||||
BALLISTA_YARD,
|
||||
HALL_OF_VALHALLA,
|
||||
MAGIC_UNIVERSITY,
|
||||
SPELL_POWER_GARRISON_BONUS,
|
||||
ATTACK_GARRISON_BONUS,
|
||||
DEFENSE_GARRISON_BONUS
|
||||
};
|
||||
}
|
||||
|
||||
namespace MappedKeys
|
||||
{
|
||||
|
||||
static const std::map<std::string, BuildingID> BUILDING_NAMES_TO_TYPES =
|
||||
{
|
||||
{ "special1", BuildingID::SPECIAL_1 },
|
||||
{ "special2", BuildingID::SPECIAL_2 },
|
||||
{ "special3", BuildingID::SPECIAL_3 },
|
||||
{ "special4", BuildingID::SPECIAL_4 },
|
||||
{ "grail", BuildingID::GRAIL }
|
||||
};
|
||||
|
||||
static const std::map<BuildingID, std::string> BUILDING_TYPES_TO_NAMES =
|
||||
{
|
||||
{ BuildingID::SPECIAL_1, "special1", },
|
||||
{ BuildingID::SPECIAL_2, "special2" },
|
||||
{ BuildingID::SPECIAL_3, "special3" },
|
||||
{ BuildingID::SPECIAL_4, "special4" },
|
||||
{ BuildingID::GRAIL, "grail"}
|
||||
};
|
||||
|
||||
static const std::map<std::string, BuildingSubID::EBuildingSubID> SPECIAL_BUILDINGS =
|
||||
{
|
||||
{ "mysticPond", BuildingSubID::MYSTIC_POND },
|
||||
{ "artifactMerchant", BuildingSubID::ARTIFACT_MERCHANT },
|
||||
{ "freelancersGuild", BuildingSubID::FREELANCERS_GUILD },
|
||||
{ "magicUniversity", BuildingSubID::MAGIC_UNIVERSITY },
|
||||
{ "castleGate", BuildingSubID::CASTLE_GATE },
|
||||
{ "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 }
|
||||
};
|
||||
}
|
||||
|
||||
namespace EAiTactic
|
||||
{
|
||||
enum EAiTactic
|
||||
|
||||
@@ -62,7 +62,7 @@ void CPrivilegedInfoCallback::getTilesInRange(std::unordered_set<int3, ShashInt3
|
||||
logGlobal->error("Illegal call to getTilesInRange!");
|
||||
return;
|
||||
}
|
||||
if (radious == -1) //reveal entire map
|
||||
if(radious == CBuilding::HEIGHT_SKYSHIP) //reveal entire map
|
||||
getAllTiles (tiles, player, -1, 0);
|
||||
else
|
||||
{
|
||||
|
||||
@@ -186,3 +186,13 @@ LibClasses::~LibClasses()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
std::shared_ptr<CContentHandler> LibClasses::getContent() const
|
||||
{
|
||||
return modh->content;
|
||||
}
|
||||
|
||||
void LibClasses::setContent(std::shared_ptr<CContentHandler> content)
|
||||
{
|
||||
modh->content = content;
|
||||
}
|
||||
|
||||
+14
-1
@@ -21,6 +21,7 @@ class CObjectClassesHandler;
|
||||
class CTownHandler;
|
||||
class CGeneralTextHandler;
|
||||
class CModHandler;
|
||||
class CContentHandler;
|
||||
class IBonusTypeHandler;
|
||||
class CBonusTypeHandler;
|
||||
class CTerrainViewPatternConfig;
|
||||
@@ -33,6 +34,9 @@ class DLL_LINKAGE LibClasses
|
||||
|
||||
void callWhenDeserializing(); //should be called only by serialize !!!
|
||||
void makeNull(); //sets all handler pointers to null
|
||||
std::shared_ptr<CContentHandler> getContent() const;
|
||||
void setContent(std::shared_ptr<CContentHandler> content);
|
||||
|
||||
public:
|
||||
bool IS_AI_ENABLED; //unused?
|
||||
|
||||
@@ -73,7 +77,16 @@ public:
|
||||
{
|
||||
h & skillh;
|
||||
}
|
||||
h & modh;
|
||||
if(!h.saving)
|
||||
{
|
||||
//modh will be changed and modh->content will be empty after deserialization
|
||||
auto content = getContent();
|
||||
h & modh;
|
||||
setContent(content);
|
||||
}
|
||||
else
|
||||
h & modh;
|
||||
|
||||
h & IS_AI_ENABLED;
|
||||
h & bth;
|
||||
if(!h.saving)
|
||||
|
||||
@@ -279,7 +279,7 @@ bool CBattleInfoEssentials::battleCanFlee(PlayerColor player) const
|
||||
if(side.get() == BattleSide::DEFENDER && battleGetSiegeLevel())
|
||||
{
|
||||
auto town = battleGetDefendedTown();
|
||||
if(!town->hasBuilt(BuildingID::ESCAPE_TUNNEL, ETownType::STRONGHOLD))
|
||||
if(!town->hasBuilt(BuildingSubID::ESCAPE_TUNNEL))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -435,16 +435,22 @@ void CGDwelling::serializeJsonOptions(JsonSerializeFormat & handler)
|
||||
}
|
||||
}
|
||||
|
||||
TPropagatorPtr CGTownInstance::emptyPropagator = TPropagatorPtr();
|
||||
|
||||
int CGTownInstance::getSightRadius() const //returns sight distance
|
||||
{
|
||||
if (subID == ETownType::TOWER)
|
||||
auto ret = CBuilding::HEIGHT_NO_TOWER;
|
||||
|
||||
for(const auto & bid : builtBuildings)
|
||||
{
|
||||
if (hasBuilt(BuildingID::GRAIL)) //skyship
|
||||
return -1; //entire map
|
||||
if (hasBuilt(BuildingID::LOOKOUT_TOWER)) //lookout tower
|
||||
return 20;
|
||||
if(bid.IsSpecialOrGrail())
|
||||
{
|
||||
auto height = town->buildings.at(bid)->height;
|
||||
if(ret < height)
|
||||
ret = height;
|
||||
}
|
||||
}
|
||||
return 5;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CGTownInstance::setPropertyDer(ui8 what, ui32 val)
|
||||
@@ -635,7 +641,7 @@ int CGTownInstance::spellsAtLevel(int level, bool checkGuild) const
|
||||
return 0;
|
||||
int ret = 6 - level; //how many spells are available at this level
|
||||
|
||||
if (hasBuilt(BuildingID::LIBRARY, ETownType::TOWER))
|
||||
if (hasBuilt(BuildingSubID::LIBRARY))
|
||||
ret++;
|
||||
|
||||
return ret;
|
||||
@@ -733,15 +739,26 @@ std::string CGTownInstance::getObjectName() const
|
||||
return name + ", " + town->faction->name;
|
||||
}
|
||||
|
||||
bool CGTownInstance::townEnvisagesSpecialBuilding(BuildingSubID::EBuildingSubID bid) const
|
||||
{
|
||||
for(const auto & it : town->buildings)
|
||||
{
|
||||
if(it.second->subId == bid)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CGTownInstance::initObj(CRandomGenerator & rand)
|
||||
///initialize town structures
|
||||
{
|
||||
blockVisit = true;
|
||||
|
||||
if (subID == ETownType::DUNGEON)
|
||||
creatures.resize(GameConstants::CREATURES_PER_TOWN+1);//extra dwelling for Dungeon
|
||||
if(townEnvisagesSpecialBuilding(BuildingSubID::PORTAL_OF_SUMMONING)) //Dungeon for example
|
||||
creatures.resize(GameConstants::CREATURES_PER_TOWN+1);
|
||||
else
|
||||
creatures.resize(GameConstants::CREATURES_PER_TOWN);
|
||||
|
||||
for (int level = 0; level < GameConstants::CREATURES_PER_TOWN; level++)
|
||||
{
|
||||
BuildingID buildID = BuildingID(BuildingID::DWELL_FIRST).advance(level);
|
||||
@@ -753,16 +770,19 @@ void CGTownInstance::initObj(CRandomGenerator & rand)
|
||||
creatures[level].second.push_back(town->creatures[level][upgradeNum]);
|
||||
}
|
||||
}
|
||||
if(townEnvisagesSpecialBuilding(BuildingSubID::STABLES))
|
||||
bonusingBuildings.push_back(new COPWBonus(BuildingID::STABLES, BuildingSubID::STABLES, this));
|
||||
|
||||
if(townEnvisagesSpecialBuilding(BuildingSubID::MANA_VORTEX))
|
||||
bonusingBuildings.push_back(new COPWBonus(BuildingID::MANA_VORTEX, BuildingSubID::MANA_VORTEX, this));
|
||||
|
||||
switch (subID)
|
||||
{ //add new visitable objects
|
||||
case ETownType::CASTLE:
|
||||
bonusingBuildings.push_back (new COPWBonus(BuildingID::STABLES, this));
|
||||
break;
|
||||
{
|
||||
//add new visitable objects
|
||||
case ETownType::DUNGEON:
|
||||
bonusingBuildings.push_back (new COPWBonus(BuildingID::MANA_VORTEX, this));
|
||||
FALLTHROUGH
|
||||
case ETownType::TOWER: case ETownType::INFERNO: case ETownType::STRONGHOLD:
|
||||
case ETownType::TOWER:
|
||||
case ETownType::INFERNO:
|
||||
case ETownType::STRONGHOLD:
|
||||
bonusingBuildings.push_back (new CTownBonus(BuildingID::SPECIAL_4, this));
|
||||
break;
|
||||
case ETownType::FORTRESS:
|
||||
@@ -775,13 +795,90 @@ void CGTownInstance::initObj(CRandomGenerator & rand)
|
||||
updateAppearance();
|
||||
}
|
||||
|
||||
void CGTownInstance::updateBonusingBuildings()
|
||||
{
|
||||
if (this->town->faction != nullptr)
|
||||
{
|
||||
//firstly, update subtype for the Bonusing objects, which are already stored in the bonusing list
|
||||
for (auto building : bonusingBuildings)
|
||||
{
|
||||
switch (this->town->faction->index)
|
||||
{
|
||||
case ETownType::CASTLE:
|
||||
if (building->getBuildingType() == BuildingID::SPECIAL_2)
|
||||
building->setBuildingSubtype(BuildingSubID::STABLES);
|
||||
break;
|
||||
|
||||
case ETownType::DUNGEON:
|
||||
if(building->getBuildingType() == BuildingID::SPECIAL_2)
|
||||
building->setBuildingSubtype(BuildingSubID::MANA_VORTEX);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//secondly, supplement bonusing buildings list and active bonuses; subtypes for these objects are already set in update792
|
||||
for (auto & kvp : town->buildings)
|
||||
{
|
||||
auto & building = kvp.second;
|
||||
|
||||
switch (building->subId)
|
||||
{
|
||||
case BuildingSubID::PORTAL_OF_SUMMONING:
|
||||
creatures.resize(GameConstants::CREATURES_PER_TOWN + 1);
|
||||
break;
|
||||
///'hasBuilt' checking for COPW bonuses is in the COPWBonus::onHeroVisit
|
||||
case BuildingSubID::STABLES:
|
||||
if(getBonusingBuilding(building->subId) == nullptr)
|
||||
bonusingBuildings.push_back(new COPWBonus(BuildingID::STABLES, BuildingSubID::STABLES, this));
|
||||
break;
|
||||
|
||||
case BuildingSubID::MANA_VORTEX:
|
||||
if(getBonusingBuilding(building->subId) == nullptr)
|
||||
bonusingBuildings.push_back(new COPWBonus(BuildingID::MANA_VORTEX, BuildingSubID::MANA_VORTEX, this));
|
||||
break;
|
||||
///add new bonus if bonusing building was built in the user added towns:
|
||||
case BuildingSubID::BROTHERHOOD_OF_SWORD:
|
||||
if(!hasBuiltInOldWay(ETownType::CASTLE, BuildingID::BROTHERHOOD))
|
||||
addBonusIfBuilt(BuildingID::BROTHERHOOD, BuildingSubID::BROTHERHOOD_OF_SWORD, Bonus::MORALE, +2);
|
||||
break;
|
||||
|
||||
case BuildingSubID::FOUNTAIN_OF_FORTUNE:
|
||||
if(!hasBuiltInOldWay(ETownType::RAMPART, BuildingID::FOUNTAIN_OF_FORTUNE))
|
||||
addBonusIfBuilt(BuildingID::FOUNTAIN_OF_FORTUNE, BuildingSubID::FOUNTAIN_OF_FORTUNE, Bonus::LUCK, +2);
|
||||
break;
|
||||
|
||||
case BuildingSubID::SPELL_POWER_GARRISON_BONUS:
|
||||
if(!hasBuiltInOldWay(ETownType::INFERNO, BuildingID::STORMCLOUDS))
|
||||
addBonusIfBuilt(BuildingID::STORMCLOUDS, BuildingSubID::SPELL_POWER_GARRISON_BONUS, Bonus::PRIMARY_SKILL, +2, PrimarySkill::SPELL_POWER);
|
||||
break;
|
||||
|
||||
case BuildingSubID::ATTACK_GARRISON_BONUS:
|
||||
if(!hasBuiltInOldWay(ETownType::FORTRESS, BuildingID::BLOOD_OBELISK))
|
||||
addBonusIfBuilt(BuildingID::BLOOD_OBELISK, BuildingSubID::ATTACK_GARRISON_BONUS, Bonus::PRIMARY_SKILL, +2, PrimarySkill::ATTACK);
|
||||
break;
|
||||
|
||||
case BuildingSubID::DEFENSE_GARRISON_BONUS:
|
||||
if(!hasBuiltInOldWay(ETownType::FORTRESS, BuildingID::GLYPHS_OF_FEAR))
|
||||
addBonusIfBuilt(BuildingID::GLYPHS_OF_FEAR, BuildingSubID::DEFENSE_GARRISON_BONUS, Bonus::PRIMARY_SKILL, +2, PrimarySkill::DEFENSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CGTownInstance::hasBuiltInOldWay(ETownType::ETownType type, BuildingID bid) const
|
||||
{
|
||||
return (this->town->faction != nullptr && this->town->faction->index == type && hasBuilt(bid));
|
||||
}
|
||||
|
||||
void CGTownInstance::newTurn(CRandomGenerator & rand) const
|
||||
{
|
||||
if (cb->getDate(Date::DAY_OF_WEEK) == 1) //reset on new week
|
||||
{
|
||||
//give resources for Rampart, Mystic Pond
|
||||
if (hasBuilt(BuildingID::MYSTIC_POND, ETownType::RAMPART)
|
||||
&& cb->getDate(Date::DAY) != 1 && (tempOwner < PlayerColor::PLAYER_LIMIT))
|
||||
//give resources if there's a Mystic Pond
|
||||
if (hasBuilt(BuildingSubID::MYSTIC_POND)
|
||||
&& cb->getDate(Date::DAY) != 1
|
||||
&& (tempOwner < PlayerColor::PLAYER_LIMIT)
|
||||
)
|
||||
{
|
||||
int resID = rand.nextInt(2, 5); //bonus to random rare resource
|
||||
resID = (resID==2)?1:resID;
|
||||
@@ -791,12 +888,10 @@ void CGTownInstance::newTurn(CRandomGenerator & rand) const
|
||||
cb->setObjProperty (id, ObjProperty::BONUS_VALUE_SECOND, resVal);
|
||||
}
|
||||
|
||||
if ( subID == ETownType::DUNGEON )
|
||||
for (auto & elem : bonusingBuildings)
|
||||
{
|
||||
if ((elem)->ID == BuildingID::MANA_VORTEX)
|
||||
cb->setObjProperty (id, ObjProperty::STRUCTURE_CLEAR_VISITORS, (elem)->id); //reset visitors for Mana Vortex
|
||||
}
|
||||
auto manaVortex = getBonusingBuilding(BuildingSubID::MANA_VORTEX);
|
||||
|
||||
if (manaVortex != nullptr)
|
||||
cb->setObjProperty(id, ObjProperty::STRUCTURE_CLEAR_VISITORS, manaVortex->indexOnTV); //reset visitors for Mana Vortex
|
||||
|
||||
//get Mana Vortex or Stables bonuses
|
||||
//same code is in the CGameHandler::buildStructure method
|
||||
@@ -882,7 +977,7 @@ void CGTownInstance::getOutOffsets( std::vector<int3> &offsets ) const
|
||||
|
||||
void CGTownInstance::mergeGarrisonOnSiege() const
|
||||
{
|
||||
auto getWeakestStackSlot = [&](int powerLimit)
|
||||
auto getWeakestStackSlot = [&](ui64 powerLimit)
|
||||
{
|
||||
std::vector<SlotID> weakSlots;
|
||||
auto stacksList = visitingHero->stacks;
|
||||
@@ -909,7 +1004,8 @@ void CGTownInstance::mergeGarrisonOnSiege() const
|
||||
return SlotID();
|
||||
};
|
||||
|
||||
int count = static_cast<int>(stacks.size());
|
||||
auto count = static_cast<int>(stacks.size());
|
||||
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
auto pair = *vstd::maxElementByFun(stacks, [&](std::pair<SlotID, CStackInstance *> elem)
|
||||
@@ -1099,9 +1195,14 @@ void CGTownInstance::recreateBuildingsBonuses()
|
||||
removeBonus(b);
|
||||
|
||||
//tricky! -> checks tavern only if no bratherhood of sword or not a castle
|
||||
if(subID != ETownType::CASTLE || !addBonusIfBuilt(BuildingID::BROTHERHOOD, Bonus::MORALE, +2))
|
||||
if(!addBonusIfBuilt(BuildingID::BROTHERHOOD, BuildingSubID::BROTHERHOOD_OF_SWORD, Bonus::MORALE, +2))
|
||||
addBonusIfBuilt(BuildingID::TAVERN, Bonus::MORALE, +1);
|
||||
|
||||
addBonusIfBuilt(BuildingID::FOUNTAIN_OF_FORTUNE, BuildingSubID::FOUNTAIN_OF_FORTUNE, Bonus::LUCK, +2); //fountain of fortune
|
||||
addBonusIfBuilt(BuildingID::STORMCLOUDS, BuildingSubID::SPELL_POWER_GARRISON_BONUS, Bonus::PRIMARY_SKILL, +2, PrimarySkill::SPELL_POWER);//works as Brimstone Clouds
|
||||
addBonusIfBuilt(BuildingID::BLOOD_OBELISK, BuildingSubID::ATTACK_GARRISON_BONUS, Bonus::PRIMARY_SKILL, +2, PrimarySkill::ATTACK);//works as Blood Obelisk
|
||||
addBonusIfBuilt(BuildingID::GLYPHS_OF_FEAR, BuildingSubID::DEFENSE_GARRISON_BONUS, Bonus::PRIMARY_SKILL, +2, PrimarySkill::DEFENSE);//works as Glyphs of Fear
|
||||
|
||||
if(subID == ETownType::CASTLE) //castle
|
||||
{
|
||||
addBonusIfBuilt(BuildingID::LIGHTHOUSE, Bonus::SEA_MOVEMENT, +500, playerProp);
|
||||
@@ -1109,17 +1210,12 @@ void CGTownInstance::recreateBuildingsBonuses()
|
||||
}
|
||||
else if(subID == ETownType::RAMPART) //rampart
|
||||
{
|
||||
addBonusIfBuilt(BuildingID::FOUNTAIN_OF_FORTUNE, Bonus::LUCK, +2); //fountain of fortune
|
||||
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::INFERNO) //Inferno
|
||||
{
|
||||
addBonusIfBuilt(BuildingID::STORMCLOUDS, Bonus::PRIMARY_SKILL, +2, PrimarySkill::SPELL_POWER); //Brimstone Clouds
|
||||
}
|
||||
else if(subID == ETownType::NECROPOLIS) //necropolis
|
||||
{
|
||||
addBonusIfBuilt(BuildingID::COVER_OF_DARKNESS, Bonus::DARKNESS, +20);
|
||||
@@ -1136,38 +1232,52 @@ void CGTownInstance::recreateBuildingsBonuses()
|
||||
}
|
||||
else if(subID == ETownType::FORTRESS) //Fortress
|
||||
{
|
||||
addBonusIfBuilt(BuildingID::GLYPHS_OF_FEAR, Bonus::PRIMARY_SKILL, +2, PrimarySkill::DEFENSE); //Glyphs of Fear
|
||||
addBonusIfBuilt(BuildingID::BLOOD_OBELISK, Bonus::PRIMARY_SKILL, +2, PrimarySkill::ATTACK); //Blood Obelisk
|
||||
addBonusIfBuilt(BuildingID::GRAIL, Bonus::PRIMARY_SKILL, +10, PrimarySkill::ATTACK); //grail
|
||||
addBonusIfBuilt(BuildingID::GRAIL, Bonus::PRIMARY_SKILL, +10, PrimarySkill::DEFENSE); //grail
|
||||
}
|
||||
else if(subID == ETownType::CONFLUX)
|
||||
{
|
||||
}
|
||||
|
||||
bool CGTownInstance::addBonusIfBuilt(BuildingID bid, BuildingSubID::EBuildingSubID subId, Bonus::BonusType type, int val, int subtype)
|
||||
{
|
||||
bool hasBuilt = false;
|
||||
std::ostringstream descr;
|
||||
|
||||
for (const auto & bid : builtBuildings)
|
||||
{
|
||||
if (town->buildings.at(bid)->subId == subId)
|
||||
{
|
||||
descr << town->buildings.at(bid)->Name();
|
||||
hasBuilt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(hasBuilt)
|
||||
hasBuilt = addBonusImpl(bid, type, val, emptyPropagator, descr.str(), subtype);
|
||||
return hasBuilt;
|
||||
}
|
||||
|
||||
bool CGTownInstance::addBonusIfBuilt(BuildingID building, Bonus::BonusType type, int val, int subtype)
|
||||
{
|
||||
static auto emptyPropagator = TPropagatorPtr();
|
||||
return addBonusIfBuilt(building, type, val, emptyPropagator, subtype);
|
||||
}
|
||||
|
||||
bool CGTownInstance::addBonusIfBuilt(BuildingID building, Bonus::BonusType type, int val, TPropagatorPtr & prop, int subtype)
|
||||
{
|
||||
if(hasBuilt(building))
|
||||
{
|
||||
std::ostringstream descr;
|
||||
descr << town->buildings.at(building)->Name();
|
||||
if(!hasBuilt(building))
|
||||
return false;
|
||||
|
||||
std::ostringstream descr;
|
||||
descr << town->buildings.at(building)->Name();
|
||||
return addBonusImpl(building, type, val, prop, descr.str(), subtype);
|
||||
}
|
||||
|
||||
auto b = std::make_shared<Bonus>(Bonus::PERMANENT, type, Bonus::TOWN_STRUCTURE, val, building, descr.str(), subtype);
|
||||
if(prop)
|
||||
b->addPropagator(prop);
|
||||
addNewBonus(b);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
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)
|
||||
@@ -1248,10 +1358,14 @@ const CTown * CGTownInstance::getTown() const
|
||||
int CGTownInstance::getTownLevel() const
|
||||
{
|
||||
// count all buildings that are not upgrades
|
||||
return (int)boost::range::count_if(builtBuildings, [&](const BuildingID & build)
|
||||
int level = 0;
|
||||
|
||||
for (const auto & bid : builtBuildings)
|
||||
{
|
||||
return town->buildings.at(build) && town->buildings.at(build)->upgrade == -1;
|
||||
});
|
||||
if(town->buildings.at(bid)->upgrade == BuildingID::NONE)
|
||||
level++;
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
CBonusSystemNode * CGTownInstance::whatShouldBeAttached()
|
||||
@@ -1266,6 +1380,31 @@ const CArmedInstance * CGTownInstance::getUpperArmy() const
|
||||
return this;
|
||||
}
|
||||
|
||||
const CGTownBuilding * CGTownInstance::getBonusingBuilding(BuildingSubID::EBuildingSubID subId) const
|
||||
{
|
||||
for(const auto building : bonusingBuildings)
|
||||
{
|
||||
if(building->getBuildingSubtype() == subId)
|
||||
return building;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool CGTownInstance::hasBuilt(BuildingSubID::EBuildingSubID buildingID) const
|
||||
{
|
||||
for(const auto & bid : builtBuildings)
|
||||
{
|
||||
if(town->buildings.at(bid)->subId == buildingID)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CGTownInstance::hasBuilt(BuildingID buildingID) const
|
||||
{
|
||||
return vstd::contains(builtBuildings, buildingID);
|
||||
}
|
||||
|
||||
bool CGTownInstance::hasBuilt(BuildingID buildingID, int townID) const
|
||||
{
|
||||
if (townID == town->faction->index || townID == ETownType::ANY)
|
||||
@@ -1285,11 +1424,6 @@ TResources CGTownInstance::getBuildingCost(BuildingID buildingID) const
|
||||
|
||||
}
|
||||
|
||||
bool CGTownInstance::hasBuilt(BuildingID buildingID) const
|
||||
{
|
||||
return vstd::contains(builtBuildings, buildingID);
|
||||
}
|
||||
|
||||
CBuilding::TRequired CGTownInstance::genBuildingRequirements(BuildingID buildID, bool deep) const
|
||||
{
|
||||
const CBuilding * building = town->buildings.at(buildID);
|
||||
@@ -1338,7 +1472,7 @@ CBuilding::TRequired CGTownInstance::genBuildingRequirements(BuildingID buildID,
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CGTownInstance::addHeroToStructureVisitors( const CGHeroInstance *h, si32 structureInstanceID ) const
|
||||
void CGTownInstance::addHeroToStructureVisitors(const CGHeroInstance *h, si64 structureInstanceID ) const
|
||||
{
|
||||
if(visitingHero == h)
|
||||
cb->setObjProperty(id, ObjProperty::STRUCTURE_ADD_VISITING_HERO, structureInstanceID); //add to visitors
|
||||
@@ -1498,12 +1632,14 @@ void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler)
|
||||
}
|
||||
}
|
||||
|
||||
COPWBonus::COPWBonus (BuildingID index, CGTownInstance *TOWN)
|
||||
COPWBonus::COPWBonus (BuildingID bid, BuildingSubID::EBuildingSubID subId, CGTownInstance *TOWN)
|
||||
{
|
||||
ID = index;
|
||||
bID = bid;
|
||||
bType = subId;
|
||||
town = TOWN;
|
||||
id = static_cast<si32>(town->bonusingBuildings.size());
|
||||
indexOnTV = static_cast<si32>(town->bonusingBuildings.size());
|
||||
}
|
||||
|
||||
void COPWBonus::setProperty(ui8 what, ui32 val)
|
||||
{
|
||||
switch (what)
|
||||
@@ -1516,16 +1652,18 @@ void COPWBonus::setProperty(ui8 what, ui32 val)
|
||||
break;
|
||||
}
|
||||
}
|
||||
void COPWBonus::onHeroVisit (const CGHeroInstance * h) const
|
||||
|
||||
void COPWBonus::onHeroVisit(const CGHeroInstance * h) const
|
||||
{
|
||||
ObjectInstanceID heroID = h->id;
|
||||
if (town->hasBuilt(ID))
|
||||
if (town->hasBuilt(bID))
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = h->tempOwner;
|
||||
switch (town->subID)
|
||||
|
||||
switch (this->bType)
|
||||
{
|
||||
case ETownType::CASTLE: //Stables
|
||||
case BuildingSubID::STABLES:
|
||||
if (!h->hasBonusFrom(Bonus::OBJECT, Obj::STABLES)) //does not stack with advMap Stables
|
||||
{
|
||||
GiveBonus gb;
|
||||
@@ -1543,7 +1681,8 @@ void COPWBonus::onHeroVisit (const CGHeroInstance * h) const
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
break;
|
||||
case ETownType::DUNGEON: //Mana Vortex
|
||||
|
||||
case BuildingSubID::MANA_VORTEX:
|
||||
if (visitors.empty())
|
||||
{
|
||||
if (h->mana < h->manaLimit() * 2)
|
||||
@@ -1553,7 +1692,7 @@ void COPWBonus::onHeroVisit (const CGHeroInstance * h) const
|
||||
iw.text << VLC->generaltexth->allTexts[579];
|
||||
cb->showInfoDialog(&iw);
|
||||
//extra visit penalty if hero alredy had double mana points (or even more?!)
|
||||
town->addHeroToStructureVisitors(h, id);
|
||||
town->addHeroToStructureVisitors(h, indexOnTV);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1561,24 +1700,28 @@ void COPWBonus::onHeroVisit (const CGHeroInstance * h) const
|
||||
}
|
||||
CTownBonus::CTownBonus (BuildingID index, CGTownInstance *TOWN)
|
||||
{
|
||||
ID = index;
|
||||
bID = index;
|
||||
town = TOWN;
|
||||
id = static_cast<si32>(town->bonusingBuildings.size());
|
||||
indexOnTV = static_cast<si32>(town->bonusingBuildings.size());
|
||||
}
|
||||
|
||||
void CTownBonus::setProperty (ui8 what, ui32 val)
|
||||
{
|
||||
if(what == ObjProperty::VISITORS)
|
||||
visitors.insert(ObjectInstanceID(val));
|
||||
}
|
||||
|
||||
void CTownBonus::onHeroVisit (const CGHeroInstance * h) const
|
||||
{
|
||||
ObjectInstanceID heroID = h->id;
|
||||
if (town->hasBuilt(ID) && visitors.find(heroID) == visitors.end())
|
||||
if (town->hasBuilt(bID) && visitors.find(heroID) == visitors.end())
|
||||
{
|
||||
si32 mid=0;
|
||||
si64 val = 0;
|
||||
InfoWindow iw;
|
||||
PrimarySkill::PrimarySkill what = PrimarySkill::ATTACK;
|
||||
int val=0, mid=0;
|
||||
switch (ID)
|
||||
|
||||
switch (bID)
|
||||
{
|
||||
case BuildingID::SPECIAL_4:
|
||||
switch(town->subID)
|
||||
@@ -1626,7 +1769,7 @@ void CTownBonus::onHeroVisit (const CGHeroInstance * h) const
|
||||
iw.text << VLC->generaltexth->allTexts[mid];
|
||||
cb->showInfoDialog(&iw);
|
||||
cb->changePrimSkill (cb->getHero(heroID), what, val);
|
||||
town->addHeroToStructureVisitors(h, id);
|
||||
town->addHeroToStructureVisitors(h, indexOnTV);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,16 +97,41 @@ class DLL_LINKAGE CGTownBuilding : public IObjectInterface
|
||||
{
|
||||
///basic class for town structures handled as map objects
|
||||
public:
|
||||
BuildingID ID; //from buildig list
|
||||
si32 id; //identifies its index on towns vector
|
||||
si32 indexOnTV; //identifies its index on towns vector
|
||||
CGTownInstance *town;
|
||||
CGTownBuilding() : bType(BuildingSubID::NONE), indexOnTV(0), town(nullptr) {};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & ID;
|
||||
h & id;
|
||||
h & bID;
|
||||
h & indexOnTV;
|
||||
|
||||
if(version >= 792)
|
||||
h & bType;
|
||||
}
|
||||
protected:
|
||||
BuildingID bID; //from buildig list
|
||||
BuildingSubID::EBuildingSubID bType;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE COPWBonus : public CGTownBuilding
|
||||
{///used for OPW bonusing structures
|
||||
public:
|
||||
@@ -114,8 +139,9 @@ public:
|
||||
void setProperty(ui8 what, ui32 val) override;
|
||||
void onHeroVisit (const CGHeroInstance * h) const override;
|
||||
|
||||
COPWBonus (BuildingID index, CGTownInstance *TOWN);
|
||||
COPWBonus (){ID = BuildingID::NONE; town = nullptr;};
|
||||
COPWBonus (BuildingID index, BuildingSubID::EBuildingSubID subId, CGTownInstance *TOWN);
|
||||
COPWBonus () {};
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CGTownBuilding&>(*this);
|
||||
@@ -133,7 +159,8 @@ public:
|
||||
void onHeroVisit (const CGHeroInstance * h) const override;
|
||||
|
||||
CTownBonus (BuildingID index, CGTownInstance *TOWN);
|
||||
CTownBonus (){ID = BuildingID::NONE; town = nullptr;};
|
||||
CTownBonus () {};
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CGTownBuilding&>(*this);
|
||||
@@ -223,6 +250,9 @@ public:
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if(!h.saving && version < 792)
|
||||
updateBonusingBuildings();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -231,6 +261,8 @@ 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(BuildingID bid, 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);
|
||||
@@ -262,9 +294,13 @@ public:
|
||||
GrowthInfo getGrowthInfo(int level) const;
|
||||
bool hasFort() const;
|
||||
bool hasCapitol() const;
|
||||
const CGTownBuilding * getBonusingBuilding(BuildingSubID::EBuildingSubID subId) const;
|
||||
//checks if special building with type buildingID is constructed
|
||||
bool hasBuilt(BuildingSubID::EBuildingSubID buildingID) const;
|
||||
//checks if building is constructed and town has same subID
|
||||
bool hasBuilt(BuildingID buildingID) const;
|
||||
bool hasBuilt(BuildingID buildingID, int townID) const;
|
||||
|
||||
TResources getBuildingCost(BuildingID buildingID) const;
|
||||
TResources dailyIncome() const; //calculates daily income of this town
|
||||
int spellsAtLevel(int level, bool checkGuild) const; //levels are counted from 1 (1 - 5)
|
||||
@@ -276,7 +312,8 @@ public:
|
||||
void mergeGarrisonOnSiege() const; // merge garrison into army of visiting hero
|
||||
void removeCapitols (PlayerColor owner) const;
|
||||
void clearArmy() const;
|
||||
void addHeroToStructureVisitors(const CGHeroInstance *h, si32 structureInstanceID) const; //hero must be visiting or garrisoned in town
|
||||
void addHeroToStructureVisitors(const CGHeroInstance *h, si64 structureInstanceID) const; //hero must be visiting or garrisoned in town
|
||||
bool townEnvisagesSpecialBuilding(BuildingSubID::EBuildingSubID bid) const;
|
||||
|
||||
const CTown * getTown() const ;
|
||||
|
||||
@@ -294,8 +331,12 @@ public:
|
||||
void afterAddToMap(CMap * map) override;
|
||||
static void reset();
|
||||
protected:
|
||||
static TPropagatorPtr emptyPropagator;
|
||||
void setPropertyDer(ui8 what, ui32 val) override;
|
||||
void serializeJsonOptions(JsonSerializeFormat & handler) override;
|
||||
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);
|
||||
};
|
||||
|
||||
@@ -193,9 +193,14 @@ void CObjectClassesHandler::loadObjectEntry(const std::string & identifier, cons
|
||||
}
|
||||
|
||||
logGlobal->debug("Loaded object %s(%d)::%s(%d)", obj->identifier, obj->id, convertedId, id);
|
||||
assert(!obj->subObjects.count(id)); // DO NOT override
|
||||
obj->subObjects[id] = handler;
|
||||
obj->subIds[convertedId] = id;
|
||||
|
||||
//some mods redefine content handlers in the decoration.json in such way:
|
||||
//"core:sign" : { "types" : { "forgeSign" : { ...
|
||||
if (!obj->subObjects.count(id)) // DO NOT override
|
||||
{
|
||||
obj->subObjects[id] = handler;
|
||||
obj->subIds[convertedId] = id;
|
||||
}
|
||||
}
|
||||
|
||||
CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(const JsonNode & json, const std::string & name)
|
||||
@@ -209,7 +214,7 @@ CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(co
|
||||
if(json["defaultAiValue"].isNull())
|
||||
obj->groupDefaultAiValue = boost::none;
|
||||
else
|
||||
obj->groupDefaultAiValue = static_cast<si32>(json["defaultAiValue"].Integer());
|
||||
obj->groupDefaultAiValue = static_cast<boost::optional<si32>>(json["defaultAiValue"].Integer());
|
||||
|
||||
for (auto entry : json["types"].Struct())
|
||||
{
|
||||
@@ -470,7 +475,7 @@ void AObjectTypeHandler::init(const JsonNode & input, boost::optional<std::strin
|
||||
if(input["aiValue"].isNull())
|
||||
aiValue = boost::none;
|
||||
else
|
||||
aiValue = static_cast<si32>(input["aiValue"].Integer());
|
||||
aiValue = static_cast<boost::optional<si32>>(input["aiValue"].Integer());
|
||||
|
||||
initTypeData(input);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "../ConstTransitivePtr.h"
|
||||
#include "../GameConstants.h"
|
||||
|
||||
const ui32 SERIALIZATION_VERSION = 791;
|
||||
const ui32 SERIALIZATION_VERSION = 792;
|
||||
const ui32 MINIMAL_SERIALIZATION_VERSION = 753;
|
||||
const std::string SAVEGAME_MAGIC = "VCMISVG";
|
||||
|
||||
|
||||
@@ -1038,7 +1038,7 @@ void CSpellHandler::update780()
|
||||
{
|
||||
static_assert(MINIMAL_SERIALIZATION_VERSION < 780, "No longer needed CSpellHandler::update780");
|
||||
|
||||
auto spellsContent = VLC->modh->content["spells"];
|
||||
auto spellsContent = (*VLC->modh->content)["spells"];
|
||||
|
||||
const ContentTypeHandler::ModInfo & coreData = spellsContent.modData.at("core");
|
||||
|
||||
|
||||
+15
-10
@@ -2361,9 +2361,14 @@ bool CGameHandler::teleportHero(ObjectInstanceID hid, ObjectInstanceID dstid, ui
|
||||
const CGTownInstance *from = h->visitedTown;
|
||||
if (((h->getOwner() != t->getOwner())
|
||||
&& complain("Cannot teleport hero to another player"))
|
||||
|| ((!from || !from->hasBuilt(BuildingID::CASTLE_GATE, ETownType::INFERNO))
|
||||
|
||||
|| (from->town->faction->index != t->town->faction->index
|
||||
&& complain("Source town and destination town should belong to the same faction"))
|
||||
|
||||
|| ((!from || !from->hasBuilt(BuildingSubID::CASTLE_GATE))
|
||||
&& complain("Hero must be in town with Castle gate for teleporting"))
|
||||
|| (!t->hasBuilt(BuildingID::CASTLE_GATE, ETownType::INFERNO)
|
||||
|
||||
|| (!t->hasBuilt(BuildingSubID::CASTLE_GATE)
|
||||
&& complain("Cannot teleport hero to town without Castle gate in it")))
|
||||
return false;
|
||||
int3 pos = t->visitablePos();
|
||||
@@ -2511,15 +2516,13 @@ void CGameHandler::heroVisitCastle(const CGTownInstance * obj, const CGHeroInsta
|
||||
sendAndApply(&vc);
|
||||
visitCastleObjects(obj, hero);
|
||||
giveSpells(obj, hero);
|
||||
|
||||
checkVictoryLossConditionsForPlayer(hero->tempOwner); //transported artifact?
|
||||
}
|
||||
|
||||
void CGameHandler::visitCastleObjects(const CGTownInstance * t, const CGHeroInstance * h)
|
||||
{
|
||||
std::vector<CGTownBuilding*>::const_iterator i;
|
||||
for (i = t->bonusingBuildings.begin(); i != t->bonusingBuildings.end(); i++)
|
||||
(*i)->onHeroVisit (h);
|
||||
for (auto building : t->bonusingBuildings)
|
||||
building->onHeroVisit(h);
|
||||
}
|
||||
|
||||
void CGameHandler::stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero)
|
||||
@@ -3082,9 +3085,11 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
|
||||
//Performs stuff that has to be done after new building is built
|
||||
auto processAfterBuiltStructure = [t, this](const BuildingID buildingID)
|
||||
{
|
||||
if (buildingID <= BuildingID::MAGES_GUILD_5 || //it's mage guild
|
||||
(t->subID == ETownType::TOWER && buildingID == BuildingID::LIBRARY) ||
|
||||
(t->subID == ETownType::CONFLUX && buildingID == BuildingID::GRAIL))
|
||||
auto isMageGuild = (buildingID <= BuildingID::MAGES_GUILD_5 && buildingID >= BuildingID::MAGES_GUILD_1);
|
||||
auto isLibrary = isMageGuild ? false
|
||||
: t->town->buildings.at(buildingID)->subId == BuildingSubID::EBuildingSubID::LIBRARY;
|
||||
|
||||
if(isMageGuild || isLibrary || (t->subID == ETownType::CONFLUX && buildingID == BuildingID::GRAIL))
|
||||
{
|
||||
if (t->visitingHero)
|
||||
giveSpells(t,t->visitingHero);
|
||||
@@ -3830,7 +3835,7 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl
|
||||
if (t)
|
||||
{
|
||||
visitCastleObjects(t, nh);
|
||||
giveSpells (t,nh);
|
||||
giveSpells(t,nh);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user