From 3c062bae32667c9f71208c2384ae460ba6b411cd Mon Sep 17 00:00:00 2001 From: nordsoft Date: Tue, 30 Aug 2022 06:38:24 +0400 Subject: [PATCH] Showing objects --- mapeditor/BitmapHandler.cpp | 2 +- mapeditor/CMakeLists.txt | 2 + mapeditor/graphics.cpp | 359 ++++++++++++++++++++++++++++++++++++ mapeditor/graphics.h | 83 +++++++++ mapeditor/mainwindow.cpp | 14 +- mapeditor/maphandler.cpp | 266 ++++++++++++++++++++++++-- mapeditor/maphandler.h | 59 +++++- 7 files changed, 765 insertions(+), 20 deletions(-) create mode 100644 mapeditor/graphics.cpp create mode 100644 mapeditor/graphics.h diff --git a/mapeditor/BitmapHandler.cpp b/mapeditor/BitmapHandler.cpp index 7eedc8582..5c33526f2 100644 --- a/mapeditor/BitmapHandler.cpp +++ b/mapeditor/BitmapHandler.cpp @@ -62,7 +62,7 @@ QImage BitmapHandler::loadH3PCX(ui8 * pcx, size_t size) { it = 0xC; //auto bitmap = QBitmap::fromData(qsize, pcx + it); - QImage image(pcx + it, width, height, QImage::Format_MonoLSB); + QImage image(pcx + it, width, height, QImage::Format_Indexed8); //palette - last 256*3 bytes QVector colorTable; diff --git a/mapeditor/CMakeLists.txt b/mapeditor/CMakeLists.txt index f9ba1818c..bca71cadf 100644 --- a/mapeditor/CMakeLists.txt +++ b/mapeditor/CMakeLists.txt @@ -8,6 +8,7 @@ set(editor_SRCS BitmapHandler.cpp maphandler.cpp Animation.cpp + graphics.cpp ) set(editor_HEADERS @@ -19,6 +20,7 @@ set(editor_HEADERS BitmapHandler.h maphandler.h Animation.h + graphics.h ) set(editor_FORMS diff --git a/mapeditor/graphics.cpp b/mapeditor/graphics.cpp new file mode 100644 index 000000000..43baed092 --- /dev/null +++ b/mapeditor/graphics.cpp @@ -0,0 +1,359 @@ +/* + * Graphics.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#include "StdInc.h" +#include "graphics.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "../lib/filesystem/Filesystem.h" +#include "../lib/filesystem/CBinaryReader.h" +#include "Animation.h" +#include "../lib/CThreadHelper.h" +#include "../lib/CModHandler.h" +#include "../lib/VCMI_Lib.h" +#include "../CCallback.h" +#include "../lib/CGeneralTextHandler.h" +#include "BitmapHandler.h" +#include "../lib/CGameState.h" +#include "../lib/JsonNode.h" +#include "../lib/vcmi_endian.h" +#include "../lib/CStopWatch.h" +#include "../lib/mapObjects/CObjectClassesHandler.h" +#include "../lib/mapObjects/CObjectHandler.h" +#include "../lib/CHeroHandler.h" +#include "CGameInfo.h" + +Graphics * graphics = nullptr; + +void Graphics::loadPaletteAndColors() +{ + auto textFile = CResourceHandler::get()->load(ResourceID("DATA/PLAYERS.PAL"))->readAll(); + std::string pals((char*)textFile.first.get(), textFile.second); + + playerColorPalette.resize(256); + playerColors.resize(PlayerColor::PLAYER_LIMIT_I); + int startPoint = 24; //beginning byte; used to read + for(int i=0; i<256; ++i) + { + QColor col; + col.setRed(pals[startPoint++]); + col.setGreen(pals[startPoint++]); + col.setBlue(pals[startPoint++]); + col.setAlpha(255); + startPoint++; + playerColorPalette[i] = col.rgba(); + } + + neutralColorPalette.resize(32); + + auto stream = CResourceHandler::get()->load(ResourceID("config/NEUTRAL.PAL")); + CBinaryReader reader(stream.get()); + + for(int i=0; i<32; ++i) + { + QColor col; + col.setRed(reader.readUInt8()); + col.setGreen(reader.readUInt8()); + col.setBlue(reader.readUInt8()); + col.setAlpha(255); + reader.readUInt8(); // this is "flags" entry, not alpha + neutralColorPalette[i] = col.rgba(); + } + + //colors initialization + QColor colors[] = { + {0xff,0, 0, 255}, + {0x31,0x52,0xff,255}, + {0x9c,0x73,0x52,255}, + {0x42,0x94,0x29,255}, + + {0xff,0x84,0, 255}, + {0x8c,0x29,0xa5,255}, + {0x09,0x9c,0xa5,255}, + {0xc6,0x7b,0x8c,255}}; + + for(int i=0;i<8;i++) + { + playerColors[i] = colors[i].rgba(); + } + //gray + neutralColor = qRgba(0x84, 0x84, 0x84, 0xFF); +} + +Graphics::Graphics() +{ +#if 0 + + std::vector tasks; //preparing list of graphics to load + tasks += std::bind(&Graphics::loadFonts,this); + tasks += std::bind(&Graphics::loadPaletteAndColors,this); + tasks += std::bind(&Graphics::initializeBattleGraphics,this); + tasks += std::bind(&Graphics::loadErmuToPicture,this); + tasks += std::bind(&Graphics::initializeImageLists,this); + + CThreadHelper th(&tasks,std::max((ui32)1,boost::thread::hardware_concurrency())); + th.run(); +#else + loadPaletteAndColors(); + initializeImageLists(); +#endif + + //(!) do not load any CAnimation here +} + +Graphics::~Graphics() +{ +} + +void Graphics::load() +{ + loadHeroAnimations(); + loadHeroFlagAnimations(); +} + +void Graphics::loadHeroAnimations() +{ + for(auto & elem : CGI->heroh->classes.objects) + { + for (auto & templ : VLC->objtypeh->getHandlerFor(Obj::HERO, elem->getIndex())->getTemplates()) + { + if (!heroAnimations.count(templ.animationFile)) + heroAnimations[templ.animationFile] = loadHeroAnimation(templ.animationFile); + } + } + + boatAnimations[0] = loadHeroAnimation("AB01_.DEF"); + boatAnimations[1] = loadHeroAnimation("AB02_.DEF"); + boatAnimations[2] = loadHeroAnimation("AB03_.DEF"); + + + mapObjectAnimations["AB01_.DEF"] = boatAnimations[0]; + mapObjectAnimations["AB02_.DEF"] = boatAnimations[1]; + mapObjectAnimations["AB03_.DEF"] = boatAnimations[2]; +} +void Graphics::loadHeroFlagAnimations() +{ + static const std::vector HERO_FLAG_ANIMATIONS = + { + "AF00", "AF01","AF02","AF03", + "AF04", "AF05","AF06","AF07" + }; + + static const std::vector< std::vector > BOAT_FLAG_ANIMATIONS = + { + { + "ABF01L", "ABF01G", "ABF01R", "ABF01D", + "ABF01B", "ABF01P", "ABF01W", "ABF01K" + }, + { + "ABF02L", "ABF02G", "ABF02R", "ABF02D", + "ABF02B", "ABF02P", "ABF02W", "ABF02K" + }, + { + "ABF03L", "ABF03G", "ABF03R", "ABF03D", + "ABF03B", "ABF03P", "ABF03W", "ABF03K" + } + }; + + for(const auto & name : HERO_FLAG_ANIMATIONS) + heroFlagAnimations.push_back(loadHeroFlagAnimation(name)); + + for(int i = 0; i < BOAT_FLAG_ANIMATIONS.size(); i++) + for(const auto & name : BOAT_FLAG_ANIMATIONS[i]) + boatFlagAnimations[i].push_back(loadHeroFlagAnimation(name)); +} + +std::shared_ptr Graphics::loadHeroFlagAnimation(const std::string & name) +{ + //first - group number to be rotated, second - group number after rotation + static const std::vector > rotations = + { + {6,10}, {7,11}, {8,12}, {1,13}, + {2,14}, {3,15} + }; + + std::shared_ptr anim = std::make_shared(name); + anim->preload(); + + for(const auto & rotation : rotations) + { + const int sourceGroup = rotation.first; + const int targetGroup = rotation.second; + + anim->createFlippedGroup(sourceGroup, targetGroup); + } + + return anim; +} + +std::shared_ptr Graphics::loadHeroAnimation(const std::string &name) +{ + //first - group number to be rotated, second - group number after rotation + static const std::vector > rotations = + { + {6,10}, {7,11}, {8,12}, {1,13}, + {2,14}, {3,15} + }; + + std::shared_ptr anim = std::make_shared(name); + anim->preload(); + + + for(const auto & rotation : rotations) + { + const int sourceGroup = rotation.first; + const int targetGroup = rotation.second; + + anim->createFlippedGroup(sourceGroup, targetGroup); + } + + return anim; +} + +void Graphics::blueToPlayersAdv(QImage * sur, PlayerColor player) +{ + if(sur->format() == QImage::Format_Indexed8) + { + auto palette = sur->colorTable(); + if(player < PlayerColor::PLAYER_LIMIT) + { + for(int i = 0; i < 32; ++i) + palette[224 + i] = playerColorPalette[player.getNum() * 32 + i]; + } + else if(player == PlayerColor::NEUTRAL) + { + palette = neutralColorPalette; + } + else + { + logGlobal->error("Wrong player id in blueToPlayersAdv (%s)!", player.getStr()); + return; + } + //FIXME: not all player colored images have player palette at last 32 indexes + //NOTE: following code is much more correct but still not perfect (bugged with status bar) + sur->setColorTable(palette); + +#if 0 + + SDL_Color * bluePalette = playerColorPalette + 32; + + SDL_Palette * oldPalette = sur->format->palette; + + SDL_Palette * newPalette = SDL_AllocPalette(256); + + for(size_t destIndex = 0; destIndex < 256; destIndex++) + { + SDL_Color old = oldPalette->colors[destIndex]; + + bool found = false; + + for(size_t srcIndex = 0; srcIndex < 32; srcIndex++) + { + if(old.b == bluePalette[srcIndex].b && old.g == bluePalette[srcIndex].g && old.r == bluePalette[srcIndex].r) + { + found = true; + newPalette->colors[destIndex] = palette[srcIndex]; + break; + } + } + if(!found) + newPalette->colors[destIndex] = old; + } + + SDL_SetSurfacePalette(sur, newPalette); + + SDL_FreePalette(newPalette); + +#endif // 0 + } + else + { + //TODO: implement. H3 method works only for images with palettes. + // Add some kind of player-colored overlay? + // Or keep palette approach here and replace only colors of specific value(s) + // Or just wait for OpenGL support? + logGlobal->warn("Image must have palette to be player-colored!"); + } +} + +std::shared_ptr Graphics::getAnimation(const CGObjectInstance* obj) +{ + return getAnimation(obj->appearance); +} + +std::shared_ptr Graphics::getAnimation(const ObjectTemplate & info) +{ + //the only(?) invisible object + if(info.id == Obj::EVENT) + { + return std::shared_ptr(); + } + + if(info.animationFile.empty()) + { + logGlobal->warn("Def name for obj (%d,%d) is empty!", info.id, info.subid); + return std::shared_ptr(); + } + + std::shared_ptr ret = mapObjectAnimations[info.animationFile]; + + //already loaded + if(ret) + { + ret->preload(); + return ret; + } + + ret = std::make_shared(info.animationFile); + mapObjectAnimations[info.animationFile] = ret; + + ret->preload(); + return ret; +} + +void Graphics::addImageListEntry(size_t index, const std::string & listName, const std::string & imageName) +{ + if (!imageName.empty()) + { + JsonNode entry; + entry["frame"].Integer() = index; + entry["file"].String() = imageName; + + imageLists["SPRITES/" + listName]["images"].Vector().push_back(entry); + } +} + +void Graphics::addImageListEntries(const EntityService * service) +{ + auto cb = std::bind(&Graphics::addImageListEntry, this, _1, _2, _3); + + auto loopCb = [&](const Entity * entity, bool & stop) + { + entity->registerIcons(cb); + }; + + service->forEachBase(loopCb); +} + +void Graphics::initializeImageLists() +{ + addImageListEntries(CGI->creatures()); + addImageListEntries(CGI->heroTypes()); + addImageListEntries(CGI->artifacts()); + addImageListEntries(CGI->factions()); + addImageListEntries(CGI->spells()); + addImageListEntries(CGI->skills()); +} diff --git a/mapeditor/graphics.h b/mapeditor/graphics.h new file mode 100644 index 000000000..270d6356e --- /dev/null +++ b/mapeditor/graphics.h @@ -0,0 +1,83 @@ +/* + * Graphics.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once + +#include "../lib/GameConstants.h" +#include + +class CGHeroInstance; +class CGTownInstance; +class CHeroClass; +struct InfoAboutHero; +struct InfoAboutTown; +class CGObjectInstance; +class ObjectTemplate; +class Animation; +class EntityService; +class JsonNode; + +/// Handles fonts, hero images, town images, various graphics +class Graphics +{ + void addImageListEntry(size_t index, const std::string & listName, const std::string & imageName); + + void addImageListEntries(const EntityService * service); + + void initializeBattleGraphics(); + void loadPaletteAndColors(); + + void loadHeroAnimations(); + //loads animation and adds required rotated frames + std::shared_ptr loadHeroAnimation(const std::string &name); + + void loadHeroFlagAnimations(); + + //loads animation and adds required rotated frames + std::shared_ptr loadHeroFlagAnimation(const std::string &name); + + void loadErmuToPicture(); + void loadFogOfWar(); + void loadFonts(); + void initializeImageLists(); + +public: + //various graphics + QVector playerColors; //array [8] + QRgb neutralColor; + QVector playerColorPalette; //palette to make interface colors good - array of size [256] + QVector neutralColorPalette; + + // [hero class def name] //added group 10: up - left, 11 - left and 12 - left down // 13 - up-left standing; 14 - left standing; 15 - left down standing + std::map< std::string, std::shared_ptr > heroAnimations; + std::vector< std::shared_ptr > heroFlagAnimations; + + // [boat type: 0 .. 2] //added group 10: up - left, 11 - left and 12 - left down // 13 - up-left standing; 14 - left standing; 15 - left down standing + std::array< std::shared_ptr, 3> boatAnimations; + + std::array< std::vector >, 3> boatFlagAnimations; + + //all other objects (not hero or boat) + std::map< std::string, std::shared_ptr > mapObjectAnimations; + + std::map imageLists; + + //functions + Graphics(); + ~Graphics(); + + void load(); + + void blueToPlayersAdv(QImage * sur, PlayerColor player); //replaces blue interface colour with a color of player + + std::shared_ptr getAnimation(const CGObjectInstance * obj); + std::shared_ptr getAnimation(const ObjectTemplate & info); +}; + +extern Graphics * graphics; diff --git a/mapeditor/mainwindow.cpp b/mapeditor/mainwindow.cpp index bce543591..864494d86 100644 --- a/mapeditor/mainwindow.cpp +++ b/mapeditor/mainwindow.cpp @@ -19,6 +19,7 @@ #include "CGameInfo.h" #include "maphandler.h" +#include "graphics.h" static CBasicLogConfigurator * logConfig; @@ -72,6 +73,9 @@ MainWindow::MainWindow(QWidget *parent) : CGI = new CGameInfo(); //contains all global informations about game (texts, lodHandlers, map handler etc.) init(); + graphics = new Graphics(); // should be before curh->init() + graphics->load();//must be after Content loading but should be in main thread + if(!testFile("DATA/new-menu/Background.png", "Cannot find file")) { @@ -128,7 +132,15 @@ void MainWindow::on_actionOpen_triggered() { for(int i = 0; i < map->width; ++i) { - mapHandler.drawTerrainTile(i, j, map->getTile((int3(i, j, 0)))); + mapHandler.drawTerrainTile(i, j, 0); + } + } + + for(int j = 0; j < map->height; ++j) + { + for(int i = 0; i < map->width; ++i) + { + mapHandler.drawObjects(i, j, 0); } } diff --git a/mapeditor/maphandler.cpp b/mapeditor/maphandler.cpp index ef21e803e..b3f82ab98 100644 --- a/mapeditor/maphandler.cpp +++ b/mapeditor/maphandler.cpp @@ -1,9 +1,23 @@ #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 TerrainTileObject & a, const TerrainTileObject & b) +{ + return MapHandler::compareObjectBlitOrder(a.obj, b.obj); +} + MapHandler::MapHandler(const CMap * Map): map(Map), surface(Map->width * tileSize, Map->height * tileSize), painter(&surface) { @@ -64,10 +78,13 @@ void MapHandler::initTerrainGraphics() loadFlipped(terrainAnimations, terrainImages, terrainFiles); loadFlipped(roadAnimations, roadImages, ROAD_FILES); loadFlipped(riverAnimations, riverImages, RIVER_FILES); + + ttiles.resize(sizes.x * sizes.y * sizes.z); } -void MapHandler::drawTerrainTile(int x, int y, const TerrainTile & tinfo) +void MapHandler::drawTerrainTile(int x, int y, int z) { + auto & tinfo = map->getTile(int3(x, y, z)); //Rect destRect(realTileRect); ui8 rotation = tinfo.extTileFlags % 4; @@ -83,7 +100,7 @@ void MapHandler::drawTerrainTile(int x, int y, const TerrainTile & tinfo) void MapHandler::initObjectRects() { //initializing objects / rects - /*for(auto & elem : map->objects) + for(auto & elem : map->objects) { const CGObjectInstance *obj = elem; if( !obj @@ -104,40 +121,255 @@ void MapHandler::initObjectRects() continue; auto image = animation->getImage(0,0); - + bool real = true; 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); - SDL_Rect cr; - cr.w = 32; - cr.h = 32; - cr.x = image->width() - fx * 32 - 32; - cr.y = image->height() - fy * 32 - 32; - TerrainTileObject toAdd(obj, cr, obj->visitableAt(currTile.x, currTile.y)); + QRect cr(image->width() - fx * 32 - 32, image->height() - fy * 32 - 32, image->width(), image->height()); + TerrainTileObject toAdd(obj, cr, real/*obj->visitableAt(currTile.x, currTile.y)*/); + real = false; if( map->isInTheMap(currTile) && // within map - cr.x + cr.w > 0 && // image has data on this tile - cr.y + cr.h > 0 && + cr.x() + cr.width() > 0 && // image has data on this tile + cr.y() + cr.height() > 0 && obj->coveringAt(currTile.x, currTile.y) // object is visible here ) { - ttiles[currTile.x][currTile.y][currTile.z].objects.push_back(toAdd); + ttiles[currTile.z * (sizes.x * sizes.y) + currTile.y * sizes.x + currTile.x].objects.push_back(toAdd); } } } } - for(int ix=0; ixappearance.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; +} + +TerrainTileObject::TerrainTileObject(const CGObjectInstance * obj_, QRect rect_, bool real_) +: obj(obj_), +rect(rect_), +real(real_) +{ +} + +TerrainTileObject::~TerrainTileObject() +{ +} + +ui8 MapHandler::getHeroFrameGroup(ui8 dir, bool isMoving) const +{ + if(isMoving) + { + static const ui8 frame [] = {0xff, 10, 5, 6, 7, 8, 9, 12, 11}; + return frame[dir]; + } + else //if(isMoving) + { + static const ui8 frame [] = {0xff, 13, 0, 1, 2, 3, 4, 15, 14}; + return frame[dir]; + } +} + +ui8 MapHandler::getPhaseShift(const CGObjectInstance *object) const +{ + auto i = animationPhase.find(object); + if(i == animationPhase.end()) + { + ui8 ret = CRandomGenerator::getDefault().nextInt(254); + animationPhase[object] = ret; + return ret; + } + + return i->second; +} + +MapHandler::AnimBitmapHolder MapHandler::findHeroBitmap(const CGHeroInstance * hero, int anim) const +{ + if(hero && hero->moveDir && hero->type) //it's hero or boat + { + if(hero->tempOwner >= PlayerColor::PLAYER_LIMIT) //Neutral hero? { - for(int iz=0; izerror("A neutral hero (%s) at %s. Should not happen!", hero->name, hero->pos.toString()); + return MapHandler::AnimBitmapHolder(); + } + + //pick graphics of hero (or boat if hero is sailing) + std::shared_ptr animation; + if (hero->boat) + animation = graphics->boatAnimations[hero->boat->subID]; + else + animation = graphics->heroAnimations[hero->appearance.animationFile]; + + bool moving = !hero->isStanding; + int group = getHeroFrameGroup(hero->moveDir, moving); + + if(animation->size(group) > 0) + { + int frame = anim % animation->size(group); + auto heroImage = animation->getImage(frame, group); + + //get flag overlay only if we have main image + auto flagImage = findFlagBitmap(hero, anim, &hero->tempOwner, group); + + return MapHandler::AnimBitmapHolder(heroImage, flagImage); + } + } + return MapHandler::AnimBitmapHolder(); +} + +MapHandler::AnimBitmapHolder MapHandler::findBoatBitmap(const CGBoat * boat, int anim) const +{ + auto animation = graphics->boatAnimations.at(boat->subID); + int group = getHeroFrameGroup(boat->direction, false); + if(animation->size(group) > 0) + return MapHandler::AnimBitmapHolder(animation->getImage(anim % animation->size(group), group)); + else + return MapHandler::AnimBitmapHolder(); +} + +std::shared_ptr MapHandler::findFlagBitmap(const CGHeroInstance * hero, int anim, const PlayerColor * color, int group) const +{ + if(!hero) + return std::shared_ptr(); + + if(hero->boat) + return findBoatFlagBitmap(hero->boat, anim, color, group, hero->moveDir); + return findHeroFlagBitmap(hero, anim, color, group); +} + +std::shared_ptr MapHandler::findHeroFlagBitmap(const CGHeroInstance * hero, int anim, const PlayerColor * color, int group) const +{ + return findFlagBitmapInternal(graphics->heroFlagAnimations.at(color->getNum()), anim, group, hero->moveDir, !hero->isStanding); +} + +std::shared_ptr MapHandler::findBoatFlagBitmap(const CGBoat * boat, int anim, const PlayerColor * color, int group, ui8 dir) const +{ + int boatType = boat->subID; + if(boatType < 0 || boatType >= graphics->boatFlagAnimations.size()) + { + logGlobal->error("Not supported boat subtype: %d", boat->subID); + return nullptr; + } + + const auto & subtypeFlags = graphics->boatFlagAnimations.at(boatType); + + int colorIndex = color->getNum(); + + if(colorIndex < 0 || colorIndex >= subtypeFlags.size()) + { + logGlobal->error("Invalid player color %d", colorIndex); + return nullptr; + } + + return findFlagBitmapInternal(subtypeFlags.at(colorIndex), anim, group, dir, false); +} + +std::shared_ptr MapHandler::findFlagBitmapInternal(std::shared_ptr 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) const +{ + if (!obj) + return MapHandler::AnimBitmapHolder(); + if (obj->ID == Obj::HERO) + return findHeroBitmap(static_cast(obj), anim); + if (obj->ID == Obj::BOAT) + return findBoatBitmap(static_cast(obj), anim); + + // normal object + std::shared_ptr animation = graphics->getAnimation(obj); + size_t groupSize = animation->size(); + if(groupSize == 0) + return MapHandler::AnimBitmapHolder(); + + animation->playerColored(obj->tempOwner); + auto bitmap = animation->getImage((anim + getPhaseShift(obj)) % groupSize); + if(!bitmap) + return MapHandler::AnimBitmapHolder(); + + return MapHandler::AnimBitmapHolder(bitmap); +} + +void MapHandler::drawObjects(int x, int y, int z) +{ + auto & objects = ttiles[z * (sizes.x * sizes.y) + y * sizes.x + x].objects; + for(auto & object : objects) + { + if(!object.real) + continue; + + //if(object.visi) + const CGObjectInstance * obj = object.obj; + if (!obj) + { + logGlobal->error("Stray map object that isn't fading"); + continue; + } + + uint8_t animationFrame = 0; + + auto objData = findObjectBitmap(obj, animationFrame); + if (objData.objBitmap) + { + QRect srcRect(object.rect.x(), object.rect.y(), tileSize, tileSize); + + painter.drawImage(x * tileSize, y * tileSize, *objData.objBitmap); + //drawObject(targetSurf, objData.objBitmap, &srcRect, objData.isMoving); + if (objData.flagBitmap) { - stable_sort(ttiles[ix][iy][iz].objects.begin(), ttiles[ix][iy][iz].objects.end(), objectBlitOrderSorter); + /*if (objData.isMoving) + { + srcRect.y += FRAMES_PER_MOVE_ANIM_GROUP * 2 - tileSize; + Rect dstRect(realPos.x, realPos.y - tileSize / 2, tileSize, tileSize); + drawHeroFlag(targetSurf, objData.flagBitmap, &srcRect, &dstRect, true); + } + else if (obj->pos.x == pos.x && obj->pos.y == pos.y) + { + Rect dstRect(realPos.x - 2 * tileSize, realPos.y - tileSize, 3 * tileSize, 2 * tileSize); + drawHeroFlag(targetSurf, objData.flagBitmap, nullptr, &dstRect, false); + }*/ } } - }*/ + } } diff --git a/mapeditor/maphandler.h b/mapeditor/maphandler.h index 960ae77ba..405870078 100644 --- a/mapeditor/maphandler.h +++ b/mapeditor/maphandler.h @@ -7,10 +7,58 @@ #include #include +#include + +class CGObjectInstance; +class CGBoat; +struct PlayerColor; + +struct TerrainTileObject +{ + const CGObjectInstance *obj; + QRect rect; + bool real; + + TerrainTileObject(const CGObjectInstance *obj_, QRect rect_, bool visitablePos = false); + ~TerrainTileObject(); +}; + +struct TerrainTile2 +{ + std::vector objects; //pointers to objects being on this tile with rects to be easier to blit this tile on screen +}; class MapHandler { public: + struct AnimBitmapHolder + { + std::shared_ptr objBitmap; // main object bitmap + std::shared_ptr flagBitmap; // flag bitmap for the object (probably only for heroes and boats with heroes) + + AnimBitmapHolder(std::shared_ptr objBitmap_ = nullptr, std::shared_ptr flagBitmap_ = nullptr) + : objBitmap(objBitmap_), + flagBitmap(flagBitmap_) + {} + }; + +private: + + ui8 getHeroFrameGroup(ui8 dir, bool isMoving) const; + ui8 getPhaseShift(const CGObjectInstance *object) const; + + // internal helper methods to choose correct bitmap(s) for object; called internally by findObjectBitmap + AnimBitmapHolder findHeroBitmap(const CGHeroInstance * hero, int anim) const; + AnimBitmapHolder findBoatBitmap(const CGBoat * hero, int anim) const; + std::shared_ptr findFlagBitmap(const CGHeroInstance * obj, int anim, const PlayerColor * color, int group) const; + std::shared_ptr findHeroFlagBitmap(const CGHeroInstance * obj, int anim, const PlayerColor * color, int group) const; + std::shared_ptr findBoatFlagBitmap(const CGBoat * obj, int anim, const PlayerColor * color, int group, ui8 dir) const; + std::shared_ptr findFlagBitmapInternal(std::shared_ptr animation, int anim, int group, ui8 dir, bool moving) const; + +public: + + AnimBitmapHolder findObjectBitmap(const CGObjectInstance * obj, int anim) const; + enum class EMapCacheType : char { TERRAIN, OBJECTS, ROADS, RIVERS, FOW, HEROES, HERO_FLAGS, FRAME, AFTER_LAST @@ -19,6 +67,7 @@ public: void initObjectRects(); void initTerrainGraphics(); + std::vector ttiles; //informations about map tiles int3 sizes; //map size (x = width, y = height, z = number of levels) const CMap * map; QPixmap surface; @@ -39,7 +88,15 @@ public: TFlippedAnimations riverAnimations;//[river type, rotation] TFlippedCache riverImages;//[river type, view type, rotation] - void drawTerrainTile(int x, int y, const TerrainTile & tinfo); + void drawTerrainTile(int x, int y, int z); + /// draws a river segment on current tile + //void drawRiver(const TerrainTile & tinfo) const; + /// draws a road segment on current tile + //void drawRoad(const TerrainTile & tinfo, const TerrainTile * tinfoUpper) const; + /// draws all objects on current tile (higher-level logic, unlike other draw*** methods) + void drawObjects(int x, int y, int z); + //void drawObject(SDL_Surface * targetSurf, std::shared_ptr source, SDL_Rect * sourceRect, bool moving) const; + //void drawHeroFlag(SDL_Surface * targetSurf, std::shared_ptr source, SDL_Rect * sourceRect, SDL_Rect * destRect, bool moving) const; mutable std::map animationPhase;