1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-10-31 00:07:39 +02:00

Mod system improvement Part I : Special buildings should work in the modders towns

This commit is contained in:
Dmitry Orlov
2020-10-03 00:55:46 +03:00
parent 124b2a7613
commit f4816b0824
23 changed files with 393 additions and 167 deletions

View File

@@ -112,8 +112,11 @@ void CBuildingRect::hover(bool on)
void CBuildingRect::clickLeft(tribool down, bool previousState) void CBuildingRect::clickLeft(tribool down, bool previousState)
{ {
if( previousState && getBuilding() && area && !down && (parent->selectedBuilding==this)) 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 if (!CSDL_Ext::isTransparent(area, GH.current->motion.x - pos.x, GH.current->motion.y - pos.y)) //inside building image
parent->buildingClicked(getBuilding()->bid); {
auto building = getBuilding();
parent->buildingClicked(building->bid, building->subId);
}
} }
void CBuildingRect::clickRight(tribool down, bool previousState) void CBuildingRect::clickRight(tribool down, bool previousState)
@@ -650,7 +653,7 @@ const CGHeroInstance * CCastleBuildings::getHero()
return town->garrisonHero; 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()); logGlobal->trace("You've clicked on %d", (int)building.toEnum());
const CBuilding *b = town->town->buildings.find(building)->second; const CBuilding *b = town->town->buildings.find(building)->second;
@@ -703,82 +706,67 @@ void CCastleBuildings::buildingClicked(BuildingID building)
enterBlacksmith(town->town->warMachine); enterBlacksmith(town->town->warMachine);
break; break;
case BuildingID::SHIP:
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[51]); //Cannot build another boat
break;
case BuildingID::SPECIAL_1: 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); enterFountain(building);
break; break;
case ETownType::TOWER: case BuildingSubID::ARTIFACT_MERCHANT:
case ETownType::DUNGEON://Artifact Merchant
case ETownType::CONFLUX:
if(town->visitingHero) if(town->visitingHero)
GH.pushIntT<CMarketplaceWindow>(town, town->visitingHero, EMarketMode::RESOURCE_ARTIFACT); GH.pushIntT<CMarketplaceWindow>(town, town->visitingHero, EMarketMode::RESOURCE_ARTIFACT);
else else
LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->Name())); //Only visiting heroes may use the %s. LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->Name())); //Only visiting heroes may use the %s.
break; break;
default: case BuildingSubID::FOUNTAIN_OF_FORTUNE:
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
enterFountain(building); enterFountain(building);
break; break;
case ETownType::STRONGHOLD: //Freelancer's Guild case BuildingSubID::FREELANCERS_GUILD:
if(getHero()) if(getHero())
GH.pushIntT<CMarketplaceWindow>(town, getHero(), EMarketMode::CREATURE_RESOURCE); GH.pushIntT<CMarketplaceWindow>(town, getHero(), EMarketMode::CREATURE_RESOURCE);
else else
LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->Name())); //Only visiting heroes may use the %s. LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->Name())); //Only visiting heroes may use the %s.
break; break;
case ETownType::CONFLUX: //Magic University case BuildingSubID::MAGIC_UNIVERSITY:
if (getHero()) if (getHero())
GH.pushIntT<CUniversityWindow>(getHero(), town); GH.pushIntT<CUniversityWindow>(getHero(), town);
else else
enterBuilding(building); enterBuilding(building);
break; break;
default: case BuildingSubID::BROTHERHOOD_OF_SWORD:
enterBuilding(building);
break;
}
break;
case BuildingID::SPECIAL_3:
switch(town->subID)
{
case ETownType::CASTLE: //Brotherhood of sword
LOCPLINT->showTavernWindow(town); LOCPLINT->showTavernWindow(town);
break; break;
case ETownType::INFERNO: //Castle Gate case BuildingSubID::CASTLE_GATE:
enterCastleGate(); enterCastleGate();
break; break;
case ETownType::NECROPOLIS: //Skeleton Transformer case BuildingSubID::CREATURE_TRANSFORMER: //Skeleton Transformer
GH.pushIntT<CTransformerWindow>(getHero(), town); GH.pushIntT<CTransformerWindow>(getHero(), town);
break; break;
case ETownType::DUNGEON: //Portal of Summoning case BuildingSubID::PORTAL_OF_SUMMONING:
if (town->creatures[GameConstants::CREATURES_PER_TOWN].second.empty())//No creatures if (town->creatures[GameConstants::CREATURES_PER_TOWN].second.empty())//No creatures
LOCPLINT->showInfoDialog(CGI->generaltexth->tcommands[30]); LOCPLINT->showInfoDialog(CGI->generaltexth->tcommands[30]);
else else
enterDwelling(GameConstants::CREATURES_PER_TOWN); enterDwelling(GameConstants::CREATURES_PER_TOWN);
break; break;
case ETownType::STRONGHOLD: //Ballista Yard case BuildingSubID::BALLISTA_YARD:
enterBlacksmith(ArtifactID::BALLISTA); enterBlacksmith(ArtifactID::BALLISTA);
break; break;
@@ -828,7 +816,8 @@ void CCastleBuildings::enterCastleGate()
{ {
const CGTownInstance *t = Town; const CGTownInstance *t = Town;
if (t->id != this->town->id && t->visitingHero == nullptr && //another town, empty and this is 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 availableTowns.push_back(t->id.getNum());//add to the list
} }
@@ -1173,6 +1162,7 @@ void CCastleInterface::close()
void CCastleInterface::castleTeleport(int where) void CCastleInterface::castleTeleport(int where)
{ {
const CGTownInstance * dest = LOCPLINT->cb->getTown(ObjectInstanceID(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->cb->teleportHero(town->visitingHero, dest);
LOCPLINT->eraseCurrentPathOf(town->visitingHero, false); LOCPLINT->eraseCurrentPathOf(town->visitingHero, false);
} }

View File

@@ -153,7 +153,7 @@ public:
void enterDwelling(int level); void enterDwelling(int level);
void enterToTheQuickRecruitmentWindow(); void enterToTheQuickRecruitmentWindow();
void buildingClicked(BuildingID building); void buildingClicked(BuildingID building, BuildingSubID::EBuildingSubID subID = BuildingSubID::NONE);
void addBuilding(BuildingID building); void addBuilding(BuildingID building);
void removeBuilding(BuildingID building);//FIXME: not tested!!! void removeBuilding(BuildingID building);//FIXME: not tested!!!
}; };

View File

@@ -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); 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); 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); exit = std::make_shared<CButton>( Point(228, 402), "ICANCEL.DEF", CButton::tooltip(), std::bind(&CObjectListWindow::exitPressed, this), SDLK_ESCAPE);
if(titleWidget) 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), 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) ); Point(14, 151), Point(0, 25), 9, items.size(), 0, 1, Rect(262, -32, 256, 256) );
list->type |= REDRAW_PARENT; 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) std::shared_ptr<CIntObject> CObjectListWindow::genItem(size_t index)

View File

@@ -170,12 +170,12 @@
"resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce": { "ore": 1, "wood": 1 } }, "resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce": { "ore": 1, "wood": 1 } },
"blacksmith": { "id" : 16 }, "blacksmith": { "id" : 16 },
"special1": { "id" : 17, "requires" : [ "shipyard" ] }, "special1": { "requires" : [ "shipyard" ] },
"horde1": { "id" : 18, "upgrades" : "dwellingLvl3" }, "horde1": { "id" : 18, "upgrades" : "dwellingLvl3" },
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl3", "requires" : [ "horde1" ], "mode" : "auto" }, "horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl3", "requires" : [ "horde1" ], "mode" : "auto" },
"ship": { "id" : 20, "upgrades" : "shipyard" }, "ship": { "id" : 20, "upgrades" : "shipyard" },
"special2": { "id" : 21, "requires" : [ "dwellingLvl4" ] }, "special2": { "type" : "stables", "requires" : [ "dwellingLvl4" ] },
"special3": { "id" : 22, "upgrades" : "tavern" }, "special3": { "type" : "brotherhoodOfSword", "upgrades" : "tavern" },
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }}, "grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
"dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] }, "dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] },

View File

@@ -175,11 +175,11 @@
"resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce": { "mercury": 1 } }, "resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce": { "mercury": 1 } },
"blacksmith": { "id" : 16 }, "blacksmith": { "id" : 16 },
"special1": { "id" : 17, "requires" : [ "marketplace" ] }, "special1": { "type" : "artifactMerchant", "requires" : [ "marketplace" ] },
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1" }, "horde1": { "id" : 18, "upgrades" : "dwellingLvl1" },
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" }, "horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
"ship": { "id" : 20, "upgrades" : "shipyard" }, "ship": { "id" : 20, "upgrades" : "shipyard" },
"special2": { "id" : 21, "requires" : [ "mageGuild1" ] }, "special2": { "type" : "magicUniversity", "requires" : [ "mageGuild1" ] },
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }}, "grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
"extraTownHall": { "id" : 27, "requires" : [ "townHall" ], "mode" : "auto" }, "extraTownHall": { "id" : 27, "requires" : [ "townHall" ], "mode" : "auto" },
"extraCityHall": { "id" : 28, "requires" : [ "cityHall" ], "mode" : "auto" }, "extraCityHall": { "id" : 28, "requires" : [ "cityHall" ], "mode" : "auto" },

View File

@@ -169,12 +169,12 @@
"resourceSilo": { "id" : 15, "requires" : [ "marketplace" ] }, "resourceSilo": { "id" : 15, "requires" : [ "marketplace" ] },
"blacksmith": { "id" : 16 }, "blacksmith": { "id" : 16 },
"special1": { "id" : 17, "requires" : [ "marketplace" ] }, "special1": { "type" : "artifactMerchant", "requires" : [ "marketplace" ] },
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1" }, "horde1": { "id" : 18, "upgrades" : "dwellingLvl1" },
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" }, "horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
"special2": { "id" : 21, "requires" : [ "mageGuild1" ] }, "special2": { "type" : "manaVortex", "requires" : [ "mageGuild1" ] },
"special3": { "id" : 22 }, "special3": { "type" : "portalOfSummoning" },
"special4": { "id" : 23 }, "special4": { },
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }}, "grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
"dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] }, "dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] },

View File

@@ -169,12 +169,12 @@
"resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce": { "wood": 1, "ore": 1 } }, "resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce": { "wood": 1, "ore": 1 } },
"blacksmith": { "id" : 16 }, "blacksmith": { "id" : 16 },
"special1": { "id" : 17, "requires" : [ "allOf", [ "townHall" ], [ "special2" ] ] }, "special1": { "requires" : [ "allOf", [ "townHall" ], [ "special2" ] ] },
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1" }, "horde1": { "id" : 18, "upgrades" : "dwellingLvl1" },
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" }, "horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
"ship": { "id" : 20, "upgrades" : "shipyard" }, "ship": { "id" : 20, "upgrades" : "shipyard" },
"special2": { "id" : 21, "requires" : [ "fort" ] }, "special2": { "type" : "defenseGarrisonBonus", "requires" : [ "fort" ] },
"special3": { "id" : 22, "requires" : [ "special2" ] }, "special3": { "type" : "attackGarrisonBonus", "requires" : [ "special2" ] },
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }}, "grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
"extraCapitol": { "id" : 29, "requires" : [ "capitol" ], "mode" : "auto" }, "extraCapitol": { "id" : 29, "requires" : [ "capitol" ], "mode" : "auto" },

View File

@@ -172,9 +172,9 @@
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1" }, "horde1": { "id" : 18, "upgrades" : "dwellingLvl1" },
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" }, "horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
"special2": { "id" : 21, "requires" : [ "fort" ] }, "special2": { "type" : "spellPowerGarrisonBonus", "requires" : [ "fort" ] },
"special3": { "id" : 22, "requires" : [ "citadel" ] }, "special3": { "type" : "castleGate", "requires" : [ "citadel" ] },
"special4": { "id" : 23, "requires" : [ "mageGuild1" ] }, "special4": { "requires" : [ "mageGuild1" ] },
"horde2": { "id" : 24, "upgrades" : "dwellingLvl3" }, "horde2": { "id" : 24, "upgrades" : "dwellingLvl3" },
"horde2Upgr": { "id" : 25, "upgrades" : "dwellingUpLvl3", "requires" : [ "horde2" ], "mode" : "auto" }, "horde2Upgr": { "id" : 25, "upgrades" : "dwellingUpLvl3", "requires" : [ "horde2" ], "mode" : "auto" },
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }}, "grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},

View File

@@ -175,12 +175,12 @@
"resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce": { "ore": 1, "wood": 1 } }, "resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce": { "ore": 1, "wood": 1 } },
"blacksmith": { "id" : 16 }, "blacksmith": { "id" : 16 },
"special1": { "id" : 17, "requires" : [ "fort" ] }, "special1": { "requires" : [ "fort" ] },
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1", "requires" : [ "special3" ] }, "horde1": { "id" : 18, "upgrades" : "dwellingLvl1", "requires" : [ "special3" ] },
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" }, "horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
"ship": { "id" : 20, "upgrades" : "shipyard" }, "ship": { "id" : 20, "upgrades" : "shipyard" },
"special2": { "id" : 21, "requires" : [ "mageGuild1" ] }, "special2": { "requires" : [ "mageGuild1" ] },
"special3": { "id" : 22, "requires" : [ "dwellingLvl1" ] }, "special3": { "type" : "creatureTransformer", "requires" : [ "dwellingLvl1" ] },
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }}, "grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
"extraTownHall": { "id" : 27, "requires" : [ "townHall" ], "mode" : "auto" }, "extraTownHall": { "id" : 27, "requires" : [ "townHall" ], "mode" : "auto" },
"extraCityHall": { "id" : 28, "requires" : [ "cityHall" ], "mode" : "auto" }, "extraCityHall": { "id" : 28, "requires" : [ "cityHall" ], "mode" : "auto" },

View File

@@ -174,11 +174,11 @@
"resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce": { "crystal": 1 } }, "resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce": { "crystal": 1 } },
"blacksmith": { "id" : 16 }, "blacksmith": { "id" : 16 },
"special1": { "id" : 17 }, "special1": { "type" : "mysticPond" },
"horde1": { "id" : 18, "upgrades" : "dwellingLvl2" }, "horde1": { "id" : 18, "upgrades" : "dwellingLvl2" },
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl2", "requires" : [ "horde1" ], "mode" : "auto" }, "horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl2", "requires" : [ "horde1" ], "mode" : "auto" },
"special2": { "id" : 21, "requires" : [ "special1" ] }, "special2": { "type" : "fountainOfFortune", "requires" : [ "special1" ] },
"special3": { "id" : 22, "requires" : [ "horde1" ] }, "special3": { "requires" : [ "horde1" ] },
"horde2": { "id" : 24, "upgrades" : "dwellingLvl5" }, "horde2": { "id" : 24, "upgrades" : "dwellingLvl5" },
"horde2Upgr": { "id" : 25, "upgrades" : "dwellingUpLvl5", "requires" : [ "horde2" ], "mode" : "auto" }, "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 }},

View File

@@ -166,12 +166,12 @@
"resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce": { "ore": 1, "wood": 1 } }, "resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce": { "ore": 1, "wood": 1 } },
"blacksmith": { "id" : 16 }, "blacksmith": { "id" : 16 },
"special1": { "id" : 17, "requires" : [ "fort" ] }, "special1": { "type" : "escapeTunnel", "requires" : [ "fort" ] },
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1" }, "horde1": { "id" : 18, "upgrades" : "dwellingLvl1" },
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" }, "horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
"special2": { "id" : 21, "requires" : [ "marketplace" ] }, "special2": { "type" : "freelancersGuild", "requires" : [ "marketplace" ] },
"special3": { "id" : 22, "requires" : [ "blacksmith" ] }, "special3": { "type" : "ballistaYard", "requires" : [ "blacksmith" ] },
"special4": { "id" : 23, "requires" : [ "fort" ] }, "special4": { "requires" : [ "fort" ] },
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }}, "grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
"dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] }, "dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] },

View File

@@ -169,13 +169,13 @@
"resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce" : { "gems": 1 } }, "resourceSilo": { "id" : 15, "requires" : [ "marketplace" ], "produce" : { "gems": 1 } },
"blacksmith": { "id" : 16 }, "blacksmith": { "id" : 16 },
"special1": { "id" : 17, "requires" : [ "marketplace" ] }, "special1": { "type" : "artifactMerchant", "requires" : [ "marketplace" ] },
"horde1": { "id" : 18, "upgrades" : "dwellingLvl2" }, "horde1": { "id" : 18, "upgrades" : "dwellingLvl2" },
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl2", "requires" : [ "horde1" ], "mode" : "auto" }, "horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl2", "requires" : [ "horde1" ], "mode" : "auto" },
"special2": { "id" : 21, "requires" : [ "fort" ] }, "special2": { "type" : "lookoutTower", "height" : "high", "requires" : [ "fort" ] },
"special3": { "id" : 22, "requires" : [ "mageGuild1" ] }, "special3": { "type" : "library", "requires" : [ "mageGuild1" ] },
"special4": { "id" : 23, "requires" : [ "mageGuild1" ] }, "special4": { "requires" : [ "mageGuild1" ] },
"grail": { "id" : 26, "mode" : "grail", "produce" : { "gold": 5000 } }, "grail": { "height" : "skyship", "produce" : { "gold": 5000 } },
"dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] }, "dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] },
"dwellingLvl2": { "id" : 31, "requires" : [ "dwellingLvl1" ] }, "dwellingLvl2": { "id" : 31, "requires" : [ "dwellingLvl1" ] },

View File

@@ -4,7 +4,6 @@
"$schema": "http://json-schema.org/draft-04/schema", "$schema": "http://json-schema.org/draft-04/schema",
"title" : "VCMI town building format", "title" : "VCMI town building format",
"description" : "Format used to define town buildings in VCMI", "description" : "Format used to define town buildings in VCMI",
"required": [ "id" ],
"definitions" : "definitions" :
{ {
@@ -29,6 +28,15 @@
"type":"number", "type":"number",
"description" : "Numeric identifier of this building" "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": { "mode": {
"type":"string", "type":"string",
"enum" : [ "normal", "auto", "special", "grail" ], "enum" : [ "normal", "auto", "special", "grail" ],

View File

@@ -343,28 +343,98 @@ void CTownHandler::loadBuildingRequirements(CBuilding * building, const JsonNode
requirementsToLoad.push_back(hlp); requirementsToLoad.push_back(hlp);
} }
template<typename R>
R CTownHandler::getMappedValue(const std::string key, const R defval, const std::map<std::string, R> & map, bool required) const
{
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) const
{
if(!node.isNull() && node.getType() == JsonNode::JsonType::DATA_STRING)
return getMappedValue<R>(node.String(), defval, map, required);
return defval;
}
void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, const JsonNode & source) void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, const JsonNode & source)
{ {
auto ret = new CBuilding(); static const std::map<std::string, CBuilding::EBuildMode> MODES =
static const std::vector<std::string> MODES =
{ {
"normal", "auto", "special", "grail" { "normal", CBuilding::BUILD_NORMAL },
{ "auto", CBuilding::BUILD_AUTO },
{ "special", CBuilding::BUILD_SPECIAL },
{ "grail", CBuilding::BUILD_GRAIL }
}; };
ret->mode = CBuilding::BUILD_NORMAL; static const std::map<std::string, BuildingID> BUILDING_TYPES =
{ {
if(source["mode"].getType() == JsonNode::JsonType::DATA_STRING) { "special1", BuildingID::SPECIAL_1 },
{ { "special2", BuildingID::SPECIAL_2 },
auto rawMode = vstd::find_pos(MODES, source["mode"].String()); { "special3", BuildingID::SPECIAL_3 },
if(rawMode > 0) { "special4", BuildingID::SPECIAL_4 },
ret->mode = static_cast<CBuilding::EBuildMode>(rawMode); { "grail", BuildingID::GRAIL }
} };
}
static const std::map<std::string, CBuilding::ETowerHeight> LOOKOUT_TYPES =
{
{ "low", CBuilding::HEIGHT_LOW },
{ "average", CBuilding::HEIGHT_AVERAGE },
{ "high", CBuilding::HEIGHT_HIGH },
{ "skyship", CBuilding::HEIGHT_SKYSHIP }
};
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 }
};
auto ret = new CBuilding();
ret->bid = getMappedValue<BuildingID>(stringID, BuildingID::NONE, BUILDING_TYPES, false);
if(ret->bid == BuildingID::NONE)
ret->bid = source["id"].isNull() ? BuildingID(BuildingID::NONE) : BuildingID(source["id"].Float());
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, MODES);
ret->subId = getMappedValue<BuildingSubID::EBuildingSubID>(source["type"], BuildingSubID::NONE, 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, LOOKOUT_TYPES);
ret->identifier = stringID; ret->identifier = stringID;
ret->town = town; ret->town = town;
ret->bid = BuildingID((si32)source["id"].Float());
ret->name = source["name"].String(); ret->name = source["name"].String();
ret->description = source["description"].String(); ret->description = source["description"].String();
ret->resources = TResources(source["cost"]); ret->resources = TResources(source["cost"]);

View File

@@ -46,6 +46,7 @@ public:
BuildingID bid; //structure ID BuildingID bid; //structure ID
BuildingID upgrade; /// indicates that building "upgrade" can be improved by this, -1 = empty 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 enum EBuildMode
{ {
@@ -55,6 +56,15 @@ public:
BUILD_GRAIL // 3 - grail - building reqires grail to be built BUILD_GRAIL // 3 - grail - building reqires grail to be built
} mode; } mode;
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;
CBuilding(); CBuilding();
const std::string &Name() const; const std::string &Name() const;
@@ -78,6 +88,17 @@ public:
h & requirements; h & requirements;
h & upgrade; h & upgrade;
h & mode; h & mode;
if(version >= 792)
{
h & subId;
h & height;
}
else if (!h.saving)
{
subId = BuildingSubID::NONE;
height = CBuilding::HEIGHT_NO_TOWER;
}
if(!h.saving) if(!h.saving)
deserializeFix(); deserializeFix();
} }
@@ -330,6 +351,12 @@ class DLL_LINKAGE CTownHandler : public IHandlerBase
CFaction * loadFromJson(const JsonNode & data, const std::string & identifier); CFaction * loadFromJson(const JsonNode & data, const std::string & identifier);
void loadRandomFaction(); void loadRandomFaction();
template<typename R>
R getMappedValue(const std::string key, const R defval, const std::map<std::string, R> & map, bool required = true) const;
template<typename R>
R getMappedValue(const JsonNode& node, const R defval, const std::map<std::string, R> & map, bool required = true) const;
public: public:
std::vector<ConstTransitivePtr<CFaction> > factions; std::vector<ConstTransitivePtr<CFaction> > factions;

View File

@@ -414,6 +414,35 @@ public:
EBuildingID num; EBuildingID num;
}; };
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
};
}
ID_LIKE_OPERATORS(BuildingID, BuildingID::EBuildingID) ID_LIKE_OPERATORS(BuildingID, BuildingID::EBuildingID)
namespace EAiTactic namespace EAiTactic

View File

@@ -62,7 +62,7 @@ void CPrivilegedInfoCallback::getTilesInRange(std::unordered_set<int3, ShashInt3
logGlobal->error("Illegal call to getTilesInRange!"); logGlobal->error("Illegal call to getTilesInRange!");
return; return;
} }
if (radious == -1) //reveal entire map if(radious == CBuilding::HEIGHT_SKYSHIP) //reveal entire map
getAllTiles (tiles, player, -1, 0); getAllTiles (tiles, player, -1, 0);
else else
{ {

View File

@@ -171,7 +171,7 @@ struct Component
{ {
enum EComponentType {PRIM_SKILL, SEC_SKILL, RESOURCE, CREATURE, ARTIFACT, EXPERIENCE, SPELL, MORALE, LUCK, BUILDING, HERO_PORTRAIT, FLAG}; enum EComponentType {PRIM_SKILL, SEC_SKILL, RESOURCE, CREATURE, ARTIFACT, EXPERIENCE, SPELL, MORALE, LUCK, BUILDING, HERO_PORTRAIT, FLAG};
ui16 id, subtype; //id uses ^^^ enums, when id==EXPPERIENCE subtype==0 means exp points and subtype==1 levels) ui16 id, subtype; //id uses ^^^ enums, when id==EXPPERIENCE subtype==0 means exp points and subtype==1 levels)
si32 val; // + give; - take si64 val; // + give; - take
si16 when; // 0 - now; +x - within x days; -x - per x days si16 when; // 0 - now; +x - within x days; -x - per x days
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
@@ -186,7 +186,7 @@ struct Component
{ {
} }
DLL_LINKAGE explicit Component(const CStackBasicDescriptor &stack); DLL_LINKAGE explicit Component(const CStackBasicDescriptor &stack);
Component(Component::EComponentType Type, ui16 Subtype, si32 Val, si16 When) Component(Component::EComponentType Type, ui16 Subtype, si64 Val, si16 When)
:id(Type),subtype(Subtype),val(Val),when(When) :id(Type),subtype(Subtype),val(Val),when(When)
{ {
} }

View File

@@ -279,7 +279,7 @@ bool CBattleInfoEssentials::battleCanFlee(PlayerColor player) const
if(side.get() == BattleSide::DEFENDER && battleGetSiegeLevel()) if(side.get() == BattleSide::DEFENDER && battleGetSiegeLevel())
{ {
auto town = battleGetDefendedTown(); auto town = battleGetDefendedTown();
if(!town->hasBuilt(BuildingID::ESCAPE_TUNNEL, ETownType::STRONGHOLD)) if(!town->hasBuilt(BuildingSubID::ESCAPE_TUNNEL))
return false; return false;
} }

View File

@@ -437,14 +437,22 @@ void CGDwelling::serializeJsonOptions(JsonSerializeFormat & handler)
int CGTownInstance::getSightRadius() const //returns sight distance 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 if(bid == BuildingID::SPECIAL_1
return -1; //entire map || bid == BuildingID::SPECIAL_2
if (hasBuilt(BuildingID::LOOKOUT_TOWER)) //lookout tower || bid == BuildingID::SPECIAL_3
return 20; || bid == BuildingID::SPECIAL_4
|| bid == BuildingID::GRAIL)
{
auto height = town->buildings.at(bid)->height;
if(ret < height)
ret = height;
}
} }
return 5; return ret;
} }
void CGTownInstance::setPropertyDer(ui8 what, ui32 val) void CGTownInstance::setPropertyDer(ui8 what, ui32 val)
@@ -635,7 +643,7 @@ int CGTownInstance::spellsAtLevel(int level, bool checkGuild) const
return 0; return 0;
int ret = 6 - level; //how many spells are available at this level int ret = 6 - level; //how many spells are available at this level
if (hasBuilt(BuildingID::LIBRARY, ETownType::TOWER)) if (hasBuilt(BuildingSubID::LIBRARY))
ret++; ret++;
return ret; return ret;
@@ -733,15 +741,26 @@ std::string CGTownInstance::getObjectName() const
return name + ", " + town->faction->name; 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) void CGTownInstance::initObj(CRandomGenerator & rand)
///initialize town structures ///initialize town structures
{ {
blockVisit = true; blockVisit = true;
if (subID == ETownType::DUNGEON) if(townEnvisagesSpecialBuilding(BuildingSubID::PORTAL_OF_SUMMONING)) //Dungeon for example
creatures.resize(GameConstants::CREATURES_PER_TOWN+1);//extra dwelling for Dungeon creatures.resize(GameConstants::CREATURES_PER_TOWN+1);
else else
creatures.resize(GameConstants::CREATURES_PER_TOWN); creatures.resize(GameConstants::CREATURES_PER_TOWN);
for (int level = 0; level < GameConstants::CREATURES_PER_TOWN; level++) for (int level = 0; level < GameConstants::CREATURES_PER_TOWN; level++)
{ {
BuildingID buildID = BuildingID(BuildingID::DWELL_FIRST).advance(level); BuildingID buildID = BuildingID(BuildingID::DWELL_FIRST).advance(level);
@@ -753,16 +772,19 @@ void CGTownInstance::initObj(CRandomGenerator & rand)
creatures[level].second.push_back(town->creatures[level][upgradeNum]); 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) switch (subID)
{ //add new visitable objects {
case ETownType::CASTLE: //add new visitable objects
bonusingBuildings.push_back (new COPWBonus(BuildingID::STABLES, this));
break;
case ETownType::DUNGEON: case ETownType::DUNGEON:
bonusingBuildings.push_back (new COPWBonus(BuildingID::MANA_VORTEX, this)); case ETownType::TOWER:
FALLTHROUGH case ETownType::INFERNO:
case ETownType::TOWER: case ETownType::INFERNO: case ETownType::STRONGHOLD: case ETownType::STRONGHOLD:
bonusingBuildings.push_back (new CTownBonus(BuildingID::SPECIAL_4, this)); bonusingBuildings.push_back (new CTownBonus(BuildingID::SPECIAL_4, this));
break; break;
case ETownType::FORTRESS: case ETownType::FORTRESS:
@@ -779,9 +801,11 @@ void CGTownInstance::newTurn(CRandomGenerator & rand) const
{ {
if (cb->getDate(Date::DAY_OF_WEEK) == 1) //reset on new week if (cb->getDate(Date::DAY_OF_WEEK) == 1) //reset on new week
{ {
//give resources for Rampart, Mystic Pond //give resources if there's a Mystic Pond
if (hasBuilt(BuildingID::MYSTIC_POND, ETownType::RAMPART) if (hasBuilt(BuildingSubID::MYSTIC_POND)
&& cb->getDate(Date::DAY) != 1 && (tempOwner < PlayerColor::PLAYER_LIMIT)) && cb->getDate(Date::DAY) != 1
&& (tempOwner < PlayerColor::PLAYER_LIMIT)
)
{ {
int resID = rand.nextInt(2, 5); //bonus to random rare resource int resID = rand.nextInt(2, 5); //bonus to random rare resource
resID = (resID==2)?1:resID; resID = (resID==2)?1:resID;
@@ -791,12 +815,10 @@ void CGTownInstance::newTurn(CRandomGenerator & rand) const
cb->setObjProperty (id, ObjProperty::BONUS_VALUE_SECOND, resVal); cb->setObjProperty (id, ObjProperty::BONUS_VALUE_SECOND, resVal);
} }
if ( subID == ETownType::DUNGEON ) auto manaVortex = getBonusingBuilding(BuildingSubID::MANA_VORTEX);
for (auto & elem : bonusingBuildings)
{ if (manaVortex != nullptr)
if ((elem)->ID == BuildingID::MANA_VORTEX) cb->setObjProperty(id, ObjProperty::STRUCTURE_CLEAR_VISITORS, manaVortex->indexOnTV); //reset visitors for Mana Vortex
cb->setObjProperty (id, ObjProperty::STRUCTURE_CLEAR_VISITORS, (elem)->id); //reset visitors for Mana Vortex
}
//get Mana Vortex or Stables bonuses //get Mana Vortex or Stables bonuses
//same code is in the CGameHandler::buildStructure method //same code is in the CGameHandler::buildStructure method
@@ -882,7 +904,7 @@ void CGTownInstance::getOutOffsets( std::vector<int3> &offsets ) const
void CGTownInstance::mergeGarrisonOnSiege() const void CGTownInstance::mergeGarrisonOnSiege() const
{ {
auto getWeakestStackSlot = [&](int powerLimit) auto getWeakestStackSlot = [&](ui64 powerLimit)
{ {
std::vector<SlotID> weakSlots; std::vector<SlotID> weakSlots;
auto stacksList = visitingHero->stacks; auto stacksList = visitingHero->stacks;
@@ -909,7 +931,8 @@ void CGTownInstance::mergeGarrisonOnSiege() const
return SlotID(); return SlotID();
}; };
int count = static_cast<int>(stacks.size()); auto count = static_cast<int>(stacks.size());
for(int i = 0; i < count; i++) for(int i = 0; i < count; i++)
{ {
auto pair = *vstd::maxElementByFun(stacks, [&](std::pair<SlotID, CStackInstance *> elem) auto pair = *vstd::maxElementByFun(stacks, [&](std::pair<SlotID, CStackInstance *> elem)
@@ -1099,9 +1122,14 @@ void CGTownInstance::recreateBuildingsBonuses()
removeBonus(b); removeBonus(b);
//tricky! -> checks tavern only if no bratherhood of sword or not a castle //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(BuildingSubID::BROTHERHOOD_OF_SWORD, Bonus::MORALE, +2))
addBonusIfBuilt(BuildingID::TAVERN, Bonus::MORALE, +1); addBonusIfBuilt(BuildingID::TAVERN, Bonus::MORALE, +1);
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
if(subID == ETownType::CASTLE) //castle if(subID == ETownType::CASTLE) //castle
{ {
addBonusIfBuilt(BuildingID::LIGHTHOUSE, Bonus::SEA_MOVEMENT, +500, playerProp); addBonusIfBuilt(BuildingID::LIGHTHOUSE, Bonus::SEA_MOVEMENT, +500, playerProp);
@@ -1109,17 +1137,12 @@ void CGTownInstance::recreateBuildingsBonuses()
} }
else if(subID == ETownType::RAMPART) //rampart 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 addBonusIfBuilt(BuildingID::GRAIL, Bonus::LUCK, +2, playerProp); //guardian spirit
} }
else if(subID == ETownType::TOWER) //tower else if(subID == ETownType::TOWER) //tower
{ {
addBonusIfBuilt(BuildingID::GRAIL, Bonus::PRIMARY_SKILL, +15, PrimarySkill::KNOWLEDGE); //grail 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 else if(subID == ETownType::NECROPOLIS) //necropolis
{ {
addBonusIfBuilt(BuildingID::COVER_OF_DARKNESS, Bonus::DARKNESS, +20); addBonusIfBuilt(BuildingID::COVER_OF_DARKNESS, Bonus::DARKNESS, +20);
@@ -1136,15 +1159,32 @@ void CGTownInstance::recreateBuildingsBonuses()
} }
else if(subID == ETownType::FORTRESS) //Fortress 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::ATTACK); //grail
addBonusIfBuilt(BuildingID::GRAIL, Bonus::PRIMARY_SKILL, +10, PrimarySkill::DEFENSE); //grail addBonusIfBuilt(BuildingID::GRAIL, Bonus::PRIMARY_SKILL, +10, PrimarySkill::DEFENSE); //grail
} }
else if(subID == ETownType::CONFLUX) }
{
bool CGTownInstance::addBonusIfBuilt(BuildingSubID::EBuildingSubID building, Bonus::BonusType type, int val, int subtype)
{
bool ret = false;
if (hasBuilt(building))
{
std::ostringstream descr;
for (const auto & it : town->buildings)
{
if (it.second->subId == building)
{
descr << it.second->Name();
break;
}
}
auto b = std::make_shared<Bonus>(Bonus::PERMANENT, type, Bonus::TOWN_STRUCTURE, val, building, descr.str(), subtype);
addNewBonus(b); //looks like a propagator is not necessary in this case
ret = true;
} }
return ret;
} }
bool CGTownInstance::addBonusIfBuilt(BuildingID building, Bonus::BonusType type, int val, int subtype) bool CGTownInstance::addBonusIfBuilt(BuildingID building, Bonus::BonusType type, int val, int subtype)
@@ -1248,10 +1288,14 @@ const CTown * CGTownInstance::getTown() const
int CGTownInstance::getTownLevel() const int CGTownInstance::getTownLevel() const
{ {
// count all buildings that are not upgrades // 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() CBonusSystemNode * CGTownInstance::whatShouldBeAttached()
@@ -1266,6 +1310,31 @@ const CArmedInstance * CGTownInstance::getUpperArmy() const
return this; 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 bool CGTownInstance::hasBuilt(BuildingID buildingID, int townID) const
{ {
if (townID == town->faction->index || townID == ETownType::ANY) if (townID == town->faction->index || townID == ETownType::ANY)
@@ -1285,11 +1354,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 CBuilding::TRequired CGTownInstance::genBuildingRequirements(BuildingID buildID, bool deep) const
{ {
const CBuilding * building = town->buildings.at(buildID); const CBuilding * building = town->buildings.at(buildID);
@@ -1338,7 +1402,7 @@ CBuilding::TRequired CGTownInstance::genBuildingRequirements(BuildingID buildID,
return ret; return ret;
} }
void CGTownInstance::addHeroToStructureVisitors( const CGHeroInstance *h, si32 structureInstanceID ) const void CGTownInstance::addHeroToStructureVisitors( const CGHeroInstance *h, si64 structureInstanceID ) const
{ {
if(visitingHero == h) if(visitingHero == h)
cb->setObjProperty(id, ObjProperty::STRUCTURE_ADD_VISITING_HERO, structureInstanceID); //add to visitors cb->setObjProperty(id, ObjProperty::STRUCTURE_ADD_VISITING_HERO, structureInstanceID); //add to visitors
@@ -1498,12 +1562,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; town = TOWN;
id = static_cast<si32>(town->bonusingBuildings.size()); indexOnTV = static_cast<si32>(town->bonusingBuildings.size());
} }
void COPWBonus::setProperty(ui8 what, ui32 val) void COPWBonus::setProperty(ui8 what, ui32 val)
{ {
switch (what) switch (what)
@@ -1516,16 +1582,18 @@ void COPWBonus::setProperty(ui8 what, ui32 val)
break; break;
} }
} }
void COPWBonus::onHeroVisit (const CGHeroInstance * h) const void COPWBonus::onHeroVisit (const CGHeroInstance * h) const
{ {
ObjectInstanceID heroID = h->id; ObjectInstanceID heroID = h->id;
if (town->hasBuilt(ID)) if (town->hasBuilt(bID))
{ {
InfoWindow iw; InfoWindow iw;
iw.player = h->tempOwner; 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 if (!h->hasBonusFrom(Bonus::OBJECT, Obj::STABLES)) //does not stack with advMap Stables
{ {
GiveBonus gb; GiveBonus gb;
@@ -1543,7 +1611,8 @@ void COPWBonus::onHeroVisit (const CGHeroInstance * h) const
cb->showInfoDialog(&iw); cb->showInfoDialog(&iw);
} }
break; break;
case ETownType::DUNGEON: //Mana Vortex
case BuildingSubID::MANA_VORTEX:
if (visitors.empty()) if (visitors.empty())
{ {
if (h->mana < h->manaLimit() * 2) if (h->mana < h->manaLimit() * 2)
@@ -1553,7 +1622,7 @@ void COPWBonus::onHeroVisit (const CGHeroInstance * h) const
iw.text << VLC->generaltexth->allTexts[579]; iw.text << VLC->generaltexth->allTexts[579];
cb->showInfoDialog(&iw); cb->showInfoDialog(&iw);
//extra visit penalty if hero alredy had double mana points (or even more?!) //extra visit penalty if hero alredy had double mana points (or even more?!)
town->addHeroToStructureVisitors(h, id); town->addHeroToStructureVisitors(h, indexOnTV);
} }
break; break;
} }
@@ -1561,24 +1630,28 @@ void COPWBonus::onHeroVisit (const CGHeroInstance * h) const
} }
CTownBonus::CTownBonus (BuildingID index, CGTownInstance *TOWN) CTownBonus::CTownBonus (BuildingID index, CGTownInstance *TOWN)
{ {
ID = index; bID = index;
town = TOWN; town = TOWN;
id = static_cast<si32>(town->bonusingBuildings.size()); indexOnTV = static_cast<si32>(town->bonusingBuildings.size());
} }
void CTownBonus::setProperty (ui8 what, ui32 val) void CTownBonus::setProperty (ui8 what, ui32 val)
{ {
if(what == ObjProperty::VISITORS) if(what == ObjProperty::VISITORS)
visitors.insert(ObjectInstanceID(val)); visitors.insert(ObjectInstanceID(val));
} }
void CTownBonus::onHeroVisit (const CGHeroInstance * h) const void CTownBonus::onHeroVisit (const CGHeroInstance * h) const
{ {
ObjectInstanceID heroID = h->id; 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; InfoWindow iw;
PrimarySkill::PrimarySkill what = PrimarySkill::ATTACK; PrimarySkill::PrimarySkill what = PrimarySkill::ATTACK;
int val=0, mid=0;
switch (ID) switch (bID)
{ {
case BuildingID::SPECIAL_4: case BuildingID::SPECIAL_4:
switch(town->subID) switch(town->subID)
@@ -1626,7 +1699,7 @@ void CTownBonus::onHeroVisit (const CGHeroInstance * h) const
iw.text << VLC->generaltexth->allTexts[mid]; iw.text << VLC->generaltexth->allTexts[mid];
cb->showInfoDialog(&iw); cb->showInfoDialog(&iw);
cb->changePrimSkill (cb->getHero(heroID), what, val); cb->changePrimSkill (cb->getHero(heroID), what, val);
town->addHeroToStructureVisitors(h, id); town->addHeroToStructureVisitors(h, indexOnTV);
} }
} }

View File

@@ -97,16 +97,31 @@ class DLL_LINKAGE CGTownBuilding : public IObjectInterface
{ {
///basic class for town structures handled as map objects ///basic class for town structures handled as map objects
public: public:
BuildingID ID; //from buildig list si32 indexOnTV; //identifies its index on towns vector
si32 id; //identifies its index on towns vector
CGTownInstance *town; CGTownInstance *town;
CGTownBuilding() : bType(BuildingSubID::NONE), indexOnTV(0), town(nullptr) {};
STRONG_INLINE
BuildingSubID::EBuildingSubID getBuildingSubtype() const
{
return bType;
}
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & ID; h & bID;
h & id; h & indexOnTV;
if(version >= 792)
h & bType;
else if(!h.saving)
bType = BuildingSubID::NONE;
} }
protected:
BuildingID bID; //from buildig list
BuildingSubID::EBuildingSubID bType;
}; };
class DLL_LINKAGE COPWBonus : public CGTownBuilding class DLL_LINKAGE COPWBonus : public CGTownBuilding
{///used for OPW bonusing structures {///used for OPW bonusing structures
public: public:
@@ -114,8 +129,9 @@ public:
void setProperty(ui8 what, ui32 val) override; void setProperty(ui8 what, ui32 val) override;
void onHeroVisit (const CGHeroInstance * h) const override; void onHeroVisit (const CGHeroInstance * h) const override;
COPWBonus (BuildingID index, CGTownInstance *TOWN); COPWBonus (BuildingID index, BuildingSubID::EBuildingSubID subId, CGTownInstance *TOWN);
COPWBonus (){ID = BuildingID::NONE; town = nullptr;}; COPWBonus () {};
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & static_cast<CGTownBuilding&>(*this); h & static_cast<CGTownBuilding&>(*this);
@@ -133,7 +149,8 @@ public:
void onHeroVisit (const CGHeroInstance * h) const override; void onHeroVisit (const CGHeroInstance * h) const override;
CTownBonus (BuildingID index, CGTownInstance *TOWN); CTownBonus (BuildingID index, CGTownInstance *TOWN);
CTownBonus (){ID = BuildingID::NONE; town = nullptr;}; CTownBonus () {};
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & static_cast<CGTownBuilding&>(*this); h & static_cast<CGTownBuilding&>(*this);
@@ -231,6 +248,7 @@ public:
void updateMoraleBonusFromArmy() override; void updateMoraleBonusFromArmy() override;
void deserializationFix(); void deserializationFix();
void recreateBuildingsBonuses(); void recreateBuildingsBonuses();
bool addBonusIfBuilt(BuildingSubID::EBuildingSubID building, 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, 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 bool addBonusIfBuilt(BuildingID building, Bonus::BonusType type, int val, int subtype = -1); //convienence version of above
void setVisitingHero(CGHeroInstance *h); void setVisitingHero(CGHeroInstance *h);
@@ -262,9 +280,13 @@ public:
GrowthInfo getGrowthInfo(int level) const; GrowthInfo getGrowthInfo(int level) const;
bool hasFort() const; bool hasFort() const;
bool hasCapitol() 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 //checks if building is constructed and town has same subID
bool hasBuilt(BuildingID buildingID) const; bool hasBuilt(BuildingID buildingID) const;
bool hasBuilt(BuildingID buildingID, int townID) const; bool hasBuilt(BuildingID buildingID, int townID) const;
TResources getBuildingCost(BuildingID buildingID) const; TResources getBuildingCost(BuildingID buildingID) const;
TResources dailyIncome() const; //calculates daily income of this town TResources dailyIncome() const; //calculates daily income of this town
int spellsAtLevel(int level, bool checkGuild) const; //levels are counted from 1 (1 - 5) int spellsAtLevel(int level, bool checkGuild) const; //levels are counted from 1 (1 - 5)
@@ -276,7 +298,8 @@ public:
void mergeGarrisonOnSiege() const; // merge garrison into army of visiting hero void mergeGarrisonOnSiege() const; // merge garrison into army of visiting hero
void removeCapitols (PlayerColor owner) const; void removeCapitols (PlayerColor owner) const;
void clearArmy() 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 ; const CTown * getTown() const ;

View File

@@ -12,7 +12,7 @@
#include "../ConstTransitivePtr.h" #include "../ConstTransitivePtr.h"
#include "../GameConstants.h" #include "../GameConstants.h"
const ui32 SERIALIZATION_VERSION = 791; const ui32 SERIALIZATION_VERSION = 792;
const ui32 MINIMAL_SERIALIZATION_VERSION = 753; const ui32 MINIMAL_SERIALIZATION_VERSION = 753;
const std::string SAVEGAME_MAGIC = "VCMISVG"; const std::string SAVEGAME_MAGIC = "VCMISVG";

View File

@@ -2361,9 +2361,14 @@ bool CGameHandler::teleportHero(ObjectInstanceID hid, ObjectInstanceID dstid, ui
const CGTownInstance *from = h->visitedTown; const CGTownInstance *from = h->visitedTown;
if (((h->getOwner() != t->getOwner()) if (((h->getOwner() != t->getOwner())
&& complain("Cannot teleport hero to another player")) && 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")) && 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"))) && complain("Cannot teleport hero to town without Castle gate in it")))
return false; return false;
int3 pos = t->visitablePos(); int3 pos = t->visitablePos();
@@ -2511,7 +2516,6 @@ void CGameHandler::heroVisitCastle(const CGTownInstance * obj, const CGHeroInsta
sendAndApply(&vc); sendAndApply(&vc);
visitCastleObjects(obj, hero); visitCastleObjects(obj, hero);
giveSpells(obj, hero); giveSpells(obj, hero);
checkVictoryLossConditionsForPlayer(hero->tempOwner); //transported artifact? checkVictoryLossConditionsForPlayer(hero->tempOwner); //transported artifact?
} }
@@ -3082,9 +3086,11 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
//Performs stuff that has to be done after new building is built //Performs stuff that has to be done after new building is built
auto processAfterBuiltStructure = [t, this](const BuildingID buildingID) auto processAfterBuiltStructure = [t, this](const BuildingID buildingID)
{ {
if (buildingID <= BuildingID::MAGES_GUILD_5 || //it's mage guild auto isMageGuild = (buildingID <= BuildingID::MAGES_GUILD_5 && buildingID >= BuildingID::MAGES_GUILD_1);
(t->subID == ETownType::TOWER && buildingID == BuildingID::LIBRARY) || auto isLibrary = isMageGuild ? false
(t->subID == ETownType::CONFLUX && buildingID == BuildingID::GRAIL)) : t->town->buildings.at(buildingID)->subId == BuildingSubID::EBuildingSubID::LIBRARY;
if(isMageGuild || isLibrary || (t->subID == ETownType::CONFLUX && buildingID == BuildingID::GRAIL))
{ {
if (t->visitingHero) if (t->visitingHero)
giveSpells(t,t->visitingHero); giveSpells(t,t->visitingHero);
@@ -3830,7 +3836,7 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl
if (t) if (t)
{ {
visitCastleObjects(t, nh); visitCastleObjects(t, nh);
giveSpells (t,nh); giveSpells(t,nh);
} }
return true; return true;
} }