// Testbed for making a match3 board
// Created: 28/10/2017 09:05:33
// John Ryland (jryland@xiaofrog.com)
// InvertedLogic, (C) Copyright 2017
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <pthread.h>
#include "board.h"
#include "windowing.h"
#include "game.h"
#include "common.h"
class MyWindow : public CanvasWindow
{
public:
MyWindow()
{
createNewGame(game, 0, 0, 0);
selectedSprite = -1;
otherSprite = -1;
// sprite sheet could come from DB
loadSpriteSheet(spriteSheet, "data/sprites-32x32.txt");
}
~MyWindow()
{
destroyGame(game);
}
void onDraw(int x1, int y1, int x2, int y2)
{
int finishedCount = 0;
for (int i = 0; i < game.m_events.size(); i++)
{
BoardEvent& evt = game.m_events[i];
if (!evt.userInit)
{
//printf("initing\n");
evt.frame = 0;
evt.frames = 40;
evt.userInit = true;
if (evt.type == BoardEvent::Added)
canvasAddSprite(canvas, 32 + evt.x*40, 32 + evt.y*40, spriteSheet.sprites[evt.value]);
if (evt.type == BoardEvent::NoMoreMoves) {
evt.frames = 0;
printf("no more moves\n");
}
}
if (evt.frame == evt.frames)
{
//printf("finished\n");
finishedCount++;
evt.type = BoardEvent::None;
}
else
{
//printf("updating\n");
switch (evt.type)
{
case BoardEvent::Swapped:
{
CanvasItem copy1 = canvas.sprites[evt.itemIndex];
canvas.sprites[evt.itemIndex] = canvas.sprites[evt.newItemIndex];
canvas.sprites[evt.newItemIndex] = copy1;
evt.type = BoardEvent::None;
break;
}
case BoardEvent::Added:
{
CanvasItem& item = canvas.sprites[evt.itemIndex];
item.x = 32 + 40*evt.x;
item.y = 32 + 40*evt.y - 40 + evt.frame;
break;
}
case BoardEvent::Removed:
{
CanvasItem& item = canvas.sprites[evt.itemIndex];
item.visible = false;
break;
}
case BoardEvent::Moved:
{
CanvasItem& item = canvas.sprites[evt.itemIndex];
item.y = 32 + 40*evt.newY - 40 + evt.frame;
break;
}
}
evt.frame++;
}
}
if (finishedCount != 0 && finishedCount == game.m_events.size())
{
printf("anims finished\n");
game.m_events.clear();
// re-normalize the board<->sprites
populateCanvasItems();
}
CanvasWindow::onDraw(x1, y1, x2, y2);
}
void populateCanvasItems()
{
updateTiles(game);
canvas.sprites.clear();
foreach (Tile& t, game.m_tiles)
canvasAddSprite(canvas, 32 + t.x*40, 32 + t.y*40, spriteSheet.sprites[t.tileTypeId]);
}
void onMouseEvent(int x, int y, int buttons)
{
//printf("mouse event: %i %i %i\n", x, y, buttons);
if (buttons) {
if (selectedSprite != -1) {
CanvasItem& item = canvas.sprites[selectedSprite];
int dx = x - selx;
int dy = y - sely;
int boardX = selectedSprite % game.width();
int boardY = selectedSprite / game.width();
int curOtherSprite = -1;
dx = std::min<int>(std::max<int>(dx, -40), 40);
dy = std::min<int>(std::max<int>(dy, -40), 40);
if (boardX == 0 && dx < 0) dx = 0;
if (boardX == (game.width()-1) && dx > 0) dx = 0;
if (boardY == 0 && dy < 0) dy = 0;
if (boardY == (game.height()-1) && dy > 0) dy = 0;
item.x = selitemx;
item.y = selitemy;
if (dx*dx > dy*dy) {
item.x += dx;
// find the item that will be swapped with this
if (dx > 0)
curOtherSprite = selectedSprite + 1;
if (dx < 0)
curOtherSprite = selectedSprite - 1;
} else {
item.y += dy;
// find the item that will be swapped with this
if (dy > 0)
curOtherSprite = selectedSprite + game.width();
if (dy < 0)
curOtherSprite = selectedSprite - game.width();
}
if (otherSprite != -1 && curOtherSprite != otherSprite)
{
CanvasItem& other = canvas.sprites[otherSprite];
other.x = otherX;
other.y = otherY;
}
if (curOtherSprite != -1)
{
CanvasItem& other = canvas.sprites[curOtherSprite];
if (curOtherSprite != otherSprite)
{
otherX = other.x;
otherY = other.y;
otherSprite = curOtherSprite;
}
other.x = otherX;
other.y = otherY;
if (dx*dx > dy*dy) {
other.x -= dx;
} else {
other.y -= dy;
}
}
} else {
// HACK: offset -3,-3 to make it line up - perhaps there is a border between the view and the window
selectedSprite = canvasHitTestWithAlpha(canvas, x-3, y-3);
if (selectedSprite != -1) {
size_t n = canvas.sprites.size() - 1;
CanvasItem itemS = canvas.sprites[selectedSprite];
/*
// Reorder so the selected one will be rendered on top
// if do this, the selected index is used to infer the position in the board, so will need a mapping of index to board position
memmove(&canvas.sprites[selectedSprite], &canvas.sprites[selectedSprite+1], sizeof(CanvasItem)*(n-selectedSprite+1));
canvas.sprites[n] = itemS;
selectedSprite = n;
*/
selx = x;
sely = y;
selitemx = itemS.x;
selitemy = itemS.y;
}
}
} else {
bool canSwap = false;
if (selectedSprite != -1 && otherSprite != -1) {
int boardX1 = selectedSprite % game.width();
int boardY1 = selectedSprite / game.width();
int boardX2 = otherSprite % game.width();
int boardY2 = otherSprite / game.width();
canSwap = swapTiles(game, boardX1, boardY1, boardX2, boardY2);
printf("can swap: %s\n", canSwap ? "true" : "false");
if (canSwap)
{
int tmp = otherSprite;
otherSprite = selectedSprite;
selectedSprite = tmp;
}
}
if (selectedSprite != -1) {
CanvasItem& item = canvas.sprites[selectedSprite];
item.x = selitemx;
item.y = selitemy;
// snaps back - but could then apply a swap if criteria is met
}
if (otherSprite != -1) {
CanvasItem& item = canvas.sprites[otherSprite];
item.x = otherX;
item.y = otherY;
// snaps back - but could then apply a swap if criteria is met
}
selectedSprite = -1;
otherSprite = -1;
}
}
void setBoardDimensions(int a_width, int a_height)
{
int sprites = spriteSheet.sprites.size();
printf("New match3 board [%ix%i] %i\n", a_width, a_height, sprites);
destroyGame(game);
createNewGame(game, a_width, a_height, sprites);
populateCanvasItems();
//canvasAddSprite(canvas, x-32, y-32, spriteSheet.sprites[0]);
}
private:
Game game;
SpriteSheet spriteSheet;
int selectedSprite;
int selx, sely, selitemx, selitemy;
int otherSprite;
int otherX, otherY;
};
class MyApp : public Application
{
public:
MyApp(int argc, char* argv[])
: Application()
{
for (int i = 0; i < argc; i++)
printf("argv[%i]: %s\n", i, argv[i]);
int boardWidth = (argc > 1) ? atoi(argv[1]) : 4;
int boardHeight = (argc > 2) ? atoi(argv[2]) : 4;
window.setBoardDimensions(boardWidth, boardHeight);
}
void onStart()
{
window.create(480, 650);
}
private:
MyWindow window;
};
int main(int argc, char ** argv)
{
MyApp app(argc, argv);
return app.run();
}