mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Large rewrite of adventure map objects:
- replaced CDefObjInfo with ObjectTemplate class - ObjectTempate is a direct member of objects instead of pointer with shared ownership across CMap, handler and game objects - simplified handling of objects that can change appearance (e.g. towns) - all object queries regarding object appearance/blockmaps use h3m pos instead of relative positions - removed need of modhandler::reload - cleanup of some old code
This commit is contained in:
		| @@ -706,7 +706,7 @@ void VCAI::makeTurnInternal() | ||||
| 			boost::sort (vec, isCloser); | ||||
| 			for (auto obj : vec) | ||||
| 			{ | ||||
| 				if(!obj || !obj->defInfo || !cb->getObj(obj->id)) | ||||
| 				if(!obj || !cb->getObj(obj->id)) | ||||
| 				{ | ||||
| 					logAi->errorStream() << "Error: there is wrong object on list for hero " << hero.first->name; | ||||
| 					continue; | ||||
|   | ||||
| @@ -954,11 +954,6 @@ void endGame() | ||||
| { | ||||
| 	client->endGame(); | ||||
| 	vstd::clear_pointer(client); | ||||
|  | ||||
| 	delete CGI->dobjinfo.get(); | ||||
| 	const_cast<CGameInfo*>(CGI)->dobjinfo = new CDefObjInfoHandler; | ||||
|  | ||||
| 	const_cast<CGameInfo*>(CGI)->modh->reload(); //add info about new creatures to dobjinfo | ||||
| } | ||||
|  | ||||
| void handleQuit() | ||||
|   | ||||
| @@ -335,12 +335,12 @@ void Graphics::loadFonts() | ||||
|  | ||||
| CDefEssential * Graphics::getDef( const CGObjectInstance * obj ) | ||||
| { | ||||
| 	return advmapobjGraphics[obj->defInfo->name]; | ||||
| 	return advmapobjGraphics[obj->appearance.animationFile]; | ||||
| } | ||||
|  | ||||
| CDefEssential * Graphics::getDef( const CGDefInfo * info ) | ||||
| CDefEssential * Graphics::getDef( const ObjectTemplate & info ) | ||||
| { | ||||
| 	return advmapobjGraphics[info->name]; | ||||
| 	return advmapobjGraphics[info.animationFile]; | ||||
| } | ||||
|  | ||||
| void Graphics::loadErmuToPicture() | ||||
|   | ||||
| @@ -25,7 +25,7 @@ struct SDL_Color; | ||||
| struct InfoAboutHero; | ||||
| struct InfoAboutTown; | ||||
| class CGObjectInstance; | ||||
| class CGDefInfo; | ||||
| class ObjectTemplate; | ||||
|  | ||||
| enum EFonts | ||||
| { | ||||
| @@ -60,7 +60,7 @@ public: | ||||
|  | ||||
| 	std::map<std::string, CDefEssential *> advmapobjGraphics; | ||||
| 	CDefEssential * getDef(const CGObjectInstance * obj); | ||||
| 	CDefEssential * getDef(const CGDefInfo * info); | ||||
| 	CDefEssential * getDef(const ObjectTemplate & info); | ||||
| 	//towns | ||||
| 	std::map<int, std::string> ERMUtoPicture[GameConstants::F_NUMBER]; //maps building ID to it's picture's name for each town type | ||||
| 	//for battles | ||||
|   | ||||
| @@ -410,14 +410,8 @@ void NewStructures::applyCl( CClient *cl ) | ||||
| 	CGTownInstance *town = GS(cl)->getTown(tid); | ||||
| 	for(const auto & id : bid) | ||||
| 	{ | ||||
| 		if(id == BuildingID::CAPITOL) //fort or capitol | ||||
| 		{ | ||||
| 			town->defInfo = const_cast<CGDefInfo*>(CGI->dobjinfo->capitols.at(town->subID).get()); | ||||
| 		} | ||||
| 		if(id == BuildingID::FORT) | ||||
| 		{ | ||||
| 			town->defInfo = const_cast<CGDefInfo*>(CGI->dobjinfo->gobjs.at(Obj::TOWN).at(town->subID).get()); | ||||
| 		} | ||||
| 		town->updateAppearance(); | ||||
|  | ||||
| 		if(vstd::contains(cl->playerint,town->tempOwner)) | ||||
| 			cl->playerint[town->tempOwner]->buildChanged(town,id,1); | ||||
| 	} | ||||
| @@ -427,10 +421,8 @@ void RazeStructures::applyCl (CClient *cl) | ||||
| 	CGTownInstance *town = GS(cl)->getTown(tid); | ||||
| 	for(const auto & id : bid) | ||||
| 	{ | ||||
| 		if (id == BuildingID::CAPITOL) //fort or capitol | ||||
| 		{ | ||||
| 			town->defInfo = const_cast<CGDefInfo*>(CGI->dobjinfo->gobjs.at(Obj::TOWN).at(town->subID).get()); | ||||
| 		} | ||||
| 		town->updateAppearance(); | ||||
|  | ||||
| 		if(vstd::contains (cl->playerint,town->tempOwner)) | ||||
| 			cl->playerint[town->tempOwner]->buildChanged (town,id,2); | ||||
| 	} | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| #include "StdInc.h" | ||||
| #include "mapHandler.h" | ||||
|  | ||||
| #include "CBitmapHandler.h" | ||||
| #include "gui/SDL_Extensions.h" | ||||
| #include "CGameInfo.h" | ||||
| #include "../lib/CDefObjInfoHandler.h" | ||||
| @@ -229,26 +230,26 @@ void CMapHandler::borderAndTerrainBitmapInit() | ||||
| 	delete bord; | ||||
| } | ||||
|  | ||||
| static void processDef (const CGDefInfo* def) | ||||
| static void processDef (const ObjectTemplate & objTempl) | ||||
| { | ||||
| 	if(def->id == Obj::EVENT) | ||||
| 	if(objTempl.id == Obj::EVENT) | ||||
| 	{ | ||||
| 		graphics->advmapobjGraphics[def->name] = nullptr; | ||||
| 		graphics->advmapobjGraphics[objTempl.animationFile] = nullptr; | ||||
| 		return; | ||||
| 	} | ||||
| 	CDefEssential * ourDef = graphics->getDef(def); | ||||
| 	CDefEssential * ourDef = graphics->getDef(objTempl); | ||||
| 	if(!ourDef) //if object has already set handler (eg. heroes) it should not be overwritten | ||||
| 	{ | ||||
| 		if(def->name.size()) | ||||
| 		if(objTempl.animationFile.size()) | ||||
| 		{ | ||||
| 			graphics->advmapobjGraphics[def->name] = CDefHandler::giveDefEss(def->name); | ||||
| 			graphics->advmapobjGraphics[objTempl.animationFile] = CDefHandler::giveDefEss(objTempl.animationFile); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
|             logGlobal->warnStream() << "No def name for " << def->id << "  " << def->subid; | ||||
| 			logGlobal->warnStream() << "No def name for " << objTempl.id << "  " << objTempl.subid; | ||||
| 			return; | ||||
| 		} | ||||
| 		ourDef = graphics->getDef(def); | ||||
| 		ourDef = graphics->getDef(objTempl); | ||||
|  | ||||
| 	} | ||||
| 	//alpha transformation | ||||
| @@ -266,44 +267,40 @@ void CMapHandler::initObjectRects() | ||||
| 		const CGObjectInstance *obj = elem; | ||||
| 		if(	!obj | ||||
| 			|| (obj->ID==Obj::HERO && static_cast<const CGHeroInstance*>(obj)->inTownGarrison) //garrisoned hero | ||||
| 			|| (obj->ID==Obj::BOAT && static_cast<const CGBoat*>(obj)->hero) //boat with hero (hero graphics is used) | ||||
| 			|| !obj->defInfo ) | ||||
| 			|| (obj->ID==Obj::BOAT && static_cast<const CGBoat*>(obj)->hero)) //boat with hero (hero graphics is used) | ||||
| 		{ | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (!graphics->getDef(obj)) //try to load it | ||||
| 			processDef(obj->defInfo); | ||||
| 			processDef(obj->appearance); | ||||
| 		if (!graphics->getDef(obj)) // stil no graphics? exit | ||||
| 			continue; | ||||
|  | ||||
| 		const SDL_Surface *bitmap = graphics->getDef(obj)->ourImages[0].bitmap; | ||||
| 		for(int fx=0; fx<bitmap->w>>5; ++fx) //bitmap->w/32 | ||||
| 		for(int fx=0; fx < obj->getWidth(); ++fx) | ||||
| 		{ | ||||
| 			for(int fy=0; fy<bitmap->h>>5; ++fy) //bitmap->h/32 | ||||
| 			for(int fy=0; fy < obj->getHeight(); ++fy) | ||||
| 			{ | ||||
| 				int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z); | ||||
| 				SDL_Rect cr; | ||||
| 				cr.w = 32; | ||||
| 				cr.h = 32; | ||||
| 				cr.x = fx<<5; //fx*32 | ||||
| 				cr.y = fy<<5; //fy*32 | ||||
| 				cr.x = bitmap->w - fx * 32 - 32; | ||||
| 				cr.y = bitmap->h - fy * 32 - 32; | ||||
| 				std::pair<const CGObjectInstance*,SDL_Rect> toAdd = std::make_pair(obj,cr); | ||||
| 				 | ||||
| 				if(    (obj->pos.x + fx - bitmap->w/32+1)  >=  0  | ||||
| 					&& (obj->pos.x + fx - bitmap->w/32+1)  <  ttiles.size() - frameW  | ||||
| 					&& (obj->pos.y + fy - bitmap->h/32+1)  >=  0  | ||||
| 					&& (obj->pos.y + fy - bitmap->h/32+1)  <  ttiles[0].size() - frameH | ||||
|  | ||||
| 				if( map->isInTheMap(currTile) && // within map | ||||
| 					cr.x + cr.w > 0 &&           // image has data on this tile | ||||
| 					cr.y + cr.h > 0 && | ||||
| 					obj->coveringAt(currTile.x, currTile.y) // object is visible here | ||||
| 				  ) | ||||
| 				{ | ||||
| 					//TerrainTile2 & curt = | ||||
| 					//	ttiles | ||||
| 					//	[obj->pos.x + fx - bitmap->w/32] | ||||
| 					//[obj->pos.y + fy - bitmap->h/32] | ||||
| 					//[obj->pos.z]; | ||||
| 					ttiles[obj->pos.x + fx - bitmap->w/32+1][obj->pos.y + fy - bitmap->h/32+1][obj->pos.z].objects.push_back(toAdd); | ||||
| 					ttiles[currTile.x][currTile.y][currTile.z].objects.push_back(toAdd); | ||||
| 				} | ||||
| 			} // for(int fy=0; fy<bitmap->h/32; ++fy) | ||||
| 		} //for(int fx=0; fx<bitmap->w/32; ++fx) | ||||
| 	} // for(int f=0; f<map->objects.size(); ++f) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for(int ix=0; ix<ttiles.size()-frameW; ++ix) | ||||
| 	{ | ||||
| @@ -319,7 +316,7 @@ void CMapHandler::initObjectRects() | ||||
|  | ||||
| void CMapHandler::initHeroDef(const CGHeroInstance * h) | ||||
| { | ||||
| 	graphics->advmapobjGraphics[h->defInfo->name] = graphics->flags1[0]; | ||||
| 	graphics->advmapobjGraphics[h->appearance.animationFile] = graphics->flags1[0]; | ||||
| } | ||||
|  | ||||
| void CMapHandler::init() | ||||
| @@ -367,9 +364,6 @@ void CMapHandler::init() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|     std::for_each(map->customDefs.begin(),map->customDefs.end(),processDef); //load h3m defs | ||||
|     logGlobal->infoStream()<<"\tUnpacking and handling defs: "<<th.getDiff(); | ||||
|  | ||||
| 	prepareFOWDefs(); | ||||
| 	roadsRiverTerrainInit();	//road's and river's DefHandlers; and simple values initialization | ||||
| 	borderAndTerrainBitmapInit(); | ||||
| @@ -510,12 +504,16 @@ void CMapHandler::terrainRect( int3 top_tile, ui8 anim, const std::vector< std:: | ||||
| 			{ | ||||
| 				const CGObjectInstance *obj = object.first; | ||||
| 				if (!graphics->getDef(obj)) | ||||
| 					processDef(obj->defInfo); | ||||
| 					processDef(obj->appearance); | ||||
| 				if (!graphics->getDef(obj) && !obj->appearance.animationFile.empty()) | ||||
| 				{ | ||||
| 					logGlobal->errorStream() << "Failed to load image " << obj->appearance.animationFile; | ||||
| 				} | ||||
|  | ||||
| 				PlayerColor color = obj->tempOwner; | ||||
|  | ||||
| 				//checking if object has non-empty graphic on this tile | ||||
| 				if(obj->ID != Obj::HERO && !obj->coveringAt(top_tile.x + bx - obj->pos.x, top_tile.y + by - obj->pos.y)) | ||||
| 				if(obj->ID != Obj::HERO && !obj->coveringAt(top_tile.x + bx, top_tile.y + by)) | ||||
| 					continue; | ||||
|  | ||||
| 				static const int notBlittedInPuzzleMode[] = {124}; | ||||
| @@ -640,10 +638,7 @@ void CMapHandler::terrainRect( int3 top_tile, ui8 anim, const std::vector< std:: | ||||
| 					if(color < PlayerColor::PLAYER_LIMIT || color==PlayerColor::NEUTRAL) | ||||
| 						CSDL_Ext::setPlayerColor(bitmap, color); | ||||
|  | ||||
| 					if( obj->hasShadowAt(top_tile.x + bx - obj->pos.x, top_tile.y + by - obj->pos.y) ) | ||||
| 						CSDL_Ext::blit8bppAlphaTo24bpp(bitmap,&pp,extSurf,&sr2); | ||||
| 					else | ||||
| 						CSDL_Ext::blitSurface(bitmap,&pp,extSurf,&sr2); | ||||
| 					CSDL_Ext::blit8bppAlphaTo24bpp(bitmap,&pp,extSurf,&sr2); | ||||
| 				} | ||||
| 			} | ||||
| 			//objects blitted | ||||
| @@ -708,33 +703,41 @@ void CMapHandler::terrainRect( int3 top_tile, ui8 anim, const std::vector< std:: | ||||
| 				 | ||||
| 				//FoW blitted | ||||
|  | ||||
| 				// TODO: these should be activable by the console | ||||
| #ifdef MARK_BLOCKED_POSITIONS | ||||
| 				if(map->getTile(int3(pos.x, pos.y, top_tile.z)).blocked) //temporary hiding blocked positions | ||||
| 				SDL_Rect tileRect = genRect(sr.h, sr.w, 0, 0); | ||||
|  | ||||
| 				if (settings["session"]["showBlock"].Bool()) | ||||
| 				{ | ||||
| 					SDL_Rect sr; | ||||
| 					if(map->getTile(int3(pos.x, pos.y, top_tile.z)).blocked) //temporary hiding blocked positions | ||||
| 					{ | ||||
| 						static SDL_Surface * block = nullptr; | ||||
| 						if (!block) | ||||
| 							block = BitmapHandler::loadBitmap("blocked"); | ||||
|  | ||||
| 					sr.x=srx; | ||||
| 					sr.y=sry; | ||||
| 					sr.h=sr.w=32; | ||||
| 						SDL_Rect sr; | ||||
|  | ||||
| 					memset(rSurf->pixels, 128, rSurf->pitch * rSurf->h); | ||||
| 					CSDL_Ext::blitSurface(rSurf,&genRect(sr.h, sr.w, 0, 0),extSurf,&sr); | ||||
| 						sr.x=srx; | ||||
| 						sr.y=sry; | ||||
| 						sr.h=sr.w=32; | ||||
|  | ||||
| 						CSDL_Ext::blitSurface(block, &tileRect, extSurf, &sr); | ||||
| 					} | ||||
| 				} | ||||
| #endif | ||||
| #ifdef MARK_VISITABLE_POSITIONS | ||||
| 				if(map->getTile(int3(pos.x, pos.y, top_tile.z)).visitable) //temporary hiding visitable positions | ||||
| 				if (settings["session"]["showVisit"].Bool()) | ||||
| 				{ | ||||
| 					SDL_Rect sr; | ||||
| 					if(map->getTile(int3(pos.x, pos.y, top_tile.z)).visitable) //temporary hiding visitable positions | ||||
| 					{ | ||||
| 						static SDL_Surface * visit = nullptr; | ||||
| 						if (!visit) | ||||
| 							visit = BitmapHandler::loadBitmap("visitable"); | ||||
|  | ||||
| 					sr.x=srx; | ||||
| 					sr.y=sry; | ||||
| 					sr.h=sr.w=32; | ||||
| 						SDL_Rect sr; | ||||
|  | ||||
| 					memset(rSurf->pixels, 128, rSurf->pitch * rSurf->h); | ||||
| 					CSDL_Ext::blitSurface(rSurf,&genRect(sr.h, sr.w, 0, 0),extSurf,&sr); | ||||
| 						sr.x=srx; | ||||
| 						sr.y=sry; | ||||
| 						sr.h=sr.w=32; | ||||
| 						CSDL_Ext::blitSurface(visit, &tileRect, extSurf, &sr); | ||||
| 					} | ||||
| 				} | ||||
| #endif | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @@ -849,7 +852,7 @@ std::pair<SDL_Surface *, bool> CMapHandler::getVisBitmap( const int3 & pos, cons | ||||
| bool CMapHandler::printObject(const CGObjectInstance *obj) | ||||
| { | ||||
|     if (!graphics->getDef(obj)) | ||||
| 		processDef(obj->defInfo); | ||||
| 		processDef(obj->appearance); | ||||
|  | ||||
| 	const SDL_Surface *bitmap = graphics->getDef(obj)->ourImages[0].bitmap;  | ||||
| 	const int tilesW = bitmap->w/32; | ||||
|   | ||||
| @@ -657,6 +657,22 @@ void CArtHandler::afterLoadFinalization() | ||||
| 			bonus->sid = art->id; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	//Note: "10" is used here because H3 text files don't define any template for art with ID 0 | ||||
| 	ObjectTemplate base = VLC->dobjinfo->pickCandidates(Obj::ARTIFACT, 10).front(); | ||||
| 	for (CArtifact * art : artifacts) | ||||
| 	{ | ||||
| 		if (!art->advMapDef.empty()) | ||||
| 		{ | ||||
| 			base.animationFile = art->advMapDef; | ||||
| 			base.subid = art->id; | ||||
|  | ||||
| 			// replace existing (if any) and add new template. | ||||
| 			// Necessary for objects added via mods that don't have any templates in H3 | ||||
| 			VLC->dobjinfo->eraseAll(Obj::ARTIFACT, art->id); | ||||
| 			VLC->dobjinfo->registerTemplate(base); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| CArtifactInstance::CArtifactInstance() | ||||
| @@ -695,13 +711,13 @@ void CArtifactInstance::init() | ||||
| 	id = static_cast<ArtifactInstanceID>(ArtifactID::NONE); //to be randomized | ||||
| 	setNodeType(ARTIFACT_INSTANCE); | ||||
| } | ||||
|  | ||||
| ArtifactPosition CArtifactInstance::firstAvailableSlot(const CArtifactSet *h) const | ||||
| { | ||||
| 	for(auto slot : artType->possibleSlots.at(h->bearerType())) | ||||
| 	{ | ||||
| 		if(canBePutAt(h, slot)) //if(artType->fitsAt(h->artifWorn, slot)) | ||||
| 		{ | ||||
|  | ||||
| ArtifactPosition CArtifactInstance::firstAvailableSlot(const CArtifactSet *h) const | ||||
| { | ||||
| 	for(auto slot : artType->possibleSlots.at(h->bearerType())) | ||||
| 	{ | ||||
| 		if(canBePutAt(h, slot)) //if(artType->fitsAt(h->artifWorn, slot)) | ||||
| 		{ | ||||
| 			//we've found a free suitable slot. | ||||
| 			return slot; | ||||
| 		} | ||||
| @@ -1101,13 +1117,13 @@ bool CArtifactSet::hasArt(ui32 aid, bool onlyWorn /*= false*/) const | ||||
| 	return getArtPos(aid, onlyWorn) != ArtifactPosition::PRE_FIRST; | ||||
| } | ||||
|  | ||||
| const ArtSlotInfo * CArtifactSet::getSlot(ArtifactPosition pos) const | ||||
| { | ||||
| 	if(vstd::contains(artifactsWorn, pos)) | ||||
| 		return &artifactsWorn.at(pos); | ||||
| 	if(pos >= ArtifactPosition::AFTER_LAST ) | ||||
| 	{ | ||||
| 		int backpackPos = (int)pos - GameConstants::BACKPACK_START; | ||||
| const ArtSlotInfo * CArtifactSet::getSlot(ArtifactPosition pos) const | ||||
| { | ||||
| 	if(vstd::contains(artifactsWorn, pos)) | ||||
| 		return &artifactsWorn.at(pos); | ||||
| 	if(pos >= ArtifactPosition::AFTER_LAST ) | ||||
| 	{ | ||||
| 		int backpackPos = (int)pos - GameConstants::BACKPACK_START; | ||||
| 		if(backpackPos < 0 || backpackPos >= artifactsInBackpack.size()) | ||||
| 			return nullptr; | ||||
| 		else | ||||
|   | ||||
| @@ -1112,6 +1112,24 @@ void CCreatureHandler::buildBonusTreeForTiers() | ||||
| 		b.attachTo(&allCreatures); | ||||
| } | ||||
|  | ||||
| void CCreatureHandler::afterLoadFinalization() | ||||
| { | ||||
| 	ObjectTemplate base = VLC->dobjinfo->pickCandidates(Obj::MONSTER, 0).front(); | ||||
| 	for (CCreature * crea : creatures) | ||||
| 	{ | ||||
| 		if (!crea->advMapDef.empty()) | ||||
| 		{ | ||||
| 			base.animationFile = crea->advMapDef; | ||||
| 			base.subid = crea->idNumber; | ||||
|  | ||||
| 			// replace existing (if any) and add new template. | ||||
| 			// Necessary for objects added via mods that don't have any templates in H3 | ||||
| 			VLC->dobjinfo->eraseAll(Obj::MONSTER, crea->idNumber); | ||||
| 			VLC->dobjinfo->registerTemplate(base); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CCreatureHandler::deserializationFix() | ||||
| { | ||||
| 	buildBonusTreeForTiers(); | ||||
|   | ||||
| @@ -198,6 +198,8 @@ public: | ||||
| 	/// generates tier-specific bonus tree entries | ||||
| 	void buildBonusTreeForTiers(); | ||||
|  | ||||
| 	void afterLoadFinalization(); | ||||
|  | ||||
| 	std::vector<JsonNode> loadLegacyData(size_t dataSize) override; | ||||
|  | ||||
| 	void loadObject(std::string scope, std::string name, const JsonNode & data) override; | ||||
|   | ||||
| @@ -2,9 +2,14 @@ | ||||
| #include "CDefObjInfoHandler.h" | ||||
|  | ||||
| #include "filesystem/Filesystem.h" | ||||
| #include "../client/CGameInfo.h" | ||||
| #include "filesystem/CBinaryReader.h" | ||||
| //#include "../client/CGameInfo.h" | ||||
| #include "../lib/VCMI_Lib.h" | ||||
| #include "GameConstants.h" | ||||
| #include "StringConstants.h" | ||||
| #include "CGeneralTextHandler.h" | ||||
| #include "CModHandler.h" | ||||
| #include "JsonNode.h" | ||||
|  | ||||
| /* | ||||
|  * CDefObjInfoHandler.cpp, part of VCMI engine | ||||
| @@ -16,164 +21,387 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| bool CGDefInfo::isVisitable() const | ||||
| static bool isVisitableFromTop(int identifier, int type) | ||||
| { | ||||
| 	for (auto & elem : visitMap) | ||||
| 	{ | ||||
| 		if (elem) | ||||
| 			return true; | ||||
| 	} | ||||
| 	if(type == 2 || type == 3 || type == 4 || type == 5) //creature, hero, artifact, resource | ||||
| 		return true; | ||||
|  | ||||
| 	static const Obj visitableFromTop[] = | ||||
| 		{Obj::FLOTSAM, | ||||
| 		Obj::SEA_CHEST, | ||||
| 		Obj::SHIPWRECK_SURVIVOR, | ||||
| 		Obj::BUOY, | ||||
| 		Obj::OCEAN_BOTTLE, | ||||
| 		Obj::BOAT, | ||||
| 		Obj::WHIRLPOOL, | ||||
| 		Obj::GARRISON, | ||||
| 		Obj::SCHOLAR, | ||||
| 		Obj::CAMPFIRE, | ||||
| 		Obj::BORDERGUARD, | ||||
| 		Obj::BORDER_GATE, | ||||
| 		Obj::QUEST_GUARD, | ||||
| 		Obj::CORPSE | ||||
| 	}; | ||||
| 	if (vstd::find_pos(visitableFromTop, identifier) != -1) | ||||
| 		return true; | ||||
| 	return false; | ||||
| } | ||||
| CGDefInfo::CGDefInfo() | ||||
| { | ||||
| 	visitDir = (8|16|32|64|128); //4,5,6,7,8 - any not-from-up direction | ||||
|  | ||||
| 	width = height = -1; | ||||
| ObjectTemplate::ObjectTemplate(): | ||||
| 	visitDir(8|16|32|64|128), // all but top | ||||
| 	id(Obj::NO_OBJ), | ||||
| 	subid(0), | ||||
| 	printPriority(0) | ||||
| { | ||||
| } | ||||
|  | ||||
| void CGDefInfo::fetchInfoFromMSK() | ||||
| void ObjectTemplate::readTxt(CLegacyConfigParser & parser) | ||||
| { | ||||
| 	ResourceID resID("SPRITES/" + name, EResType::MASK); | ||||
| 	std::string data = parser.readString(); | ||||
| 	std::vector<std::string> strings; | ||||
| 	boost::split(strings, data, boost::is_any_of(" ")); | ||||
| 	assert(strings.size() == 9); | ||||
|  | ||||
| 	animationFile = strings[0]; | ||||
|  | ||||
| 	std::string & blockStr = strings[1]; //block map, 0 = blocked, 1 = unblocked | ||||
| 	std::string & visitStr = strings[2]; //visit map, 1 = visitable, 0 = not visitable | ||||
|  | ||||
| 	assert(blockStr.size() == 6*8); | ||||
| 	assert(visitStr.size() == 6*8); | ||||
|  | ||||
| 	setSize(8, 6); | ||||
| 	for (size_t i=0; i<6; i++) // 6 rows | ||||
| 	{ | ||||
| 		for (size_t j=0; j<8; j++) // 8 columns | ||||
| 		{ | ||||
| 			auto & tile = usedTiles[i][j]; | ||||
| 			tile |= VISIBLE; // assume that all tiles are visible | ||||
| 			if (blockStr[i*8 + j] == '0') | ||||
| 				tile |= BLOCKED; | ||||
|  | ||||
| 			if (visitStr[i*8 + j] == '1') | ||||
| 				tile |= VISITABLE; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// strings[3] most likely - terrains on which this object can be placed in editor. | ||||
| 	// e.g. Whirpool can be placed manually only on water while mines can be placed everywhere despite terrain-specific gfx | ||||
| 	// so these two fields can be interpreted as "strong affinity" and "weak affinity" towards terrains | ||||
| 	std::string & terrStr = strings[4]; // allowed terrains, 1 = object can be placed on this terrain | ||||
|  | ||||
| 	assert(terrStr.size() == 9); // all terrains but rock | ||||
| 	for (size_t i=0; i<9; i++) | ||||
| 	{ | ||||
| 		if (terrStr[8-i] == '1') | ||||
| 			allowedTerrains.insert(ETerrainType(i)); | ||||
| 	} | ||||
|  | ||||
| 	id    = Obj(boost::lexical_cast<int>(strings[5])); | ||||
| 	subid = boost::lexical_cast<int>(strings[6]); | ||||
| 	int type  = boost::lexical_cast<int>(strings[7]); | ||||
| 	printPriority = boost::lexical_cast<int>(strings[8]) * 100; // to have some space in future | ||||
|  | ||||
| 	if (isVisitableFromTop(id, type)) | ||||
| 		visitDir = 0xff; | ||||
| 	else | ||||
| 		visitDir = (8|16|32|64|128); | ||||
|  | ||||
| 	readMsk(); | ||||
| } | ||||
|  | ||||
| void ObjectTemplate::readMsk() | ||||
| { | ||||
| 	ResourceID resID("SPRITES/" + animationFile, EResType::MASK); | ||||
|  | ||||
| 	if (CResourceHandler::get()->existsResource(resID)) | ||||
| 	{ | ||||
| 		auto msk = CResourceHandler::get()->load(resID)->readAll(); | ||||
| 		setSize(msk.first.get()[0], msk.first.get()[1]); | ||||
| 	} | ||||
| 	else //maximum possible size of H3 object //TODO: remove hardcode and move this data into modding system | ||||
| 	{ | ||||
| 		setSize(8, 6); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 		width = msk.first.get()[0]; | ||||
| 		height = msk.first.get()[1]; | ||||
| 		for(int i=0; i<6; ++i) | ||||
| void ObjectTemplate::readMap(CBinaryReader & reader) | ||||
| { | ||||
| 	animationFile = reader.readString(); | ||||
|  | ||||
| 	setSize(8, 6); | ||||
| 	ui8 blockMask[6]; | ||||
| 	ui8 visitMask[6]; | ||||
| 	for(auto & byte : blockMask) | ||||
| 		byte = reader.readUInt8(); | ||||
| 	for(auto & byte : visitMask) | ||||
| 		byte = reader.readUInt8(); | ||||
|  | ||||
| 	for (size_t i=0; i<6; i++) // 6 rows | ||||
| 	{ | ||||
| 		for (size_t j=0; j<8; j++) // 8 columns | ||||
| 		{ | ||||
| 			coverageMap[i] = msk.first.get()[i+2]; | ||||
| 			shadowCoverage[i] = msk.first.get()[i+8]; | ||||
| 			auto & tile = usedTiles[5 - i][7 - j]; | ||||
| 			tile |= VISIBLE; // assume that all tiles are visible | ||||
| 			if (((blockMask[i] >> j) & 1 ) == 0) | ||||
| 				tile |= BLOCKED; | ||||
|  | ||||
| 			if (((visitMask[i] >> j) & 1 ) != 0) | ||||
| 				tile |= VISITABLE; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
|  | ||||
| 	reader.readUInt16(); | ||||
| 	ui16 terrMask = reader.readUInt16(); | ||||
| 	for (size_t i=0; i<9; i++) | ||||
| 	{ | ||||
| 		//maximum possible size of H3 object | ||||
| 		//TODO: remove hardcode and move this data into modding system | ||||
| 		width = 8; | ||||
| 		height = 6; | ||||
| 		if (((terrMask >> i) & 1 ) != 0) | ||||
| 			allowedTerrains.insert(ETerrainType(i)); | ||||
| 	} | ||||
|  | ||||
| 	id = Obj(reader.readUInt32()); | ||||
| 	subid = reader.readUInt32(); | ||||
| 	int type = reader.readUInt8(); | ||||
| 	printPriority = reader.readUInt8() * 100; // to have some space in future | ||||
|  | ||||
| 	if (isVisitableFromTop(id, type)) | ||||
| 		visitDir = 0xff; | ||||
| 	else | ||||
| 		visitDir = (8|16|32|64|128); | ||||
|  | ||||
| 	reader.skip(16); | ||||
| 	readMsk(); | ||||
|  | ||||
| 	if (id == Obj::EVENT) | ||||
| 	{ | ||||
| 		setSize(1,1); | ||||
| 		usedTiles[0][0] = VISITABLE; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void ObjectTemplate::readJson(const JsonNode &node) | ||||
| { | ||||
| 	id = Obj(node["basebase"].Float()); // temporary, should be replaced | ||||
| 	subid = node["base"].Float(); | ||||
| 	animationFile = node["animation"].String(); | ||||
|  | ||||
| 	const JsonVector & visitDirs = node["visitableFrom"].Vector(); | ||||
| 	if (!visitDirs.empty()) | ||||
| 	{ | ||||
| 		if (visitDirs[0].String()[0] == '+') visitDir |= 1; | ||||
| 		if (visitDirs[0].String()[1] == '+') visitDir |= 2; | ||||
| 		if (visitDirs[0].String()[2] == '+') visitDir |= 4; | ||||
| 		if (visitDirs[1].String()[2] == '+') visitDir |= 8; | ||||
| 		if (visitDirs[2].String()[2] == '+') visitDir |= 16; | ||||
| 		if (visitDirs[2].String()[1] == '+') visitDir |= 32; | ||||
| 		if (visitDirs[2].String()[0] == '+') visitDir |= 64; | ||||
| 		if (visitDirs[1].String()[0] == '+') visitDir |= 128; | ||||
| 	} | ||||
| 	else | ||||
| 		visitDir = 0xff; | ||||
|  | ||||
| 	for (auto & entry : node["allowedTerrains"].Vector()) | ||||
| 		allowedTerrains.insert(ETerrainType(vstd::find_pos(GameConstants::TERRAIN_NAMES, entry.String()))); | ||||
|  | ||||
| 	auto charToTile = [&](const char & ch) -> ui8 | ||||
| 	{ | ||||
| 		switch (ch) | ||||
| 		{ | ||||
| 			case '0' : return 0; | ||||
| 			case 'V' : return VISIBLE; | ||||
| 			case 'B' : return VISIBLE | BLOCKED; | ||||
| 			case 'H' : return BLOCKED; | ||||
| 			case 'A' : return VISIBLE | BLOCKED | VISITABLE; | ||||
| 			case 'T' : return VISIBLE | VISITABLE; | ||||
| 			default: | ||||
| 				logGlobal->errorStream() << "Unrecognized char " << ch << " in template mask"; | ||||
| 				return 0; | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	size_t maxHeight = 0, maxWidth = 0; | ||||
| 	size_t width  = node["mask"].Vector()[0].String().size(); | ||||
| 	size_t height = node["mask"].Vector().size(); | ||||
| 	setSize(width, height); | ||||
|  | ||||
| 	for (size_t i=0; i<height; i++) | ||||
| 	{ | ||||
| 		const std::string & line = node["mask"].Vector()[i].String(); | ||||
| 		assert(line.size() == width); | ||||
| 		for (size_t j=0; j < width; j++) | ||||
| 		{ | ||||
| 			ui8 tile = charToTile(line[j]); | ||||
| 			if (tile != 0) | ||||
| 			{ | ||||
| 				vstd::amax(maxHeight, i); | ||||
| 				vstd::amax(maxWidth,  j); | ||||
| 				usedTiles[i][j] = tile; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	setSize(maxWidth, maxHeight); | ||||
|  | ||||
| 	printPriority = node["zIndex"].Float(); | ||||
| } | ||||
|  | ||||
| ui32 ObjectTemplate::getWidth() const | ||||
| { | ||||
| 	return usedTiles.empty() ? 0 : usedTiles.front().size(); | ||||
| } | ||||
|  | ||||
| ui32 ObjectTemplate::getHeight() const | ||||
| { | ||||
| 	return usedTiles.size(); | ||||
| } | ||||
|  | ||||
| void ObjectTemplate::setSize(ui32 width, ui32 height) | ||||
| { | ||||
| 	usedTiles.resize(height); | ||||
| 	for (auto & line : usedTiles) | ||||
| 		line.resize(width); | ||||
| } | ||||
|  | ||||
| bool ObjectTemplate::isVisitable() const | ||||
| { | ||||
| 	for (auto & line : usedTiles) | ||||
| 		for (auto & tile : line) | ||||
| 			if (tile & VISITABLE) | ||||
| 				return true; | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool ObjectTemplate::isWithin(si32 X, si32 Y) const | ||||
| { | ||||
| 	if (X < 0 || Y < 0) | ||||
| 		return false; | ||||
| 	if (X >= getWidth() || Y >= getHeight()) | ||||
| 		return false; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool ObjectTemplate::isVisitableAt(si32 X, si32 Y) const | ||||
| { | ||||
| 	if (isWithin(X, Y)) | ||||
| 		return usedTiles[Y][X] & VISITABLE; | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool ObjectTemplate::isVisibleAt(si32 X, si32 Y) const | ||||
| { | ||||
| 	if (isWithin(X, Y)) | ||||
| 		return usedTiles[Y][X] & VISIBLE; | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool ObjectTemplate::isBlockedAt(si32 X, si32 Y) const | ||||
| { | ||||
| 	if (isWithin(X, Y)) | ||||
| 		return usedTiles[Y][X] & BLOCKED; | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool ObjectTemplate::isVisitableFrom(si8 X, si8 Y) const | ||||
| { | ||||
| 	// visitDir uses format | ||||
| 	// 1 2 3 | ||||
| 	// 8   4 | ||||
| 	// 7 6 5 | ||||
| 	int dirMap[3][3] = | ||||
| 	{ | ||||
| 		{ visitDir &   1, visitDir &   2, visitDir &   4 }, | ||||
| 		{ visitDir & 128,        1      , visitDir &   8 }, | ||||
| 		{ visitDir &  64, visitDir &  32, visitDir &  16 } | ||||
| 	}; | ||||
| 	// map input values to range 0..2 | ||||
| 	int dx = X < 0 ? 0 : X == 0 ? 1 : 2; | ||||
| 	int dy = Y < 0 ? 0 : Y == 0 ? 1 : 2; | ||||
|  | ||||
| 	return dirMap[dy][dx] != 0; | ||||
| } | ||||
|  | ||||
| bool ObjectTemplate::canBePlacedAt(ETerrainType terrain) const | ||||
| { | ||||
| 	return allowedTerrains.count(terrain) != 0; | ||||
| } | ||||
|  | ||||
| void CDefObjInfoHandler::readTextFile(std::string path) | ||||
| { | ||||
| 	CLegacyConfigParser parser(path); | ||||
| 	size_t totalNumber = parser.readNumber(); // first line contains number of objects to read and nothing else | ||||
| 	parser.endLine(); | ||||
|  | ||||
| 	for (size_t i=0; i<totalNumber; i++) | ||||
| 	{ | ||||
| 		ObjectTemplate templ; | ||||
| 		templ.readTxt(parser); | ||||
| 		parser.endLine(); | ||||
| 		objects.push_back(templ); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| CDefObjInfoHandler::CDefObjInfoHandler() | ||||
| { | ||||
| 	VLC->dobjinfo = this; | ||||
| 	readTextFile("Data/Objects.txt"); | ||||
| 	readTextFile("Data/Heroes.txt"); | ||||
| /* | ||||
| 	const JsonNode node = JsonUtils::assembleFromFiles("config/objectTemplates.json"); | ||||
| 	std::vector<ObjectTemplate> newTemplates; | ||||
| 	newTemplates.reserve(node.Struct().size()); | ||||
|  | ||||
| 	auto textFile = CResourceHandler::get()->load(ResourceID("DATA/OBJECTS.TXT"))->readAll(); | ||||
| 	std::istringstream inp(std::string((char*)textFile.first.get(), textFile.second)); | ||||
| 	int objNumber; | ||||
| 	inp>>objNumber; | ||||
| 	std::string mapStr; | ||||
| 	for(int hh=0; hh<objNumber; ++hh) | ||||
| 	// load all new templates | ||||
| 	for (auto & entry : node.Struct()) | ||||
| 	{ | ||||
| 		auto  nobj = new CGDefInfo(); | ||||
| 		std::string dump; | ||||
| 		inp>>nobj->name; | ||||
|  | ||||
| 		std::transform(nobj->name.begin(), nobj->name.end(), nobj->name.begin(), (int(*)(int))toupper); | ||||
|  | ||||
| 		for(int o=0; o<6; ++o) | ||||
| 		{ | ||||
| 			nobj->blockMap[o] = 0xff; | ||||
| 			nobj->visitMap[o] = 0x00; | ||||
| 			nobj->coverageMap[o] = 0x00; | ||||
| 			nobj->shadowCoverage[o] = 0x00; | ||||
| 		} | ||||
| 		inp>>mapStr; | ||||
| 		std::reverse(mapStr.begin(), mapStr.end()); | ||||
| 		for(int v=0; v<mapStr.size(); ++v) | ||||
| 		{ | ||||
| 			if(mapStr[v]=='0') | ||||
| 			{ | ||||
| 				nobj->blockMap[v/8] &= 255 - (128 >> (v%8)); | ||||
| 			} | ||||
| 		} | ||||
| 		inp>>mapStr; | ||||
| 		std::reverse(mapStr.begin(), mapStr.end()); | ||||
| 		for(int v=0; v<mapStr.size(); ++v) | ||||
| 		{ | ||||
| 			if(mapStr[v]=='1') | ||||
| 			{ | ||||
| 				nobj->visitMap[v/8] |= (128 >> (v%8)); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		for(int yy=0; yy<2; ++yy) //first - on which types of terrain object can be placed; | ||||
| 			inp>>dump; //second -in which terrains' menus object in the editor will be available (?) | ||||
| 		si32 id; inp >> id; nobj->id = Obj(id); | ||||
| 		inp>>nobj->subid; | ||||
| 		inp>>nobj->type; | ||||
|  | ||||
| 		nobj->visitDir = (8|16|32|64|128); //disabled visiting from the top | ||||
|  | ||||
| 		if(nobj->type == 2 || nobj->type == 3 || nobj->type == 4 || nobj->type == 5) //creature, hero, artifact, resource | ||||
| 		{ | ||||
| 			nobj->visitDir = 0xff; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			static const Obj visitableFromTop[] = | ||||
| 				{Obj::FLOTSAM, | ||||
| 				Obj::SEA_CHEST, | ||||
| 				Obj::SHIPWRECK_SURVIVOR, | ||||
| 				Obj::BUOY, | ||||
| 				Obj::OCEAN_BOTTLE, | ||||
| 				Obj::BOAT, | ||||
| 				Obj::WHIRLPOOL, | ||||
| 				Obj::GARRISON, | ||||
| 				Obj::SCHOLAR, | ||||
| 				Obj::CAMPFIRE, | ||||
| 				Obj::BORDERGUARD, | ||||
| 				Obj::BORDER_GATE, | ||||
| 				Obj::QUEST_GUARD, | ||||
| 				Obj::CORPSE}; | ||||
|  | ||||
| 			for(auto & elem : visitableFromTop) | ||||
| 			{ | ||||
| 				if(elem == nobj->id) | ||||
| 				{ | ||||
| 					nobj->visitDir = 0xff; | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		inp >> nobj->printPriority; | ||||
|  | ||||
| 		//coverageMap calculating | ||||
| 		nobj->fetchInfoFromMSK(); | ||||
|  | ||||
| 		auto dest = nobj->id.toDefObjInfo(); | ||||
| 		if (dest.find(nobj->subid) != dest.end() && dest[nobj->subid] != nullptr) | ||||
| 		{ | ||||
| 			// there is just too many of these. Note that this data is almost unused | ||||
| 			// exceptions are: town(village-capitol) and creation of new objects (holes, creatures, heroes, etc) | ||||
| 			//logGlobal->warnStream() << "Warning: overwriting def info for " << dest[nobj->subid]->name << " with " << nobj->name; | ||||
| 			dest[nobj->subid].dellNull(); // do not leak | ||||
| 		} | ||||
|  | ||||
| 		nobj->id.toDefObjInfo()[nobj->subid] = nobj; | ||||
|  | ||||
| 		ObjectTemplate templ; | ||||
| 		templ.readJson(entry.second); | ||||
| 		newTemplates.push_back(templ); | ||||
| 	} | ||||
|  | ||||
| 	for (int i = 0; i < 8 ; i++) | ||||
| 	{ | ||||
| 	// erase old ones to avoid conflicts | ||||
| 	for (auto & entry : newTemplates) | ||||
| 		eraseAll(entry.id, entry.subid); | ||||
|  | ||||
| 		static const char *holeDefs[] = {"AVLHOLD0.DEF", "AVLHLDS0.DEF", "AVLHOLG0.DEF", "AVLHLSN0.DEF", | ||||
| 			"AVLHOLS0.DEF", "AVLHOLR0.DEF", "AVLHOLX0.DEF", "AVLHOLL0.DEF"}; | ||||
|  | ||||
| 		if(i) | ||||
| 		{ | ||||
| 			gobjs[Obj::HOLE][i] = new CGDefInfo(*gobjs[Obj::HOLE][0]); | ||||
| 		} | ||||
| 		gobjs[Obj::HOLE][i]->name = holeDefs[i]; | ||||
| 	} | ||||
| 	// merge new templates into storage | ||||
| 	objects.insert(objects.end(), newTemplates.begin(), newTemplates.end()); | ||||
| */ | ||||
| } | ||||
|  | ||||
| CDefObjInfoHandler::~CDefObjInfoHandler() | ||||
| void CDefObjInfoHandler::eraseAll(Obj type, si32 subtype) | ||||
| { | ||||
| 	for(auto & elem : gobjs) | ||||
| 		for(auto j=elem.second.begin(); j!=elem.second.end(); j++) | ||||
| 			j->second.dellNull(); | ||||
| 	auto it = std::remove_if(objects.begin(), objects.end(), [&](const ObjectTemplate & obj) | ||||
| 	{ | ||||
| 		return obj.id == type && obj.subid == subtype; | ||||
| 	}); | ||||
| 	objects.erase(it, objects.end()); | ||||
| } | ||||
|  | ||||
| void CDefObjInfoHandler::registerTemplate(ObjectTemplate obj) | ||||
| { | ||||
| 	objects.push_back(obj); | ||||
| } | ||||
|  | ||||
| std::vector<ObjectTemplate> CDefObjInfoHandler::pickCandidates(Obj type, si32 subtype) const | ||||
| { | ||||
| 	std::vector<ObjectTemplate> ret; | ||||
|  | ||||
| 	std::copy_if(objects.begin(), objects.end(), std::back_inserter(ret), [&](const ObjectTemplate & obj) | ||||
| 	{ | ||||
| 		return obj.id == type && obj.subid == subtype; | ||||
| 	}); | ||||
| 	if (ret.empty()) | ||||
| 		logGlobal->errorStream() << "Failed to find template for " << type << ":" << subtype; | ||||
| 	assert(!ret.empty()); // Can't create object of this type/subtype | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| std::vector<ObjectTemplate> CDefObjInfoHandler::pickCandidates(Obj type, si32 subtype, ETerrainType terrain) const | ||||
| { | ||||
| 	std::vector<ObjectTemplate> ret = pickCandidates(type, subtype); | ||||
| 	std::vector<ObjectTemplate> filtered; | ||||
|  | ||||
| 	std::copy_if(ret.begin(), ret.end(), std::back_inserter(filtered), [&](const ObjectTemplate & obj) | ||||
| 	{ | ||||
| 		return obj.canBePlacedAt(terrain); | ||||
| 	}); | ||||
| 	// it is possible that there are no templates usable on specific terrain. In this case - return list before filtering | ||||
| 	return filtered.empty() ? ret : filtered; | ||||
| } | ||||
|   | ||||
| @@ -13,52 +13,96 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| class CDefEssential; | ||||
| class DLL_LINKAGE CGDefInfo | ||||
| class CBinaryReader; | ||||
| class CLegacyConfigParser; | ||||
| class JsonNode; | ||||
|  | ||||
| class DLL_LINKAGE ObjectTemplate | ||||
| { | ||||
| 	enum EBlockMapBits | ||||
| 	{ | ||||
| 		VISIBLE = 1, | ||||
| 		VISITABLE = 2, | ||||
| 		BLOCKED = 4 | ||||
| 	}; | ||||
|  | ||||
| 	/// tiles that are covered by this object, uses EBlockMapBits enum as flags | ||||
| 	std::vector<std::vector<ui8>> usedTiles; | ||||
| 	/// directions from which object can be entered, format same as for moveDir in CGHeroInstance(but 0 - 7) | ||||
| 	ui8 visitDir; | ||||
| 	/// list of terrains on which this object can be placed | ||||
| 	std::set<ETerrainType> allowedTerrains; | ||||
|  | ||||
| public: | ||||
| 	std::string name; | ||||
|  | ||||
| 	ui8 visitMap[6]; | ||||
| 	ui8 blockMap[6]; | ||||
| 	ui8 coverageMap[6], shadowCoverage[6]; //to determine which tiles are covered by picture of this object | ||||
| 	ui8 visitDir; //directions from which object can be entered, format same as for moveDir in CGHeroInstance(but 0 - 7) | ||||
| 	/// H3 ID/subID of this object | ||||
| 	Obj id; | ||||
| 	si32 subid; //of object described by this defInfo | ||||
| 	si32 terrainAllowed, //on which terrain it is possible to place object | ||||
| 		 terrainMenu; //in which menus in map editor object will be showed | ||||
| 	si32 width, height; //tiles | ||||
| 	si32 type; //(0- ground, 1- towns, 2-creatures, 3- heroes, 4-artifacts, 5- resources)    | ||||
| 	si32 subid; | ||||
| 	/// print priority, objects with higher priority will be print first, below everything else | ||||
| 	si32 printPriority; | ||||
| 	/// animation file that should be used to display object | ||||
| 	std::string animationFile; | ||||
|  | ||||
| 	ui32 getWidth() const; | ||||
| 	ui32 getHeight() const; | ||||
| 	void setSize(ui32 width, ui32 height); | ||||
|  | ||||
| 	bool isVisitable() const; | ||||
| 	bool operator<(const CGDefInfo& por) const | ||||
| 	{ | ||||
| 		if(id!=por.id) | ||||
| 			return id<por.id; | ||||
| 		else | ||||
| 			return subid<por.subid; | ||||
| 	} | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
| 		h & name & visitMap & blockMap & visitDir & id & subid &terrainAllowed | ||||
| 			& terrainMenu & width & height & type & printPriority & coverageMap & shadowCoverage; | ||||
| 	} | ||||
| 	CGDefInfo(); | ||||
| void fetchInfoFromMSK(); | ||||
| }; | ||||
| class DLL_LINKAGE CDefObjInfoHandler | ||||
| { | ||||
| public: | ||||
| 	std::map<int, std::map<int, ConstTransitivePtr<CGDefInfo> > > gobjs; | ||||
|  | ||||
| 	std::map<TFaction, ConstTransitivePtr<CGDefInfo> > capitols; | ||||
| 	std::map<TFaction, ConstTransitivePtr<CGDefInfo> > villages; | ||||
|  | ||||
| 	CDefObjInfoHandler(); | ||||
| 	~CDefObjInfoHandler(); | ||||
|  | ||||
| 	// Checks object used tiles | ||||
| 	// Position is relative to bottom-right corner of the object, can not be negative | ||||
| 	bool isWithin(si32 X, si32 Y) const; | ||||
| 	bool isVisitableAt(si32 X, si32 Y) const; | ||||
| 	bool isVisibleAt(si32 X, si32 Y) const; | ||||
| 	bool isBlockedAt(si32 X, si32 Y) const; | ||||
|  | ||||
| 	// Checks if object is visitable from certain direction. X and Y must be between -1..+1 | ||||
| 	bool isVisitableFrom(si8 X, si8 Y) const; | ||||
|  | ||||
| 	// Checks if object can be placed on specific terrain | ||||
| 	bool canBePlacedAt(ETerrainType terrain) const; | ||||
|  | ||||
| 	ObjectTemplate(); | ||||
|  | ||||
| 	void readTxt(CLegacyConfigParser & parser); | ||||
| 	void readMsk(); | ||||
| 	void readMap(CBinaryReader & reader); | ||||
| 	void readJson(const JsonNode & node); | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
| 		h & gobjs & capitols & villages; | ||||
| 		h & usedTiles & allowedTerrains & animationFile; | ||||
| 		h & id & subid & printPriority & visitDir; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE CDefObjInfoHandler | ||||
| { | ||||
| 	/// list of all object templates loaded from text files | ||||
| 	/// actual object have ObjectTemplate as member "appearance" | ||||
| 	std::vector<ObjectTemplate> objects; | ||||
|  | ||||
| 	/// reads one of H3 text files that contain object templates description | ||||
| 	void readTextFile(std::string path); | ||||
| public: | ||||
|  | ||||
| 	CDefObjInfoHandler(); | ||||
|  | ||||
| 	/// Erases all templates with given type/subtype | ||||
| 	void eraseAll(Obj type, si32 subtype); | ||||
|  | ||||
| 	/// Add new template into the list | ||||
| 	void registerTemplate(ObjectTemplate obj); | ||||
|  | ||||
| 	/// picks all possible candidates for specific pair <type, subtype> | ||||
| 	std::vector<ObjectTemplate> pickCandidates(Obj type, si32 subtype) const; | ||||
| 	/// picks all candidates for <type, subtype> and of possible - also filters them by terrain | ||||
| 	std::vector<ObjectTemplate> pickCandidates(Obj type, si32 subtype, ETerrainType terrain) const; | ||||
|  | ||||
| 	// TODO: as above, but also filters out templates that are not applicable according to accepted test | ||||
| 	// std::vector<ObjectTemplate> pickCandidates(Obj type, si32 subtype, ETerrainType terrain, std::function<bool(ObjectTemplate &)> accept) const; | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
| 		h & objects; | ||||
| 	} | ||||
| }; | ||||
|   | ||||
| @@ -343,36 +343,23 @@ static CGObjectInstance * createObject(Obj id, int subid, int3 pos, PlayerColor | ||||
| 	switch(id) | ||||
| 	{ | ||||
| 	case Obj::HERO: | ||||
| 		{ | ||||
| 			auto  nobj = new CGHeroInstance(); | ||||
| 			nobj->pos = pos; | ||||
| 			nobj->tempOwner = owner; | ||||
| 			nobj->subID = subid; | ||||
| 			//nobj->initHero(ran); | ||||
| 			return nobj; | ||||
| 		} | ||||
| 		nobj = new CGHeroInstance(); | ||||
| 		nobj->appearance = VLC->dobjinfo->pickCandidates(id, VLC->heroh->heroes[subid]->heroClass->id).front(); | ||||
| 		break; | ||||
| 	case Obj::TOWN: | ||||
| 		nobj = new CGTownInstance; | ||||
| 		break; | ||||
| 	default: //rest of objects | ||||
| 		nobj = new CGObjectInstance; | ||||
| 		nobj->defInfo = id.toDefObjInfo()[subid]; | ||||
| 		break; | ||||
| 	} | ||||
| 	nobj->ID = id; | ||||
| 	nobj->subID = subid; | ||||
| 	if(!nobj->defInfo) | ||||
|         logGlobal->warnStream() <<"No def declaration for " <<id <<" "<<subid; | ||||
| 	nobj->pos = pos; | ||||
| 	//nobj->state = nullptr;//new CLuaObjectScript(); | ||||
| 	nobj->tempOwner = owner; | ||||
| 	nobj->defInfo->id = id; | ||||
| 	nobj->defInfo->subid = subid; | ||||
| 	if (id != Obj::HERO) | ||||
| 		nobj->appearance = VLC->dobjinfo->pickCandidates(id, subid).front(); | ||||
|  | ||||
| 	//assigning defhandler | ||||
| 	if(nobj->ID==Obj::HERO || nobj->ID==Obj::TOWN) | ||||
| 		return nobj; | ||||
| 	nobj->defInfo = id.toDefObjInfo()[subid]; | ||||
| 	return nobj; | ||||
| } | ||||
|  | ||||
| @@ -641,14 +628,11 @@ void CGameState::randomizeObject(CGObjectInstance *cur) | ||||
| 	{ | ||||
| 		if(cur->ID==Obj::TOWN) //town - set def | ||||
| 		{ | ||||
| 			const TerrainTile &tile = map->getTile(cur->pos); | ||||
| 			CGTownInstance *t = dynamic_cast<CGTownInstance*>(cur); | ||||
| 			t->town = VLC->townh->factions[t->subID]->town; | ||||
| 			if(t->hasCapitol()) | ||||
| 				t->defInfo = VLC->dobjinfo->capitols[t->subID]; | ||||
| 			else if(t->hasFort()) | ||||
| 				t->defInfo = VLC->dobjinfo->gobjs[Obj::TOWN][t->subID]; | ||||
| 			else | ||||
| 				t->defInfo = VLC->dobjinfo->villages[t->subID]; | ||||
| 			t->appearance = VLC->dobjinfo->pickCandidates(Obj::TOWN, t->subID, tile.terType).front(); | ||||
| 			t->updateAppearance(); | ||||
| 		} | ||||
| 		return; | ||||
| 	} | ||||
| @@ -666,33 +650,33 @@ void CGameState::randomizeObject(CGObjectInstance *cur) | ||||
| 	} | ||||
| 	else if(ran.first==Obj::TOWN)//special code for town | ||||
| 	{ | ||||
| 		const TerrainTile &tile = map->getTile(cur->pos); | ||||
| 		CGTownInstance *t = dynamic_cast<CGTownInstance*>(cur); | ||||
|         if(!t) {logGlobal->warnStream()<<"Wrong random town at "<<cur->pos; return;} | ||||
| 		cur->ID = ran.first; | ||||
| 		cur->subID = ran.second; | ||||
| 		//FIXME: copy-pasted from above | ||||
| 		t->town = VLC->townh->factions[t->subID]->town; | ||||
| 		if(t->hasCapitol()) | ||||
| 			t->defInfo = VLC->dobjinfo->capitols[t->subID]; | ||||
| 		else if(t->hasFort()) | ||||
| 			t->defInfo = VLC->dobjinfo->gobjs[Obj::TOWN][t->subID]; | ||||
| 		else | ||||
| 			t->defInfo = VLC->dobjinfo->villages[t->subID]; | ||||
| 		t->appearance = VLC->dobjinfo->pickCandidates(Obj::TOWN,t->subID, tile.terType).front(); | ||||
| 		t->updateAppearance(); | ||||
|  | ||||
| 		t->randomizeArmy(t->subID); | ||||
| 		map->towns.push_back(t); | ||||
| 		return; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		if (ran.first  != cur->appearance.id || | ||||
| 			ran.second != cur->appearance.subid) | ||||
| 		{ | ||||
| 			const TerrainTile &tile = map->getTile(cur->pos); | ||||
| 			cur->appearance = VLC->dobjinfo->pickCandidates(Obj(ran.first),ran.second, tile.terType).front(); | ||||
| 		} | ||||
| 	} | ||||
| 	//we have to replace normal random object | ||||
| 	cur->ID = ran.first; | ||||
| 	cur->subID = ran.second; | ||||
| 	map->removeBlockVisTiles(cur); //recalculate blockvis tiles - picked object might have different than random placeholder | ||||
| 	map->customDefs.push_back(cur->defInfo = ran.first.toDefObjInfo()[ran.second]); | ||||
| 	if(!cur->defInfo) | ||||
| 	{ | ||||
|         logGlobal->errorStream()<<"*BIG* WARNING: Missing def declaration for "<<cur->ID<<" "<<cur->subID; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	map->removeBlockVisTiles(cur, true); //recalculate blockvis tiles - picked object might have different than random placeholder | ||||
| 	map->addBlockVisTiles(cur); | ||||
| } | ||||
|  | ||||
| @@ -1189,7 +1173,6 @@ void CGameState::placeStartingHero(PlayerColor playerColor, HeroTypeID heroTypeI | ||||
| 	townPos.x += 1; | ||||
|  | ||||
| 	CGHeroInstance * hero =  static_cast<CGHeroInstance*>(createObject(Obj::HERO, heroTypeId.getNum(), townPos, playerColor)); | ||||
| 	hero->initHeroDefInfo(); | ||||
| 	map->getEditManager()->insertObject(hero, townPos); | ||||
| } | ||||
|  | ||||
| @@ -2205,14 +2188,15 @@ bool CGameState::isVisible( const CGObjectInstance *obj, boost::optional<PlayerC | ||||
| 	if(*player == PlayerColor::NEUTRAL) //-> TODO ??? needed? | ||||
| 		return false; | ||||
| 	//object is visible when at least one blocked tile is visible | ||||
| 	for(int fx=0; fx<8; ++fx) | ||||
| 	for(int fy=0; fy < obj->getHeight(); ++fy) | ||||
| 	{ | ||||
| 		for(int fy=0; fy<6; ++fy) | ||||
| 		for(int fx=0; fx < obj->getWidth(); ++fx) | ||||
| 		{ | ||||
| 			int3 pos = obj->pos + int3(fx-7,fy-5,0); | ||||
| 			if(map->isInTheMap(pos) | ||||
| 				&& !((obj->defInfo->blockMap[fy] >> (7 - fx)) & 1) | ||||
| 				&& isVisible(pos, *player)  ) | ||||
| 			int3 pos = obj->pos + int3(-fx, -fy, 0); | ||||
|  | ||||
| 			if ( map->isInTheMap(pos) && | ||||
| 				 obj->coveringAt(pos.x, pos.y) && | ||||
| 				 isVisible(pos, *player)) | ||||
| 				return true; | ||||
| 		} | ||||
| 	} | ||||
| @@ -2232,39 +2216,10 @@ bool CGameState::checkForVisitableDir( const int3 & src, const TerrainTile *pom, | ||||
| 		if(!vstd::contains(pom->blockingObjects, pom->visitableObjects[b])) //this visitable object is not blocking, ignore | ||||
| 			continue; | ||||
|  | ||||
| 		CGDefInfo * di = pom->visitableObjects[b]->defInfo; | ||||
| 		if( (dst.x == src.x-1 && dst.y == src.y-1) && !(di->visitDir & (1<<4)) ) | ||||
| 		{ | ||||
| 		const CGObjectInstance * obj = pom->visitableObjects[b]; | ||||
|  | ||||
| 		if (!obj->appearance.isVisitableFrom(src.x - dst.x, src.y - dst.y)) | ||||
| 			return false; | ||||
| 		} | ||||
| 		if( (dst.x == src.x && dst.y == src.y-1) && !(di->visitDir & (1<<5)) ) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 		if( (dst.x == src.x+1 && dst.y == src.y-1) && !(di->visitDir & (1<<6)) ) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 		if( (dst.x == src.x+1 && dst.y == src.y) && !(di->visitDir & (1<<7)) ) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 		if( (dst.x == src.x+1 && dst.y == src.y+1) && !(di->visitDir & (1<<0)) ) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 		if( (dst.x == src.x && dst.y == src.y+1) && !(di->visitDir & (1<<1)) ) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 		if( (dst.x == src.x-1 && dst.y == src.y+1) && !(di->visitDir & (1<<2)) ) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 		if( (dst.x == src.x-1 && dst.y == src.y) && !(di->visitDir & (1<<3)) ) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
|   | ||||
| @@ -223,6 +223,18 @@ void CHeroClassHandler::afterLoadFinalization() | ||||
| 			heroClass->selectionProbability[faction->index] = static_cast<int>(sqrt(chance) + 0.5); //FIXME: replace with std::round once MVS supports it | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	ObjectTemplate base = VLC->dobjinfo->pickCandidates(Obj::HERO, 0).front(); | ||||
| 	for (CHeroClass * hc : heroClasses) | ||||
| 	{ | ||||
| 		base.animationFile = hc->imageMapMale; | ||||
| 		base.subid = hc->id; | ||||
|  | ||||
| 		// replace existing (if any) and add new template. | ||||
| 		// Necessary for objects added via mods that don't have any templates in H3 | ||||
| 		VLC->dobjinfo->eraseAll(Obj::HERO, hc->id); | ||||
| 		VLC->dobjinfo->registerTemplate(base); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| std::vector<bool> CHeroClassHandler::getDefaultAllowed() const | ||||
|   | ||||
| @@ -789,91 +789,4 @@ void CModHandler::afterLoad() | ||||
|  | ||||
| 	std::ofstream file(*CResourceHandler::get()->getResourceName(ResourceID("config/modSettings.json")), std::ofstream::trunc); | ||||
| 	file << modSettings; | ||||
| 	reload(); | ||||
| } | ||||
|  | ||||
| void CModHandler::reload() | ||||
| { | ||||
| 	{ | ||||
| 		//recreate adventure map defs | ||||
| 		assert(!VLC->dobjinfo->gobjs[Obj::MONSTER].empty()); //make sure that at least some def info was found | ||||
|  | ||||
| 		const CGDefInfo * baseInfo = VLC->dobjinfo->gobjs[Obj::MONSTER].begin()->second; | ||||
|  | ||||
| 		for(auto & crea : VLC->creh->creatures) | ||||
| 		{ | ||||
| 			if (!vstd::contains(VLC->dobjinfo->gobjs[Obj::MONSTER], crea->idNumber)) // no obj info for this type | ||||
| 			{ | ||||
| 				auto  info = new CGDefInfo(*baseInfo); | ||||
| 				info->subid = crea->idNumber; | ||||
| 				info->name = crea->advMapDef; | ||||
|  | ||||
| 				VLC->dobjinfo->gobjs[Obj::MONSTER][crea->idNumber] = info; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	{ | ||||
| 		assert(!VLC->dobjinfo->gobjs[Obj::ARTIFACT].empty()); | ||||
|  | ||||
| 		const CGDefInfo * baseInfo = VLC->dobjinfo->gobjs[Obj::ARTIFACT].begin()->second; | ||||
|  | ||||
| 		for(auto & art : VLC->arth->artifacts) | ||||
| 		{ | ||||
| 			if (!vstd::contains(VLC->dobjinfo->gobjs[Obj::ARTIFACT], art->id)) // no obj info for this type | ||||
| 			{ | ||||
| 				auto  info = new CGDefInfo(*baseInfo); | ||||
| 				info->subid = art->id; | ||||
| 				info->name = art->advMapDef; | ||||
|  | ||||
| 				VLC->dobjinfo->gobjs[Obj::ARTIFACT][art->id] = info; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	{ | ||||
| 		assert(!VLC->dobjinfo->gobjs[Obj::TOWN].empty()); //make sure that at least some def info was found | ||||
|  | ||||
| 		const CGDefInfo * baseInfo = VLC->dobjinfo->gobjs[Obj::TOWN].begin()->second; | ||||
| 		auto & townInfos = VLC->dobjinfo->gobjs[Obj::TOWN]; | ||||
|  | ||||
| 		for(auto & faction : VLC->townh->factions) | ||||
| 		{ | ||||
| 			TFaction index = faction->index; | ||||
| 			CTown * town = faction->town; | ||||
| 			if (town) | ||||
| 			{ | ||||
| 				auto & cientInfo = town->clientInfo; | ||||
|  | ||||
| 				if (!vstd::contains(VLC->dobjinfo->gobjs[Obj::TOWN], index)) // no obj info for this type | ||||
| 				{ | ||||
| 					auto  info = new CGDefInfo(*baseInfo); | ||||
| 					info->subid = index; | ||||
|  | ||||
| 					townInfos[index] = info; | ||||
| 				} | ||||
| 				townInfos[index]->name = cientInfo.advMapCastle; | ||||
|  | ||||
| 				VLC->dobjinfo->villages[index] = new CGDefInfo(*townInfos[index]); | ||||
| 				VLC->dobjinfo->villages[index]->name = cientInfo.advMapVillage; | ||||
|  | ||||
| 				VLC->dobjinfo->capitols[index] = new CGDefInfo(*townInfos[index]); | ||||
| 				VLC->dobjinfo->capitols[index]->name = cientInfo.advMapCapitol; | ||||
|  | ||||
| 				for (int i = 0; i < town->dwellings.size(); ++i) | ||||
| 				{ | ||||
| 					const CGDefInfo * baseInfo = VLC->dobjinfo->gobjs[Obj::CREATURE_GENERATOR1][i]; //get same blockmap as first dwelling of tier i | ||||
|  | ||||
| 					for (auto cre : town->creatures[i]) //both unupgraded and upgraded get same dwelling | ||||
| 					{ | ||||
| 						auto  info = new CGDefInfo(*baseInfo); | ||||
| 						info->subid = cre; | ||||
| 						info->name = town->dwellings[i]; | ||||
| 						VLC->dobjinfo->gobjs[Obj::CREATURE_GENERATOR1][cre] = info; | ||||
|  | ||||
| 						VLC->objh->cregens[cre] = cre; //map of dwelling -> creature id | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -213,10 +213,6 @@ public: | ||||
| 	void load(); | ||||
| 	void afterLoad(); | ||||
|  | ||||
| 	/// actions that should be triggered on map restart | ||||
| 	/// TODO: merge into appropriate handlers? | ||||
| 	void reload(); | ||||
|  | ||||
| 	struct DLL_LINKAGE hardcodedFeatures | ||||
| 	{ | ||||
| 		JsonNode data; | ||||
|   | ||||
| @@ -33,11 +33,6 @@ | ||||
|  | ||||
| using namespace boost::assign; | ||||
|  | ||||
| // It looks that we can't rely on shadowCoverage correctness (Mantis #866). This may result | ||||
| // in notable performance decrease (SDL blit with custom alpha blit) not notable on my system (Ivan) | ||||
| #define USE_COVERAGE_MAP 0 | ||||
|  | ||||
|  | ||||
| std::map<Obj, std::map<int, std::vector<ObjectInstanceID> > > CGTeleport::objs; | ||||
| std::vector<std::pair<ObjectInstanceID, ObjectInstanceID> > CGTeleport::gates; | ||||
| IGameCallback * IObjectInterface::cb = nullptr; | ||||
| @@ -298,7 +293,6 @@ CGObjectInstance::CGObjectInstance(): | ||||
| 	pos(-1,-1,-1), | ||||
| 	ID(Obj::NO_OBJ), | ||||
| 	subID(-1), | ||||
| 	defInfo(nullptr), | ||||
| 	tempOwner(PlayerColor::UNFLAGGABLE), | ||||
| 	blockVisit(false) | ||||
| { | ||||
| @@ -323,62 +317,24 @@ void CGObjectInstance::setOwner(PlayerColor ow) | ||||
| } | ||||
| int CGObjectInstance::getWidth() const//returns width of object graphic in tiles | ||||
| { | ||||
| 	return defInfo->width; | ||||
| 	return appearance.getWidth(); | ||||
| } | ||||
| int CGObjectInstance::getHeight() const //returns height of object graphic in tiles | ||||
| { | ||||
| 	return defInfo->height; | ||||
| 	return appearance.getHeight(); | ||||
| } | ||||
| bool CGObjectInstance::visitableAt(int x, int y) const //returns true if object is visitable at location (x, y) form left top tile of image (x, y in tiles) | ||||
| { | ||||
| 	if(defInfo==nullptr) | ||||
| 	{ | ||||
|         logGlobal->warnStream() << "Warning: VisitableAt for obj "<< id.getNum() <<": nullptr defInfo!"; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	if((defInfo->visitMap[y] >> (7-x) ) & 1) | ||||
| 	{ | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	return false; | ||||
| 	return appearance.isVisitableAt(pos.x - x, pos.y - y); | ||||
| } | ||||
| bool CGObjectInstance::blockingAt(int x, int y) const | ||||
| { | ||||
| 	if(x<0 || y<0 || x>=getWidth() || y>=getHeight() || defInfo==nullptr) | ||||
| 		return false; | ||||
| 	if((defInfo->blockMap[y+6-getHeight()] >> (7-(8-getWidth()+x) )) & 1) | ||||
| 		return false; | ||||
| 	return true; | ||||
| 	return appearance.isBlockedAt(pos.x - x, pos.y - y); | ||||
| } | ||||
|  | ||||
| bool CGObjectInstance::coveringAt(int x, int y) const | ||||
| { | ||||
| 	//input coordinates are always negative | ||||
| 	x = -x; | ||||
| 	y = -y; | ||||
| #if USE_COVERAGE_MAP | ||||
| 	//NOTE: this code may be broken | ||||
| 	if((defInfo->coverageMap[y] >> (7-(x) )) & 1 | ||||
| 		||  (defInfo->shadowCoverage[y] >> (7-(x) )) & 1) | ||||
| 		return true; | ||||
| 	return false; | ||||
| #else | ||||
| 	return x >= 0 && y >= 0 && x < getWidth() && y < getHeight(); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| bool CGObjectInstance::hasShadowAt( int x, int y ) const | ||||
| { | ||||
| #if USE_COVERAGE_MAP | ||||
| 	//NOTE: this code may be broken | ||||
| 	if( (defInfo->shadowCoverage[y] >> (7-(x) )) & 1 ) | ||||
| 		return true; | ||||
| 	return false; | ||||
| #else | ||||
| 	return coveringAt(x,y);// ignore unreliable shadowCoverage map | ||||
| #endif | ||||
| 	return appearance.isVisibleAt(pos.x - x, pos.y - y); | ||||
| } | ||||
|  | ||||
| std::set<int3> CGObjectInstance::getBlockedPos() const | ||||
| @@ -388,8 +344,8 @@ std::set<int3> CGObjectInstance::getBlockedPos() const | ||||
| 	{ | ||||
| 		for(int h=0; h<getHeight(); ++h) | ||||
| 		{ | ||||
| 			if(blockingAt(w, h)) | ||||
| 				ret.insert(int3(pos.x - getWidth() + w + 1, pos.y - getHeight() + h + 1, pos.z)); | ||||
| 			if (appearance.isBlockedAt(-w, -h)) | ||||
| 				ret.insert(int3(pos.x - w, pos.y - h, pos.z)); | ||||
| 		} | ||||
| 	} | ||||
| 	return ret; | ||||
| @@ -397,21 +353,20 @@ std::set<int3> CGObjectInstance::getBlockedPos() const | ||||
|  | ||||
| bool CGObjectInstance::operator<(const CGObjectInstance & cmp) const  //screen printing priority comparing | ||||
| { | ||||
| 	if(defInfo->printPriority==1 && cmp.defInfo->printPriority==0) | ||||
| 		return true; | ||||
| 	if(cmp.defInfo->printPriority==1 && defInfo->printPriority==0) | ||||
| 		return false; | ||||
| 	if(this->pos.y<cmp.pos.y) | ||||
| 		return true; | ||||
| 	if(this->pos.y>cmp.pos.y) | ||||
| 		return false; | ||||
| 	if (appearance.printPriority != cmp.appearance.printPriority) | ||||
| 		return appearance.printPriority > cmp.appearance.printPriority; | ||||
|  | ||||
| 	if(pos.y != cmp.pos.y) | ||||
| 		return pos.y < cmp.pos.y; | ||||
|  | ||||
| 	if(cmp.ID==Obj::HERO && ID!=Obj::HERO) | ||||
| 		return true; | ||||
| 	if(cmp.ID!=Obj::HERO && ID==Obj::HERO) | ||||
| 		return false; | ||||
| 	if(!defInfo->isVisitable() && cmp.defInfo->isVisitable()) | ||||
|  | ||||
| 	if(!isVisitable() && cmp.isVisitable()) | ||||
| 		return true; | ||||
| 	if(!cmp.defInfo->isVisitable() && defInfo->isVisitable()) | ||||
| 	if(!cmp.isVisitable() && isVisitable()) | ||||
| 		return false; | ||||
| 	if(this->pos.x<cmp.pos.x) | ||||
| 		return true; | ||||
| @@ -490,13 +445,13 @@ void CGObjectInstance::hideTiles(PlayerColor ourplayer, int radius) const | ||||
| } | ||||
| int3 CGObjectInstance::getVisitableOffset() const | ||||
| { | ||||
| 	for(int y = 0; y < 6; y++) | ||||
| 		for (int x = 0; x < 8; x++) | ||||
| 			if((defInfo->visitMap[5-y] >> x) & 1) | ||||
| 	for(int y = 0; y < appearance.getHeight(); y++) | ||||
| 		for (int x = 0; x < appearance.getWidth(); x++) | ||||
| 			if (appearance.isVisitableAt(x, y)) | ||||
| 				return int3(x,y,0); | ||||
|  | ||||
|     logGlobal->warnStream() << "Warning: getVisitableOffset called on non-visitable obj!"; | ||||
| 	return int3(-1,-1,-1); | ||||
| 	return int3(0,0,0); | ||||
| } | ||||
|  | ||||
| void CGObjectInstance::getNameVis( std::string &hname ) const | ||||
| @@ -556,14 +511,7 @@ int3 CGObjectInstance::visitablePos() const | ||||
|  | ||||
| bool CGObjectInstance::isVisitable() const | ||||
| { | ||||
| 	for(int g=0; g<ARRAY_COUNT(defInfo->visitMap); ++g) | ||||
| 	{ | ||||
| 		if(defInfo->visitMap[g] != 0) | ||||
| 		{ | ||||
| 			return true; | ||||
| 		} | ||||
| 	} | ||||
| 	return false; | ||||
| 	return appearance.isVisitable(); | ||||
| } | ||||
|  | ||||
| bool CGObjectInstance::passableFor(PlayerColor color) const | ||||
| @@ -766,11 +714,12 @@ void CGHeroInstance::initHero(HeroTypeID SUBID) | ||||
| void CGHeroInstance::initHero() | ||||
| { | ||||
| 	assert(validTypes(true)); | ||||
| 	if(ID == Obj::HERO) | ||||
| 		initHeroDefInfo(); | ||||
| 	if(!type) | ||||
| 		type = VLC->heroh->heroes[subID]; | ||||
|  | ||||
| 	if (ID == Obj::HERO) | ||||
| 		appearance = VLC->dobjinfo->pickCandidates(Obj::HERO, type->heroClass->id).front(); | ||||
|  | ||||
| 	if(!vstd::contains(spells, SpellID::PRESET)) //hero starts with a spell | ||||
| 	{ | ||||
| 		for(auto spellID : type->spells) | ||||
| @@ -888,27 +837,7 @@ void CGHeroInstance::initArmy(IArmyDescriptor *dst /*= nullptr*/) | ||||
| 			dst->setCreature(SlotID(stackNo-warMachinesGiven), stack.creature, count); | ||||
| 	} | ||||
| } | ||||
| void CGHeroInstance::initHeroDefInfo() | ||||
| { | ||||
| 	if(!defInfo  ||  defInfo->id != Obj::HERO) | ||||
| 	{ | ||||
| 		defInfo = new CGDefInfo(); | ||||
| 		defInfo->id = Obj::HERO; | ||||
| 		defInfo->subid = subID; | ||||
| 		defInfo->printPriority = 0; | ||||
| 		defInfo->visitDir = 0xff; | ||||
| 	} | ||||
| 	for(int i=0;i<6;i++) | ||||
| 	{ | ||||
| 		defInfo->blockMap[i] = 255; | ||||
| 		defInfo->visitMap[i] = 0; | ||||
| 		defInfo->coverageMap[i] = 0; | ||||
| 		defInfo->shadowCoverage[i] = 0; | ||||
| 	} | ||||
| 	defInfo->blockMap[5] = 253; | ||||
| 	defInfo->visitMap[5] = 2; | ||||
| 	defInfo->coverageMap[4] = defInfo->coverageMap[5] = 224; | ||||
| } | ||||
|  | ||||
| CGHeroInstance::~CGHeroInstance() | ||||
| { | ||||
| 	commander.dellNull(); | ||||
| @@ -1780,8 +1709,8 @@ void CGDwelling::initObj() | ||||
| 			if (subID >= VLC->generaltexth->creGens.size()) //very messy workaround | ||||
| 			{ | ||||
| 				auto & dwellingNames = VLC->townh->factions[crs->faction]->town->dwellingNames; | ||||
| 				assert (!dwellingNames.empty()); | ||||
| 				hoverName = dwellingNames[VLC->creh->creatures[subID]->level - 1]; | ||||
| 				assert (dwellingNames.size() > crs->level - 1); | ||||
| 				hoverName = dwellingNames[crs->level - 1]; | ||||
| 			} | ||||
| 			else | ||||
| 				hoverName = VLC->generaltexth->creGens[subID]; | ||||
| @@ -2537,6 +2466,16 @@ std::vector<int> CGTownInstance::availableItemsIds(EMarketMode::EMarketMode mode | ||||
| 		return IMarket::availableItemsIds(mode); | ||||
| } | ||||
|  | ||||
| void CGTownInstance::updateAppearance() | ||||
| { | ||||
| 	if (!hasFort()) | ||||
| 		appearance.animationFile = town->clientInfo.advMapVillage; | ||||
| 	else if(hasCapitol()) | ||||
| 		appearance.animationFile = town->clientInfo.advMapCapitol; | ||||
| 	else | ||||
| 		appearance.animationFile = town->clientInfo.advMapCastle; | ||||
| } | ||||
|  | ||||
| std::string CGTownInstance::nodeName() const | ||||
| { | ||||
| 	return "Town (" + (town ? town->faction->name : "unknown") + ") of " +  name; | ||||
| @@ -5378,7 +5317,7 @@ std::vector<int3> CGMagicSpring::getVisitableOffsets() const | ||||
|  | ||||
| 	for(int y = 0; y < 6; y++) | ||||
| 		for (int x = 0; x < 8; x++) //starting from left | ||||
| 			if((defInfo->visitMap[5-y] >> x) & 1) | ||||
| 			if (appearance.isVisitableAt(x, y)) | ||||
| 				visitableTiles.push_back (int3(x, y , 0)); | ||||
|  | ||||
| 	return visitableTiles; | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| #include "../lib/CCreatureSet.h" | ||||
| #include "../lib/CTownHandler.h" | ||||
| #include "../lib/CDefObjInfoHandler.h" | ||||
| #include "CArtHandler.h" | ||||
| #include "../lib/ConstTransitivePtr.h" | ||||
| #include "int3.h" | ||||
| @@ -36,7 +37,6 @@ class CSpell; | ||||
| class CGTownInstance; | ||||
| class CGTownBuilding; | ||||
| class CArtifact; | ||||
| class CGDefInfo; | ||||
| class CSpecObjInfo; | ||||
| class CCastleEvent; | ||||
| struct TerrainTile; | ||||
| @@ -180,7 +180,7 @@ public: | ||||
| 	Obj ID; | ||||
| 	si32 subID; //normal subID (this one from OH3 maps ;]) | ||||
| 	ObjectInstanceID id;//number of object in map's vector | ||||
| 	CGDefInfo * defInfo; | ||||
| 	ObjectTemplate appearance; | ||||
|  | ||||
| 	PlayerColor tempOwner; | ||||
| 	bool blockVisit; //if non-zero then blocks the tile but is visitable from neighbouring tile | ||||
| @@ -194,12 +194,11 @@ public: | ||||
| 	void setOwner(PlayerColor ow); | ||||
| 	int getWidth() const; //returns width of object graphic in tiles | ||||
| 	int getHeight() const; //returns height of object graphic in tiles | ||||
| 	virtual bool visitableAt(int x, int y) const; //returns true if object is visitable at location (x, y) form left top tile of image (x, y in tiles) | ||||
| 	virtual bool visitableAt(int x, int y) const; //returns true if object is visitable at location (x, y) (h3m pos) | ||||
| 	virtual int3 getVisitableOffset() const; //returns (x,y,0) offset to first visitable tile from bottom right obj tile (0,0,0) (h3m pos) | ||||
| 	int3 visitablePos() const; | ||||
| 	bool blockingAt(int x, int y) const; //returns true if object is blocking location (x, y) form left top tile of image (x, y in tiles) | ||||
| 	bool coveringAt(int x, int y) const; //returns true if object covers with picture location (x, y) form left top tile of maximal possible image (8 x 6 tiles) (x, y in tiles) | ||||
| 	bool hasShadowAt(int x, int y) const;//returns true if object covers with shadow location (x, y) form left top tile of maximal possible image (8 x 6 tiles) (x, y in tiles) | ||||
| 	bool blockingAt(int x, int y) const; //returns true if object is blocking location (x, y) (h3m pos) | ||||
| 	bool coveringAt(int x, int y) const; //returns true if object covers with picture location (x, y) (h3m pos) | ||||
| 	std::set<int3> getBlockedPos() const; //returns set of positions blocked by this object | ||||
| 	bool isVisitable() const; //returns true if object is visitable | ||||
| 	bool operator<(const CGObjectInstance & cmp) const;  //screen printing priority comparing | ||||
| @@ -220,7 +219,7 @@ public: | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
| 		h & hoverName & pos & ID & subID & id & tempOwner & blockVisit & defInfo; | ||||
| 		h & hoverName & pos & ID & subID & id & tempOwner & blockVisit & appearance; | ||||
| 		//definfo is handled by map serializer | ||||
| 	} | ||||
| protected: | ||||
| @@ -446,7 +445,6 @@ public: | ||||
| 	void initExp(); | ||||
| 	void initArmy(IArmyDescriptor *dst = nullptr); | ||||
| 	//void giveArtifact (ui32 aid); | ||||
| 	void initHeroDefInfo(); | ||||
| 	void pushPrimSkill(PrimarySkill::PrimarySkill which, int val); | ||||
| 	ui8 maxlevelsToMagicSchool() const; | ||||
| 	ui8 maxlevelsToWisdom() const; | ||||
| @@ -688,6 +686,7 @@ public: | ||||
| 	bool allowsTrade(EMarketMode::EMarketMode mode) const; | ||||
| 	std::vector<int> availableItemsIds(EMarketMode::EMarketMode mode) const; | ||||
|  | ||||
| 	void updateAppearance(); | ||||
|  | ||||
| 	////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|   | ||||
| @@ -11,6 +11,8 @@ | ||||
| #include "CArtHandler.h" | ||||
| #include "CSpellHandler.h" | ||||
| #include "filesystem/Filesystem.h" | ||||
| #include "CDefObjInfoHandler.h" | ||||
| #include "CObjectHandler.h" | ||||
|  | ||||
| /* | ||||
|  * CTownHandler.cpp, part of VCMI engine | ||||
| @@ -709,6 +711,38 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod | ||||
| void CTownHandler::afterLoadFinalization() | ||||
| { | ||||
| 	initializeRequirements(); | ||||
| 	ObjectTemplate base = VLC->dobjinfo->pickCandidates(Obj::TOWN, 0).front(); | ||||
| 	for (CFaction * fact : factions) | ||||
| 	{ | ||||
| 		if (fact->town) | ||||
| 		{ | ||||
| 			base.animationFile = fact->town->clientInfo.advMapCastle; | ||||
| 			base.subid = fact->index; | ||||
|  | ||||
| 			// replace existing (if any) and add new template. | ||||
| 			// Necessary for objects added via mods that don't have any templates in H3 | ||||
| 			VLC->dobjinfo->eraseAll(Obj::TOWN, fact->index); | ||||
| 			VLC->dobjinfo->registerTemplate(base); | ||||
|  | ||||
| 			assert(fact->town->dwellings.size() == fact->town->dwellingNames.size()); | ||||
| 			for (size_t i=0; i<fact->town->dwellings.size(); i++) | ||||
| 			{ | ||||
| 				ObjectTemplate base = VLC->dobjinfo->pickCandidates(Obj::CREATURE_GENERATOR1, 0).front(); | ||||
|  | ||||
| 				//both unupgraded and upgraded get same dwelling | ||||
| 				 for (auto cre : fact->town->creatures[i]) | ||||
| 				 { | ||||
| 					base.subid = 80 + cre; | ||||
| 					base.animationFile = fact->town->dwellings[i]; | ||||
| 					if (VLC->objh->cregens.count(cre) == 0) | ||||
| 					{ | ||||
| 						VLC->dobjinfo->registerTemplate(base); | ||||
| 						VLC->objh->cregens[80 + cre] = cre; //map of dwelling -> creature id | ||||
| 					} | ||||
| 				 } | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CTownHandler::initializeRequirements() | ||||
|   | ||||
| @@ -73,13 +73,7 @@ ID_LIKE_OPERATORS(SpellID, SpellID::ESpellID) | ||||
|  | ||||
| ID_LIKE_OPERATORS(BuildingID, BuildingID::EBuildingID) | ||||
|  | ||||
| ID_LIKE_OPERATORS(BFieldType, BFieldType::EBFieldType) | ||||
|  | ||||
|  | ||||
| std::map<int, ConstTransitivePtr<CGDefInfo> > & Obj::toDefObjInfo() const | ||||
| { | ||||
| 	return VLC->dobjinfo->gobjs[*this]; | ||||
| } | ||||
| ID_LIKE_OPERATORS(BFieldType, BFieldType::EBFieldType) | ||||
|  | ||||
| CArtifact * ArtifactID::toArtifact() const | ||||
| { | ||||
|   | ||||
| @@ -55,7 +55,6 @@ namespace GameConstants | ||||
|  | ||||
| class CArtifact; | ||||
| class CArtifactInstance; | ||||
| class CGDefInfo; | ||||
| class CCreature; | ||||
| class CSpell; | ||||
| class CGameInfoCallback; | ||||
| @@ -598,8 +597,6 @@ public: | ||||
|  | ||||
| 	ID_LIKE_CLASS_COMMON(Obj, EObj) | ||||
|  | ||||
| 	std::map<int, ConstTransitivePtr<CGDefInfo> > & toDefObjInfo() const; | ||||
|  | ||||
| 	EObj num; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -311,10 +311,7 @@ DLL_LINKAGE void RemoveObject::applyGs( CGameState *gs ) | ||||
| 	CGObjectInstance *obj = gs->getObjInstance(id); | ||||
| 	logGlobal->debugStream() << "removing object id=" << id << "; address=" << (intptr_t)obj << "; name=" << obj->getHoverText(); | ||||
| 	//unblock tiles | ||||
| 	if(obj->defInfo) | ||||
| 	{ | ||||
| 		gs->map->removeBlockVisTiles(obj); | ||||
| 	} | ||||
| 	gs->map->removeBlockVisTiles(obj); | ||||
|  | ||||
| 	if(obj->ID==Obj::HERO) | ||||
| 	{ | ||||
| @@ -534,7 +531,6 @@ DLL_LINKAGE void HeroRecruited::applyGs( CGameState *gs ) | ||||
| 	else | ||||
| 		gs->map->objects[h->id.getNum()] = h; | ||||
|  | ||||
| 	h->initHeroDefInfo(); | ||||
| 	gs->map->heroesOnMap.push_back(h); | ||||
| 	p->heroes.push_back(h); | ||||
| 	h->attachTo(p); | ||||
| @@ -558,7 +554,6 @@ DLL_LINKAGE void GiveHero::applyGs( CGameState *gs ) | ||||
| 	gs->map->removeBlockVisTiles(h,true); | ||||
| 	h->setOwner(player); | ||||
| 	h->movement =  h->maxMovePoints(true); | ||||
| 	h->initHeroDefInfo(); | ||||
| 	gs->map->heroesOnMap.push_back(h); | ||||
| 	gs->getPlayer(h->getOwner())->heroes.push_back(h); | ||||
| 	gs->map->addBlockVisTiles(h); | ||||
| @@ -594,27 +589,14 @@ DLL_LINKAGE void NewObject::applyGs( CGameState *gs ) | ||||
| 	o->ID = ID; | ||||
| 	o->subID = subID; | ||||
| 	o->pos = pos; | ||||
| 	o->defInfo = VLC->dobjinfo->gobjs[ID][subID]; | ||||
| 	const TerrainTile &t = gs->map->getTile(pos); | ||||
| 	o->appearance = VLC->dobjinfo->pickCandidates(o->ID, o->subID, t.terType).front(); | ||||
| 	id = o->id = ObjectInstanceID(gs->map->objects.size()); | ||||
| 	o->hoverName = VLC->generaltexth->names[ID]; | ||||
|  | ||||
| 	switch(ID) | ||||
| 	{ | ||||
| 		case Obj::MONSTER: | ||||
| 			o->defInfo = VLC->dobjinfo->gobjs[ID][subID]; | ||||
| 			assert(o->defInfo); | ||||
| 			break; | ||||
| 		case Obj::HOLE: | ||||
| 			const TerrainTile &t = gs->map->getTile(pos); | ||||
| 			o->defInfo = VLC->dobjinfo->gobjs[ID][t.terType]; | ||||
| 			assert(o->defInfo); | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	gs->map->objects.push_back(o); | ||||
| 	gs->map->addBlockVisTiles(o); | ||||
| 	o->initObj(); | ||||
| 	assert(o->defInfo); | ||||
|  | ||||
| 	logGlobal->debugStream() << "added object id=" << id << "; address=" << (intptr_t)o << "; name=" << o->getHoverText(); | ||||
| } | ||||
|   | ||||
| @@ -215,22 +215,22 @@ CMap::~CMap() | ||||
|  | ||||
| void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total) | ||||
| { | ||||
| 	for(int fx=0; fx<8; ++fx) | ||||
| 	for(int fx=0; fx<obj->getWidth(); ++fx) | ||||
| 	{ | ||||
| 		for(int fy=0; fy<6; ++fy) | ||||
| 		for(int fy=0; fy<obj->getHeight(); ++fy) | ||||
| 		{ | ||||
| 			int xVal = obj->pos.x + fx - 7; | ||||
| 			int yVal = obj->pos.y + fy - 5; | ||||
| 			int xVal = obj->pos.x - fx; | ||||
| 			int yVal = obj->pos.y - fy; | ||||
| 			int zVal = obj->pos.z; | ||||
| 			if(xVal>=0 && xVal<width && yVal>=0 && yVal<height) | ||||
| 			{ | ||||
| 				TerrainTile & curt = terrain[xVal][yVal][zVal]; | ||||
| 				if(total || ((obj->defInfo->visitMap[fy] >> (7 - fx)) & 1)) | ||||
| 				if(total || obj->visitableAt(xVal, yVal)) | ||||
| 				{ | ||||
| 					curt.visitableObjects -= obj; | ||||
| 					curt.visitable = curt.visitableObjects.size(); | ||||
| 				} | ||||
| 				if(total || !((obj->defInfo->blockMap[fy] >> (7 - fx)) & 1)) | ||||
| 				if(total || obj->blockingAt(xVal, yVal)) | ||||
| 				{ | ||||
| 					curt.blockingObjects -= obj; | ||||
| 					curt.blocked = curt.blockingObjects.size(); | ||||
| @@ -242,22 +242,22 @@ void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total) | ||||
|  | ||||
| void CMap::addBlockVisTiles(CGObjectInstance * obj) | ||||
| { | ||||
| 	for(int fx=0; fx<8; ++fx) | ||||
| 	for(int fx=0; fx<obj->getWidth(); ++fx) | ||||
| 	{ | ||||
| 		for(int fy=0; fy<6; ++fy) | ||||
| 		for(int fy=0; fy<obj->getHeight(); ++fy) | ||||
| 		{ | ||||
| 			int xVal = obj->pos.x + fx - 7; | ||||
| 			int yVal = obj->pos.y + fy - 5; | ||||
| 			int xVal = obj->pos.x - fx; | ||||
| 			int yVal = obj->pos.y - fy; | ||||
| 			int zVal = obj->pos.z; | ||||
| 			if(xVal>=0 && xVal<width && yVal>=0 && yVal<height) | ||||
| 			{ | ||||
| 				TerrainTile & curt = terrain[xVal][yVal][zVal]; | ||||
| 				if(((obj->defInfo->visitMap[fy] >> (7 - fx)) & 1)) | ||||
| 				if( obj->visitableAt(xVal, yVal)) | ||||
| 				{ | ||||
| 					curt.visitableObjects.push_back(obj); | ||||
| 					curt.visitable = true; | ||||
| 				} | ||||
| 				if(!((obj->defInfo->blockMap[fy] >> (7 - fx)) & 1)) | ||||
| 				if( obj->blockingAt(xVal, yVal)) | ||||
| 				{ | ||||
| 					curt.blockingObjects.push_back(obj); | ||||
| 					curt.blocked = true; | ||||
|   | ||||
| @@ -19,7 +19,6 @@ | ||||
| #include "../LogicalExpression.h" | ||||
|  | ||||
| class CArtifactInstance; | ||||
| class CGDefInfo; | ||||
| class CGObjectInstance; | ||||
| class CGHeroInstance; | ||||
| class CCommanderInstance; | ||||
| @@ -408,7 +407,6 @@ public: | ||||
| 	std::vector<Rumor> rumors; | ||||
| 	std::vector<DisposedHero> disposedHeroes; | ||||
| 	std::vector<ConstTransitivePtr<CGHeroInstance> > predefinedHeroes; | ||||
| 	std::vector<ConstTransitivePtr<CGDefInfo> > customDefs; | ||||
| 	std::vector<bool> allowedSpell; | ||||
| 	std::vector<bool> allowedArtifact; | ||||
| 	std::vector<bool> allowedAbilities; | ||||
| @@ -484,7 +482,7 @@ public: | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		h & customDefs & objects; | ||||
| 		h & objects; | ||||
| 		h & heroesOnMap & towns & artInstances; | ||||
|  | ||||
| 		// static members | ||||
|   | ||||
| @@ -124,7 +124,6 @@ void CMapLoaderH3M::init() | ||||
| 	// Calculate blocked / visitable positions | ||||
| 	for(auto & elem : map->objects) | ||||
| 	{ | ||||
| 		if(!elem->defInfo) continue; | ||||
| 		map->addBlockVisTiles(elem); | ||||
| 	} | ||||
| 	times.push_back(MapLoadingTime("blocked/visitable tiles", sw.getDiff())); | ||||
| @@ -945,78 +944,14 @@ void CMapLoaderH3M::readDefInfo() | ||||
| { | ||||
| 	int defAmount = reader.readUInt32(); | ||||
|  | ||||
| 	map->customDefs.reserve(defAmount + 8); | ||||
| 	templates.reserve(defAmount); | ||||
|  | ||||
| 	// Read custom defs | ||||
| 	for(int idd = 0; idd < defAmount; ++idd) | ||||
| 	{ | ||||
| 		auto  defInfo = new CGDefInfo(); | ||||
|  | ||||
| 		defInfo->name = reader.readString(); | ||||
| 		std::transform(defInfo->name.begin(),defInfo->name.end(),defInfo->name.begin(),(int(*)(int))toupper); | ||||
|  | ||||
| 		ui8 bytes[12]; | ||||
| 		for(auto & byte : bytes) | ||||
| 		{ | ||||
| 			byte = reader.readUInt8(); | ||||
| 		} | ||||
|  | ||||
| 		defInfo->terrainAllowed = reader.readUInt16(); | ||||
| 		defInfo->terrainMenu = reader.readUInt16(); | ||||
| 		defInfo->id = Obj(reader.readUInt32()); | ||||
| 		defInfo->subid = reader.readUInt32(); | ||||
| 		defInfo->type = reader.readUInt8(); | ||||
| 		defInfo->printPriority = reader.readUInt8(); | ||||
|  | ||||
| 		for(int zi = 0; zi < 6; ++zi) | ||||
| 		{ | ||||
| 			defInfo->blockMap[zi] = reverse(bytes[zi]); | ||||
| 		} | ||||
| 		for(int zi = 0; zi < 6; ++zi) | ||||
| 		{ | ||||
| 			defInfo->visitMap[zi] = reverse(bytes[6 + zi]); | ||||
| 		} | ||||
|  | ||||
| 		reader.skip(16); | ||||
| 		if(defInfo->id != Obj::HERO && defInfo->id != Obj::RANDOM_HERO) | ||||
| 		{ | ||||
| 			CGDefInfo * h = VLC->dobjinfo->gobjs[defInfo->id][defInfo->subid]; | ||||
| 			if(!h) | ||||
| 			{ | ||||
| 				//remove fake entry | ||||
| 				VLC->dobjinfo->gobjs[defInfo->id].erase(defInfo->subid); | ||||
| 				if(VLC->dobjinfo->gobjs[defInfo->id].size()) | ||||
| 				{ | ||||
| 					VLC->dobjinfo->gobjs.erase(defInfo->id); | ||||
| 				} | ||||
|                 logGlobal->warnStream() << "\t\tWarning: no defobjinfo entry for object ID=" | ||||
|                       << defInfo->id << " subID=" << defInfo->subid; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				defInfo->visitDir = VLC->dobjinfo->gobjs[defInfo->id][defInfo->subid]->visitDir; | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			defInfo->visitDir = 0xff; | ||||
| 		} | ||||
|  | ||||
| 		if(defInfo->id == Obj::EVENT) | ||||
| 		{ | ||||
| 			std::memset(defInfo->blockMap, 255, 6); | ||||
| 		} | ||||
|  | ||||
| 		//calculating coverageMap | ||||
| 		defInfo->fetchInfoFromMSK(); | ||||
|  | ||||
| 		map->customDefs.push_back(defInfo); | ||||
| 	} | ||||
|  | ||||
| 	//add holes - they always can appear | ||||
| 	for(int i = 0; i < 8 ; ++i) | ||||
| 	{ | ||||
| 		map->customDefs.push_back(VLC->dobjinfo->gobjs[Obj::HOLE][i]); | ||||
| 		ObjectTemplate tmpl; | ||||
| 		tmpl.readMap(reader); | ||||
| 		templates.push_back(tmpl); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -1033,10 +968,10 @@ void CMapLoaderH3M::readObjects() | ||||
| 		int defnum = reader.readUInt32(); | ||||
| 		ObjectInstanceID idToBeGiven = ObjectInstanceID(map->objects.size()); | ||||
|  | ||||
| 		CGDefInfo * defInfo = map->customDefs.at(defnum); | ||||
| 		ObjectTemplate & objTempl = templates.at(defnum); | ||||
| 		reader.skip(5); | ||||
|  | ||||
| 		switch(defInfo->id) | ||||
| 		switch(objTempl.id) | ||||
| 		{ | ||||
| 		case Obj::EVENT: | ||||
| 			{ | ||||
| @@ -1142,7 +1077,7 @@ void CMapLoaderH3M::readObjects() | ||||
| 				break; | ||||
| 			} | ||||
| 		case Obj::TREASURE_CHEST: | ||||
| 				if(defInfo->subid == 0) | ||||
| 				if(objTempl.subid == 0) | ||||
| 				{ | ||||
| 					nobj = new CGPickable(); | ||||
| 				} | ||||
| @@ -1315,15 +1250,15 @@ void CMapLoaderH3M::readObjects() | ||||
|  | ||||
| 				readMessageAndGuards(art->message, art); | ||||
|  | ||||
| 				if(defInfo->id == Obj::SPELL_SCROLL) | ||||
| 				if(objTempl.id == Obj::SPELL_SCROLL) | ||||
| 				{ | ||||
| 					spellID = reader.readUInt32(); | ||||
| 					artID = 1; | ||||
| 				} | ||||
| 				else if(defInfo->id == Obj::ARTIFACT) | ||||
| 				else if(objTempl.id == Obj::ARTIFACT) | ||||
| 				{ | ||||
| 					//specific artifact | ||||
| 					artID = defInfo->subid; | ||||
| 					artID = objTempl.subid; | ||||
| 				} | ||||
|  | ||||
| 				art->storedArtifact = createArtifact(artID, spellID); | ||||
| @@ -1338,7 +1273,7 @@ void CMapLoaderH3M::readObjects() | ||||
| 				readMessageAndGuards(res->message, res); | ||||
|  | ||||
| 				res->amount = reader.readUInt32(); | ||||
| 				if(defInfo->subid == Res::GOLD) | ||||
| 				if(objTempl.subid == Res::GOLD) | ||||
| 				{ | ||||
| 					// Gold is multiplied by 100. | ||||
| 					res->amount *= 100; | ||||
| @@ -1349,7 +1284,7 @@ void CMapLoaderH3M::readObjects() | ||||
| 		case Obj::RANDOM_TOWN: | ||||
| 		case Obj::TOWN: | ||||
| 			{ | ||||
| 				nobj = readTown(defInfo->subid); | ||||
| 				nobj = readTown(objTempl.subid); | ||||
| 				break; | ||||
| 			} | ||||
| 		case Obj::MINE: | ||||
| @@ -1455,7 +1390,7 @@ void CMapLoaderH3M::readObjects() | ||||
| 			{ | ||||
| 				nobj = new CGDwelling(); | ||||
| 				CSpecObjInfo * spec = nullptr; | ||||
| 				switch(defInfo->id) | ||||
| 				switch(objTempl.id) | ||||
| 				{ | ||||
| 					break; case Obj::RANDOM_DWELLING: spec = new CCreGenLeveledCastleInfo(); | ||||
| 					break; case Obj::RANDOM_DWELLING_LVL: spec = new CCreGenAsCastleInfo(); | ||||
| @@ -1607,7 +1542,7 @@ void CMapLoaderH3M::readObjects() | ||||
| 			} | ||||
| 		case Obj::PYRAMID: //Pyramid of WoG object | ||||
| 			{ | ||||
| 				if(defInfo->subid == 0) | ||||
| 				if(objTempl.subid == 0) | ||||
| 				{ | ||||
| 					nobj = new CGPyramid(); | ||||
| 				} | ||||
| @@ -1671,13 +1606,13 @@ void CMapLoaderH3M::readObjects() | ||||
| 		} | ||||
|  | ||||
| 		nobj->pos = objPos; | ||||
| 		nobj->ID = defInfo->id; | ||||
| 		nobj->ID = objTempl.id; | ||||
| 		nobj->id = idToBeGiven; | ||||
| 		if(nobj->ID != Obj::HERO && nobj->ID != Obj::HERO_PLACEHOLDER && nobj->ID != Obj::PRISON) | ||||
| 		{ | ||||
| 			nobj->subID = defInfo->subid; | ||||
| 			nobj->subID = objTempl.subid; | ||||
| 		} | ||||
| 		nobj->defInfo = defInfo; | ||||
| 		nobj->appearance = objTempl; | ||||
| 		assert(idToBeGiven == ObjectInstanceID(map->objects.size())); | ||||
| 		map->objects.push_back(nobj); | ||||
| 		if(nobj->ID == Obj::TOWN) | ||||
|   | ||||
| @@ -14,6 +14,7 @@ | ||||
| #include "CMapService.h" | ||||
| #include "../GameConstants.h" | ||||
| #include "../ResourceSet.h" | ||||
| #include "../CDefObjInfoHandler.h" | ||||
|  | ||||
| #include "../int3.h" | ||||
|  | ||||
| @@ -250,6 +251,10 @@ private: | ||||
| 		return p; | ||||
| 	} | ||||
|  | ||||
| 	/** List of templates loaded from the map, used on later stage to create | ||||
| 	 *  objects but not needed for fully functional CMap */ | ||||
| 	std::vector<ObjectTemplate> templates; | ||||
|  | ||||
| 	/** ptr to the map object which gets filled by data from the buffer */ | ||||
| 	CMap * map; | ||||
|  | ||||
| @@ -262,4 +267,4 @@ private: | ||||
| 	CBinaryReader reader; | ||||
| 	CInputStream * inputStream; | ||||
|  | ||||
| }; | ||||
| }; | ||||
|   | ||||
| @@ -161,7 +161,7 @@ void CMapGenerator::genTowns() | ||||
| 		if(townId == CMapGenOptions::CPlayerSettings::RANDOM_TOWN) townId = gen.getInteger(0, 8); // Default towns | ||||
| 		town->subID = townId; | ||||
| 		town->tempOwner = owner; | ||||
| 		town->defInfo = VLC->dobjinfo->gobjs[town->ID][town->subID]; | ||||
| 		town->appearance = VLC->dobjinfo->pickCandidates(town->ID, town->subID, map->getTile(townPos[side]).terType).front(); | ||||
| 		town->builtBuildings.insert(BuildingID::FORT); | ||||
| 		town->builtBuildings.insert(BuildingID::DEFAULT); | ||||
| 		editManager->insertObject(town, int3(townPos[side].x, townPos[side].y + (i / 2) * 5, 0)); | ||||
|   | ||||
| @@ -5221,7 +5221,7 @@ bool CGameHandler::dig( const CGHeroInstance *h ) | ||||
| 	NewObject no; | ||||
| 	no.ID = Obj::HOLE; | ||||
| 	no.pos = h->getPosition(); | ||||
| 	no.subID = getTile(no.pos)->terType; | ||||
| 	no.subID = 0; | ||||
| 	sendAndApply(&no); | ||||
|  | ||||
| 	//take MPs | ||||
|   | ||||
		Reference in New Issue
	
	Block a user