Blob Blame History Raw

#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);
}