1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-22 22:13:35 +02:00

Merge branch 'EraseAction' of github.com:Nordsoft91/vcmi into EraseAction

This commit is contained in:
Tomasz Zieliński 2022-09-06 20:53:05 +02:00
commit 0f8adabd71
10 changed files with 385 additions and 407 deletions

View File

@ -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 <QBitmap>
#include <QImage>
@ -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<QRgb> 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<QRgb> colorTable;
it = (int)size-256*3;
for (int i=0;i<256;i++)
{
QVector<QRgb> 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<QRgb> 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<QRgb> 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<QRgb> 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;
}

View File

@ -10,6 +10,8 @@
#define read_le_u16(p) (* reinterpret_cast<const ui16 *>(p))
#define read_le_u32(p) (* reinterpret_cast<const ui32 *>(p))
#include <QImage>
namespace BitmapHandler
{
//Load file from /DATA or /SPRITES

View File

@ -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<SpellID> 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;

View File

@ -18,12 +18,23 @@ void setProperty(x*, const QString &, const QVariant &);
#define UPDATE_OBJ_PROPERTIES(x) updateProperties(dynamic_cast<x*>(obj))
#define SET_PROPERTIES(x) setProperty(dynamic_cast<x*>(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
{

View File

@ -103,7 +103,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);
@ -141,7 +143,9 @@ void MapController::commitObjectErase(int level)
}
else if (selectedObjects.size() == 1)
{
_map->getEditManager()->removeObject(*selectedObjects.begin());
_map->getEditManager()->removeObject(selectedObjects.front());
//invalidate tiles under object
_mapHandler->invalidate(_mapHandler->geTilesUnderObject(selectedObjects.front()));
}
else //nothing to erase - shouldn't be here
{
@ -149,8 +153,8 @@ void MapController::commitObjectErase(int level)
}
_scenes[level]->selectionObjectsView.clear();
resetMapHandler();
_scenes[level]->updateViews();
_scenes[level]->objectsView.draw();
_scenes[level]->selectionObjectsView.draw();
_miniscenes[level]->updateViews();
main->mapChanged();
@ -201,8 +205,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();
@ -210,7 +220,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();
@ -244,16 +255,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();
@ -264,9 +277,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();
}

View File

@ -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> 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<QImage> MapHandler::findFlagBitmap(const CGHeroInstance * hero, int anim, const PlayerColor * color, int group) const
{
if(!hero)
if(!hero || hero->boat)
return std::shared_ptr<QImage>();
if(hero->boat)
return findBoatFlagBitmap(hero->boat, anim, color, group, hero->moveDir);
return findHeroFlagBitmap(hero, anim, color, group);
}
std::shared_ptr<QImage> 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<QImage> 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<QImage> MapHandler::findFlagBitmapInternal(std::shared_ptr<Animation> animation, int anim, int group, ui8 dir, bool moving) const
{
size_t groupSize = animation->size(group);
@ -370,16 +264,11 @@ std::shared_ptr<QImage> MapHandler::findFlagBitmapInternal(std::shared_ptr<Anima
return animation->getImage((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<const CGHeroInstance*>(obj), anim);
if (obj->ID == Obj::BOAT)
return findBoatBitmap(static_cast<const CGBoat*>(obj), anim);
// normal object
std::shared_ptr<Animation> 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<TerrainTileObject> & MapHandler::getObjects(int x, int y, int z)
std::vector<TileObject> & 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> 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<int3> MapHandler::geTilesUnderObject(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
obj->coveringAt(currTile.x, currTile.y) // object is visible here
)
{
result.push_back(currTile);
}
}
}
return result;
}
void MapHandler::invalidate(const std::vector<int3> & tiles)
{
for(auto & currTile : tiles)
{
invalidate(currTile.x, currTile.y, currTile.z);
}
}

View File

@ -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<TerrainTileObject> objects; //pointers to objects being on this tile with rects to be easier to blit this tile on screen
};
using TileObjects = std::vector<TileObject>; //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<QImage> findFlagBitmap(const CGHeroInstance * obj, int anim, const PlayerColor * color, int group) const;
std::shared_ptr<QImage> findHeroFlagBitmap(const CGHeroInstance * obj, int anim, const PlayerColor * color, int group) const;
std::shared_ptr<QImage> 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<QImage> findFlagBitmapInternal(std::shared_ptr<Animation> animation, int anim, int group, ui8 dir, bool moving) const;
public:
std::shared_ptr<QImage> 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<std::string, std::shared_ptr<Animation>> TFlippedAnimations; //[type, rotation]
typedef std::map<std::string, std::vector<std::shared_ptr<QImage>>> 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<TileObjects> 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<TerrainTile2> 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<std::string, std::shared_ptr<Animation>> TFlippedAnimations; //[type, rotation]
typedef std::map<std::string, std::vector<std::shared_ptr<QImage>>> 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<int3> &); //invalidates all tiles
std::vector<int3> 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<TerrainTileObject> & getObjects(int x, int y, int z);
//void drawObject(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, SDL_Rect * sourceRect, bool moving) const;
//void drawHeroFlag(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, SDL_Rect * sourceRect, SDL_Rect * destRect, bool moving) const;
QRgb getTileColor(int x, int y, int z);
std::vector<TileObject> & getObjects(int x, int y, int z);
void drawMinimapTile(QPainter & painter, int x, int y, int z);
mutable std::map<const CGObjectInstance*, ui8> 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);
};

View File

@ -329,7 +329,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)

View File

@ -109,7 +109,8 @@ public:
void setDirty(const CGObjectInstance * object);
private:
std::set<const CGObjectInstance *> dirty;
std::set<const CGObjectInstance *> objDirty;
std::set<int3> dirty;
};

View File

@ -88,6 +88,13 @@ void WindowNewMap::on_okButtong_clicked()
std::unique_ptr<CMap> 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
{