Browse Source

Merge pull request #28 from huderlem/multiblock

Add multi-block and smart-path tile painting
yenatch 6 years ago
parent
commit
997bfe7fea
No account linked to committer's email address
4 changed files with 172 additions and 22 deletions
  1. 152
    17
      editor.cpp
  2. 5
    1
      editor.h
  3. 8
    4
      map.cpp
  4. 7
    0
      map.h

+ 152
- 17
editor.cpp View File

@@ -271,11 +271,6 @@ void MetatilesPixmapItem::draw() {
271 271
     setPixmap(map->renderMetatiles());
272 272
 }
273 273
 
274
-void MetatilesPixmapItem::pick(uint tile) {
275
-    map->paint_tile = tile;
276
-    emit map->paintTileChanged(map);
277
-}
278
-
279 274
 void MetatilesPixmapItem::updateCurHoveredMetatile(QPointF pos) {
280 275
     int x = ((int)pos.x()) / 16;
281 276
     int y = ((int)pos.y()) / 16;
@@ -299,19 +294,42 @@ void MetatilesPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
299 294
     QPointF pos = event->pos();
300 295
     int x = ((int)pos.x()) / 16;
301 296
     int y = ((int)pos.y()) / 16;
302
-    //qDebug() << QString("(%1, %2)").arg(x).arg(y);
303
-    int width = pixmap().width() / 16;
304
-    int height = pixmap().height() / 16;
305
-    if ((x >= 0 && x < width) && (y >=0 && y < height)) {
306
-        pick(y * width + x);
307
-    }
297
+    map->paint_metatile_initial_x = x;
298
+    map->paint_metatile_initial_y = y;
299
+    updateSelection(event->pos(), event->button());
308 300
 }
309 301
 void MetatilesPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
310 302
     updateCurHoveredMetatile(event->pos());
311
-    mousePressEvent(event);
303
+    Qt::MouseButton button = event->button();
304
+    if (button == Qt::MouseButton::NoButton) {
305
+        Qt::MouseButtons heldButtons = event->buttons();
306
+        if (heldButtons & Qt::RightButton) {
307
+            button = Qt::RightButton;
308
+        } else if (heldButtons & Qt::LeftButton) {
309
+            button = Qt::LeftButton;
310
+        }
311
+    }
312
+    updateSelection(event->pos(), button);
312 313
 }
313 314
 void MetatilesPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
314
-    mousePressEvent(event);
315
+    updateSelection(event->pos(), event->button());
316
+}
317
+void MetatilesPixmapItem::updateSelection(QPointF pos, Qt::MouseButton button) {
318
+    int x = ((int)pos.x()) / 16;
319
+    int y = ((int)pos.y()) / 16;
320
+    int width = pixmap().width() / 16;
321
+    int height = pixmap().height() / 16;
322
+    if ((x >= 0 && x < width) && (y >=0 && y < height)) {
323
+        int baseTileX = x < map->paint_metatile_initial_x ? x : map->paint_metatile_initial_x;
324
+        int baseTileY = y < map->paint_metatile_initial_y ? y : map->paint_metatile_initial_y;
325
+        map->paint_tile = baseTileY * 8 + baseTileX;
326
+        map->paint_tile_width = abs(map->paint_metatile_initial_x - x) + 1;
327
+        map->paint_tile_height = abs(map->paint_metatile_initial_y - y) + 1;
328
+        map->smart_paths_enabled = button == Qt::RightButton
329
+                                && map->paint_tile_width == 3
330
+                                && map->paint_tile_height == 3;
331
+        emit map->paintTileChanged(map);
332
+    }
315 333
 }
316 334
 
317 335
 void CollisionMetatilesPixmapItem::updateCurHoveredMetatile(QPointF pos) {
@@ -345,11 +363,13 @@ void MapPixmapItem::paint(QGraphicsSceneMouseEvent *event) {
345 363
         QPointF pos = event->pos();
346 364
         int x = (int)(pos.x()) / 16;
347 365
         int y = (int)(pos.y()) / 16;
348
-        Block *block = map->getBlock(x, y);
349
-        if (block) {
350
-            block->tile = map->paint_tile;
351
-            map->_setBlock(x, y, *block);
366
+
367
+        if (map->smart_paths_enabled) {
368
+            paintSmartPath(x, y);
369
+        } else {
370
+            paintNormal(x, y);
352 371
         }
372
+
353 373
         if (event->type() == QEvent::GraphicsSceneMouseRelease) {
354 374
             map->commit();
355 375
         }
@@ -357,6 +377,114 @@ void MapPixmapItem::paint(QGraphicsSceneMouseEvent *event) {
357 377
     }
358 378
 }
359 379
 
380
+void MapPixmapItem::paintNormal(int x, int y) {
381
+    // Snap the selected position to the top-left of the block boundary.
382
+    // This allows painting via dragging the mouse to tile the painted region.
383
+    int xDiff = x - map->paint_tile_initial_x;
384
+    int yDiff = y - map->paint_tile_initial_y;
385
+    if (xDiff < 0 && xDiff % map->paint_tile_width != 0) xDiff -= map->paint_tile_width;
386
+    if (yDiff < 0 && yDiff % map->paint_tile_height != 0) yDiff -= map->paint_tile_height;
387
+
388
+    x = map->paint_tile_initial_x + (xDiff / map->paint_tile_width) * map->paint_tile_width;
389
+    y = map->paint_tile_initial_y + (yDiff / map->paint_tile_height) * map->paint_tile_height;
390
+    for (int i = 0; i < map->paint_tile_width && i + x < map->getWidth(); i++)
391
+    for (int j = 0; j < map->paint_tile_height && j + y < map->getHeight(); j++) {
392
+        int actualX = i + x;
393
+        int actualY = j + y;
394
+        Block *block = map->getBlock(actualX, actualY);
395
+        if (block) {
396
+            block->tile = map->paint_tile + i + (j * 8);
397
+            map->_setBlock(actualX, actualY, *block);
398
+        }
399
+    }
400
+}
401
+
402
+// These are tile offsets from the top-left tile in the 3x3 smart path selection.
403
+// Each entry is for one possibility from the marching squares value for a tile.
404
+// (Marching Squares: https://en.wikipedia.org/wiki/Marching_squares)
405
+QList<int> MapPixmapItem::smartPathTable = QList<int>({
406
+    8 + 1, // 0000
407
+    8 + 1, // 0001
408
+    8 + 1, // 0010
409
+   16 + 0, // 0011
410
+    8 + 1, // 0100
411
+    8 + 1, // 0101
412
+    0 + 0, // 0110
413
+    8 + 0, // 0111
414
+    8 + 1, // 1000
415
+   16 + 2, // 1001
416
+    8 + 1, // 1010
417
+   16 + 1, // 1011
418
+    0 + 2, // 1100
419
+    8 + 2, // 1101
420
+    0 + 1, // 1110
421
+    8 + 1, // 1111
422
+});
423
+
424
+#define IS_SMART_PATH_TILE(block) ((block->tile >= map->paint_tile && block->tile < map->paint_tile + 3) \
425
+                                || (block->tile >= map->paint_tile + 8 && block->tile < map->paint_tile + 11) \
426
+                                || (block->tile >= map->paint_tile + 16 && block->tile < map->paint_tile + 19))
427
+
428
+void MapPixmapItem::paintSmartPath(int x, int y) {
429
+    // Smart path should never be enabled without a 3x3 block selection.
430
+    if (map->paint_tile_width != 3 || map->paint_tile_height != 3) return;
431
+
432
+    // Shift to the middle tile of the smart path selection.
433
+    int openTile = map->paint_tile + 8 + 1;
434
+
435
+    // Fill the region with the open tile.
436
+    for (int i = -1; i <= 1 && i + x < map->getWidth() && i + x >= 0; i++)
437
+    for (int j = -1; j <= 1 && j + y < map->getHeight() && j + y >= 0; j++) {
438
+        int actualX = i + x;
439
+        int actualY = j + y;
440
+        Block *block = map->getBlock(actualX, actualY);
441
+        if (block) {
442
+            block->tile = openTile;
443
+            map->_setBlock(actualX, actualY, *block);
444
+        }
445
+    }
446
+
447
+    // Go back and resolve the edge tiles
448
+    for (int i = -2; i <= 2 && i + x < map->getWidth() && i + x >= 0; i++)
449
+    for (int j = -2; j <= 2 && j + y < map->getHeight() && j + y >= 0; j++) {
450
+        // Ignore the corners, which can't possible be affected by the smart path.
451
+        if ((i == -2 && j == -2) || (i == 2 && j == -2) ||
452
+            (i == -2 && j ==  2) || (i == 2 && j ==  2))
453
+            continue;
454
+
455
+        // Ignore tiles that aren't part of the smart path set.
456
+        int actualX = i + x;
457
+        int actualY = j + y;
458
+        Block *block = map->getBlock(actualX, actualY);
459
+        if (!block || !IS_SMART_PATH_TILE(block)) {
460
+            continue;
461
+        }
462
+
463
+        int id = 0;
464
+        Block *top = map->getBlock(actualX, actualY - 1);
465
+        Block *right = map->getBlock(actualX + 1, actualY);
466
+        Block *bottom = map->getBlock(actualX, actualY + 1);
467
+        Block *left = map->getBlock(actualX - 1, actualY);
468
+
469
+        // Get marching squares value, to determine which tile to use.
470
+        if (top && IS_SMART_PATH_TILE(top))
471
+            id += 1;
472
+        if (right && IS_SMART_PATH_TILE(right))
473
+            id += 2;
474
+        if (bottom && IS_SMART_PATH_TILE(bottom))
475
+            id += 4;
476
+        if (left && IS_SMART_PATH_TILE(left))
477
+            id += 8;
478
+
479
+        if (block) {
480
+            qDebug() << "tile: " << block->tile << "base: " << map->paint_tile << "id: " << id;
481
+        }
482
+
483
+        block->tile = map->paint_tile + smartPathTable[id];;
484
+        map->_setBlock(actualX, actualY, *block);
485
+    }
486
+}
487
+
360 488
 void MapPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) {
361 489
     if (map) {
362 490
         QPointF pos = event->pos();
@@ -374,6 +502,8 @@ void MapPixmapItem::pick(QGraphicsSceneMouseEvent *event) {
374 502
     Block *block = map->getBlock(x, y);
375 503
     if (block) {
376 504
         map->paint_tile = block->tile;
505
+        map->paint_tile_width = 1;
506
+        map->paint_tile_height = 1;
377 507
         emit map->paintTileChanged(map);
378 508
     }
379 509
 }
@@ -451,6 +581,11 @@ void MapPixmapItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) {
451 581
     map->clearHoveredTile();
452 582
 }
453 583
 void MapPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
584
+    QPointF pos = event->pos();
585
+    int x = ((int)pos.x()) / 16;
586
+    int y = ((int)pos.y()) / 16;
587
+    map->paint_tile_initial_x = x;
588
+    map->paint_tile_initial_y = y;
454 589
     emit mouseEvent(event, this);
455 590
 }
456 591
 void MapPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {

+ 5
- 1
editor.h View File

@@ -204,6 +204,9 @@ public:
204 204
 
205 205
 private:
206 206
     void updateCurHoveredTile(QPointF pos);
207
+    void paintNormal(int x, int y);
208
+    void paintSmartPath(int x, int y);
209
+    static QList<int> smartPathTable;
207 210
 
208 211
 signals:
209 212
     void mouseEvent(QGraphicsSceneMouseEvent *, MapPixmapItem *);
@@ -248,8 +251,9 @@ public:
248 251
         connect(map, SIGNAL(paintTileChanged(Map*)), this, SLOT(paintTileChanged(Map *)));
249 252
     }
250 253
     Map* map = NULL;
251
-    virtual void pick(uint);
252 254
     virtual void draw();
255
+private:
256
+    void updateSelection(QPointF pos, Qt::MouseButton button);
253 257
 protected:
254 258
     virtual void updateCurHoveredMetatile(QPointF pos);
255 259
 private slots:

+ 8
- 4
map.cpp View File

@@ -453,11 +453,15 @@ void Map::drawSelection(int i, int w, QPainter *painter) {
453 453
     int x = i % w;
454 454
     int y = i / w;
455 455
     painter->save();
456
-    painter->setPen(QColor(0xff, 0xff, 0xff));
457
-    painter->drawRect(x * 16, y * 16, 15, 15);
456
+
457
+    QColor penColor = smart_paths_enabled ? QColor(0xff, 0x0, 0xff) : QColor(0xff, 0xff, 0xff);
458
+    painter->setPen(penColor);
459
+    int rectWidth = paint_tile_width * 16;
460
+    int rectHeight = paint_tile_height * 16;
461
+    painter->drawRect(x * 16, y * 16, rectWidth - 1, rectHeight -1);
458 462
     painter->setPen(QColor(0, 0, 0));
459
-    painter->drawRect(x * 16 - 1, y * 16 - 1, 17, 17);
460
-    painter->drawRect(x * 16 + 1, y * 16 + 1, 13, 13);
463
+    painter->drawRect(x * 16 - 1, y * 16 - 1, rectWidth + 1, rectHeight + 1);
464
+    painter->drawRect(x * 16 + 1, y * 16 + 1, rectWidth - 3, rectHeight - 3);
461 465
     painter->restore();
462 466
 }
463 467
 

+ 7
- 0
map.h View File

@@ -138,7 +138,14 @@ public:
138 138
     QImage image;
139 139
     QPixmap pixmap;
140 140
     QList<QImage> metatile_images;
141
+    bool smart_paths_enabled = false;
142
+    int paint_metatile_initial_x;
143
+    int paint_metatile_initial_y;
141 144
     int paint_tile;
145
+    int paint_tile_width = 1;
146
+    int paint_tile_height = 1;
147
+    int paint_tile_initial_x;
148
+    int paint_tile_initial_y;
142 149
     int paint_collision;
143 150
     int paint_elevation;
144 151