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
     setPixmap(map->renderMetatiles());
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
 void MetatilesPixmapItem::updateCurHoveredMetatile(QPointF pos) {
274
 void MetatilesPixmapItem::updateCurHoveredMetatile(QPointF pos) {
280
     int x = ((int)pos.x()) / 16;
275
     int x = ((int)pos.x()) / 16;
281
     int y = ((int)pos.y()) / 16;
276
     int y = ((int)pos.y()) / 16;
299
     QPointF pos = event->pos();
294
     QPointF pos = event->pos();
300
     int x = ((int)pos.x()) / 16;
295
     int x = ((int)pos.x()) / 16;
301
     int y = ((int)pos.y()) / 16;
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
 void MetatilesPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
301
 void MetatilesPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
310
     updateCurHoveredMetatile(event->pos());
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
 void MetatilesPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
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
 void CollisionMetatilesPixmapItem::updateCurHoveredMetatile(QPointF pos) {
335
 void CollisionMetatilesPixmapItem::updateCurHoveredMetatile(QPointF pos) {
345
         QPointF pos = event->pos();
363
         QPointF pos = event->pos();
346
         int x = (int)(pos.x()) / 16;
364
         int x = (int)(pos.x()) / 16;
347
         int y = (int)(pos.y()) / 16;
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
         if (event->type() == QEvent::GraphicsSceneMouseRelease) {
373
         if (event->type() == QEvent::GraphicsSceneMouseRelease) {
354
             map->commit();
374
             map->commit();
355
         }
375
         }
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
 void MapPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) {
488
 void MapPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) {
361
     if (map) {
489
     if (map) {
362
         QPointF pos = event->pos();
490
         QPointF pos = event->pos();
374
     Block *block = map->getBlock(x, y);
502
     Block *block = map->getBlock(x, y);
375
     if (block) {
503
     if (block) {
376
         map->paint_tile = block->tile;
504
         map->paint_tile = block->tile;
505
+        map->paint_tile_width = 1;
506
+        map->paint_tile_height = 1;
377
         emit map->paintTileChanged(map);
507
         emit map->paintTileChanged(map);
378
     }
508
     }
379
 }
509
 }
451
     map->clearHoveredTile();
581
     map->clearHoveredTile();
452
 }
582
 }
453
 void MapPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
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
     emit mouseEvent(event, this);
589
     emit mouseEvent(event, this);
455
 }
590
 }
456
 void MapPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
591
 void MapPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {

+ 5
- 1
editor.h View File

204
 
204
 
205
 private:
205
 private:
206
     void updateCurHoveredTile(QPointF pos);
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
 signals:
211
 signals:
209
     void mouseEvent(QGraphicsSceneMouseEvent *, MapPixmapItem *);
212
     void mouseEvent(QGraphicsSceneMouseEvent *, MapPixmapItem *);
248
         connect(map, SIGNAL(paintTileChanged(Map*)), this, SLOT(paintTileChanged(Map *)));
251
         connect(map, SIGNAL(paintTileChanged(Map*)), this, SLOT(paintTileChanged(Map *)));
249
     }
252
     }
250
     Map* map = NULL;
253
     Map* map = NULL;
251
-    virtual void pick(uint);
252
     virtual void draw();
254
     virtual void draw();
255
+private:
256
+    void updateSelection(QPointF pos, Qt::MouseButton button);
253
 protected:
257
 protected:
254
     virtual void updateCurHoveredMetatile(QPointF pos);
258
     virtual void updateCurHoveredMetatile(QPointF pos);
255
 private slots:
259
 private slots:

+ 8
- 4
map.cpp View File

453
     int x = i % w;
453
     int x = i % w;
454
     int y = i / w;
454
     int y = i / w;
455
     painter->save();
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
     painter->setPen(QColor(0, 0, 0));
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
     painter->restore();
465
     painter->restore();
462
 }
466
 }
463
 
467
 

+ 7
- 0
map.h View File

138
     QImage image;
138
     QImage image;
139
     QPixmap pixmap;
139
     QPixmap pixmap;
140
     QList<QImage> metatile_images;
140
     QList<QImage> metatile_images;
141
+    bool smart_paths_enabled = false;
142
+    int paint_metatile_initial_x;
143
+    int paint_metatile_initial_y;
141
     int paint_tile;
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
     int paint_collision;
149
     int paint_collision;
143
     int paint_elevation;
150
     int paint_elevation;
144
 
151