123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697 |
- /**
- This is a simple SAF frontend for Simon Tatham's Portable Puzzle Collection.
- Not all games work, the frontend is KISS and not 100% compliant, some games
- can't bear the low resolution.
- At start input is in normal mode, C button toggles mouse emulation. If mouse
- emulation is active, going completely to the righ brings up menu with
- options to start new game, undo, solve, write digits etc.
- how to:
- 1. clone the STPPC
- 2. copy this file and saf.h to that directory
- 3. compile specific games e.g. as:
- gcc -O3 -DSAF_PLATFORM_SDL2 -DGAME_NAME=\"game.c\" \
- -lm -lSDL2 -o game stppc.c
- game stats:
- - workikng (fully or with very tiny glitches):
- cube, untangle, fifteen, pegs, unequal, samegame, unruly, lightup,
- netslide, mosaic, singles, palisade
- - kinda working (minor glitches but playable):
- twiddle, dominosa, sixteen, pearl, towers, rect, range, tents, galaxies,
- inertia, undead
- - not working (unplayable):
- blackbox, bridges, map, mines, filling, flip (icons too small to see),
- net (too tiny), solo, slant (too tiny), tracks (divide by zero,
- possible bug due to small screen?), keen, loopy (too tiny),
- guess (not sure), magnets (almost works), signpost, pattern (tiny AF)
- This file is by drummyfish, released under CC0 (the puzzle collection itself
- is not included here and is available under different license).
- */
- #define memswap _memswp // prevent name collision
- #include "sort.c"
- #undef memswap
- #include "loopgen.h" // include everything!
- #include "loopgen.c"
- #include "penrose.h"
- #include "penrose.c"
- #include "grid.h"
- #include "grid.c"
- #include "tdq.c"
- #include "combi.c"
- #include "laydomino.c"
- #include "findloop.c"
- #include "tree234.c"
- #include "latin.c"
- #include "version.c"
- #include "malloc.c"
- #include "misc.c"
- #include "drawing.c"
- #include "printing.c"
- #include "midend.c"
- #include "random.c"
- #include "matching.h"
- #include "matching.c"
- #include "dsf.c"
- #include "divvy.c"
- #include "puzzles.h"
- #define encode_params _encp1 // prevent name collision
- #define grid_type _gridt // name collision in bridges.c
- #include GAME_NAME
- #define SAF_PROGRAM_NAME "STPPC " GAME_NAME
- #define SAF_SETTING_ENABLE_SOUND 0
- #define SAF_SETTING_ENABLE_SAVES 0
- #include "saf.h"
- #define MAX_COLORS 32
- #define BAR_WIDTH 5
- #define BAR_MAX_LENGTH 64
- #define DEFAULT_COLOR SAF_COLOR_GRAY
- #define GAME_MENU_ITEMS (10 + 3)
- midend *middleEnd;
- char barText[BAR_MAX_LENGTH + 1];
- uint8_t colors[MAX_COLORS];
- int colorCount;
- uint8_t inputEmulation = 0;
- uint8_t gameMenuItem = 0;
- uint8_t cursorPos[2];
- uint8_t cursorButtons[2];
- int16_t clipRect[4];
- int sw = SAF_SCREEN_WIDTH;
- int sh = SAF_SCREEN_HEIGHT;
- struct blitter
- {
- uint8_t dummy;
- };
- struct blitter safBlitter;
- char getGameMenuItem(int index)
- {
- index %= GAME_MENU_ITEMS;
- switch (index)
- {
- case 0: return 'N'; break;
- case 1: return 'U'; break;
- case 2: return 'S'; break;
- default:
- return '0' + index - 4;
- break;
- }
- }
- void drawCursor(void)
- {
- SAF_drawPixel(cursorPos[0],cursorPos[1],255);
- for (int i = 1; i <= 2; ++i)
- {
- SAF_drawPixel(cursorPos[0] - i,cursorPos[1],0);
- SAF_drawPixel(cursorPos[0] + i,cursorPos[1],0);
- SAF_drawPixel(cursorPos[0],cursorPos[1] - i,0);
- SAF_drawPixel(cursorPos[0],cursorPos[1] + i,0);
- }
- if (cursorPos[0] > SAF_SCREEN_WIDTH - BAR_WIDTH)
- {
- SAF_drawRect(SAF_SCREEN_WIDTH - BAR_WIDTH,0,BAR_WIDTH,
- SAF_SCREEN_HEIGHT,SAF_COLOR_WHITE,1);
-
- char mt[2];
-
- mt[1] = 0;
- for (int i = 0; i < SAF_SCREEN_HEIGHT / 5; ++i)
- {
- mt[0] = getGameMenuItem(gameMenuItem + i);
- SAF_drawText(mt,SAF_SCREEN_WIDTH - BAR_WIDTH + 1,1 + i * 5,
- (mt[0] >= 'A' && mt[0] <= 'Z') ? SAF_COLOR_BLUE : SAF_COLOR_BLACK,1);
- }
- SAF_drawRect(SAF_SCREEN_WIDTH - BAR_WIDTH,0,BAR_WIDTH,6,
- SAF_COLOR_GREEN_DARK,0);
- }
- }
- int pointNotClipped(int x, int y)
- {
- return (x >= clipRect[0] && x < clipRect[2] &&
- y >= clipRect[1] && y < clipRect[3]);
- }
- void deactivate_timer(frontend *fe)
- {
- }
- void activate_timer(frontend *fe)
- {
- }
- void fatal(const char *fmt, ...)
- {
- }
- void frontend_default_colour(frontend *fe, float *output)
- {
- uint8_t r, g, b;
- SAF_colorToRGB(DEFAULT_COLOR,&r,&g,&b);
- output[0] = ((float) r) / 255.0;
- output[1] = ((float) g) / 255.0;
- output[2] = ((float) b) / 255.0;
- }
- void get_random_seed(void **randseed, int *randseedsize)
- {
- *randseed = malloc(4);
- **((uint32_t **) randseed) = SAF_time();
- *randseedsize = 4;
- }
- static void saf_status_bar(void *handle, const char *text)
- {
- int i = 0;
- while (*text != 0 && i < BAR_MAX_LENGTH)
- {
- barText[i] = *text;
- text++;
- i++;
- }
- barText[i] = 0;
- }
- static blitter *saf_blitter_new(void *handle, int w, int h)
- {
- return &safBlitter;
- }
- static void saf_blitter_free(void *handle, blitter *bl)
- {
- }
- static void saf_blitter_save(void *handle, blitter *bl, int x, int y)
- {
- }
- static void saf_blitter_load(void *handle, blitter *bl, int x, int y)
- {
- }
- static void saf_set_brush(frontend *fe, int colour)
- {
- }
- static void saf_reset_brush(frontend *fe)
- {
- }
- static void saf_set_pen(frontend *fe, int colour, int thin)
- {
- }
- static void saf_reset_pen(frontend *fe)
- {
- }
- static void saf_clip(void *handle, int x, int y, int w, int h)
- {
- clipRect[0] = x;
- clipRect[1] = y;
- clipRect[2] = x + w;
- clipRect[3] = y + h;
- }
- static void saf_unclip(void *handle)
- {
- clipRect[0] = -1000;
- clipRect[1] = -1000;
- clipRect[2] = 1000;
- clipRect[3] = 1000;
- }
- static void saf_draw_text(void *handle, int x, int y, int fonttype,
- int fontsize, int align, int colour, const char *text)
- {
- int l = 0;
- const char *t = text;
- while (*t != 0)
- {
- l++;
- t++;
- }
- if (align & ALIGN_HCENTRE)
- x -= (l * 5) / 2;
- if (align & ALIGN_VCENTRE)
- y -= 2;
- #define TOLERANCE 2
- if (pointNotClipped(x + TOLERANCE,y + TOLERANCE) &&
- pointNotClipped(x + l * 5 - TOLERANCE,y + 4 - TOLERANCE))
- SAF_drawText(text,x,y,colors[colour],1);
- }
- static void saf_draw_rect(void *handle, int x, int y, int w, int h, int colour)
- {
- w += x;
- h += y;
- if (x < clipRect[0])
- x = clipRect[0];
- else if (x > clipRect[2])
- x = clipRect[2];
- if (y < clipRect[1])
- y = clipRect[1];
- else if (y > clipRect[3])
- y = clipRect[3];
- if (w < clipRect[0])
- w = clipRect[0];
- else if (w > clipRect[2])
- w = clipRect[2];
- if (h < clipRect[1])
- h = clipRect[1];
- else if (h > clipRect[3])
- h = clipRect[3];
- w -= x;
- h -= y;
- SAF_drawRect(x,y,w,h,colors[colour],1);
- }
- static void saf_draw_line(void *handle, int x1, int y1, int x2, int y2,
- int colour)
- {
- // TODO: points could actually be clipped and drawn
- if (pointNotClipped(x1,y1) && pointNotClipped(x2,y2))
- SAF_drawLine(x1,y1,x2,y2,colors[colour]);
- }
-
- void saf_draw_thick_line(void *handle, float thickness, float x1, float y1,
- float x2, float y2, int colour)
- {
- if (pointNotClipped(x1,y1) && pointNotClipped(x2,y2))
- SAF_drawLine(x1,y1,x2,y2,colors[colour]);
- }
- static void saf_draw_circle(void *handle, int cx, int cy, int radius,
- int fillcolour, int outlinecolour)
- {
- if (pointNotClipped(cx,cy))
- {
- if (fillcolour >= 0)
- SAF_drawCircle(cx,cy,radius,colors[fillcolour],1);
-
- SAF_drawCircle(cx,cy,radius,colors[outlinecolour],0);
- }
- }
- int pointIsInTriangle(int px, int py, int tp[6])
- {
- // winding of the whole triangle:
- int w = (tp[3] - tp[1]) * (tp[4] - tp[2]) - (tp[2] - tp[0]) * (tp[5] - tp[3]);
- int sign = w > 0 ? 1 : (w < 0 ? -1 : 0);
- for (int i = 0; i < 3; ++i) // test winding of point with each side
- {
- int i1 = 2 * i;
- int i2 = i1 != 4 ? i1 + 2 : 0;
- int w2 = (tp[i1 + 1] - py) * (tp[i2] - tp[i1]) - (tp[i1] - px) * (tp[i2 + 1] - tp[i1 + 1]);
- int sign2 = w2 > 0 ? 1 : (w2 < 0 ? -1 : 0);
-
- if (sign * sign2 == -1) // includes edges
- //if (sign != sign2) // excludes edges
- return 0;
- }
-
- return 1;
- }
- static void saf_draw_polygon(void *handle, const int *coords, int npoints,
- int fillcolour, int outlinecolour)
- {
- if (fillcolour >= 0)
- {
- /* filling a polygon is hard, this method is imperfect, it just draws
- triangles in hopes it will mostly work :) */
- int cx = 0, cy = 0;
- int minX = clipRect[2],
- minY = clipRect[3],
- maxX = clipRect[0],
- maxY = clipRect[1];
- for (int i = 0; i < npoints; ++i)
- {
- cx += coords[2 * i];
- cy += coords[2 * i + 1];
- if (coords[2 * i] > maxX)
- maxX = coords[2 * i];
- if (coords[2 * i] < minX)
- minX = coords[2 * i];
- if (coords[2 * i + 1] > maxY)
- maxY = coords[2 * i + 1];
- if (coords[2 * i + 1] < minY)
- minY = coords[2 * i + 1];
- }
- cx /= npoints;
- cy /= npoints;
- for (int i = 0; i < npoints; ++i)
- {
- int tp[6];
- tp[0] = cx; tp[1] = cy;
- tp[2] = coords[2 * i]; tp[3] = coords[2 * i + 1];
- tp[4] = coords[2 * ((i + 1) % npoints)],
- tp[5] = coords[2 * ((i + 1) % npoints) + 1];
- if (pointNotClipped(tp[2],tp[3]) &&
- pointNotClipped(tp[4],tp[5]))
- for (int y = minY; y < maxY; ++y)
- for (int x = minX; x < maxX; ++x)
- if (pointIsInTriangle(x,y,tp))
- SAF_drawPixel(x,y,colors[fillcolour]);
- }
- }
- for (int i = 0; i < npoints; ++i)
- {
- int i1 = 2 * i, i2 = 2 * ((i + 1) % npoints);
- if (pointNotClipped(coords[i],coords[i + 1]) &&
- pointNotClipped(coords[i2],coords[i2 + 1]))
- SAF_drawLine(
- coords[i1],coords[i1 + 1],
- coords[i2],coords[i2 + 1],
- colors[outlinecolour]
- );
- }
- }
- static void saf_start_draw(void *handle)
- {
- SAF_drawRect(sw,0,SAF_SCREEN_WIDTH - sw,SAF_SCREEN_HEIGHT,DEFAULT_COLOR,1);
- SAF_drawRect(0,sh,SAF_SCREEN_WIDTH,SAF_SCREEN_HEIGHT - sh,DEFAULT_COLOR,1);
- }
- static void saf_draw_update(void *handle, int x, int y, int w, int h)
- {
- }
- static void saf_end_draw(void *handle)
- {
- }
- static void saf_line_width(void *handle, float width)
- {
- }
- static void saf_line_dotted(void *handle, bool dotted)
- {
- }
- static void saf_begin_doc(void *handle, int pages)
- {
- }
- static void saf_begin_page(void *handle, int number)
- {
- }
- static void saf_begin_puzzle(void *handle, float xm, float xc,
- float ym, float yc, int pw, int ph, float wmm)
- {
- }
- static void saf_end_puzzle(void *handle)
- {
- }
- static void saf_end_page(void *handle, int number)
- {
- }
- static void saf_end_doc(void *handle)
- {
- }
- char *saf_text_fallback(void *handle, const char *const *strings, int nstrings)
- {
- return NULL;
- }
- const struct drawing_api drawAPI =
- {
- saf_draw_text,
- saf_draw_rect,
- saf_draw_line,
- saf_draw_polygon,
- saf_draw_circle,
- saf_draw_update,
- saf_clip,
- saf_unclip,
- saf_start_draw,
- saf_end_draw,
- saf_status_bar,
- saf_blitter_new,
- saf_blitter_free,
- saf_blitter_save,
- saf_blitter_load,
- saf_begin_doc,
- saf_begin_page,
- saf_begin_puzzle,
- saf_end_puzzle,
- saf_end_page,
- saf_end_doc,
- saf_line_width,
- saf_line_dotted,
- saf_text_fallback,
- saf_draw_thick_line
- };
- void clearBar(void)
- {
- for (int i = 0; i < BAR_MAX_LENGTH; ++i)
- barText[i] = ' ';
- barText[BAR_MAX_LENGTH] = 0;
- }
- void SAF_init(void)
- {
- float *tmpColors;
- SAF_clearScreen(DEFAULT_COLOR);
- saf_unclip(NULL);
- clearBar();
- middleEnd = midend_new(NULL,&thegame,&drawAPI,NULL);
- tmpColors = midend_colours(middleEnd,&colorCount);
- colorCount %= MAX_COLORS;
- for (int i = 0; i < colorCount; ++i)
- {
- colors[i] = SAF_colorFromRGB(
- tmpColors[3 * i] * 255,
- tmpColors[3 * i + 1] * 255,
- tmpColors[3 * i + 2] * 255);
- }
- midend_new_game(middleEnd);
- sw = SAF_SCREEN_WIDTH;
- sh = SAF_SCREEN_HEIGHT - BAR_WIDTH;
- midend_reset_tilesize(middleEnd);
- midend_size(middleEnd,&sw,&sh,1);
- cursorPos[0] = SAF_SCREEN_WIDTH / 2;
- cursorPos[1] = SAF_SCREEN_HEIGHT / 2;
- cursorButtons[0] = 0;
- cursorButtons[1] = 0;
- midend_which_preset(middleEnd);
- midend_redraw(middleEnd);
- }
- int buttonOn(uint8_t button)
- {
- return SAF_buttonPressed(button) == 1 || SAF_buttonPressed(button) > 10;
- }
- uint8_t SAF_loop(void)
- {
- midend_timer(middleEnd,1.0 / ((float) SAF_MS_PER_FRAME));
- if (inputEmulation)
- {
- if (cursorPos[0] <= SAF_SCREEN_WIDTH - BAR_WIDTH)
- {
- uint8_t redraw = 1;
- if (buttonOn(SAF_BUTTON_LEFT) && cursorPos[0] > 0)
- {
- cursorPos[0]--;
- if (cursorButtons[0])
- midend_process_key(middleEnd,cursorPos[0],cursorPos[1],LEFT_DRAG);
- }
- else if (buttonOn(SAF_BUTTON_RIGHT) && cursorPos[0] < SAF_SCREEN_WIDTH - 1)
- {
- cursorPos[0]++;
- if (cursorButtons[0])
- midend_process_key(middleEnd,cursorPos[0],cursorPos[1],LEFT_DRAG);
- }
- else if (buttonOn(SAF_BUTTON_UP) && cursorPos[1] > 0)
- {
- cursorPos[1]--;
- if (cursorButtons[0])
- midend_process_key(middleEnd,cursorPos[0],cursorPos[1],LEFT_DRAG);
- }
- else if (buttonOn(SAF_BUTTON_DOWN) && cursorPos[1] < SAF_SCREEN_HEIGHT - 1)
- {
- cursorPos[1]++;
- if (cursorButtons[0])
- midend_process_key(middleEnd,cursorPos[0],cursorPos[1],LEFT_DRAG);
- }
- else if ((SAF_buttonPressed(SAF_BUTTON_A) != 0) != cursorButtons[0])
- {
- cursorButtons[0] = SAF_buttonPressed(SAF_BUTTON_A) != 0;
- midend_process_key(middleEnd,cursorPos[0],cursorPos[1],
- cursorButtons[0] ? LEFT_BUTTON : LEFT_RELEASE);
- }
- else if ((SAF_buttonPressed(SAF_BUTTON_B) != 0) != cursorButtons[1])
- {
- cursorButtons[1] = SAF_buttonPressed(SAF_BUTTON_B) != 0;
- midend_process_key(middleEnd,cursorPos[0],cursorPos[1],
- cursorButtons[1] ? RIGHT_BUTTON : RIGHT_RELEASE);
- }
- else
- redraw = 0;
- if (redraw)
- midend_force_redraw(middleEnd);
- }
- else
- {
- if (buttonOn(SAF_BUTTON_LEFT))
- cursorPos[0]--;
- else if (buttonOn(SAF_BUTTON_UP))
- gameMenuItem =
- gameMenuItem > 0 ? gameMenuItem - 1 : GAME_MENU_ITEMS - 1;
- else if (buttonOn(SAF_BUTTON_DOWN))
- gameMenuItem =
- gameMenuItem < GAME_MENU_ITEMS - 1 ? gameMenuItem + 1 : 0;
- else if (buttonOn(SAF_BUTTON_A))
- {
- char item = getGameMenuItem(gameMenuItem);
- switch (item)
- {
- case 'S':
- midend_solve(middleEnd);
- midend_force_redraw(middleEnd);
- break;
-
- case 'N':
- midend_new_game(middleEnd);
- midend_force_redraw(middleEnd);
- break;
-
- case 'U':
- midend_undo(middleEnd);
- midend_force_redraw(middleEnd);
- break;
- default:
- midend_process_key(middleEnd,cursorPos[0],cursorPos[1],item);
- break;
- }
- }
- }
- }
- else
- {
- if (SAF_buttonJustPressed(SAF_BUTTON_LEFT))
- midend_process_key(middleEnd,0,0,CURSOR_LEFT);
- if (SAF_buttonJustPressed(SAF_BUTTON_RIGHT))
- midend_process_key(middleEnd,0,0,CURSOR_RIGHT);
- if (SAF_buttonJustPressed(SAF_BUTTON_UP))
- midend_process_key(middleEnd,0,0,CURSOR_UP);
- if (SAF_buttonJustPressed(SAF_BUTTON_DOWN))
- midend_process_key(middleEnd,0,0,CURSOR_DOWN);
- if (SAF_buttonJustPressed(SAF_BUTTON_A))
- midend_process_key(middleEnd,0,0,CURSOR_SELECT);
- if (SAF_buttonJustPressed(SAF_BUTTON_B))
- midend_process_key(middleEnd,0,0,CURSOR_SELECT2);
- }
- if (SAF_buttonJustPressed(SAF_BUTTON_C))
- {
- inputEmulation = !inputEmulation;
- midend_force_redraw(middleEnd);
- }
-
- SAF_drawRect(0,SAF_SCREEN_HEIGHT - BAR_WIDTH,SAF_SCREEN_WIDTH,BAR_WIDTH,
- SAF_COLOR_WHITE,1);
- SAF_drawText(barText,1,SAF_SCREEN_HEIGHT - BAR_WIDTH + 1,SAF_COLOR_BLACK,1);
- int status = midend_status(middleEnd);
- if (status == 1)
- SAF_drawText("WIN",1,1,SAF_COLOR_GREEN,1);
- else if (status == -1)
- SAF_drawText("LOST",1,1,SAF_COLOR_RED,1);
- if (inputEmulation)
- drawCursor();
- return 1;
- }
|