mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Heroes placed on water in map will be automatically given boat
This commit is contained in:
		| @@ -9,6 +9,7 @@ | ||||
| 			"120px" : "TPCASCAS", | ||||
| 			"130px" : "CRBKGCAS" | ||||
| 		}, | ||||
| 		"boat" : "boatCastle", | ||||
| 		"puzzleMap" : | ||||
| 		{ | ||||
| 			"prefix" : "PUZCAS", | ||||
| @@ -148,7 +149,6 @@ | ||||
| 			"mageGuild" : 4, | ||||
| 			"warMachine" : "ballista", | ||||
| 			"moatAbility" : "castleMoat", | ||||
| 			"boat" : "boatCastle", | ||||
| 			// primaryResource not specified so town get both Wood and Ore for resource bonus | ||||
|  | ||||
| 			"buildings" : | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
| 			"120px" : "TPCASELE", | ||||
| 			"130px" : "CRBKGELE" | ||||
| 		}, | ||||
| 		"boat" : "boatNecropolis", | ||||
| 		"puzzleMap" : | ||||
| 		{ | ||||
| 			"prefix" : "PUZELE", | ||||
| @@ -153,7 +154,6 @@ | ||||
| 			"primaryResource" : "mercury", | ||||
| 			"warMachine" : "ballista", | ||||
| 			"moatAbility" : "castleMoat", | ||||
| 			"boat" : "boatNecropolis", | ||||
|  | ||||
| 			"buildings" : | ||||
| 			{ | ||||
|   | ||||
| @@ -10,6 +10,7 @@ | ||||
| 			"120px" : "TPCASDUN", | ||||
| 			"130px" : "CRBKGDUN" | ||||
| 		}, | ||||
| 		"boat" : "boatCastle", | ||||
| 		"puzzleMap" : | ||||
| 		{ | ||||
| 			"prefix" : "PUZDUN", | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
| 			"120px" : "TPCASFOR", | ||||
| 			"130px" : "CRBKGFOR" | ||||
| 		}, | ||||
| 		"boat" : "boatFortress", | ||||
| 		"puzzleMap" : | ||||
| 		{ | ||||
| 			"prefix" : "PUZFOR", | ||||
| @@ -148,7 +149,6 @@ | ||||
| 			"mageGuild" : 3, | ||||
| 			"warMachine" : "firstAidTent", | ||||
| 			"moatAbility" : "fortressMoat", | ||||
| 			"boat" : "boatFortress", | ||||
| 			// primaryResource not specified so town get both Wood and Ore for resource bonus | ||||
|  | ||||
| 			"buildings" : | ||||
|   | ||||
| @@ -10,6 +10,7 @@ | ||||
| 			"120px" : "TPCASINF", | ||||
| 			"130px" : "CRBKGINF" | ||||
| 		}, | ||||
| 		"boat" : "boatCastle", | ||||
| 		"puzzleMap" : | ||||
| 		{ | ||||
| 			"prefix" : "PUZINF", | ||||
|   | ||||
| @@ -10,6 +10,7 @@ | ||||
| 			"120px" : "TPCASNEC", | ||||
| 			"130px" : "CRBKGNEC" | ||||
| 		}, | ||||
| 		"boat" : "boatNecropolis", | ||||
| 		"puzzleMap" : | ||||
| 		{ | ||||
| 			"prefix" : "PUZNEC", | ||||
| @@ -153,7 +154,6 @@ | ||||
| 			"mageGuild" : 5, | ||||
| 			"warMachine" : "firstAidTent", | ||||
| 			"moatAbility" : "necropolisMoat", | ||||
| 			"boat" : "boatNecropolis", | ||||
| 			// primaryResource not specified so town get both Wood and Ore for resource bonus | ||||
|  | ||||
| 			"buildings" : | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
| 			"120px" : "TPCASRAM", | ||||
| 			"130px" : "CRBKGRAM" | ||||
| 		}, | ||||
| 		"boat" : "boatCastle", | ||||
| 		"puzzleMap" : | ||||
| 		{ | ||||
| 			"prefix" : "PUZRAM", | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
| 			"120px" : "TPCASSTR", | ||||
| 			"130px" : "CRBKGSTR" | ||||
| 		}, | ||||
| 		"boat" : "boatCastle", | ||||
| 		"puzzleMap" : | ||||
| 		{ | ||||
| 			"prefix" : "PUZSTR", | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
| 			"120px" : "TPCASTOW", | ||||
| 			"130px" : "CRBKGTOW" | ||||
| 		}, | ||||
| 		"boat" : "boatCastle", | ||||
| 		"puzzleMap" : | ||||
| 		{ | ||||
| 			"prefix" : "PUZTOW", | ||||
|   | ||||
| @@ -16,12 +16,14 @@ VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| class FactionID; | ||||
| enum class EAlignment : uint8_t; | ||||
| enum class EBoatId : int32_t; | ||||
|  | ||||
| class DLL_LINKAGE Faction : public EntityT<FactionID>, public INativeTerrainProvider | ||||
| { | ||||
| public: | ||||
| 	virtual bool hasTown() const = 0; | ||||
| 	virtual EAlignment getAlignment() const = 0; | ||||
| 	virtual EBoatId getBoatType() const = 0; | ||||
| }; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -1250,6 +1250,31 @@ void CGameState::initHeroes() | ||||
| 		map->allHeroes[hero->type->getIndex()] = hero; | ||||
| 	} | ||||
|  | ||||
| 	// generate boats for all heroes on water | ||||
| 	for(auto hero : map->heroesOnMap) | ||||
| 	{ | ||||
| 		assert(map->isInTheMap(hero->visitablePos())); | ||||
| 		const auto & tile = map->getTile(hero->visitablePos()); | ||||
| 		if (tile.terType->isWater()) | ||||
| 		{ | ||||
| 			auto handler = VLC->objtypeh->getHandlerFor(Obj::BOAT, hero->getBoatType().getNum()); | ||||
| 			CGBoat * boat = dynamic_cast<CGBoat*>(handler->create()); | ||||
| 			handler->configureObject(boat, gs->getRandomGenerator()); | ||||
|  | ||||
| 			boat->ID = Obj::BOAT; | ||||
| 			boat->subID = hero->getBoatType().getNum(); | ||||
| 			boat->pos = hero->pos; | ||||
| 			boat->appearance = handler->getTemplates().front(); | ||||
| 			boat->id = ObjectInstanceID(static_cast<si32>(gs->map->objects.size())); | ||||
|  | ||||
| 			map->objects.emplace_back(boat); | ||||
| 			map->addBlockVisTiles(boat); | ||||
|  | ||||
| 			boat->hero = hero; | ||||
| 			hero->boat = boat; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for(auto obj : map->objects) //prisons | ||||
| 	{ | ||||
| 		if(obj && obj->ID == Obj::PRISON) | ||||
| @@ -2535,6 +2560,10 @@ void CGameState::buildBonusSystemTree() | ||||
| 	} | ||||
| 	// CStackInstance <-> CCreature, CStackInstance <-> CArmedInstance, CArtifactInstance <-> CArtifact | ||||
| 	// are provided on initializing / deserializing | ||||
|  | ||||
| 	// NOTE: calling deserializationFix() might be more correct option, but might lead to side effects | ||||
| 	for (auto hero : map->heroesOnMap) | ||||
| 		hero->boatDeserializationFix(); | ||||
| } | ||||
|  | ||||
| void CGameState::deserializationFix() | ||||
|   | ||||
| @@ -174,6 +174,11 @@ EAlignment CFaction::getAlignment() const | ||||
| 	return alignment; | ||||
| } | ||||
|  | ||||
| EBoatId CFaction::getBoatType() const | ||||
| { | ||||
| 	return boatType.toEnum(); | ||||
| } | ||||
|  | ||||
| TerrainId CFaction::getNativeTerrain() const | ||||
| { | ||||
| 	return nativeTerrain; | ||||
| @@ -893,16 +898,6 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source) | ||||
|  | ||||
| 	warMachinesToLoad[town] = source["warMachine"]; | ||||
|  | ||||
|  | ||||
| 	town->shipyardBoat = EBoatId::NONE; | ||||
| 	if (!source["boat"].isNull()) | ||||
| 	{ | ||||
| 		VLC->modh->identifiers.requestIdentifier("core:boat", source["boat"], [=](int32_t boatTypeID) | ||||
| 		{ | ||||
| 			town->shipyardBoat = BoatId(boatTypeID); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	town->mageLevel = static_cast<ui32>(source["mageGuild"].Float()); | ||||
|  | ||||
| 	town->namesCount = 0; | ||||
| @@ -1028,6 +1023,15 @@ CFaction * CTownHandler::loadFromJson(const std::string & scope, const JsonNode | ||||
| 	faction->creatureBg120 = source["creatureBackground"]["120px"].String(); | ||||
| 	faction->creatureBg130 = source["creatureBackground"]["130px"].String(); | ||||
|  | ||||
| 	faction->boatType = EBoatId::NONE; | ||||
| 	if (!source["boat"].isNull()) | ||||
| 	{ | ||||
| 		VLC->modh->identifiers.requestIdentifier("core:boat", source["boat"], [=](int32_t boatTypeID) | ||||
| 		{ | ||||
| 			faction->boatType = BoatId(boatTypeID); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	int alignment = vstd::find_pos(GameConstants::ALIGNMENT_NAMES, source["alignment"].String()); | ||||
| 	if (alignment == -1) | ||||
| 		faction->alignment = EAlignment::NEUTRAL; | ||||
| @@ -1158,25 +1162,6 @@ void CTownHandler::afterLoadFinalization() | ||||
| 	initializeRequirements(); | ||||
| 	initializeOverridden(); | ||||
| 	initializeWarMachines(); | ||||
|  | ||||
| 	for(auto & faction : objects) | ||||
| 	{ | ||||
| 		if (!faction->town) | ||||
| 			continue; | ||||
|  | ||||
| 		bool hasBoat = faction->town->shipyardBoat != EBoatId::NONE; | ||||
| 		bool hasShipyard = faction->town->buildings.count(BuildingID::SHIPYARD); | ||||
|  | ||||
| 		if ( hasBoat && !hasShipyard ) | ||||
| 			logMod->warn("Town %s has boat but has no shipyard!", faction->getJsonKey()); | ||||
|  | ||||
| 		if ( !hasBoat && hasShipyard ) | ||||
| 		{ | ||||
| 			logMod->warn("Town %s has shipyard but has no boat set!", faction->getJsonKey()); | ||||
| 			// Mod compatibility for 1.3 | ||||
| 			faction->town->shipyardBoat = EBoatId::CASTLE; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CTownHandler::initializeRequirements() | ||||
|   | ||||
| @@ -204,6 +204,11 @@ public: | ||||
| 	EAlignment alignment = EAlignment::NEUTRAL; | ||||
| 	bool preferUndergroundPlacement = false; | ||||
|  | ||||
| 	/// Boat that will be used by town shipyard (if any) | ||||
| 	/// and for placing heroes directly on boat (in map editor, water prisons & taverns) | ||||
| 	BoatId boatType; | ||||
|  | ||||
|  | ||||
| 	CTown * town = nullptr; //NOTE: can be null | ||||
|  | ||||
| 	std::string creatureBg120; | ||||
| @@ -226,6 +231,7 @@ public: | ||||
| 	bool hasTown() const override; | ||||
| 	TerrainId getNativeTerrain() const override; | ||||
| 	EAlignment getAlignment() const override; | ||||
| 	EBoatId getBoatType() const override; | ||||
|  | ||||
| 	void updateFrom(const JsonNode & data); | ||||
| 	void serializeJson(JsonSerializeFormat & handler); | ||||
| @@ -236,6 +242,7 @@ public: | ||||
| 		h & identifier; | ||||
| 		h & index; | ||||
| 		h & nativeTerrain; | ||||
| 		h & boatType; | ||||
| 		h & alignment; | ||||
| 		h & town; | ||||
| 		h & creatureBg120; | ||||
| @@ -282,8 +289,6 @@ public: | ||||
| 	ArtifactID warMachine; | ||||
| 	SpellID moatAbility; | ||||
|  | ||||
| 	/// boat that will be built by town shipyard, if exists | ||||
| 	BoatId shipyardBoat; | ||||
| 	// default chance for hero of specific class to appear in tavern, if field "tavern" was not set | ||||
| 	// resulting chance = sqrt(town.chance * heroClass.chance) | ||||
| 	ui32 defaultTavernChance; | ||||
| @@ -349,7 +354,6 @@ public: | ||||
| 		h & mageLevel; | ||||
| 		h & primaryRes; | ||||
| 		h & warMachine; | ||||
| 		h & shipyardBoat; | ||||
| 		h & clientInfo; | ||||
| 		h & moatAbility; | ||||
| 		h & defaultTavernChance; | ||||
|   | ||||
| @@ -1282,7 +1282,7 @@ class BattleField : public BaseForID<BattleField, si32> | ||||
| 	DLL_LINKAGE static BattleField fromString(const std::string & identifier); | ||||
| }; | ||||
|  | ||||
| enum class EBoatId | ||||
| enum class EBoatId : int32_t | ||||
| { | ||||
| 	NONE = -1, | ||||
| 	NECROPOLIS = 0, | ||||
|   | ||||
| @@ -323,7 +323,7 @@ void MetaString::jsonSerialize(JsonNode & dest) const | ||||
| 	for (const auto & entry : localStrings ) | ||||
| 	{ | ||||
| 		JsonNode value; | ||||
| 		value.Float() = static_cast<int>(entry.first) * 10000 + entry.second; | ||||
| 		value.Integer() = static_cast<int>(entry.first) * 10000 + entry.second; | ||||
| 		jsonLocalStrings.Vector().push_back(value); | ||||
| 	} | ||||
|  | ||||
| @@ -344,7 +344,7 @@ void MetaString::jsonSerialize(JsonNode & dest) const | ||||
| 	for (const auto & entry : numbers ) | ||||
| 	{ | ||||
| 		JsonNode value; | ||||
| 		value.Float() = entry; | ||||
| 		value.Integer() = entry; | ||||
| 		jsonNumbers.Vector().push_back(value); | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -452,9 +452,8 @@ void CGHeroInstance::onHeroVisit(const CGHeroInstance * h) const | ||||
| 				//Create a new boat for hero | ||||
| 				NewObject no; | ||||
| 				no.ID = Obj::BOAT; | ||||
| 				no.subID = BoatId(EBoatId::CASTLE); | ||||
| 				no.pos = CGBoat::translatePos(boatPos); | ||||
| 				 | ||||
| 				no.subID = getBoatType().getNum(); | ||||
|  | ||||
| 				cb->sendAndApply(&no); | ||||
|  | ||||
| 				boatId = cb->getTopObj(boatPos)->id; | ||||
| @@ -953,8 +952,7 @@ si32 CGHeroInstance::getManaNewTurn() const | ||||
|  | ||||
| BoatId CGHeroInstance::getBoatType() const | ||||
| { | ||||
| 	// hero can only generate boat via "Summon Boat" spell which always create same boat as in Necropolis shipyard | ||||
| 	return EBoatId::NECROPOLIS; | ||||
| 	return BoatId(VLC->townh->getById(type->heroClass->faction)->getBoatType()); | ||||
| } | ||||
|  | ||||
| void CGHeroInstance::getOutOffsets(std::vector<int3> &offsets) const | ||||
| @@ -1105,6 +1103,13 @@ int CGHeroInstance::maxSpellLevel() const | ||||
| void CGHeroInstance::deserializationFix() | ||||
| { | ||||
| 	artDeserializationFix(this); | ||||
| 	boatDeserializationFix(); | ||||
| } | ||||
|  | ||||
| void CGHeroInstance::boatDeserializationFix() | ||||
| { | ||||
| 	if (boat) | ||||
| 		attachTo(const_cast<CGBoat&>(*boat)); | ||||
| } | ||||
|  | ||||
| CBonusSystemNode * CGHeroInstance::whereShouldBeAttachedOnSiege(const bool isBattleOutsideTown) const | ||||
|   | ||||
| @@ -274,6 +274,7 @@ public: | ||||
| 	void getCastDescription(const spells::Spell * spell, const std::vector<const battle::Unit *> & attacked, MetaString & text) const override; | ||||
| 	void spendMana(ServerCallback * server, const int spellCost) const override; | ||||
|  | ||||
| 	void boatDeserializationFix(); | ||||
| 	void deserializationFix(); | ||||
|  | ||||
| 	void initObj(CRandomGenerator & rand) override; | ||||
|   | ||||
| @@ -682,7 +682,7 @@ void CGTownInstance::clearArmy() const | ||||
|  | ||||
| BoatId CGTownInstance::getBoatType() const | ||||
| { | ||||
| 	return town->shipyardBoat; | ||||
| 	return town->faction->boatType; | ||||
| } | ||||
|  | ||||
| int CGTownInstance::getMarketEfficiency() const | ||||
|   | ||||
| @@ -1294,11 +1294,6 @@ CGBoat::CGBoat() | ||||
| 	layer = EPathfindingLayer::EEPathfindingLayer::SAIL; | ||||
| } | ||||
|  | ||||
| void CGBoat::initObj(CRandomGenerator & rand) | ||||
| { | ||||
| 	hero = nullptr; | ||||
| } | ||||
|  | ||||
| int3 CGBoat::translatePos(const int3& pos, bool reverse /* = false */) | ||||
| { | ||||
| 	//pos - offset we want to place the boat at the map | ||||
|   | ||||
| @@ -359,7 +359,6 @@ public: | ||||
| 	std::array<std::string, PlayerColor::PLAYER_LIMIT_I> flagAnimations; | ||||
|  | ||||
| 	CGBoat(); | ||||
| 	void initObj(CRandomGenerator & rand) override; | ||||
| 	static int3 translatePos(const int3 &pos, bool reverse = false); | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
|   | ||||
| @@ -4428,7 +4428,7 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl | ||||
| 		//Create a new boat for hero | ||||
| 		NewObject no; | ||||
| 		no.ID = Obj::BOAT; | ||||
| 		no.subID = BoatId(EBoatId::CASTLE); | ||||
| 		no.subID = nh->getBoatType().getNum(); | ||||
| 		no.pos = hr.tile + int3(1,0,0); | ||||
| 		sendAndApply(&no); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user