Blob Blame History Raw

#include "game.h"



void gameFree(Game *gm) {
  puzzleFree(&gm->pz);
  if (gm->pz.image) animationDestroy(gm->pz.image);
  free(gm->path);
  free(gm->imgname);
  memset(gm, 0, sizeof(*gm));
}


int gameCreate(Game *gm, const char *path, const char *imgname, int rows, int cols, int turn, int seed) {
  gameFree(gm);
  char* imgpath = findImage(imgname);
  if (!imgpath) return ERR("gameCreate: cannot find image: %s", imgname);
  
  gm->pz.image = createAnimationEx(imgpath, TRUE, FALSE, FALSE);
  free(imgpath);
  
  int iw = animationGetOrigWidth(gm->pz.image);
  int ih = animationGetOrigHeight(gm->pz.image);
  
  if (rows < 1 || cols < 1 || rows*cols < 2) {
    int count = rows;
    if (count < 4) count = 4;
    double s = sqrt(iw*ih/(double)count);
    cols = (int)round(iw/s);
    if (cols < 1) cols = 1;
    rows = (int)round(ih/s);
    if (rows < 1) rows = 1;
  }

  double cw = iw/(double)cols;
  double ch = ih/(double)rows;
  if (cw > ch) { ch /= cw; cw = 1; }
          else { cw /= ch; ch = 1; }
  
  int px = (int)exp2(round(log2(1024/cols)));
  if (px < 8) px = 8;
  if (px > 1024) px = 1024;
  DBG("iw %d, ih %d, cols %d, rows %d, px %d, cw %g, ch %g", iw, ih, cols, rows, px, cw , ch);

  int prevseed = rand();
  gm->pz.turn = !!turn;
  gm->path = strprintf("%s", path);
  gm->imgname = strprintf("%s", imgname);
  gm->seed = seed;
  while(!gm->seed) gm->seed = rand();
  srand(gm->seed);
  puzzleAlloc(&gm->pz, rows, cols);
  puzzleGenLines(&gm->pz, cw, ch, 0.05, 1/5.0);
  puzzleGenChunks(&gm->pz);
  puzzleRenderChunks(&gm->pz, px, 0.3);
  srand(prevseed);
  
  gameSave(gm);
  return 1;
}


int gameSave(Game *gm) {
  char *tmp = strprintf("%s.tmp", gm->path);
  FILE *f = fopen(tmp, "w");
  if (!f) {
    ERR("gameSave: cannot open file for write: %s", tmp);
    free(tmp);
    return 0;
  }

  puzzleRecalcGroups(&gm->pz);
  
  // puzzle1
  // <[img-file]>
  // <rows <cols> <groups> <seed>
  // <gid> [<order> <x> <y> <angle>]...
  fprintf(f, "puzzle1.1\n[%s]\n%d %d %d %d %d\n%g %g\n",
    gm->imgname,
    gm->pz.rows, gm->pz.cols, !!gm->pz.turn, gm->pz.groups, gm->seed,
    gm->pz.ph.b1.x*2, gm->pz.ph.b1.y*2 );
  Chunk *chunks = gm->pz.chunks[0];
  int cnt = gm->pz.rows * gm->pz.cols;
  
  int *orders = (int*)alloc(cnt*ASIZEOF(int));
  for(int i = cnt-1; i >= 0; --i) orders[ chunkGid(gm->pz.chunksOrder[i]) ] = i;
  for(int i = 0; i < cnt; ++i) {
    Chunk *c = &chunks[i];
    PhGroup *g = c->parent;
    int gid = chunkGid(c);
    if (i == gid) fprintf(f, "%d %d %g %g %g\n", gid, orders[gid], g->o.x, g->o.y, vangled(g->dx));
             else fprintf(f, "%d\n", gid);
  }
  free(orders);
  
  fflush(f);
  int err = ferror(f);
  if (err) ERR("gameSave: cannot write to file: %s", tmp);
  fclose(f);
  
  if (!err) {
    err = rename(tmp, gm->path);
    if (err) ERR("gameSave: cannot rename file: [%s] -> [%s]", tmp, gm->path);
  }
  if (err) remove(tmp);
  free(tmp);
  return !err;
}


static FILE* internalLoadGameInfo(GameInfo *gi, const char *path) {
  FILE *f = fopen(path, "r");
  if (!f) {
    ERR("loadGameInfo: cannot open file for read: %s", path);
    return NULL;
  }

  char *l = trim(readline(f));
  if (strcmp(l, "puzzle1.1")) {
    ERR("loadGameInfo: wrong file version (expected: puzzle1): %s", l);
    free(l); fclose(f); return NULL;
  }

  l = unquote(readline(f), '[', ']');
  fscanf(f, "%d%d%d%d%d", &gi->rows, &gi->cols, &gi->turn, &gi->groups, &gi->seed);
  if ( gi->rows < 1 || gi->cols < 1 || gi->rows*gi->cols < 2
    || gi->turn < 0 || gi->turn > 1
    || gi->groups < 1 || gi->groups > gi->rows*gi->cols )
  {
    ERR("loadGameInfo: bad data in file: %s, [%dx%d, %d], %d", path, gi->rows, gi->cols, gi->turn, gi->groups);
    free(l); fclose(f); return NULL;
  }

  if (ferror(f)) {
    ERR("loadGameInfo: cannot read from file: %s", path);
    free(l); fclose(f); return NULL;
  }

  gi->imgname = l;
  return f;
}


GameInfo* loadGameInfo(const char *path) {
  GameInfo gi = { .t = filetime(path) };
  FILE *f = internalLoadGameInfo(&gi, path);
  if (!f) return NULL;
  fclose(f);
  
  GameInfo *r = alloc(ASIZEOF(*r) + strlen(gi.imgname) + 1);
  memcpy(r, &gi, sizeof(*r));
  r->imgname = strcpy((char*)(r+1), gi.imgname);
  return r;
}


int gameLoad(Game *gm, const char *path) {
  gameFree(gm);
  
  GameInfo gi = {};
  FILE *f = internalLoadGameInfo(&gi, path);
  if (!f) return ERR("gameLoad: cannot load game info: %s", path);
  if (!gameCreate(gm, path, gi.imgname, gi.rows, gi.cols, gi.turn, gi.seed)) {
    ERR("gameLoad: cannot create game: %s, %s, %dx%d, seed %d", path, gi.imgname, gi.rows, gi.cols, gi.seed);
    free(gi.imgname);
    fclose(f);
    return 0;
  }
  
  Vec s = {};
  fscanf(f, "%lg%lg", &s.x, &s.y);
  gm->pz.ph.b1 = vdiv(s, 2);
  gm->pz.ph.b0 = vneg(gm->pz.ph.b1);
  
  int cnt = gm->pz.rows * gm->pz.cols;
  Chunk *chunks = gm->pz.chunks[0];
  int *orders = (int*)alloc(2*cnt*ASIZEOF(int));
  int *groups = orders + cnt;
  double *coords = (double*)alloc(cnt*3*ASIZEOF(double));
  int processed = 0;
  for(int i = 0; i < cnt && !feof(f); ++i, ++processed) {
    int gid = -1;
    fscanf(f, "%d", &gid);
    if (gid < 0 || gid >= cnt) {
      WRN("gameLoad: invalid group");
      break;
    }
    
    Chunk *c = &chunks[i];
    Chunk *p = &chunks[gid];
    if (i == gid) {
      double v[3] = {};
      fscanf(f, "%d%lg%lg%lg", &groups[gid], v+0, v+1, v+2);
      if (!(v[0] == v[0]) || !(v[1] == v[1]) || !(v[2] == v[2])) {
        WRN("gameLoad: invalid chunk data");
        break;
      }
      memcpy(coords + i*3, v, sizeof(v));
    } else {
      chunkMerge(p, c);
    }
  }
  fclose(f);

  int orderIsValid = 1;
  for(int i = 0; i < cnt; ++i) orders[i] = -1;
  for(int i = 0; i < cnt; ++i) {
    int order = groups[ chunkGid(&chunks[i]) ]++;
    if (order >= 0 && order < cnt) orders[order] = i; else
      WRN("gameLoad: invalid order");
  }
  for(int i = 0; i < cnt; ++i) if (orders[i] < 0)
    orderIsValid = 0;
  if (!orderIsValid) WRN("gameLoad: invalid order"); else
    for(int i = 0; i < cnt; ++i) gm->pz.chunksOrder[i] = &chunks[orders[i]];
  free(orders);
  
  for(int i = 0; i < cnt; ++i) {
    Chunk *c = &chunks[i];
    if (c->parent != &c->group) continue;
    PhGroup *g = c->parent;
    g->o.x = coords[3*i + 0];
    g->o.y = coords[3*i + 1];
    g->dx = gm->pz.turn ? vcossind(coords[3*i + 2]) : vec(1,0);
    phGroupPlaceNodes(g);
  }
  free(coords);

  return 1;
}


void gameDraw(Game *gm) {
  int w = windowGetWidth();
  int h = windowGetHeight();
  Vec s = { w, h };
  saveState();

  translate(w/2, h/2);
  
  double pw = gm->pz.cols*gm->pz.cs.x;
  double ph = gm->pz.rows*gm->pz.cs.y;
  
  double k = mind(w/pw, h/ph);
  double kwin = k*0.9;
  k /= 1.5;

  double dt = windowGetFrameTime();
  if (gm->pz.longframe)
    { gm->pz.longframe = 0; dt = 0.01; }
  
  if (gameWon(gm)) {
    gm->winTransition += dt;
    if (gm->winTransition > 1) gm->winTransition = 1;
    
    PhGroup *g0 = gm->pz.ph.first;
    k = lintr(k, kwin, gm->winTransition);
    Vec o = vlintr(g0->o, vzero(), gm->winTransition);
    double a = lintr(vangled(g0->dx), 0, gm->winTransition);
    
    zoom(k);
    strokeWidth(3/k);
    translate(o.x, o.y);
    rotate(a);
    rectTextured(gm->pz.image, -pw/2, -ph/2, pw, ph);
  } else {
    zoom(k);
    strokeWidth(3/k);

    Vec mouse = { mouseTransformedX(), mouseTransformedY() };
    if (mouseWentDown("left"))
      puzzleChooseChunks(&gm->pz, mouse);
    if (mouseWentUp("left"))
      puzzleReleaseChunks(&gm->pz);
    puzzleUpdate(&gm->pz, vdiv(s, 2*k), mouse);

    int cnt = gm->pz.cols*gm->pz.rows;
    for(int i = cnt-1; i >= 0; --i) {
      chunkDraw(gm->pz.chunksOrder[i]);
      //chunkDrawDebug(gm->pz.chunksOrder[i]);
    }
    //physicsDrawDebug(&gm->pz.ph);
  }

  restoreState();

  /*saveState();
  noFill();
  stroke(COLOR_WHITE);
  textf(10, 10, "fps: %f", 1/windowGetFrameTime());
  restoreState();*/
}