diff --git a/KDCEditor.pro b/KDCEditor.pro index 0b37ea6..2749ecd 100644 --- a/KDCEditor.pro +++ b/KDCEditor.pro @@ -8,10 +8,10 @@ TEMPLATE = app CONFIG += c++11 CONFIG(debug, debug|release) { - DESTDIR = debug + DESTDIR = debug } CONFIG(release, debug|release) { - DESTDIR = release + DESTDIR = release } OBJECTS_DIR = obj/$$DESTDIR @@ -20,10 +20,10 @@ RCC_DIR = $$OBJECTS_DIR # copy docs and samples on build copydata.commands += \ - $(COPY_DIR) \"$$PWD/docs\" $$DESTDIR &&\ - $(COPY_FILE) \"$$PWD/CHANGES.txt\" $$DESTDIR &&\ - $(COPY_FILE) \"$$PWD/COPYING.txt\" $$DESTDIR &&\ - $(COPY_DIR) \"$$PWD/samples\" $$DESTDIR + $(COPY_DIR) \"$$PWD/docs\" $$DESTDIR &&\ + $(COPY_FILE) \"$$PWD/CHANGES.txt\" $$DESTDIR &&\ + $(COPY_FILE) \"$$PWD/COPYING.txt\" $$DESTDIR &&\ + $(COPY_DIR) \"$$PWD/samples\" $$DESTDIR first.depends = $(first) copydata export(first.depends) @@ -38,52 +38,55 @@ macx:ICON = src/images/main.icns macx:QMAKE_CXXFLAGS += -stdlib=libc++ SOURCES += src/main.cpp\ - src/mainwindow.cpp \ - src/tileeditwindow.cpp \ - src/compress.c \ - src/level.cpp \ - src/kirby.cpp \ - src/mapscene.cpp \ - src/previewwindow.cpp \ - src/propertieswindow.cpp \ - src/romfile.cpp \ - src/metatile.cpp \ - src/metatile_terrain.cpp \ - src/metatile_borders.cpp \ - src/metatile_obstacles.cpp \ - src/coursewindow.cpp \ - src/previewscene.cpp \ - src/mapchange.cpp + src/mainwindow.cpp \ + src/tileeditwindow.cpp \ + src/compress.c \ + src/level.cpp \ + src/kirby.cpp \ + src/mapscene.cpp \ + src/previewwindow.cpp \ + src/propertieswindow.cpp \ + src/romfile.cpp \ + src/metatile.cpp \ + src/metatile_terrain.cpp \ + src/metatile_borders.cpp \ + src/metatile_obstacles.cpp \ + src/coursewindow.cpp \ + src/previewscene.cpp \ + src/object_list_window.cpp \ + src/mapchange.cpp HEADERS += src/mainwindow.h \ - src/tileeditwindow.h \ - src/compress.h \ - src/level.h \ - src/kirby.h \ - src/mapscene.h \ - src/graphics.h \ - src/previewwindow.h \ - src/propertieswindow.h \ - src/romfile.h \ - src/metatile.h \ - src/coursewindow.h \ - src/version.h \ - src/previewscene.h \ - src/mapchange.h + src/tileeditwindow.h \ + src/compress.h \ + src/level.h \ + src/kirby.h \ + src/mapscene.h \ + src/graphics.h \ + src/previewwindow.h \ + src/propertieswindow.h \ + src/romfile.h \ + src/metatile.h \ + src/coursewindow.h \ + src/version.h \ + src/previewscene.h \ + src/object_list_window.h \ + src/mapchange.h FORMS += src/mainwindow.ui \ - src/tileeditwindow.ui \ - src/previewwindow.ui \ - src/propertieswindow.ui \ - src/coursewindow.ui + src/tileeditwindow.ui \ + src/previewwindow.ui \ + src/propertieswindow.ui \ + src/object_list_window.ui \ + src/coursewindow.ui RESOURCES += \ - src/images.qrc \ - src/icons.qrc + src/images.qrc \ + src/icons.qrc OTHER_FILES += \ - CHANGES.txt \ - README.md \ - src/TODO.txt \ - src/coursefiles.txt \ - src/windows.rc + CHANGES.txt \ + README.md \ + src/TODO.txt \ + src/coursefiles.txt \ + src/windows.rc diff --git a/src/kirby.cpp b/src/kirby.cpp index 52d0f8f..5ef31ff 100644 --- a/src/kirby.cpp +++ b/src/kirby.cpp @@ -4,8 +4,14 @@ */ #include "kirby.h" +#include "level.h" +#include "metatile.h" #include +#include + +using namespace stuff; + const int fgPaletteBase[] = {0xD4A9, 0xD8ED, 0xD8ED}; const int waterBase[][3] = {{0xA444, 0xE030, 0xE030}, {0xA46E, 0xE05A, 0xE05A}}; @@ -372,3 +378,229 @@ const StringMap kirbyObstacles ({ {0xc2, "C2: Starting line (east end)"}, {0xc3, "C3: Kirby (course 24-1 only)"}, }); + +/* + This array maps conveyor belt types to their counterparts for the different slope types. + Dimension 1 is the belt direction, dimension 2 is the slope direction +*/ +const stuff::type_e conveyorMap[4][4] = { +//slope south east north west +//beltSouth + {beltSouthDown, nothing, beltSouthUp, nothing}, +//beltEast + {nothing, beltEastDown, nothing, beltEastUp}, +//beltNorth + {beltNorthUp, nothing, beltNorthDown, nothing}, +//beltWest + {nothing, beltWestUp, nothing, beltWestDown} +}; + +Util* Util::_instance = nullptr; + +Util::Util() { + _bounce.load (":images/bounce.png"); + _bumpers.load (":images/bumpers.png"); + _conveyor.load(":images/conveyor.png"); + _dedede.load (":images/dedede.png"); + _enemies.load (":images/enemies.png"); + _gordo.load (":images/gordo3d.png"); + _movers.load (":images/movers.png"); + _player.load (":images/kirby.png"); + _rotate.load (":images/rotate.png"); + _switches.load(":images/switches.png"); + _tiles.load (":images/terrain.png"); + _traps.load (":images/traps.png"); + _unknown.load (":images/unknown.png"); + _warps.load (":images/warps.png"); + _water.load (":images/water.png"); +} + +Util* Util::Instance() { + if (_instance == nullptr) { + _instance = new Util(); + } + + return _instance; +} + +bool Util::IsObstacleCharacterType(const int& obstacle) { + if (obstacle == 0x02) { // whispy woods + return true; + } else if (obstacle == 0x0c || obstacle == 0xc3) { // kirby + return true; + } else if (obstacle == 0x0d) { // dedede + return true; + } else if (obstacle >= 0x40 && obstacle <= 0x52) { // most enemies + return true; + } else if (obstacle == 0x57) { // transformer + return true; + } else if (obstacle >= 0x80 && obstacle <= 0x97) { // gordo + return true; + } else if (obstacle >= 0xac && obstacle <= 0xae) { // kracko + return true; + } else { + return false; + } +} + +bool Util::GetPixmapSettingsForObstacle(const int& obstacle, const QPixmap** pixmap, int* frame) { + if (obstacle == 0) { + return false; + } + + // whispy woods (index 0x00 in enemies.png) + if (obstacle == 0x02) { + (*pixmap) = &_enemies; + (*frame) = 0; + + // kirby's start pos (kirby.png) + // (this time also use the final boss version) + } else if (obstacle == 0x0c || obstacle == 0xc3) { + (*pixmap) = &_player; + (*frame) = 0; + + // dedede (frame 0 in dedede.png) + } else if (obstacle == 0x0d) { + (*pixmap) = &_dedede; + (*frame) = 0; + + // most enemies (ind. 01 to 13 in enemies.png) + } else if (obstacle >= 0x40 && obstacle <= 0x52) { + (*pixmap) = &_enemies; + (*frame) = obstacle - 0x40 + 1; + + // transformer (ind. 14 in enemies.png + } else if (obstacle == 0x57) { + (*pixmap) = &_enemies; + (*frame) = 0x14; + + // gordo (ind. 00 to 21 in gordo.png) + } else if (obstacle >= 0x80 && obstacle <= 0x97) { + (*pixmap) = &_gordo; + (*frame) = obstacle - 0x80; + + // kracko (index 15-17 in enemies.png) + } else if (obstacle >= 0xac && obstacle <= 0xae) { + (*pixmap) = &_enemies; + (*frame) = obstacle - 0xac + 0x15; + + // sand trap (index 0 in traps.png) + } else if (obstacle == 0x04) { + (*pixmap) = &_traps; + (*frame) = 0; + + // spike pit (index 1 in traps.png) + } else if (obstacle == 0x05) { + (*pixmap) = &_traps; + (*frame) = 1; + + // current, arrows, boosters, vents + // (ind. 00 to 0d in movers.png) + } else if (obstacle >= 0x10 && obstacle <= 0x1d) { + (*pixmap) = &_movers; + (*frame) = obstacle - 0x10; + + // bouncy pads (ind. 0 to 4 in bounce.png) + } else if (obstacle >= 0x20 && obstacle <= 0x24) { + (*pixmap) = &_bounce; + (*frame) = obstacle - 0x20; + + // bumpers (start at index 4 in bumpers.png) + } else if (obstacle >= 0x28 && obstacle <= 0x2d) { + (*pixmap) = &_bumpers; + (*frame) = obstacle - 0x28 + 4; + + // conveyor belts (ind. 0 to b in conveyor.png) + } else if (obstacle >= 0x30 && obstacle <= 0x3b) { + (*pixmap) = &_conveyor; + (*frame) = obstacle - 0x30; + + // switches (ind. 0 to 5 in switches.png) + } else if (obstacle >= 0x58 && obstacle <= 0x5d) { + (*pixmap) = &_switches; + (*frame) = obstacle - 0x58; + + // water hazards (ind. 0 to e in water.png) + // (note types 62 & 63 seem unused) + } else if (obstacle >= 0x61 && obstacle <= 0x6f) { + (*pixmap) = &_water; + (*frame) = obstacle - 0x61; + + // rotating spaces (ind. 0-b in rotate.png) + } else if (obstacle >= 0x70 && obstacle <= 0x7b) { + (*pixmap) = &_rotate; + (*frame) = obstacle & 0x01; + + // warps (ind. 0 to 9 in warps.png) + } else if (obstacle >= 0xb0 && obstacle <= 0xb9) { + (*pixmap) = &_warps; + (*frame) = obstacle - 0xb0; + + // starting line (ind. 1 to 4 in dedede.png) + } else if (obstacle >= 0xc0 && obstacle <= 0xc3) { + (*pixmap) = &_dedede; + (*frame) = obstacle - 0xc0 + 1; + + // anything else - question mark (or don't draw) + } else { +#ifdef QT_DEBUG + (*pixmap) = &_unknown; + (*frame) = 0; +#endif + return false; + } + + return true; +} + +bool Util::ApplyTileToExistingTile(const tileinfo_t& tileInfo, maptile_t* newTile) { + const bool relativeHeight = tileInfo.height == -1; + + if (newTile->geometry == 0 && tileInfo.geometry == -1) { + return false; + } else if (tileInfo.geometry == 0) { + *newTile = noTile; + return false; + } + + if (tileInfo.geometry >= 0) + newTile->geometry = tileInfo.geometry; + if (tileInfo.obstacle >= 0) { + // handle multiple types for water hazard based on terrain value + if (tileInfo.obstacle == water + && newTile->geometry >= slopes && newTile->geometry < endSlopes) + newTile->obstacle = water - 1 + newTile->geometry; + // handle multiple types for bounce pads + else if (tileInfo.obstacle == bounceFlat + && newTile->geometry >= slopes && newTile->geometry < slopesDouble) + newTile->obstacle = bounce + newTile->geometry - slopes; + // handle multiple types for conveyor belts + else if (tileInfo.obstacle >= belts && tileInfo.obstacle < beltSlopes + && newTile->geometry >= slopes && newTile->geometry < slopesDouble) + newTile->obstacle = conveyorMap[tileInfo.obstacle - belts][newTile->geometry - slopes]; + else + newTile->obstacle = tileInfo.obstacle; + } + + if (tileInfo.bumperNorth >= 0) + newTile->flags.bumperNorth = tileInfo.bumperNorth; + if (tileInfo.bumperSouth >= 0) + newTile->flags.bumperSouth = tileInfo.bumperSouth; + if (tileInfo.bumperEast >= 0) + newTile->flags.bumperEast = tileInfo.bumperEast; + if (tileInfo.bumperWest>= 0) + newTile->flags.bumperWest = tileInfo.bumperWest; + + if (relativeHeight) + newTile->height += tileInfo.height; + else + newTile->height = tileInfo.height; + + if (tileInfo.layer >= 0) { + newTile->flags.layer = tileInfo.layer; + } + + newTile->flags.dummy = 0; + + return true; +} diff --git a/src/kirby.h b/src/kirby.h index 4c33612..e611977 100644 --- a/src/kirby.h +++ b/src/kirby.h @@ -14,6 +14,8 @@ #define BG_PALETTE_SIZE 0x28 #define NUM_BACKGROUNDS 6 +#include "level.h" +#include #include #include @@ -45,4 +47,34 @@ extern const StringMap musicNames; extern const StringMap kirbyGeometry; extern const StringMap kirbyObstacles; +class Util { + + public: + static Util* Instance(); + + bool GetPixmapSettingsForObstacle(const int& obstacle, const QPixmap** pixmap, int* frame); + bool ApplyTileToExistingTile(const tileinfo_t& tileInfo, maptile_t* newTile); + bool IsObstacleCharacterType(const int& obstacle); + + private: + explicit Util(); + static Util* _instance; + + QPixmap _bounce; + QPixmap _bumpers; + QPixmap _conveyor; + QPixmap _dedede; + QPixmap _enemies; + QPixmap _gordo; + QPixmap _movers; + QPixmap _player; + QPixmap _rotate; + QPixmap _switches; + QPixmap _tiles; + QPixmap _traps; + QPixmap _unknown; + QPixmap _warps; + QPixmap _water; +}; + #endif // KIRBY_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 72a17f0..95034ba 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -49,7 +49,8 @@ MainWindow::MainWindow(QWidget *parent) : levelLabel(new QLabel()), scene(new MapScene(this, ¤tLevel)), - previewWin(new PreviewWindow(this, ¤tLevel)) + previewWin(new PreviewWindow(this, ¤tLevel)), + objectListWindow(new ObjectListWindow(this)) { ui->setupUi(this); @@ -90,6 +91,8 @@ MainWindow::MainWindow(QWidget *parent) : ui->action_Delete->setShortcut(Qt::Key_Backspace); #endif + + this->objectListWindow->show(); } MainWindow::~MainWindow() diff --git a/src/mainwindow.h b/src/mainwindow.h index 6a5dcdf..b512541 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -1,6 +1,6 @@ /* - This code is released under the terms of the MIT license. - See COPYING.txt for details. + This code is released under the terms of the MIT license. + See COPYING.txt for details. */ #ifndef MAINWINDOW_H @@ -16,98 +16,101 @@ #include "level.h" #include "previewwindow.h" +#include "object_list_window.h" + namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { - Q_OBJECT - -public: - explicit MainWindow(QWidget *parent = 0); - ~MainWindow(); - -protected slots: - // file menu - void openFile(); - void saveFile(); - void saveFileAs(); - int closeFile(); - - void setUnsaved(); - - // level menu - void loadLevelFromFile(); - void saveLevelToFile(); - void loadCourseFromFile(); - void saveCourseToFile(); - - void saveCurrentLevel(); - - void levelProperties(); - - void selectCourse(); - void prevLevel(); - void nextLevel(); - void prevCourse(); - void nextCourse(); - - // help menu - void showHelp(); - void showAbout(); - - // display text on the statusbar - void status(const QString &msg); - - // debug menu crap - void dumpLevel(); - - // toolbar updates - void setOpenFileActions(bool val); - void setEditActions(bool val); - void setUndoRedoActions(bool val = true); - void setLevelChangeActions(bool val); - -protected: - void closeEvent(QCloseEvent *); - -private: - Ui::MainWindow *ui; - - QSettings *settings; - - // Information about the currently open file - QString fileName; - ROMFile rom; - bool fileOpen, unsaved, saving; - - // The level data (28 courses, 8 holes each) - int level; - leveldata_t* levels[224]; - leveldata_t currentLevel; - QLabel *levelLabel; - - // course background/palette settings - // (background selectiosn repeat every 8 courses) - int background[8]; - int palette[28]; - int waterPalette[28]; - - // renderin stuff - MapScene *scene; - // the preview window - PreviewWindow *previewWin; - - // various funcs - void setupSignals(); - void setupActions(); - void getSettings(); - void saveSettings(); - void updateTitle(); - void setLevel(int); - QMessageBox::StandardButton checkSaveLevel(); - QMessageBox::StandardButton checkSaveROM(); + Q_OBJECT + + public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + + protected slots: + // file menu + void openFile(); + void saveFile(); + void saveFileAs(); + int closeFile(); + + void setUnsaved(); + + // level menu + void loadLevelFromFile(); + void saveLevelToFile(); + void loadCourseFromFile(); + void saveCourseToFile(); + + void saveCurrentLevel(); + + void levelProperties(); + + void selectCourse(); + void prevLevel(); + void nextLevel(); + void prevCourse(); + void nextCourse(); + + // help menu + void showHelp(); + void showAbout(); + + // display text on the statusbar + void status(const QString &msg); + + // debug menu crap + void dumpLevel(); + + // toolbar updates + void setOpenFileActions(bool val); + void setEditActions(bool val); + void setUndoRedoActions(bool val = true); + void setLevelChangeActions(bool val); + + protected: + void closeEvent(QCloseEvent *); + + private: + Ui::MainWindow *ui; + + QSettings *settings; + + // Information about the currently open file + QString fileName; + ROMFile rom; + bool fileOpen, unsaved, saving; + + // The level data (28 courses, 8 holes each) + int level; + leveldata_t* levels[224]; + leveldata_t currentLevel; + QLabel *levelLabel; + + // course background/palette settings + // (background selectiosn repeat every 8 courses) + int background[8]; + int palette[28]; + int waterPalette[28]; + + // renderin stuff + MapScene *scene; + // the preview window + PreviewWindow *previewWin; + ObjectListWindow* objectListWindow; + + // various funcs + void setupSignals(); + void setupActions(); + void getSettings(); + void saveSettings(); + void updateTitle(); + void setLevel(int); + QMessageBox::StandardButton checkSaveLevel(); + QMessageBox::StandardButton checkSaveROM(); }; #endif // MAINWINDOW_H diff --git a/src/mapscene.cpp b/src/mapscene.cpp index ed049e1..c65f3a4 100644 --- a/src/mapscene.cpp +++ b/src/mapscene.cpp @@ -3,6 +3,7 @@ See COPYING.txt for details. */ +#include #include #include #include @@ -17,6 +18,8 @@ #include "tileeditwindow.h" #include "graphics.h" +#include + #define MAP_TEXT_PAD_H 2 #define MAP_TEXT_PAD_V 1 @@ -59,6 +62,7 @@ MapScene::MapScene(QWidget *parent, leveldata_t *currentLevel) switches.load(":images/switches.png"); unknown.load (":images/unknown.png"); + this->setAcceptDrops(true); this->setMouseTracking(true); this->setFocusPolicy(Qt::WheelFocus); @@ -122,7 +126,7 @@ void MapScene::mousePressEvent(QMouseEvent *event) { Handle when a double-click occurs (used to start the tile edit window) */ void MapScene::mouseDoubleClickEvent(QMouseEvent *event) { - editTiles(); + editTiles(); emit doubleClicked(); event->accept(); @@ -133,7 +137,7 @@ void MapScene::mouseDoubleClickEvent(QMouseEvent *event) { Handle when the left mouse button is released */ void MapScene::mouseReleaseEvent(QMouseEvent *event) { - if (event->button() == Qt::LeftButton) { + if (event->button() == Qt::LeftButton) { selecting = false; // normalize selection dimensions (i.e. handle negative height/width) @@ -156,7 +160,7 @@ void MapScene::mouseReleaseEvent(QMouseEvent *event) { Handle when the mouse is moved over the scene */ void MapScene::mouseMoveEvent(QMouseEvent *event) { - if (selecting && event->buttons() & Qt::LeftButton) { + if (selecting && event->buttons() & Qt::LeftButton) { // left button down: generate/show selection updateSelection(event); } else { @@ -478,6 +482,78 @@ void MapScene::updateSelection(QMouseEvent *event) { update(); } +void MapScene::dragEnterEvent(QDragEnterEvent *event) { + if (event->mimeData()->hasFormat("application/x-dnditemdata")) { + if (event->source() == this) { + event->setDropAction(Qt::MoveAction); + event->accept(); + } else { + event->acceptProposedAction(); + } + } else { + event->ignore(); + } +} + +void MapScene::dropEvent(QDropEvent *event) { + qDebug() << "Got drop event" << event->pos(); + + if (event->mimeData()->hasFormat("application/x-dnditemdata")) { + QByteArray itemData = event->mimeData()->data("application/x-dnditemdata"); + QDataStream dataStream(&itemData, QIODevice::ReadOnly); + + int identifier; + dataStream >> identifier; + + qDebug() << "Dropping item with identifier:" << identifier; + + QPointF pos = event->pos(); + tileX = floor(pos.x() / TILE_SIZE); + tileY = floor(pos.y() / TILE_SIZE); + + qDebug() << "Tile:" << tileX << tileY; + + maptile_t* tile = &level->tiles[tileY][tileX]; + tileinfo_t tileInfo; + tileInfo.geometry = 1; + tileInfo.obstacle = identifier; + tileInfo.height = 0; + tileInfo.layer = 0; + tileInfo.bumperSouth = 0; + tileInfo.bumperEast = 0; + tileInfo.bumperNorth = 0; + tileInfo.bumperWest = 0; + + Util::Instance()->ApplyTileToExistingTile(tileInfo, tile); + + qDebug() << "New Tile Properties:" + << tile->geometry + << tile->obstacle + << tile->height + << tile->flags.bumperSouth + << tile->flags.bumperEast + << tile->flags.bumperNorth + << tile->flags.bumperWest + << tile->flags.layer + << tile->flags.dummy + << tile->flags.layer; + + MapChange *edit = new MapChange(level, tileX, tileY, 1, 1); + stack.push(edit); + + emit edited(); + + if (event->source() == this) { + event->setDropAction(Qt::MoveAction); + event->accept(); + } else { + event->acceptProposedAction(); + } + } else { + event->ignore(); + } +} + /* Display information about a map tile being hovered over. Called when the mouse is over the MapScene without the left button held down. @@ -539,6 +615,7 @@ void MapScene::paintEvent(QPaintEvent *event) { for (int h = rect.top() / TILE_SIZE; h < MAX_2D_SIZE && h <= rect.bottom() / TILE_SIZE; h++) { for (int w = rect.left() / TILE_SIZE; w < MAX_2D_SIZE && w <= rect.right() / TILE_SIZE; w++) { maptile_t *tile = &level->tiles[h][w]; + int geo = tile->geometry; if (geo) { painter.drawPixmap(w * TILE_SIZE, h * TILE_SIZE, @@ -550,129 +627,16 @@ void MapScene::paintEvent(QPaintEvent *event) { // include obstacles and all other stuff in the same pass int obs = tile->obstacle; - QPixmap gfx; + const QPixmap* gfx; int frame = 0; - - /* - * - * START OF KIRBY OBSTACLE CHECK - * (TODO: move the enum from metatile.h and use it instead of magic numbers - */ - - // whispy woods (index 0x00 in enemies.png) - if (obs == 0x02) { - gfx = enemies; - frame = 0; - - // sand trap (index 0 in traps.png) - } else if (obs == 0x04) { - gfx = traps; - frame = 0; - - // spike pit (index 1 in traps.png) - } else if (obs == 0x05) { - gfx = traps; - frame = 1; - - // kirby's start pos (kirby.png) - } else if (obs == 0x0c) { - gfx = kirby; - frame = 0; - - // dedede (frame 0 in dedede.png) - } else if (obs == 0x0d) { - gfx = dedede; - frame = 0; - - // current, arrows, boosters, vents - // (ind. 00 to 0d in movers.png) - } else if (obs >= 0x10 && obs <= 0x1d) { - gfx = movers; - frame = obs - 0x10; - - // bouncy pads (ind. 0 to 4 in bounce.png) - } else if (obs >= 0x20 && obs <= 0x24) { - gfx = bounce; - frame = obs - 0x20; - - // bumpers (start at index 4 in bumpers.png) - } else if (obs >= 0x28 && obs <= 0x2d) { - gfx = bumpers; - frame = obs - 0x28 + 4; - - // conveyor belts (ind. 0 to b in conveyor.png) - } else if (obs >= 0x30 && obs <= 0x3b) { - gfx = conveyor; - frame = obs - 0x30; - - // most enemies (ind. 01 to 13 in enemies.png) - } else if (obs >= 0x40 && obs <= 0x52) { - gfx = enemies; - frame = obs - 0x40 + 1; - - // transformer (ind. 14 in enemies.png - } else if (obs == 0x57) { - gfx = enemies; - frame = 0x14; - - // switches (ind. 0 to 5 in switches.png) - } else if (obs >= 0x58 && obs <= 0x5d) { - gfx = switches; - frame = obs - 0x58; - - // water hazards (ind. 0 to e in water.png) - // (note types 62 & 63 seem unused) - } else if (obs >= 0x61 && obs <= 0x6f) { - gfx = water; - frame = obs - 0x61; - - // rotating spaces (ind. 0-b in rotate.png) - } else if (obs >= 0x70 && obs <= 0x7b) { - gfx = rotate; - frame = obs & 0x01; - - // gordo (ind. 00 to 21 in gordo.png) - } else if (obs >= 0x80 && obs <= 0xa1) { - gfx = gordo; - frame = obs - 0x80; - - // kracko (index 15-17 in enemies.png) - } else if (obs >= 0xac && obs <= 0xae) { - gfx = enemies; - frame = obs - 0xac + 0x15; - - // warps (ind. 0 to 9 in warps.png) - } else if (obs >= 0xb0 && obs <= 0xb9) { - gfx = warps; - frame = obs - 0xb0; - - // starting line (ind. 1 to 4 in dedede.png) - } else if (obs >= 0xc0 && obs <= 0xc3) { - gfx = dedede; - frame = obs - 0xc0 + 1; - - // anything else - question mark (or don't draw) - } else { -#ifdef QT_DEBUG - gfx = unknown; - frame = 0; -#else - obs = 0; -#endif - } - - /* - * - * END OF KIRBY OBSTACLE CHECK - * - */ + Util::Instance()->GetPixmapSettingsForObstacle(obs, &gfx, &frame); // draw the selected obstacle if (obs) { painter.drawPixmap(w * TILE_SIZE, - (h + 1) * TILE_SIZE - gfx.height(), - gfx, frame * TILE_SIZE, 0, - TILE_SIZE, gfx.height()); + (h + 1) * TILE_SIZE - gfx->height(), + *gfx, frame * TILE_SIZE, 0, + TILE_SIZE, gfx->height()); } // render side bumpers (ind. 0 - 3 in bumpers.png) diff --git a/src/mapscene.h b/src/mapscene.h index 8559392..4c7f03d 100644 --- a/src/mapscene.h +++ b/src/mapscene.h @@ -81,6 +81,8 @@ public slots: void mouseDoubleClickEvent(QMouseEvent *event); void wheelEvent(QWheelEvent *event); void paintEvent(QPaintEvent *event); + void dropEvent(QDropEvent *event); + void dragEnterEvent(QDragEnterEvent *event); }; #endif // MAPSCENE_H diff --git a/src/object_list_window.cpp b/src/object_list_window.cpp new file mode 100644 index 0000000..f71fde9 --- /dev/null +++ b/src/object_list_window.cpp @@ -0,0 +1,230 @@ +#include "object_list_window.h" +#include "ui_object_list_window.h" + +#include "graphics.h" +#include "kirby.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using std::string; + +ObjectListWindow::ObjectListWindow(QWidget* parent): + QDialog(parent, Qt::Tool + | Qt::CustomizeWindowHint + | Qt::WindowTitleHint + | Qt::WindowMinimizeButtonHint + | Qt::WindowCloseButtonHint + // | Qt::WindowStaysOnTopHint + ), + ui(new Ui::ObjectListWindow) +{ + ui->setupUi(this); + setAcceptDrops(true); + + this->setupObjectIcons(); +} + +ObjectListWindow::~ObjectListWindow() { + delete ui; +} + +void ObjectListWindow::setupObjectIcons() { + // add obstacle types to dropdown (only valid ones) + for (StringMap::const_iterator i = kirbyObstacles.begin(); i != kirbyObstacles.end(); i++) { + // Skip obstacles that shouldn't really be used. + if (i->first == 0x0d // King Dedede (course 24-1 only) + || i->first == 0xc3 // Kirby (course 24-1 only) + ) { + continue; + } + + if (i->second.size()) { + const uint offset = i->first; + const QString name = i->second; + + // Get the pixmap for the obstacle. + const QPixmap* pixmap; + int frame; + if (Util::Instance()->GetPixmapSettingsForObstacle(offset, &pixmap, &frame)) { + QPixmap piece = pixmap->copy(frame * TILE_SIZE, 0, TILE_SIZE, pixmap->height()); + + // Crop to non-transparent bounds. + const QImage image = piece.toImage(); + bool should_break = false; + int ymin = 0; + int ymax = image.height(); + + for (int y = 0; y < image.height(); ++y) { + const uchar* scanline = image.constScanLine(y); + for (int x = 0; x < image.width(); ++x) { + if (scanline[x * 4] != 0) { + should_break = true; + ymin = y - 3; + } + } + + if (should_break) { + break; + } + } + + should_break = false; + for (int y = image.height() - 1; y >= 0; --y) { + const uchar* scanline = image.scanLine(y); + for (int x = 0; x < image.width(); ++x) { + if (scanline[x * 4] != 0) { + should_break = true; + ymax = y + 3; + } + } + + if (should_break) { + break; + } + } + + qDebug() << name << ": (" << ymin << ", " << ymax << ")"; + + // Create a new label and assign the pixmap + QLabel* label = new QLabel(this); + label->setAlignment(Qt::AlignCenter); + label->setPixmap(piece.copy(0, ymin, piece.width(), ymax - ymin)); + // label->setText(name); + + RenderableAsset asset; + asset.label = label; + asset.width = TILE_SIZE; + asset.height = ymax - ymin; + asset.identifier = i->first; + + if (Util::Instance()->IsObstacleCharacterType(offset)) { + this->_character_assets.push_back(asset); + } else { + this->_obstacle_assets.push_back(asset); + } + } + } + } + + this->layoutIcons(); +} + +void ObjectListWindow::layoutIconSet(const QString& layoutName, const std::vector& assets) { + QGridLayout* const layout = this->findChild(layoutName); + layout->setContentsMargins(0,0,0,0); + + for (int i = 0; i < assets.size(); ++i) { + layout->removeWidget(assets[i].label); + } + + const int width = this->width(); + const int padding = 20; + int consumed = 0; + int col = 0; + int row = 0; + for (int i = 0; i < assets.size(); ++i) { + if (consumed + assets[i].width + padding > width) { + consumed = 0; + col = 0; + row++; + } + + layout->addWidget(assets[i].label, row, col); + consumed += assets[i].width + padding; + col++; + } +} + +void ObjectListWindow::layoutIcons() { + layoutIconSet("charactersHolder", _character_assets); + layoutIconSet("obstaclesHolder", _obstacle_assets); +} + +void ObjectListWindow::resizeEvent(QResizeEvent* event) { + this->layoutIcons(); +} + +void ObjectListWindow::dragEnterEvent(QDragEnterEvent *event) +{ + if (event->mimeData()->hasFormat("application/x-dnditemdata")) { + if (event->source() == this) { + event->setDropAction(Qt::MoveAction); + event->accept(); + } else { + event->acceptProposedAction(); + } + } else { + event->ignore(); + } +} + +void ObjectListWindow::dragMoveEvent(QDragMoveEvent *event) +{ + if (event->mimeData()->hasFormat("application/x-dnditemdata")) { + if (event->source() == this) { + event->setDropAction(Qt::MoveAction); + event->accept(); + } else { + event->acceptProposedAction(); + } + } else { + event->ignore(); + } +} + +void ObjectListWindow::mousePressEvent(QMouseEvent* event) { + qDebug() << "Mouse Press Event: [" << event->button() << ", " << event->x() << ", " << event->y() << "]"; + + QLabel *child = static_cast(childAt(event->pos())); + auto it = std::find(_character_assets.begin(), _character_assets.end(), child); + + if (it == _character_assets.end()) { + it = std::find(_obstacle_assets.begin(), _obstacle_assets.end(), child); + if (it == _obstacle_assets.end()) { + qDebug() << "No element found at click"; + return; + } + } + + QPixmap pixmap = *child->pixmap(); + const int identifier = it->identifier; + + QByteArray itemData; + QDataStream dataStream(&itemData, QIODevice::WriteOnly); + dataStream << identifier; + + QMimeData* mimeData = new QMimeData; + mimeData->setData("application/x-dnditemdata", itemData); + + QDrag *drag = new QDrag(this); + drag->setMimeData(mimeData); + drag->setPixmap(pixmap); + drag->setHotSpot(event->pos() - child->pos()); + + QPixmap tempPixmap = pixmap; + QPainter painter; + painter.begin(&tempPixmap); + painter.fillRect(pixmap.rect(), QColor(127, 127, 127, 127)); + painter.end(); + + child->setPixmap(tempPixmap); + + if (drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction) == Qt::MoveAction) { + child->close(); + } else { + child->show(); + child->setPixmap(pixmap); + } +} diff --git a/src/object_list_window.h b/src/object_list_window.h new file mode 100644 index 0000000..fa50126 --- /dev/null +++ b/src/object_list_window.h @@ -0,0 +1,66 @@ +#ifndef __OBJECT_LIST_WINDOW_H__ +#define __OBJECT_LIST_WINDOW_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace Ui { +class ObjectListWindow; +} + +struct RenderableAsset { + QLabel* label; + int width; + int height; + int identifier; + + friend bool operator==(const RenderableAsset& n1, const RenderableAsset& n2) { + return n1.label == n2.label; + } + + friend bool operator==(const RenderableAsset& n1, const QLabel* label) { + return n1.label == label; + } + + friend bool operator==(const QLabel* label, const RenderableAsset& n1) { + return n1.label == label; + } +}; + +class ObjectListWindow : public QDialog { + Q_OBJECT + + public: + explicit ObjectListWindow(QWidget* parent = nullptr); + ~ObjectListWindow(); + + protected slots: + void mousePressEvent(QMouseEvent* event); + void resizeEvent(QResizeEvent* event); + void dragEnterEvent(QDragEnterEvent *event); + void dragMoveEvent(QDragMoveEvent *event); + + protected: + void setupObjectIcons(); + void layoutIcons(); + void layoutIconSet(const QString& layoutName, const std::vector& assets); + + private: + Ui::ObjectListWindow* ui; + + std::vector _character_assets; + std::vector _obstacle_assets; +}; + +#endif diff --git a/src/object_list_window.ui b/src/object_list_window.ui new file mode 100644 index 0000000..bc6e65a --- /dev/null +++ b/src/object_list_window.ui @@ -0,0 +1,39 @@ + + + ObjectListWindow + + + + 800 + 0 + 640 + 350 + + + + Insert Objects + + + + + + + Characters + + + + + + + Obstacles + + + + + + + + + + + diff --git a/src/previewscene.cpp b/src/previewscene.cpp index 4c16b8e..c1f7025 100644 --- a/src/previewscene.cpp +++ b/src/previewscene.cpp @@ -127,52 +127,17 @@ void PreviewScene::refresh(uint16_t (&playfield)[2][MAX_FIELD_HEIGHT][MAX_FIELD_ if (sprites) { for (int y = 0; y < mapLength; y++) { for (int x = 0; x < mapWidth; x++) { - QPixmap gfx; + const QPixmap* gfx; int frame; int obs = level->tiles[y][x].obstacle; int z = level->tiles[y][x].height; - if (obs == 0) continue; - - // whispy woods (index 0x00 in enemies.png) - if (obs == 0x02) { - gfx = enemies; - frame = 0; - - // kirby's start pos (kirby.png) - // (this time also use the final boss version) - } else if (obs == 0x0c || obs == 0xc3) { - gfx = player; - frame = 0; - - // dedede (frame 0 in dedede.png) - } else if (obs == 0x0d) { - gfx = dedede; - frame = 0; - - // most enemies (ind. 01 to 13 in enemies.png) - } else if (obs >= 0x40 && obs <= 0x52) { - gfx = enemies; - frame = obs - 0x40 + 1; - - // transformer (ind. 14 in enemies.png - } else if (obs == 0x57) { - gfx = enemies; - frame = 0x14; - - // gordo (ind. 00 to 21 in gordo.png) - } else if (obs >= 0x80 && obs <= 0x97) { - gfx = gordo; - frame = obs - 0x80; - - // kracko (index 15-17 in enemies.png) - } else if (obs >= 0xac && obs <= 0xae) { - gfx = enemies; - frame = obs - 0xac + 0x15; - - // anything else - question mark (or don't draw) - } else { - obs = 0; + if (!Util::Instance()->GetPixmapSettingsForObstacle(obs, &gfx, &frame)) { + continue; + } + + if (!Util::Instance()->IsObstacleCharacterType(obs)) { + continue; } // draw the selected obstacle @@ -186,14 +151,14 @@ void PreviewScene::refresh(uint16_t (&playfield)[2][MAX_FIELD_HEIGHT][MAX_FIELD_ // and TILE_SIZE / 4 down for each positive move on the y-axis (north to south) // and TILE_SIZE / 4 up for each positive move on the z-axis (tile z) // and then adjust for height of sprites - int startY = (TILE_SIZE / 4) * (mapHeight + x + y - z + 4) - gfx.height(); + int startY = (TILE_SIZE / 4) * (mapHeight + x + y - z + 4) - gfx->height(); // move down half a tile's worth if the sprite is on a slope if (level->tiles[y][x].geometry >= stuff::slopes) startY += TILE_SIZE / 8; painter.drawPixmap(startX, startY, - gfx, frame * TILE_SIZE, 0, - TILE_SIZE, gfx.height()); + *gfx, frame * TILE_SIZE, 0, + TILE_SIZE, gfx->height()); } } } @@ -205,4 +170,3 @@ void PreviewScene::refresh(uint16_t (&playfield)[2][MAX_FIELD_HEIGHT][MAX_FIELD_ this->addPixmap(pixmap); this->update(); } - diff --git a/src/tileeditwindow.cpp b/src/tileeditwindow.cpp index 52a7f8c..1291071 100644 --- a/src/tileeditwindow.cpp +++ b/src/tileeditwindow.cpp @@ -157,22 +157,6 @@ int TileEditWindow::startEdit(leveldata_t *level, QRect sel) { return this->exec(); } -/* - This array maps conveyor belt types to their counterparts for the different slope types. - Dimension 1 is the belt direction, dimension 2 is the slope direction -*/ -const stuff::type_e conveyorMap[4][4] = { -//slope south east north west -//beltSouth - {beltSouthDown, nothing, beltSouthUp, nothing}, -//beltEast - {nothing, beltEastDown, nothing, beltEastUp}, -//beltNorth - {beltNorthUp, nothing, beltNorthDown, nothing}, -//beltWest - {nothing, beltWestUp, nothing, beltWestDown} -}; - void TileEditWindow::accept() { // get terrain and obstacle values // (if multiple geom/obstacle values are selected, only use index >0) @@ -191,61 +175,24 @@ void TileEditWindow::accept() { // get height slider status int newHeight = ui->horizontalSlider_Height->value(); - bool relativeHeight = tileInfo.height == -1; + tileInfo.height = newHeight; // get layer selection status bool layer1 = ui->radioButton_Layer1->isChecked(); bool layer2 = ui->radioButton_Layer2->isChecked(); + if (layer2) { + tileInfo.layer = 1; + } else if (layer1) { + tileInfo.layer = 0; + } else { + tileInfo.layer = -1; + } // after the tile edit window is done, apply the changes to the tiles for (int v = selY; v < selY + selLength; v++) { for (int h = selX; h < selX + selWidth; h++) { maptile_t *newTile = &(level->tiles[v][h]); - - if (newTile->geometry == 0 && tileInfo.geometry == -1) { - continue; - } else if (tileInfo.geometry == 0) { - *newTile = noTile; - continue; - } - - if (tileInfo.geometry >= 0) - newTile->geometry = tileInfo.geometry; - if (tileInfo.obstacle >= 0) { - // handle multiple types for water hazard based on terrain value - if (tileInfo.obstacle == water - && newTile->geometry >= slopes && newTile->geometry < endSlopes) - newTile->obstacle = water - 1 + newTile->geometry; - // handle multiple types for bounce pads - else if (tileInfo.obstacle == bounceFlat - && newTile->geometry >= slopes && newTile->geometry < slopesDouble) - newTile->obstacle = bounce + newTile->geometry - slopes; - // handle multiple types for conveyor belts - else if (tileInfo.obstacle >= belts && tileInfo.obstacle < beltSlopes - && newTile->geometry >= slopes && newTile->geometry < slopesDouble) - newTile->obstacle = conveyorMap[tileInfo.obstacle - belts][newTile->geometry - slopes]; - else - newTile->obstacle = tileInfo.obstacle; - } - - if (tileInfo.bumperNorth >= 0) - newTile->flags.bumperNorth = tileInfo.bumperNorth; - if (tileInfo.bumperSouth >= 0) - newTile->flags.bumperSouth = tileInfo.bumperSouth; - if (tileInfo.bumperEast >= 0) - newTile->flags.bumperEast = tileInfo.bumperEast; - if (tileInfo.bumperWest>= 0) - newTile->flags.bumperWest = tileInfo.bumperWest; - - if (relativeHeight) - newTile->height += newHeight; - else - newTile->height = newHeight; - - if (layer2) newTile->flags.layer = 1; - else if (layer1) newTile->flags.layer = 0; - - newTile->flags.dummy = 0; + Util::Instance()->ApplyTileToExistingTile(tileInfo, newTile); } }