| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531 | #include "StdInc.h"#include "maphandler.h"#include "graphics.h"#include "../lib/mapping/CMap.h"#include "../lib/mapObjects/CGHeroInstance.h"#include "../lib/mapObjects/CObjectClassesHandler.h"#include "../lib/CHeroHandler.h"#include "../lib/CTownHandler.h"#include "../lib/CModHandler.h"#include "../lib/mapping/CMap.h"#include "../lib/GameConstants.h"#include "../lib/JsonDetail.h"const int tileSize = 32;static bool objectBlitOrderSorter(const TileObject & a, const TileObject & b){	return MapHandler::compareObjectBlitOrder(a.obj, b.obj);}int MapHandler::index(int x, int y, int z) const{	return z * (sizes.x * sizes.y) + y * sizes.x + x;}int MapHandler::index(const int3 & p) const{	return index(p.x, p.y, p.z);}MapHandler::MapHandler(){	initTerrainGraphics();	logGlobal->info("\tPreparing terrain, roads, rivers, borders");}void MapHandler::reset(const CMap * Map){	ttiles.clear();	map = Map;		//sizes of terrain	sizes.x = map->width;	sizes.y = map->height;	sizes.z = map->twoLevel ? 2 : 1;		initObjectRects();	logGlobal->info("\tMaking object rects");}void MapHandler::initTerrainGraphics(){	static const std::map<std::string, std::string> ROAD_FILES =	{		{ROAD_NAMES[1], "dirtrd"},		{ROAD_NAMES[2], "gravrd"},		{ROAD_NAMES[3], "cobbrd"}	};		static const std::map<std::string, std::string> RIVER_FILES =	{		{RIVER_NAMES[1], "clrrvr"},		{RIVER_NAMES[2], "icyrvr"},		{RIVER_NAMES[3], "mudrvr"},		{RIVER_NAMES[4], "lavrvr"}	};			auto loadFlipped = [](TFlippedAnimations & animation, TFlippedCache & cache, const std::map<std::string, std::string> & files)	{		for(auto & type : files)		{			animation[type.first] = make_unique<Animation>(type.second);			animation[type.first]->preload();			const size_t views = animation[type.first]->size(0);			cache[type.first].resize(views);						for(int j = 0; j < views; j++)				cache[type.first][j] = animation[type.first]->getImage(j);		}	};		std::map<std::string, std::string> terrainFiles;	for(auto & terrain : Terrain::Manager::terrains())	{		terrainFiles[terrain] = Terrain::Manager::getInfo(terrain).tilesFilename;	}		loadFlipped(terrainAnimations, terrainImages, terrainFiles);	loadFlipped(roadAnimations, roadImages, ROAD_FILES);	loadFlipped(riverAnimations, riverImages, RIVER_FILES);}void MapHandler::drawTerrainTile(QPainter & painter, int x, int y, int z){	auto & tinfo = map->getTile(int3(x, y, z));	ui8 rotation = tinfo.extTileFlags % 4;		if(terrainImages.at(tinfo.terType).size() <= tinfo.terView)		return;		bool hflip = (rotation == 1 || rotation == 3), vflip = (rotation == 2 || rotation == 3);	painter.drawImage(x * tileSize, y * tileSize, terrainImages.at(tinfo.terType)[tinfo.terView]->mirrored(hflip, vflip));}void MapHandler::drawRoad(QPainter & painter, int x, int y, int z){	auto & tinfo = map->getTile(int3(x, y, z));	auto * tinfoUpper = map->isInTheMap(int3(x, y - 1, z)) ? &map->getTile(int3(x, y - 1, z)) : nullptr;	if (tinfoUpper && tinfoUpper->roadType != ROAD_NAMES[0])	{		QRect source(0, tileSize / 2, tileSize, tileSize / 2);		ui8 rotation = (tinfoUpper->extTileFlags >> 4) % 4;		bool hflip = (rotation == 1 || rotation == 3), vflip = (rotation == 2 || rotation == 3);		if(roadImages.at(tinfoUpper->roadType).size() > tinfoUpper->roadDir)		{			painter.drawImage(QPoint(x * tileSize, y * tileSize), roadImages.at(tinfoUpper->roadType)[tinfoUpper->roadDir]->mirrored(hflip, vflip), source);		}	}		if(tinfo.roadType != ROAD_NAMES[0]) //print road from this tile	{		QRect source(0, 0, tileSize, tileSize / 2);		ui8 rotation = (tinfo.extTileFlags >> 4) % 4;		bool hflip = (rotation == 1 || rotation == 3), vflip = (rotation == 2 || rotation == 3);		if(roadImages.at(tinfo.roadType).size() > tinfo.roadDir)		{			painter.drawImage(QPoint(x * tileSize, y * tileSize + tileSize / 2), roadImages.at(tinfo.roadType)[tinfo.roadDir]->mirrored(hflip, vflip), source);		}	}}void MapHandler::drawRiver(QPainter & painter, int x, int y, int z){	auto & tinfo = map->getTile(int3(x, y, z));	if(tinfo.riverType == RIVER_NAMES[0])		return;	if(riverImages.at(tinfo.riverType).size() <= tinfo.riverDir)		return;	ui8 rotation = (tinfo.extTileFlags >> 2) % 4;	bool hflip = (rotation == 1 || rotation == 3), vflip = (rotation == 2 || rotation == 3);	painter.drawImage(x * tileSize, y * tileSize, riverImages.at(tinfo.riverType)[tinfo.riverDir]->mirrored(hflip, vflip));}void setPlayerColor(QImage * sur, PlayerColor player){	if(player == PlayerColor::UNFLAGGABLE)		return;	if(sur->format() == QImage::Format_Indexed8)	{		QRgb color = graphics->neutralColor;		if(player != PlayerColor::NEUTRAL && player < PlayerColor::PLAYER_LIMIT)			color = graphics->playerColors.at(player.getNum());		sur->setColor(5, color);	}	else		logGlobal->warn("Warning, setPlayerColor called on not 8bpp surface!");}void MapHandler::initObjectRects(){	ttiles.resize(sizes.x * sizes.y * sizes.z);		//initializing objects / rects	for(const CGObjectInstance * elem : map->objects)	{		CGObjectInstance *obj = const_cast<CGObjectInstance *>(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)		{			continue;		}				std::shared_ptr<Animation> animation = graphics->getAnimation(obj);				//no animation at all		if(!animation)			continue;				//empty animation		if(animation->size(0) == 0)			continue;				auto image = animation->getImage(0, obj->ID == Obj::HERO ? 2 : 0);		if(!image)		{			//workaround for prisons			image = animation->getImage(0, 0);			if(!image)				continue;		}							for(int fx=0; fx < obj->getWidth(); ++fx)		{			for(int fy=0; fy < obj->getHeight(); ++fy)			{				int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z);				QRect cr(image->width() - fx * tileSize - tileSize,						 image->height() - fy * tileSize - tileSize,						 image->width(),						 image->height());								TileObject toAdd(obj, cr);								if( map->isInTheMap(currTile) && // within map				   cr.x() + cr.width() > 0 &&           // image has data on this tile				   cr.y() + cr.height() > 0)				{					ttiles[index(currTile)].push_back(toAdd);				}			}		}	}		for(auto & tt : ttiles)	{		stable_sort(tt.begin(), tt.end(), objectBlitOrderSorter);	}}bool MapHandler::compareObjectBlitOrder(const CGObjectInstance * a, const CGObjectInstance * b){	if (!a)		return true;	if (!b)		return false;	if (a->appearance->printPriority != b->appearance->printPriority)		return a->appearance->printPriority > b->appearance->printPriority;		if(a->pos.y != b->pos.y)		return a->pos.y < b->pos.y;		if(b->ID == Obj::HERO && a->ID != Obj::HERO)		return true;	if(b->ID != Obj::HERO && a->ID == Obj::HERO)		return false;		if(!a->isVisitable() && b->isVisitable())		return true;	if(!b->isVisitable() && a->isVisitable())		return false;	if(a->pos.x < b->pos.x)		return true;	return false;}TileObject::TileObject(CGObjectInstance * obj_, QRect rect_): obj(obj_),rect(rect_){}TileObject::~TileObject(){}std::shared_ptr<QImage> MapHandler::findFlagBitmap(const CGHeroInstance * hero, int anim, const PlayerColor color, int group) const{	if(!hero || hero->boat)		return std::shared_ptr<QImage>();		return findFlagBitmapInternal(graphics->heroFlagAnimations.at(color.getNum()), anim, group, hero->moveDir, !hero->isStanding);}std::shared_ptr<QImage> MapHandler::findFlagBitmapInternal(std::shared_ptr<Animation> animation, int anim, int group, ui8 dir, bool moving) const{	size_t groupSize = animation->size(group);	if(groupSize == 0)		return nullptr;		if(moving)		return animation->getImage(anim % groupSize, group);	else		return animation->getImage((anim / 4) % groupSize, group);}MapHandler::AnimBitmapHolder MapHandler::findObjectBitmap(const CGObjectInstance * obj, int anim, int group) const{	if(!obj)		return MapHandler::AnimBitmapHolder();	// normal object	std::shared_ptr<Animation> animation = graphics->getAnimation(obj);	size_t groupSize = animation->size(group);	if(groupSize == 0)		return MapHandler::AnimBitmapHolder();		animation->playerColored(obj->tempOwner);	auto bitmap = animation->getImage(anim % groupSize, group);		if(!bitmap)		return MapHandler::AnimBitmapHolder();	setPlayerColor(bitmap.get(), obj->tempOwner);		return MapHandler::AnimBitmapHolder(bitmap);}std::vector<TileObject> & MapHandler::getObjects(int x, int y, int z){	return ttiles[index(x, y, z)];}void MapHandler::drawObjects(QPainter & painter, int x, int y, int z){	for(auto & object : getObjects(x, y, z))	{		const CGObjectInstance * obj = object.obj;		if(!obj)		{			logGlobal->error("Stray map object that isn't fading");			return;		}		uint8_t animationFrame = 0;		auto objData = findObjectBitmap(obj, animationFrame, obj->ID == Obj::HERO ? 2 : 0);		if(obj->ID == Obj::HERO && obj->tempOwner.isValidPlayer())			objData.flagBitmap = findFlagBitmap(dynamic_cast<const CGHeroInstance*>(obj), 0, obj->tempOwner, 4);				if(objData.objBitmap)		{			auto pos = obj->getPosition();			painter.drawImage(QPoint(x * tileSize, y * tileSize), *objData.objBitmap, object.rect);						if(objData.flagBitmap)			{				if(x == pos.x && y == pos.y)					painter.drawImage(QPoint((x - 2) * tileSize, (y - 1) * tileSize), *objData.flagBitmap);			}		}	}}void MapHandler::drawObject(QPainter & painter, const TileObject & object){	const CGObjectInstance * obj = object.obj;	if (!obj)	{		logGlobal->error("Stray map object that isn't fading");		return;	}	uint8_t animationFrame = 0;	auto objData = findObjectBitmap(obj, animationFrame, obj->ID == Obj::HERO ? 2 : 0);	if(obj->ID == Obj::HERO && obj->tempOwner.isValidPlayer())		objData.flagBitmap = findFlagBitmap(dynamic_cast<const CGHeroInstance*>(obj), 0, obj->tempOwner, 0);		if (objData.objBitmap)	{		auto pos = obj->getPosition();		painter.drawImage(pos.x * tileSize - object.rect.x(), pos.y * tileSize - object.rect.y(), *objData.objBitmap);				if (objData.flagBitmap)		{			if(object.rect.x() == pos.x && object.rect.y() == pos.y)				painter.drawImage(pos.x * tileSize - object.rect.x(), pos.y * tileSize - object.rect.y(), *objData.flagBitmap);		}	}}void MapHandler::drawObjectAt(QPainter & painter, const CGObjectInstance * obj, int x, int y){	if (!obj)	{		logGlobal->error("Stray map object that isn't fading");		return;	}	uint8_t animationFrame = 0;	auto objData = findObjectBitmap(obj, animationFrame, obj->ID == Obj::HERO ? 2 : 0);	if(obj->ID == Obj::HERO && obj->tempOwner.isValidPlayer())		objData.flagBitmap = findFlagBitmap(dynamic_cast<const CGHeroInstance*>(obj), 0, obj->tempOwner, 4);		if (objData.objBitmap)	{		painter.drawImage(QPoint((x + 1) * 32 - objData.objBitmap->width(), (y + 1) * 32 - objData.objBitmap->height()), *objData.objBitmap);				if (objData.flagBitmap)			painter.drawImage(QPoint((x + 1) * 32 - objData.objBitmap->width(), (y + 1) * 32 - objData.objBitmap->height()), *objData.flagBitmap);	}}QRgb MapHandler::getTileColor(int x, int y, int z){	// if object at tile is owned - it will be colored as its owner	for(auto & object : getObjects(x, y, z))	{		if(!object.obj->getBlockedPos().count(int3(x, y, z)))			continue;				PlayerColor player = object.obj->getOwner();		if(player == PlayerColor::NEUTRAL)			return graphics->neutralColor;		else			if (player < PlayerColor::PLAYER_LIMIT)				return graphics->playerColors[player.getNum()];	}		// else - use terrain color (blocked version or normal)	auto & tile = map->getTile(int3(x, y, z));	auto color = Terrain::Manager::getInfo(tile.terType).minimapUnblocked;	if (tile.blocked && (!tile.visitable))		color = Terrain::Manager::getInfo(tile.terType).minimapBlocked;		return qRgb(color[0], color[1], color[2]);}void MapHandler::drawMinimapTile(QPainter & painter, int x, int y, int z){	painter.setPen(getTileColor(x, y, z));	painter.drawPoint(x, y);}void MapHandler::invalidate(int x, int y, int z){	auto & objects = getObjects(x, y, z);		for(auto obj = objects.begin(); obj != objects.end();)	{		//object was removed		if(std::find(map->objects.begin(), map->objects.end(), obj->obj) == map->objects.end())		{			obj = objects.erase(obj);			continue;		}					//object was moved		auto & pos = obj->obj->pos;		if(pos.z != z || pos.x < x || pos.y < y || pos.x - obj->obj->getWidth() >= x || pos.y - obj->obj->getHeight() >= y)		{			obj = objects.erase(obj);			continue;		}				++obj;	}		stable_sort(objects.begin(), objects.end(), objectBlitOrderSorter);}void MapHandler::invalidate(CGObjectInstance * obj){	std::shared_ptr<Animation> animation = graphics->getAnimation(obj);			//no animation at all or empty animation	if(!animation || animation->size(0) == 0)		return;			auto image = animation->getImage(0, obj->ID == Obj::HERO ? 2 : 0);	if(!image)		return;		for(int fx=0; fx < obj->getWidth(); ++fx)	{		for(int fy=0; fy < obj->getHeight(); ++fy)		{			//object presented on the tile			int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z);			QRect cr(image->width() - fx * tileSize - tileSize, image->height() - fy * tileSize - tileSize, image->width(), image->height());						if( map->isInTheMap(currTile) && // within map			   cr.x() + cr.width() > 0 &&           // image has data on this tile			   cr.y() + cr.height() > 0)			{				auto & objects = ttiles[index(currTile)];				bool found = false;				for(auto & o : objects)				{					if(o.obj == obj)					{						o.rect = cr;						found = true;						break;					}				}				if(!found)					objects.emplace_back(obj, cr);								stable_sort(objects.begin(), objects.end(), objectBlitOrderSorter);			}		}	}}std::vector<int3> MapHandler::getTilesUnderObject(CGObjectInstance * obj) const{	std::vector<int3> result;	for(int fx=0; fx < obj->getWidth(); ++fx)	{		for(int fy=0; fy < obj->getHeight(); ++fy)		{			//object presented on the tile			int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z);			if(map->isInTheMap(currTile)) // within map			{				result.push_back(currTile);			}		}	}	return result;}void MapHandler::invalidateObjects(){	for(auto obj : map->objects)	{		invalidate(obj);	}}void MapHandler::invalidate(const std::vector<int3> & tiles){	for(auto & currTile : tiles)	{		invalidate(currTile.x, currTile.y, currTile.z);	}}
 |