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:
		| @@ -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); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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!!! | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -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) | ||||||
|   | |||||||
| @@ -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" ] }, | ||||||
|   | |||||||
| @@ -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" }, | ||||||
|   | |||||||
| @@ -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" ] }, | ||||||
|   | |||||||
| @@ -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" }, | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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 }}, | ||||||
|   | |||||||
| @@ -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" }, | ||||||
|   | |||||||
| @@ -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 }}, | ||||||
|   | |||||||
| @@ -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" ] }, | ||||||
|   | |||||||
| @@ -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" ] }, | ||||||
|   | |||||||
| @@ -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" ], | ||||||
|   | |||||||
| @@ -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"]); | ||||||
|   | |||||||
| @@ -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; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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 | ||||||
| 	{ | 	{ | ||||||
|   | |||||||
| @@ -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) | ||||||
| 	{ | 	{ | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -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; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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 ; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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"; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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; | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user