#include "puzzle.h"
void puzzleAlloc(Puzzle *pz, int rows, int cols) {
pz->rows = rows < 1 ? 1 : rows;
pz->cols = cols < 1 ? 1 : cols;
pz->cs = vone();
pz->hlines = (Vertex**)alloc2d(pz->rows+1, (pz->cols*LN_SEGS+1)*ASIZEOF(**pz->hlines));
pz->vlines = (Vertex**)alloc2d(pz->cols+1, (pz->rows*LN_SEGS+1)*ASIZEOF(**pz->vlines));
pz->chunks = (Chunk**)alloc2d(pz->rows, pz->cols*ASIZEOF(**pz->chunks));
pz->chunksOrder = (Chunk**)alloc(pz->rows*pz->cols*ASIZEOF(*pz->chunksOrder));
physicsAlloc(&pz->ph, pz->rows*2, pz->cols*2, 1);
}
void puzzleFree(Puzzle *pz) {
free(pz->chunksOrder);
free(pz->chunks);
physicsFree(&pz->ph);
tileFree(&pz->tm);
free(pz->hlines);
free(pz->vlines);
memset(pz, 0, sizeof(*pz));
}
void puzzleClearImages(Puzzle *pz) {
for(int r = 0; r < pz->rows; ++r)
for(int c = 0; c < pz->cols; ++c)
pz->chunks[r][c].t = (Tile){};
tileClear(&pz->tm);
}
void puzzleGenLines(Puzzle *pz, double cellw, double cellh, double jitter, double depth) {
puzzleClearImages(pz);
pz->cs.x = maxd(PRECISION, cellw);
pz->cs.y = maxd(PRECISION, cellh);
for(int r = 0; r <= pz->rows; ++r) {
double j = r && r < pz->rows ? jitter : 0.0;
double d = r && r < pz->rows ? depth : 0.0;
lineGenH(pz->hlines[r], pz->cols, r*pz->cs.y, pz->cs.x, pz->cs.y, j, d);
}
for(int c = 0; c <= pz->cols; ++c) {
double j = c && c < pz->cols ? jitter : 0.0;
double d = c && c < pz->cols ? depth : 0.0;
lineGenV(pz->vlines[c], pz->rows, c*pz->cs.x, pz->cs.x, pz->cs.y, j, d);
}
for(int r = 1; r < pz->rows; ++r)
for(int c = 1; c < pz->cols; ++c)
pz->vlines[c][r*LN_SEGS].p = pz->hlines[r][c*LN_SEGS].p;
}
void puzzleRecalcGroups(Puzzle *pz) {
pz->groups = 0;
for(PhGroup *g = pz->ph.first; g; g = g->next) ++pz->groups;
}
void puzzleGenChunks(Puzzle *pz) {
pz->ph.fixangle = 1;
double nr = mind(pz->cs.x, pz->cs.y)/4;
Vec nd = { pz->cs.x/2 - nr, pz->cs.y/2 - nr };
Vec s = vmul( vmulv(vec(pz->cols, pz->rows), pz->cs), 1.5/2 );
physicsClear(&pz->ph);
pz->ph.cs = maxd(pz->cs.x, pz->cs.y);
Vec nds[] = { vneg(nd), vnegy(nd), vnegx(nd), nd, vzero() };
double nrs[] = { nr, nr, nr, nr, 2*nr };
for(int r = 0; r < pz->rows; ++r)
for(int c = 0; c < pz->cols; ++c) {
Chunk *a = &pz->chunks[r][c];
*a = (Chunk){ pz, r, c };
a->parent = &a->group;
a->flags |= PF_HANDLES;
Vec o = vmulv(s, vrandTwo());
Vec go = chunkGroupPos(a);
for(int i = 0; i < 5; ++i) {
PhNode *n = &a->nodes[i];
n->gp = vadd(go, nds[i]);
n->r = nrs[i];
n->p = vadd(o, vrandTwo());
phNodeReg(n, a->parent);
}
phGroupReg(a->parent, &pz->ph);
phGroupRecalc(a->parent);
phGroupFix(a->parent, !pz->turn);
pz->chunksOrder[r*pz->cols + c] = a;
}
int cnt = pz->rows*pz->cols;
for(int i = 0; i < cnt; ++i) {
int j = rand()%(cnt-i) + i;
Chunk *c = pz->chunksOrder[j];
pz->chunksOrder[j] = pz->chunksOrder[i];
pz->chunksOrder[i] = c;
}
puzzleRecalcGroups(pz);
}
void puzzleRenderChunks(Puzzle *pz, int size, double m) {
puzzleClearImages(pz);
int cnt = pz->rows*pz->cols;
double s = maxd(pz->cs.x, pz->cs.y) + 2*m;
for(int i = 0; i < cnt; ++i) {
Chunk *c = &pz->chunks[0][i];
c->t = tileAdd(&pz->tm, size, s);
chunkRender(c);
}
}
int puzzlePopupGroup(Puzzle *pz, PhGroup *g) {
int cnt = pz->rows*pz->cols, res = 0;
for(int i = 0; i < cnt; ++i) {
Chunk *c = pz->chunksOrder[i];
if (c->parent != g) continue;
memmove(pz->chunksOrder + res + 1, pz->chunksOrder + res, (i - res)*sizeof(*pz->chunksOrder));
pz->chunksOrder[res++] = c;
}
return res;
}
int puzzleChooseChunks(Puzzle *pz, Vec mouse) {
pz->activeChunks = 0;
pz->mousefix = !pz->turn;
Chunk *choosen = NULL;
Chunk *choosenE = NULL;
int cnt = pz->rows*pz->cols;
for(int i = cnt-1; i >= 0; --i) {
Chunk *c = pz->chunksOrder[i];
Flags f = chunkCheckPoint(c, mouse);
if (f&PCP_BODY) choosen = c;
if (f&PCP_EDGE) choosenE = c;
}
if (!choosen) choosen = choosenE;
if (!choosen) return 0;
choosen->parent->fixed = 1;
pz->gmouse = phGroupUntrans(choosen->parent, mouse);
return pz->activeChunks = puzzlePopupGroup(pz, choosen->parent);
}
void puzzleReleaseChunks(Puzzle *pz) {
if (!pz->activeChunks) return;
while(pz->activeChunks > 0) {
Chunk *c = pz->chunksOrder[--pz->activeChunks];
chunkTryMerge(c, -1, 0);
chunkTryMerge(c, 1, 0);
chunkTryMerge(c, 0, -1);
chunkTryMerge(c, 0, 1);
}
pz->chunksOrder[0]->parent->fixed = 0;
puzzlePopupGroup(pz, pz->chunksOrder[0]->parent);
puzzleRecalcGroups(pz);
pz->longframe = 1;
}
void puzzleUpdate(Puzzle *pz, Vec hs, Vec mouse) {
Vec mmin = vdiv(pz->cs, 2);
Vec bmax = vmulv(vec(pz->ph.hw, pz->ph.hh), pz->cs);
Vec b1 = vec( maxd(mmin.x, mind(hs.x, bmax.x)),
maxd(mmin.y, mind(hs.y, bmax.y)) );
Vec d1 = vabs(vsub(b1, pz->ph.b1));
if ( b1.x > PRECISION && pz->ph.b1.x > PRECISION
&& b1.y > PRECISION && pz->ph.b1.y > PRECISION
&& !(d1.x <= PRECISION && d1.y <= PRECISION) )
{
Vec k = vdivv(b1, pz->ph.b1);
for(PhGroup *g = pz->ph.first; g; g = g->next) {
g->o = vmulv(g->o, k);
phGroupPlaceNodes(g);
}
}
pz->ph.b1 = b1;
pz->ph.b0 = vneg(b1);
physicsUpdate(&pz->ph);
if (pz->activeChunks)
phGroupMove(pz->chunksOrder[0]->parent, pz->gmouse, mouse, pz->mousefix ? 0 : 0.1);
}