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