From e7de865f26f739ea80dde9f00864754da3d0f852 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Tue, 6 Sep 2022 21:15:13 +0400 Subject: [PATCH] Implement smart invalidation for objects --- mapeditor/BitmapHandler.cpp | 265 +++++++++++++++--------------- mapeditor/BitmapHandler.h | 2 + mapeditor/inspector.cpp | 15 +- mapeditor/inspector.h | 29 ++-- mapeditor/mapcontroller.cpp | 34 ++-- mapeditor/maphandler.cpp | 317 ++++++++++++++++-------------------- mapeditor/maphandler.h | 104 +++++------- mapeditor/scenelayer.cpp | 1 - mapeditor/scenelayer.h | 3 +- mapeditor/windownewmap.cpp | 20 ++- 10 files changed, 384 insertions(+), 406 deletions(-) diff --git a/mapeditor/BitmapHandler.cpp b/mapeditor/BitmapHandler.cpp index bbd0fa177..e3c1eed37 100644 --- a/mapeditor/BitmapHandler.cpp +++ b/mapeditor/BitmapHandler.cpp @@ -4,13 +4,10 @@ // // Created by nordsoft on 29.08.2022. // - -#include "BitmapHandler.h" - #include "StdInc.h" +#include "BitmapHandler.h" #include "../lib/filesystem/Filesystem.h" -#include "BitmapHandler.h" #include #include @@ -21,152 +18,152 @@ namespace BitmapHandler QImage loadH3PCX(ui8 * data, size_t size); QImage loadBitmapFromDir(std::string path, std::string fname, bool setKey=true); -} -bool isPCX(const ui8 *header)//check whether file can be PCX according to header -{ - ui32 fSize = read_le_u32(header + 0); - ui32 width = read_le_u32(header + 4); - ui32 height = read_le_u32(header + 8); - return fSize == width*height || fSize == width*height*3; -} - -enum Epcxformat -{ - PCX8B, - PCX24B -}; - -QImage BitmapHandler::loadH3PCX(ui8 * pcx, size_t size) -{ - //SDL_Surface * ret; - - Epcxformat format; - int it=0; - - ui32 fSize = read_le_u32(pcx + it); it+=4; - ui32 width = read_le_u32(pcx + it); it+=4; - ui32 height = read_le_u32(pcx + it); it+=4; - - if (fSize==width*height*3) - format=PCX24B; - else if (fSize==width*height) - format=PCX8B; - else - return QImage(); - - QSize qsize(width, height); - - if (format==PCX8B) + bool isPCX(const ui8 *header)//check whether file can be PCX according to header { - it = 0xC; - //auto bitmap = QBitmap::fromData(qsize, pcx + it); - QImage image(pcx + it, width, height, QImage::Format_Indexed8); + ui32 fSize = read_le_u32(header + 0); + ui32 width = read_le_u32(header + 4); + ui32 height = read_le_u32(header + 8); + return fSize == width*height || fSize == width*height*3; + } + + enum Epcxformat + { + PCX8B, + PCX24B + }; + + QImage loadH3PCX(ui8 * pcx, size_t size) + { + //SDL_Surface * ret; - //palette - last 256*3 bytes - QVector colorTable; - it = (int)size-256*3; - for (int i=0;i<256;i++) + Epcxformat format; + int it=0; + + ui32 fSize = read_le_u32(pcx + it); it+=4; + ui32 width = read_le_u32(pcx + it); it+=4; + ui32 height = read_le_u32(pcx + it); it+=4; + + if (fSize==width*height*3) + format=PCX24B; + else if (fSize==width*height) + format=PCX8B; + else + return QImage(); + + QSize qsize(width, height); + + if (format==PCX8B) { - char bytes[3]; - bytes[0] = pcx[it++]; - bytes[1] = pcx[it++]; - bytes[2] = pcx[it++]; - colorTable.append(qRgb(bytes[0], bytes[1], bytes[2])); - } - image.setColorTable(colorTable); - return image; - } - else - { - QImage image(pcx + it, width, height, QImage::Format_RGB32); - return image; - } -} - -QImage BitmapHandler::loadBitmapFromDir(std::string path, std::string fname, bool setKey) -{ - if(!fname.size()) - { - logGlobal->warn("Call to loadBitmap with void fname!"); - return QImage(); - } - if (!CResourceHandler::get()->existsResource(ResourceID(path + fname, EResType::IMAGE))) - { - return QImage(); - } - - auto fullpath = CResourceHandler::get()->getResourceName(ResourceID(path + fname, EResType::IMAGE)); - auto readFile = CResourceHandler::get()->load(ResourceID(path + fname, EResType::IMAGE))->readAll(); - - if (isPCX(readFile.first.get())) - {//H3-style PCX - auto image = loadH3PCX(readFile.first.get(), readFile.second); - if(!image.isNull()) - { - if(image.bitPlaneCount() == 1 && setKey) + it = 0xC; + //auto bitmap = QBitmap::fromData(qsize, pcx + it); + QImage image(pcx + it, width, height, QImage::Format_Indexed8); + + //palette - last 256*3 bytes + QVector colorTable; + it = (int)size-256*3; + for (int i=0;i<256;i++) { - QVector colorTable = image.colorTable(); - colorTable[0] = qRgba(255, 255, 255, 0); - image.setColorTable(colorTable); + char bytes[3]; + bytes[0] = pcx[it++]; + bytes[1] = pcx[it++]; + bytes[2] = pcx[it++]; + colorTable.append(qRgb(bytes[0], bytes[1], bytes[2])); } + image.setColorTable(colorTable); + return image; } else { - logGlobal->error("Failed to open %s as H3 PCX!", fname); - } - return image; - } - else - { //loading via SDL_Image - QImage image(QString::fromStdString(fullpath->make_preferred().string())); - if(!image.isNull()) - { - if(image.bitPlaneCount() == 1) - { - //set correct value for alpha\unused channel - QVector colorTable = image.colorTable(); - for(auto & c : colorTable) - c = qRgb(qRed(c), qGreen(c), qBlue(c)); - image.setColorTable(colorTable); - } - } - else - { - logGlobal->error("Failed to open %s via QImage", fname); + QImage image(pcx + it, width, height, QImage::Format_RGB32); return image; } } - return QImage(); - // When modifying anything here please check two use cases: - // 1) Vampire mansion in Necropolis (not 1st color is transparent) - // 2) Battle background when fighting on grass/dirt, topmost sky part (NO transparent color) - // 3) New objects that may use 24-bit images for icons (e.g. witchking arts) - /*if (ret->format->palette) - { - CSDL_Ext::setDefaultColorKeyPresize(ret); - } - else if (ret->format->Amask) - { - SDL_SetSurfaceBlendMode(ret, SDL_BLENDMODE_BLEND); - } - else // always set - { - CSDL_Ext::setDefaultColorKey(ret); - } - return ret;*/ -} -QImage BitmapHandler::loadBitmap(std::string fname, bool setKey) -{ - QImage image = loadBitmapFromDir("DATA/", fname, setKey); - if(image.isNull()) + QImage loadBitmapFromDir(std::string path, std::string fname, bool setKey) { - image = loadBitmapFromDir("SPRITES/", fname, setKey); + if(!fname.size()) + { + logGlobal->warn("Call to loadBitmap with void fname!"); + return QImage(); + } + if (!CResourceHandler::get()->existsResource(ResourceID(path + fname, EResType::IMAGE))) + { + return QImage(); + } + + auto fullpath = CResourceHandler::get()->getResourceName(ResourceID(path + fname, EResType::IMAGE)); + auto readFile = CResourceHandler::get()->load(ResourceID(path + fname, EResType::IMAGE))->readAll(); + + if (isPCX(readFile.first.get())) + {//H3-style PCX + auto image = BitmapHandler::loadH3PCX(readFile.first.get(), readFile.second); + if(!image.isNull()) + { + if(image.bitPlaneCount() == 1 && setKey) + { + QVector colorTable = image.colorTable(); + colorTable[0] = qRgba(255, 255, 255, 0); + image.setColorTable(colorTable); + } + } + else + { + logGlobal->error("Failed to open %s as H3 PCX!", fname); + } + return image; + } + else + { //loading via SDL_Image + QImage image(QString::fromStdString(fullpath->make_preferred().string())); + if(!image.isNull()) + { + if(image.bitPlaneCount() == 1) + { + //set correct value for alpha\unused channel + QVector colorTable = image.colorTable(); + for(auto & c : colorTable) + c = qRgb(qRed(c), qGreen(c), qBlue(c)); + image.setColorTable(colorTable); + } + } + else + { + logGlobal->error("Failed to open %s via QImage", fname); + return image; + } + } + return QImage(); + // When modifying anything here please check two use cases: + // 1) Vampire mansion in Necropolis (not 1st color is transparent) + // 2) Battle background when fighting on grass/dirt, topmost sky part (NO transparent color) + // 3) New objects that may use 24-bit images for icons (e.g. witchking arts) + /*if (ret->format->palette) + { + CSDL_Ext::setDefaultColorKeyPresize(ret); + } + else if (ret->format->Amask) + { + SDL_SetSurfaceBlendMode(ret, SDL_BLENDMODE_BLEND); + } + else // always set + { + CSDL_Ext::setDefaultColorKey(ret); + } + return ret;*/ + } + + QImage loadBitmap(std::string fname, bool setKey) + { + QImage image = loadBitmapFromDir("DATA/", fname, setKey); if(image.isNull()) { - logGlobal->error("Error: Failed to find file %s", fname); + image = loadBitmapFromDir("SPRITES/", fname, setKey); + if(image.isNull()) + { + logGlobal->error("Error: Failed to find file %s", fname); + } } + return image; } - return image; } diff --git a/mapeditor/BitmapHandler.h b/mapeditor/BitmapHandler.h index 762544aed..bd8c307ff 100644 --- a/mapeditor/BitmapHandler.h +++ b/mapeditor/BitmapHandler.h @@ -10,6 +10,8 @@ #define read_le_u16(p) (* reinterpret_cast(p)) #define read_le_u32(p) (* reinterpret_cast(p)) +#include + namespace BitmapHandler { //Load file from /DATA or /SPRITES diff --git a/mapeditor/inspector.cpp b/mapeditor/inspector.cpp index df7adf7f1..e3af1142b 100644 --- a/mapeditor/inspector.cpp +++ b/mapeditor/inspector.cpp @@ -4,9 +4,10 @@ #include "../lib/spells/CSpellHandler.h" #include "../lib/CRandomGenerator.h" #include "../lib/mapObjects/CObjectClassesHandler.h" +#include "../lib/mapping/CMap.h" //===============IMPLEMENT OBJECT INITIALIZATION FUNCTIONS================ -Initializer::Initializer(CGObjectInstance * o) +Initializer::Initializer(CMap * m, CGObjectInstance * o) : map(m) { ///IMPORTANT! initialize order should be from base objects to derived objects INIT_OBJ_TYPE(CGResource); @@ -16,14 +17,14 @@ Initializer::Initializer(CGObjectInstance * o) INIT_OBJ_TYPE(CGTownInstance); } -void initialize(CArmedInstance * o) +void Initializer::initialize(CArmedInstance * o) { if(!o) return; o->tempOwner = PlayerColor::NEUTRAL; } -void initialize(CGTownInstance * o) +void Initializer::initialize(CGTownInstance * o) { if(!o) return; @@ -37,7 +38,7 @@ void initialize(CGTownInstance * o) } } -void initialize(CGArtifact * o) +void Initializer::initialize(CGArtifact * o) { if(!o) return; @@ -46,7 +47,7 @@ void initialize(CGArtifact * o) std::vector out; for(auto spell : VLC->spellh->objects) //spellh size appears to be greater (?) { - if(/*map.isAllowedSpell(spell->id) && spell->level == i + 1*/ true) + //if(map->isAllowedSpell(spell->id)) { out.push_back(spell->id); } @@ -56,7 +57,7 @@ void initialize(CGArtifact * o) } } -void initialize(CGMine * o) +void Initializer::initialize(CGMine * o) { if(!o) return; @@ -64,7 +65,7 @@ void initialize(CGMine * o) o->producedQuantity = o->defaultResProduction(); } -void initialize(CGResource * o) +void Initializer::initialize(CGResource * o) { if(!o) return; diff --git a/mapeditor/inspector.h b/mapeditor/inspector.h index 206c7c83c..5c5a2d3a3 100644 --- a/mapeditor/inspector.h +++ b/mapeditor/inspector.h @@ -18,12 +18,23 @@ void setProperty(x*, const QString &, const QVariant &); #define UPDATE_OBJ_PROPERTIES(x) updateProperties(dynamic_cast(obj)) #define SET_PROPERTIES(x) setProperty(dynamic_cast(obj), key, value) -//===============DECLARE MAP OBJECTS====================================== -DECLARE_OBJ_TYPE(CArmedInstance); -DECLARE_OBJ_TYPE(CGTownInstance); -DECLARE_OBJ_TYPE(CGArtifact); -DECLARE_OBJ_TYPE(CGMine); -DECLARE_OBJ_TYPE(CGResource); + +class Initializer +{ +public: + //===============DECLARE MAP OBJECTS====================================== + DECLARE_OBJ_TYPE(CArmedInstance); + DECLARE_OBJ_TYPE(CGTownInstance); + DECLARE_OBJ_TYPE(CGArtifact); + DECLARE_OBJ_TYPE(CGMine); + DECLARE_OBJ_TYPE(CGResource); + + + Initializer(CMap *, CGObjectInstance *); + +private: + CMap * map; +}; class Inspector { @@ -90,11 +101,7 @@ protected: }; -class Initializer -{ -public: - Initializer(CGObjectInstance *); -}; + class PlayerColorDelegate : public QStyledItemDelegate { diff --git a/mapeditor/mapcontroller.cpp b/mapeditor/mapcontroller.cpp index 29744aed1..96df46f98 100644 --- a/mapeditor/mapcontroller.cpp +++ b/mapeditor/mapcontroller.cpp @@ -88,7 +88,9 @@ void MapController::sceneForceUpdate(int level) void MapController::resetMapHandler() { - _mapHandler.reset(new MapHandler(_map.get())); + if(!_mapHandler) + _mapHandler.reset(new MapHandler()); + _mapHandler->reset(map()); _scenes[0]->initialize(*this); _scenes[1]->initialize(*this); _miniscenes[0]->initialize(*this); @@ -121,11 +123,13 @@ void MapController::commitObjectErase(int level) for(auto * obj : _scenes[level]->selectionObjectsView.getSelection()) { _map->getEditManager()->removeObject(obj); + //invalidate tiles under object + _mapHandler->invalidate(_mapHandler->geTilesUnderObject(obj)); delete obj; } _scenes[level]->selectionObjectsView.clear(); - resetMapHandler(); - _scenes[level]->updateViews(); + _scenes[level]->objectsView.draw(); + _scenes[level]->selectionObjectsView.draw(); _miniscenes[level]->updateViews(); main->mapChanged(); @@ -176,8 +180,14 @@ void MapController::commitObstacleFill(int level) sel.second.placeObstacles(_map.get(), CRandomGenerator::getDefault()); } - resetMapHandler(); - _scenes[level]->updateViews(); + for(auto & t : selection) + { + _mapHandler->invalidate(t.x, t.y, t.z); + } + + _scenes[level]->selectionTerrainView.clear(); + _scenes[level]->selectionTerrainView.draw(); + _scenes[level]->objectsView.draw(); _miniscenes[level]->updateViews(); main->mapChanged(); @@ -185,7 +195,8 @@ void MapController::commitObstacleFill(int level) void MapController::commitObjectChange(int level) { - resetMapHandler(); + //for(auto * o : _scenes[level]->selectionObjectsView.getSelection()) + //_mapHandler->invalidate(o); _scenes[level]->objectsView.draw(); _scenes[level]->selectionObjectsView.draw(); @@ -219,16 +230,18 @@ void MapController::commitObjectShiftOrCreate(int level) } else { + auto prevPositions = _mapHandler->geTilesUnderObject(obj); _map->getEditManager()->moveObject(obj, pos); + _mapHandler->invalidate(prevPositions); + _mapHandler->invalidate(obj); } } _scenes[level]->selectionObjectsView.newObject = nullptr; _scenes[level]->selectionObjectsView.shift = QPoint(0, 0); _scenes[level]->selectionObjectsView.selectionMode = 0; - - resetMapHandler(); - _scenes[level]->updateViews(); + _scenes[level]->objectsView.draw(); + _scenes[level]->selectionObjectsView.draw(); _miniscenes[level]->updateViews(); main->mapChanged(); @@ -239,9 +252,10 @@ void MapController::commitObjectCreate(int level) auto * newObj = _scenes[level]->selectionObjectsView.newObject; if(!newObj) return; - Initializer init(newObj); + Initializer init(map(), newObj); _map->getEditManager()->insertObject(newObj); + _mapHandler->invalidate(newObj); main->mapChanged(); } diff --git a/mapeditor/maphandler.cpp b/mapeditor/maphandler.cpp index 1903b29db..9837bc009 100644 --- a/mapeditor/maphandler.cpp +++ b/mapeditor/maphandler.cpp @@ -13,26 +13,37 @@ const int tileSize = 32; -static bool objectBlitOrderSorter(const TerrainTileObject & a, const TerrainTileObject & b) +static bool objectBlitOrderSorter(const TileObject & a, const TileObject & b) { return MapHandler::compareObjectBlitOrder(a.obj, b.obj); } -MapHandler::MapHandler(const CMap * Map): - map(Map) +int MapHandler::index(int x, int y, int z) const { - init(); + return z * (sizes.x * sizes.y) + y * sizes.x + x; } -void MapHandler::init() +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; - initTerrainGraphics(); - logGlobal->info("\tPreparing terrain, roads, rivers, borders"); initObjectRects(); logGlobal->info("\tMaking object rects"); } @@ -78,22 +89,17 @@ 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(QPainter & painter, int x, int y, int z) { auto & tinfo = map->getTile(int3(x, y, z)); - //Rect destRect(realTileRect); - 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)); } @@ -102,24 +108,11 @@ 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]) - { - 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(x * tileSize, y * tileSize, roadImages.at(tinfoUpper->roadType)[tinfoUpper->roadDir]->mirrored(hflip, vflip)); - } - }*/ - if(tinfo.roadType != ROAD_NAMES[0]) //print road from this tile { 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(x * tileSize, y * tileSize, roadImages.at(tinfo.roadType)[tinfo.roadDir]->mirrored(hflip, vflip)); @@ -138,7 +131,6 @@ void MapHandler::drawRiver(QPainter & painter, int x, int y, int z) 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)); @@ -162,6 +154,8 @@ void setPlayerColor(QImage * sur, PlayerColor player) void MapHandler::initObjectRects() { + ttiles.resize(sizes.x * sizes.y * sizes.z); + //initializing objects / rects for(const CGObjectInstance * elem : map->objects) { @@ -184,16 +178,17 @@ 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); - 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; + 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 @@ -201,7 +196,7 @@ void MapHandler::initObjectRects() obj->coveringAt(currTile.x, currTile.y) // object is visible here ) { - ttiles[currTile.z * (sizes.x * sizes.y) + currTile.y * sizes.x + currTile.x].objects.push_back(toAdd); + ttiles[index(currTile)].push_back(toAdd); } } } @@ -209,7 +204,7 @@ void MapHandler::initObjectRects() for(auto & tt : ttiles) { - stable_sort(tt.objects.begin(), tt.objects.end(), objectBlitOrderSorter); + stable_sort(tt.begin(), tt.end(), objectBlitOrderSorter); } } @@ -239,125 +234,24 @@ bool MapHandler::compareObjectBlitOrder(const CGObjectInstance * a, const CGObje return false; } -TerrainTileObject::TerrainTileObject(CGObjectInstance * obj_, QRect rect_, bool real_) +TileObject::TileObject(CGObjectInstance * obj_, QRect rect_) : obj(obj_), -rect(rect_), -real(real_) +rect(rect_) { } -TerrainTileObject::~TerrainTileObject() +TileObject::~TileObject() { } -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? - { - logGlobal->error("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) + if(!hero || hero->boat) 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); @@ -370,16 +264,11 @@ std::shared_ptr MapHandler::findFlagBitmapInternal(std::shared_ptrgetImage((anim / 4) % groupSize, group); } - MapHandler::AnimBitmapHolder MapHandler::findObjectBitmap(const CGObjectInstance * obj, int anim) const { - if (!obj) + if(!obj || obj->ID == Obj::HERO || obj->ID == Obj::BOAT) 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(); @@ -387,7 +276,7 @@ MapHandler::AnimBitmapHolder MapHandler::findObjectBitmap(const CGObjectInstance return MapHandler::AnimBitmapHolder(); animation->playerColored(obj->tempOwner); - auto bitmap = animation->getImage((anim + getPhaseShift(obj)) % groupSize); + auto bitmap = animation->getImage(anim % groupSize); if(!bitmap) return MapHandler::AnimBitmapHolder(); @@ -396,9 +285,9 @@ MapHandler::AnimBitmapHolder MapHandler::findObjectBitmap(const CGObjectInstance return MapHandler::AnimBitmapHolder(bitmap); } -std::vector & MapHandler::getObjects(int x, int y, int z) +std::vector & MapHandler::getObjects(int x, int y, int z) { - return ttiles[z * (sizes.x * sizes.y) + y * sizes.x + x].objects; + return ttiles[index(x, y, z)]; } void MapHandler::drawObjects(QPainter & painter, int x, int y, int z) @@ -418,33 +307,19 @@ void MapHandler::drawObjects(QPainter & painter, int x, int y, int z) if (objData.objBitmap) { auto pos = obj->getPosition(); - QRect srcRect(object.rect.x() + pos.x * 32, object.rect.y() + pos.y * 32, tileSize, tileSize); - painter.drawImage(QPoint(x * 32, y * 32), *objData.objBitmap, object.rect); - //painter.drawImage(pos.x * 32 - object.rect.x(), pos.y * 32 - object.rect.y(), *objData.objBitmap); - //drawObject(targetSurf, objData.objBitmap, &srcRect, objData.isMoving); + painter.drawImage(QPoint(x * tileSize, y * tileSize), *objData.objBitmap, object.rect); if(objData.flagBitmap) { - /*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); - }*/ if (obj->pos.x == pos.x && obj->pos.y == pos.y) - { - //Rect dstRect(realPos.x - 2 * tileSize, realPos.y - tileSize, 3 * tileSize, 2 * tileSize); - painter.drawImage(QPoint(x * 32, y * 32), *objData.flagBitmap, object.rect); - //drawHeroFlag(targetSurf, objData.flagBitmap, nullptr, &dstRect, false); - } + painter.drawImage(QPoint(x * tileSize, y * tileSize), *objData.flagBitmap, object.rect); } } } } -void MapHandler::drawObject(QPainter & painter, const TerrainTileObject & object) +void MapHandler::drawObject(QPainter & painter, const TileObject & object) { - //if(object.visi) const CGObjectInstance * obj = object.obj; if (!obj) { @@ -458,24 +333,13 @@ void MapHandler::drawObject(QPainter & painter, const TerrainTileObject & object if (objData.objBitmap) { auto pos = obj->getPosition(); - QRect srcRect(object.rect.x() + pos.x * 32, object.rect.y() + pos.y * 32, tileSize, tileSize); - painter.drawImage(pos.x * 32 - object.rect.x(), pos.y * 32 - object.rect.y(), *objData.objBitmap); - //drawObject(targetSurf, objData.objBitmap, &srcRect, objData.isMoving); + painter.drawImage(pos.x * tileSize - object.rect.x(), pos.y * tileSize - object.rect.y(), *objData.objBitmap); if (objData.flagBitmap) { - /*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); - painter.drawImage(pos.x * 32 - object.rect.x(), pos.y * 32 - object.rect.y(), *objData.flagBitmap); + painter.drawImage(pos.x * tileSize - object.rect.x(), pos.y * tileSize - object.rect.y(), *objData.flagBitmap); } } } @@ -538,3 +402,102 @@ 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; + //invalidate(obj->obj); + } + + stable_sort(objects.begin(), objects.end(), objectBlitOrderSorter); +} + +void MapHandler::invalidate(CGObjectInstance * obj) +{ + std::shared_ptr animation = graphics->getAnimation(obj); + + //no animation at all or empty animation + if(!animation || animation->size(0) == 0) + return; + + auto image = animation->getImage(0,0); + 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 && + obj->coveringAt(currTile.x, currTile.y) // object is visible here + ) + { + 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 MapHandler::geTilesUnderObject(CGObjectInstance * obj) const +{ + std::vector 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 + obj->coveringAt(currTile.x, currTile.y) // object is visible here + ) + { + result.push_back(currTile); + } + } + } + return result; +} + +void MapHandler::invalidate(const std::vector & tiles) +{ + for(auto & currTile : tiles) + { + invalidate(currTile.x, currTile.y, currTile.z); + } +} diff --git a/mapeditor/maphandler.h b/mapeditor/maphandler.h index 521abbff0..749797cfd 100644 --- a/mapeditor/maphandler.h +++ b/mapeditor/maphandler.h @@ -13,20 +13,16 @@ class CGObjectInstance; class CGBoat; class PlayerColor; -struct TerrainTileObject +struct TileObject { CGObjectInstance *obj; QRect rect; - bool real; - TerrainTileObject(CGObjectInstance *obj_, QRect rect_, bool visitablePos = false); - ~TerrainTileObject(); + TileObject(CGObjectInstance *obj_, QRect rect_); + ~TileObject(); }; -struct TerrainTile2 -{ - std::vector objects; //pointers to objects being on this tile with rects to be easier to blit this tile on screen -}; +using TileObjects = std::vector; //pointers to objects being on this tile with rects to be easier to blit this tile on screen class MapHandler { @@ -44,21 +40,30 @@ public: 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; + int index(int x, int y, int z) const; + int index(const int3 &) const; + std::shared_ptr findFlagBitmapInternal(std::shared_ptr animation, int anim, int group, ui8 dir, bool moving) const; - -public: - + std::shared_ptr findFlagBitmap(const CGHeroInstance * obj, int anim, const PlayerColor * color, int group) const; AnimBitmapHolder findObjectBitmap(const CGObjectInstance * obj, int anim) const; + //FIXME: unique_ptr should be enough, but fails to compile in MSVS 2013 + typedef std::map> TFlippedAnimations; //[type, rotation] + typedef std::map>> TFlippedCache;//[type, view type, rotation] + + TFlippedAnimations terrainAnimations;//[terrain type, rotation] + TFlippedCache terrainImages;//[terrain type, view type, rotation] + + TFlippedAnimations roadAnimations;//[road type, rotation] + TFlippedCache roadImages;//[road type, view type, rotation] + + TFlippedAnimations riverAnimations;//[river type, rotation] + TFlippedCache riverImages;//[river type, view type, rotation] + + std::vector ttiles; //informations about map tiles + int3 sizes; //map size (x = width, y = height, z = number of levels) + const CMap * map; + enum class EMapCacheType : char { TERRAIN, OBJECTS, ROADS, RIVERS, FOW, HEROES, HERO_FLAGS, FRAME, AFTER_LAST @@ -66,59 +71,34 @@ public: void initObjectRects(); void initTerrainGraphics(); + QRgb getTileColor(int x, int y, int z); + +public: + MapHandler(); + ~MapHandler() = default; + + void reset(const CMap * Map); - std::vector ttiles; //informations about map tiles - int3 sizes; //map size (x = width, y = height, z = number of levels) - const CMap * map; - - //terrain graphics - - //FIXME: unique_ptr should be enough, but fails to compile in MSVS 2013 - typedef std::map> TFlippedAnimations; //[type, rotation] - typedef std::map>> TFlippedCache;//[type, view type, rotation] - - TFlippedAnimations terrainAnimations;//[terrain type, rotation] - TFlippedCache terrainImages;//[terrain type, view type, rotation] - - TFlippedAnimations roadAnimations;//[road type, rotation] - TFlippedCache roadImages;//[road type, view type, rotation] - - TFlippedAnimations riverAnimations;//[river type, rotation] - TFlippedCache riverImages;//[river type, view type, rotation] + void updateWater(); void drawTerrainTile(QPainter & painter, int x, int y, int z); /// draws a river segment on current tile void drawRiver(QPainter & painter, int x, int y, int z); /// draws a road segment on current tile void drawRoad(QPainter & painter, int x, int y, int z); + + void invalidate(int x, int y, int z); //invalidates all objects in particular tile + void invalidate(CGObjectInstance *); //invalidates object rects + void invalidate(const std::vector &); //invalidates all tiles + std::vector geTilesUnderObject(CGObjectInstance *) const; + /// draws all objects on current tile (higher-level logic, unlike other draw*** methods) void drawObjects(QPainter & painter, int x, int y, int z); - void drawObject(QPainter & painter, const TerrainTileObject & object); + void drawObject(QPainter & painter, const TileObject & object); void drawObjectAt(QPainter & painter, const CGObjectInstance * object, int x, int y); - std::vector & getObjects(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; - QRgb getTileColor(int x, int y, int z); + std::vector & getObjects(int x, int y, int z); + void drawMinimapTile(QPainter & painter, int x, int y, int z); - - mutable std::map animationPhase; - - MapHandler(const CMap * Map); - ~MapHandler() = default; - - void init(); - - //void getTerrainDescr(const int3 & pos, std::string & out, bool isRMB) const; // isRMB = whether Right Mouse Button is clicked - //bool printObject(const CGObjectInstance * obj, bool fadein = false); //puts appropriate things to tiles, so obj will be visible on map - //bool hideObject(const CGObjectInstance * obj, bool fadeout = false); //removes appropriate things from ttiles, so obj will be no longer visible on map (but still will exist) - //bool hasObjectHole(const int3 & pos) const; // Checks if TerrainTile2 tile has a pit remained after digging. - - //EMapAnimRedrawStatus drawTerrainRectNew(SDL_Surface * targetSurface, const MapDrawingInfo * info, bool redrawOnlyAnim = false); - void updateWater(); - /// determines if the map is ready to handle new hero movement (not available during fading animations) - //bool canStartHeroMovement(); - - //void discardWorldViewCache(); static bool compareObjectBlitOrder(const CGObjectInstance * a, const CGObjectInstance * b); }; diff --git a/mapeditor/scenelayer.cpp b/mapeditor/scenelayer.cpp index 669d4dfdc..6d55b2258 100644 --- a/mapeditor/scenelayer.cpp +++ b/mapeditor/scenelayer.cpp @@ -319,7 +319,6 @@ void ObjectsLayer::setDirty(int x, int y) void ObjectsLayer::setDirty(const CGObjectInstance * object) { - dirty.insert(object); } SelectionObjectsLayer::SelectionObjectsLayer(MapSceneBase * s): AbstractLayer(s), newObject(nullptr) diff --git a/mapeditor/scenelayer.h b/mapeditor/scenelayer.h index a5318f818..9e70be13c 100644 --- a/mapeditor/scenelayer.h +++ b/mapeditor/scenelayer.h @@ -98,7 +98,8 @@ public: void setDirty(const CGObjectInstance * object); private: - std::set dirty; + std::set objDirty; + std::set dirty; }; diff --git a/mapeditor/windownewmap.cpp b/mapeditor/windownewmap.cpp index 6ba9a8efe..e9a23907d 100644 --- a/mapeditor/windownewmap.cpp +++ b/mapeditor/windownewmap.cpp @@ -88,6 +88,13 @@ void WindowNewMap::on_okButtong_clicked() std::unique_ptr nmap; if(ui->randomMapCheck->isChecked()) { + //verify map template + if(mapGenOptions.getPossibleTemplates().empty()) + { + QMessageBox::warning(this, "No template", "No template for parameters scecified. Random map cannot be generated."); + return; + } + CMapGenerator generator(mapGenOptions); //TODO: fix water and roads generator.disableModificator("RoadPlacer"); @@ -96,9 +103,16 @@ void WindowNewMap::on_okButtong_clicked() auto progressBarWnd = new GeneratorProgress(generator, this); progressBarWnd->show(); - auto f = std::async(std::launch::async, &CMapGenerator::generate, &generator); - progressBarWnd->update(); - nmap = f.get(); + try + { + auto f = std::async(std::launch::async, &CMapGenerator::generate, &generator); + progressBarWnd->update(); + nmap = f.get(); + } + catch(const std::exception & e) + { + QMessageBox::critical(this, "RMG failure", e.what()); + } } else {