Blame onefile/shisen.c

Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
#include <math.h>
Ivan Mahonin 7362e0
#include <stdio.h>
Ivan Mahonin 7362e0
#include <assert.h>
Ivan Mahonin 7362e0
#include <stdlib.h>
Ivan Mahonin 7362e0
#include <string.h>
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
#include "helianthus.h"
Ivan Mahonin 7362e0
Ivan Mahonin cf0aba
#define COLS          16
Ivan Mahonin cf0aba
#define ROWS           9
Ivan Mahonin cf0aba
#define ACTIVELETTERS 20
Ivan Mahonin cf0aba
#define MAXLETTERS   256
Ivan Mahonin beb95c
#define MAXSOUNDS     16
Ivan Mahonin 7362e0
#define RMTIME       1.0
Ivan Mahonin cf0aba
#define EXTIME       2.0
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
#define COUNT (ROWS*COLS)
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
typedef struct {
Ivan Mahonin 7362e0
  Animation glyphs[4];
Ivan Mahonin 7362e0
  int glyphsCount;
Ivan Mahonin beb95c
  Sound sounds[MAXSOUNDS];
Ivan Mahonin beb95c
  int soundsCount;
Ivan Mahonin 7362e0
  Animation g[4];
Ivan Mahonin 7362e0
} Letter;
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
typedef struct {
Ivan Mahonin 7362e0
  int r, c;
Ivan Mahonin 7362e0
} Pos;
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
typedef struct {
Ivan Mahonin 7362e0
  int len;
Ivan Mahonin 7362e0
  Pos p[4];
Ivan Mahonin 7362e0
  double tm;
Ivan Mahonin 7362e0
} Track;
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
typedef struct {
Ivan Mahonin 7362e0
  Letter *l;
Ivan Mahonin 7362e0
  Animation g;
Ivan Mahonin 7362e0
  double tm;
Ivan Mahonin 7362e0
} Cell;
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Ivan Mahonin cf0aba
Letter menuLetter;
Ivan Mahonin 7362e0
Letter letters[MAXLETTERS];
Ivan Mahonin 7362e0
int lettersCount;
Ivan Mahonin 7362e0
int cellWidth = 1;
Ivan Mahonin 7362e0
int cellHeight = 1;
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Cell board[ROWS][COLS];
Ivan Mahonin 7362e0
Pos selected = { -1, -1 };
Ivan Mahonin 7362e0
Track tracks[COUNT];
Ivan Mahonin 7362e0
int tracksCount;
Ivan Mahonin 7362e0
Animation bgtex;
Ivan Mahonin cf0aba
int glyphsMask = 0;
Ivan Mahonin 396d7d
int mode;
Ivan Mahonin 4958c9
int mute;
Ivan Mahonin cf0aba
double exitTm;
Ivan Mahonin 7362e0
Ivan Mahonin beb95c
Ivan Mahonin 7362e0
void clearBoard() {
Ivan Mahonin cf0aba
  exitTm = 0;
Ivan Mahonin 396d7d
  mode = 0;
Ivan Mahonin 7362e0
  memset(tracks, 0, sizeof(tracks));
Ivan Mahonin 7362e0
  memset(board, 0, sizeof(board));
Ivan Mahonin 7362e0
  tracksCount = 0;
Ivan Mahonin 7362e0
  selected = (Pos){ -1, -1 };
Ivan Mahonin 7362e0
}
Ivan Mahonin 7362e0
Ivan Mahonin beb95c
Ivan Mahonin beb95c
void clearSounds() {
Ivan Mahonin beb95c
  for(int i = 0; i < lettersCount; ++i)
Ivan Mahonin beb95c
    for(int j = 0; j < letters[i].soundsCount; ++i)
Ivan Mahonin beb95c
      soundDestroy(letters[i].sounds[j]);
Ivan Mahonin beb95c
}
Ivan Mahonin beb95c
Ivan Mahonin beb95c
Ivan Mahonin 7362e0
void clearLetters() {
Ivan Mahonin 7362e0
  clearBoard();
Ivan Mahonin beb95c
  clearSounds();
Ivan Mahonin 7362e0
  for(int i = 0; i < lettersCount; ++i)
Ivan Mahonin 7362e0
    for(int j = 0; j < letters[i].glyphsCount; ++j)
Ivan Mahonin 7362e0
      animationDestroy(letters[i].glyphs[j]);
Ivan Mahonin 7362e0
  memset(letters, 0, sizeof(letters));
Ivan Mahonin cf0aba
  memset(&menuLetter, 0, sizeof(menuLetter));
Ivan Mahonin 7362e0
  lettersCount = 0;
Ivan Mahonin 7362e0
  cellWidth = cellHeight = 1;
Ivan Mahonin 7362e0
}
Ivan Mahonin 7362e0
Ivan Mahonin beb95c
Ivan Mahonin 7362e0
int loadLetters(const char *filename, int rows, int cols) {
Ivan Mahonin 7362e0
  clearLetters();
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  int w, h;
Ivan Mahonin 7362e0
  unsigned char *pixels;
Ivan Mahonin 7362e0
  if (!imageLoad(filename, &w, &h, &pixels))
Ivan Mahonin 7362e0
    return FALSE;
Ivan Mahonin 7362e0
  int stride = w*4;
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  int ww = w/cols/2;
Ivan Mahonin 7362e0
  int hh = h/rows/2;
Ivan Mahonin 7362e0
  if (!ww || !hh)
Ivan Mahonin 7362e0
    { free(pixels); return FALSE; }
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  int bufstride = ww*4;
Ivan Mahonin 7362e0
  int bufsize = hh*bufstride;
Ivan Mahonin 7362e0
  unsigned char *buf = calloc(bufsize, 1);
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  Letter *letter = letters;
Ivan Mahonin 7362e0
  for(int r = 0; r < rows; ++r)
Ivan Mahonin 7362e0
  for(int c = 0; c < cols; ++c)
Ivan Mahonin 7362e0
  {
Ivan Mahonin 7362e0
    for(int rr = 0; rr < 2; ++rr)
Ivan Mahonin 7362e0
    for(int cc = 0; cc < 2; ++cc) {
Ivan Mahonin 7362e0
      unsigned char *p = pixels + (r*2 + rr)*hh*stride + (c*2 + cc)*bufstride;
Ivan Mahonin 7362e0
      for(int rrr = 0; rrr < hh; ++rrr) memcpy(buf + rrr*bufstride, p + rrr*stride, bufstride);
Ivan Mahonin 7362e0
      unsigned int visible = 0;
Ivan Mahonin 7362e0
      for(unsigned char *b = buf + 3, *e = b + bufsize; b < e; b += 4) visible += *b;
Ivan Mahonin 7362e0
      if (!visible) continue;
Ivan Mahonin 7362e0
      letter->glyphs[ letter->glyphsCount++ ] = createAnimationFromImage(ww, hh, buf, TRUE);
Ivan Mahonin 7362e0
    }
Ivan Mahonin cf0aba
    if (letter->glyphsCount > menuLetter.glyphsCount) menuLetter = *letter;
Ivan Mahonin 7362e0
    if (letter->glyphsCount > 0) ++letter;
Ivan Mahonin 7362e0
  }
Ivan Mahonin 7362e0
  lettersCount = letter - letters;
Ivan Mahonin 7362e0
  cellWidth = ww;
Ivan Mahonin 7362e0
  cellHeight = hh;
Ivan Mahonin cf0aba
  assert(menuLetter.glyphsCount == 4);
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  free(buf);
Ivan Mahonin 7362e0
  free(pixels);
Ivan Mahonin 7362e0
  return TRUE;
Ivan Mahonin 7362e0
}
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Ivan Mahonin beb95c
void loadSounds(const char *path) {
Ivan Mahonin beb95c
  Directory dir = openDirectoryEx(path, "", ".ogg", FALSE, TRUE, FALSE);
Ivan Mahonin beb95c
  if (!dir) return;
Ivan Mahonin beb95c
  for(int i = 0; i < directoryGetCount(dir); ++i) {
Ivan Mahonin beb95c
    int index = atoi(directoryGet(dir, i));
Ivan Mahonin beb95c
    if (index <= 0 || index > lettersCount) continue;
Ivan Mahonin beb95c
    Letter *l = &letters[index-1];
Ivan Mahonin beb95c
    if (l->soundsCount >= MAXSOUNDS) continue;
Ivan Mahonin beb95c
    l->sounds[l->soundsCount] = createSound(directoryGetFull(dir, i));
Ivan Mahonin beb95c
    if (l->sounds[l->soundsCount]) ++l->soundsCount;
Ivan Mahonin beb95c
  }
Ivan Mahonin beb95c
  closeDirectory(dir);
Ivan Mahonin beb95c
}
Ivan Mahonin beb95c
Ivan Mahonin beb95c
Ivan Mahonin 396d7d
void shuffleLetters(int glyps) {
Ivan Mahonin 7362e0
  for(int i = 0; i < lettersCount; ++i) {
Ivan Mahonin 7362e0
    int j = randomNumber(i, lettersCount - 1);
Ivan Mahonin 7362e0
    if (j != i) { Letter l = letters[i]; letters[i] = letters[j]; letters[j] = l; }
Ivan Mahonin 7362e0
    Letter *l = &letters[i];
Ivan Mahonin 7362e0
    assert(l->glyphsCount > 0);
Ivan Mahonin 7362e0
Ivan Mahonin 396d7d
    int gc = 0;
Ivan Mahonin 396d7d
    Animation g[4] = {};
Ivan Mahonin 396d7d
    for(int j = 0; j < l->glyphsCount; ++j)
Ivan Mahonin 396d7d
      if (glyps & (1 << j)) g[gc++] = l->glyphs[j];
Ivan Mahonin 396d7d
    if (!gc) g[gc++] = l->glyphs[0];
Ivan Mahonin 7362e0
Ivan Mahonin 396d7d
    for(int j = 0; j < gc; ++j) {
Ivan Mahonin 396d7d
      int k = randomNumber(j, gc - 1);
Ivan Mahonin 396d7d
      if (k != j) { Animation a = g[j]; g[j] = g[k]; g[k] = a; }
Ivan Mahonin 7362e0
    }
Ivan Mahonin 7362e0
Ivan Mahonin 396d7d
    for(int j = 0; j < 4; ++j)
Ivan Mahonin 396d7d
      l->g[j] = g[j%gc];
Ivan Mahonin 396d7d
Ivan Mahonin 7362e0
    for(int j = 0; j < 4; ++j) {
Ivan Mahonin 7362e0
      int k = randomNumber(j, 3);
Ivan Mahonin 7362e0
      if (k != j) { Animation g = l->g[j]; l->g[j] = l->g[k]; l->g[k] = g; }
Ivan Mahonin 7362e0
      assert(l->g[j]);
Ivan Mahonin 7362e0
    }
Ivan Mahonin 7362e0
  }
Ivan Mahonin 7362e0
}
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Ivan Mahonin beb95c
void simpleBoard() {
Ivan Mahonin beb95c
  clearBoard();
Ivan Mahonin beb95c
  for(int i = 0; i < COUNT; ++i) {
Ivan Mahonin beb95c
    Letter *l = &letters[i%lettersCount];
Ivan Mahonin beb95c
    board[i/COLS][i%COLS].l = l;
Ivan Mahonin beb95c
    board[i/COLS][i%COLS].g = l->glyphs[1];
Ivan Mahonin beb95c
  }
Ivan Mahonin beb95c
  mode = 1;
Ivan Mahonin beb95c
}
Ivan Mahonin beb95c
Ivan Mahonin beb95c
Ivan Mahonin 396d7d
void generateBoard(int glyphsMask, int maxLetters) {
Ivan Mahonin 396d7d
  if (maxLetters < 1) maxLetters = 1;
Ivan Mahonin 396d7d
Ivan Mahonin 7362e0
  clearBoard();
Ivan Mahonin 396d7d
  shuffleLetters(glyphsMask);
Ivan Mahonin 396d7d
Ivan Mahonin 7362e0
  int indices[COUNT];
Ivan Mahonin 7362e0
  for(int i = 0; i < COUNT; ++i) indices[i] = i;
Ivan Mahonin 396d7d
Ivan Mahonin eecafb
  int lc = lettersCount < maxLetters ? lettersCount : maxLetters;
Ivan Mahonin 7362e0
  for(int i = 0; i < COUNT; ++i) {
Ivan Mahonin 7362e0
    int j = randomNumber(i, COUNT-1);
Ivan Mahonin 7362e0
    int id = indices[j]; indices[j] = indices[i]; indices[i] = id;
Ivan Mahonin 396d7d
    Letter *l = &letters[ (id/4)%lc ];
Ivan Mahonin 7362e0
    board[i/COLS][i%COLS].l = l;
Ivan Mahonin 7362e0
    board[i/COLS][i%COLS].g = l->g[id%4];
Ivan Mahonin 7362e0
  }
Ivan Mahonin 396d7d
Ivan Mahonin 396d7d
  mode = 1;
Ivan Mahonin 7362e0
}
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Letter* get(Pos p)
Ivan Mahonin 7362e0
  { return p.r >= 0 && p.r < ROWS && p.c >= 0 && p.c < COLS ? board[p.r][p.c].l : NULL; }
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
int findSubTrack1(Track *t, Pos b) {
Ivan Mahonin 7362e0
  Pos p = t->p[t->len - 1];
Ivan Mahonin 7362e0
  //printf(" --  -- ft1: %d %d -> %d %d\n", p.r, p.c, b.r, b.c);
Ivan Mahonin 7362e0
  if (p.c == b.c && p.r < b.r) { for(++p.r; p.r != b.r; ++p.r) if (get(p)) return FALSE; } else
Ivan Mahonin 7362e0
  if (p.c == b.c && p.r > b.r) { for(--p.r; p.r != b.r; --p.r) if (get(p)) return FALSE; } else
Ivan Mahonin 7362e0
  if (p.r == b.r && p.c < b.c) { for(++p.c; p.c != b.c; ++p.c) if (get(p)) return FALSE; } else
Ivan Mahonin 7362e0
  if (p.r == b.r && p.c > b.c) { for(--p.c; p.c != b.c; --p.c) if (get(p)) return FALSE; } else return FALSE;
Ivan Mahonin 7362e0
  t->p[t->len++] = p;
Ivan Mahonin 7362e0
  return TRUE;
Ivan Mahonin 7362e0
}
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
int findSubTrack2(Track *t, Pos b, int horz) {
Ivan Mahonin 7362e0
  Pos *p = &t->p[t->len];
Ivan Mahonin 7362e0
  *p = t->p[t->len - 1];
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  //printf(" -- ft2: %d %d -> %d %d %s\n", p->r, p->c, b.r, b.c, horz ? "horz" : "vert");
Ivan Mahonin 7362e0
  if (p->c == b.c || p->r == b.r) return findSubTrack1(t, b);
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  ++t->len;
Ivan Mahonin 7362e0
  if (horz) {
Ivan Mahonin 7362e0
    if (p->c < b.c) { for(++p->c; p->c != b.c; ++p->c) if (get(*p)) break; }
Ivan Mahonin 7362e0
               else { for(--p->c; p->c != b.c; --p->c) if (get(*p)) break; }
Ivan Mahonin 7362e0
  } else {
Ivan Mahonin 7362e0
    if (p->r < b.r) { for(++p->r; p->r != b.r; ++p->r) if (get(*p)) break; }
Ivan Mahonin 7362e0
               else { for(--p->r; p->r != b.r; --p->r) if (get(*p)) break; }
Ivan Mahonin 7362e0
  }
Ivan Mahonin 7362e0
  if (!get(*p) && findSubTrack1(t, b)) return TRUE;
Ivan Mahonin 7362e0
  --t->len;
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  return FALSE;
Ivan Mahonin 7362e0
}
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
int findSubTrack3(Track *t, Pos b) {
Ivan Mahonin 7362e0
  Pos p0 = t->p[t->len - 1];
Ivan Mahonin 7362e0
  Pos *p = &t->p[t->len];
Ivan Mahonin 7362e0
  if (p->c == b.c && p->r == b.r) return FALSE;
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  //printf("ft3: %d %d -> %d %d\n", p0.r, p0.c, b.r, b.c);
Ivan Mahonin 7362e0
  if (findSubTrack2(t, b, FALSE)) return TRUE;
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  ++t->len;
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  *p = p0;
Ivan Mahonin 7362e0
  for(++p->c; p->c <= COLS; ++p->c)
Ivan Mahonin 7362e0
    if (get(*p)) break; else
Ivan Mahonin 7362e0
      if (findSubTrack2(t, b, FALSE)) return TRUE;
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  *p = p0;
Ivan Mahonin 7362e0
  for(--p->c; p->c >= -1; --p->c)
Ivan Mahonin 7362e0
    if (get(*p)) break; else
Ivan Mahonin 7362e0
      if (findSubTrack2(t, b, FALSE)) return TRUE;
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  *p = p0;
Ivan Mahonin 7362e0
  for(++p->r; p->r <= ROWS; ++p->r)
Ivan Mahonin 7362e0
    if (get(*p)) break; else
Ivan Mahonin 7362e0
      if (findSubTrack2(t, b, TRUE)) return TRUE;
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  *p = p0;
Ivan Mahonin 7362e0
  for(--p->r; p->r >= -1; --p->r)
Ivan Mahonin 7362e0
    if (get(*p)) break; else
Ivan Mahonin 7362e0
      if (findSubTrack2(t, b, TRUE)) return TRUE;
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  --t->len;
Ivan Mahonin 7362e0
  return FALSE;
Ivan Mahonin 7362e0
}
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Track findTrack(Pos a, Pos b) {
Ivan Mahonin 7362e0
  Track t = {};
Ivan Mahonin 7362e0
  t.p[t.len++] = a;
Ivan Mahonin 7362e0
  Letter *la = get(a);
Ivan Mahonin 7362e0
  Letter *lb = get(b);
Ivan Mahonin 7362e0
  return la && la == lb && (a.c != b.c || a.r != b.r) && findSubTrack3(&t, b) ? t : (Track){};
Ivan Mahonin 7362e0
}
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
void hint() {
Ivan Mahonin 7362e0
  if (tracksCount) return;
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  for(int i = 0; i < COUNT; ++i) {
Ivan Mahonin 7362e0
    Pos a = { i/COLS, i%COLS };
Ivan Mahonin 7362e0
    for(int j = 0; j < COUNT; ++j) {
Ivan Mahonin 7362e0
      Pos b = { j/COLS, j%COLS };
Ivan Mahonin 7362e0
      Track t = findTrack(a, b);
Ivan Mahonin 7362e0
      if (t.len) {
Ivan Mahonin 7362e0
        t.tm = 1;
Ivan Mahonin 7362e0
        tracks[tracksCount++] = t;
Ivan Mahonin 7362e0
      }
Ivan Mahonin 7362e0
    }
Ivan Mahonin 7362e0
  }
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  if (!tracksCount) return; // beep
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  int i = randomNumber(0, tracksCount - 1);
Ivan Mahonin 7362e0
  if (i) tracks[0] = tracks[i];
Ivan Mahonin 7362e0
  tracksCount = 1;
Ivan Mahonin 7362e0
}
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
void init() {
Ivan Mahonin 7362e0
  bgtex = createAnimationEx("data/shisen/minas.png", TRUE, TRUE, TRUE);
Ivan Mahonin 7362e0
  if (!loadLetters("data/shisen/letters-gen.png", 5, 8)) {
Ivan Mahonin 7362e0
    printf("cannot load letters\n");
Ivan Mahonin 7362e0
    windowStop();
Ivan Mahonin 7362e0
    return;
Ivan Mahonin 7362e0
  }
Ivan Mahonin beb95c
  loadSounds("data/shisen/sounds");
Ivan Mahonin beb95c
  //simpleBoard();
Ivan Mahonin 7362e0
}
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
void draw() {
Ivan Mahonin 7362e0
  double w = windowGetWidth();
Ivan Mahonin 7362e0
  double h = windowGetHeight();
Ivan Mahonin 7362e0
  double dt = windowGetFrameTime();
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  saveState();
Ivan Mahonin 7362e0
  translate(w*0.5, h*0.5);
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  {
Ivan Mahonin 7362e0
    saveState();
Ivan Mahonin 7362e0
    double aw = animationGetFrameOrigWidth(bgtex, 0);
Ivan Mahonin 7362e0
    double ah = animationGetFrameOrigHeight(bgtex, 0);
Ivan Mahonin 7362e0
    double kw = w/aw;
Ivan Mahonin 7362e0
    double kh = h/ah;
Ivan Mahonin 7362e0
    double k = kw > kh ? kw: kh;
Ivan Mahonin 7362e0
    aw *= k;
Ivan Mahonin 7362e0
    ah *= k;
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
    noStroke();
Ivan Mahonin 7362e0
    rectTextured(bgtex, -0.5*aw, -0.5*ah, aw, ah);
Ivan Mahonin 7362e0
    fill(colorByRGBA(1.0, 1.0, 1.0, 0.5));
Ivan Mahonin 7362e0
    rect(-0.5*aw, -0.5*ah, aw, ah);
Ivan Mahonin 7362e0
    restoreState();
Ivan Mahonin 7362e0
  }
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Ivan Mahonin 396d7d
  if (mode == 0) {
Ivan Mahonin 396d7d
    double kw = w/(cellWidth*6);
Ivan Mahonin 396d7d
    double kh = h/(cellHeight*3);
Ivan Mahonin 396d7d
    double k = kw < kh ? kw: kh;
Ivan Mahonin 396d7d
    if (k > 1) k = 1;
Ivan Mahonin 396d7d
    zoom(k);
Ivan Mahonin 396d7d
    noStroke();
Ivan Mahonin 396d7d
    translate(-cellWidth*2, -0.5*cellHeight);
Ivan Mahonin 396d7d
    assert(letters[0].glyphsCount >= 4);
Ivan Mahonin 396d7d
Ivan Mahonin 396d7d
    int mc = (int)floor(mouseTransformedX()/cellWidth);
Ivan Mahonin 396d7d
    int mr = (int)floor(mouseTransformedY()/cellHeight);
Ivan Mahonin 396d7d
    if (!mr && mc >= 0 && mc < 4 && mouseWentDown("left")) glyphsMask ^= 1 << mc;
Ivan Mahonin 396d7d
Ivan Mahonin 396d7d
    for(int i = 0; i < 4; ++i) {
Ivan Mahonin 396d7d
      double s = !mr && mc == i ? 1 : 0.9;
Ivan Mahonin 396d7d
      double d = (1 - s)/2;
Ivan Mahonin 396d7d
      double a = glyphsMask & (1 << i) ? 1.0 : 0.5;
Ivan Mahonin 396d7d
      fill(colorByRGBA(1, 1, 1, a));
Ivan Mahonin cf0aba
      rectTextured(menuLetter.glyphs[i], (i + d)*cellWidth, d*cellHeight, cellWidth*s, cellHeight*s);
Ivan Mahonin 7362e0
    }
Ivan Mahonin 7362e0
Ivan Mahonin 396d7d
    if (glyphsMask) {
Ivan Mahonin 396d7d
      noFill();
Ivan Mahonin 396d7d
      stroke(colorByRGBA(0.0, 0.0, 0.0, 0.75));
Ivan Mahonin cf0aba
      textAlign(HALIGN_CENTER, VALIGN_CENTER);
Ivan Mahonin 396d7d
      textSize(cellHeight*0.5);
Ivan Mahonin 396d7d
      text(2*cellWidth, 1.5*cellHeight, "press enter");
Ivan Mahonin 396d7d
      if ( keyWentDown("any enter")
Ivan Mahonin 396d7d
        || (mr == 1 && mc >= 0 && mc < 4 && mouseWentDown("left")) )
Ivan Mahonin cf0aba
          generateBoard(glyphsMask, ACTIVELETTERS);
Ivan Mahonin 396d7d
    }
Ivan Mahonin cf0aba
Ivan Mahonin cf0aba
    if (keyWentDown("escape")) windowStop();
Ivan Mahonin 396d7d
  } else {
Ivan Mahonin cf0aba
    double kw = w/(cellWidth*(COLS + 4));
Ivan Mahonin 396d7d
    double kh = h/(cellHeight*(ROWS + 2));
Ivan Mahonin 396d7d
    double k = kw < kh ? kw: kh;
Ivan Mahonin 396d7d
    zoom(k);
Ivan Mahonin 396d7d
Ivan Mahonin 396d7d
    translate(-0.5*COLS*cellWidth, -0.5*ROWS*cellHeight);
Ivan Mahonin 396d7d
    strokeWidth(0.1*cellWidth);
Ivan Mahonin 396d7d
Ivan Mahonin cf0aba
    saveState();
Ivan Mahonin cf0aba
    noFill();
Ivan Mahonin cf0aba
    stroke(colorByRGBA(0.0, 0.0, 0.0, 0.75));
Ivan Mahonin cf0aba
    textSize(cellWidth*0.5);
Ivan Mahonin cf0aba
    textAlign(HALIGN_RIGHT, VALIGN_BOTTOM);
Ivan Mahonin cf0aba
    text(-0.2*cellWidth, -0.2*cellHeight, "back");
Ivan Mahonin cf0aba
    textAlign(HALIGN_LEFT, VALIGN_TOP);
Ivan Mahonin cf0aba
    text((COLS + 0.2)*cellWidth, (ROWS + 0.2)*cellHeight, "hint");
Ivan Mahonin 4958c9
    textAlign(HALIGN_LEFT, VALIGN_BOTTOM);
Ivan Mahonin 4958c9
    text((COLS + 0.2)*cellWidth, -0.2*cellHeight, mute ? "silence" : "sound");
Ivan Mahonin cf0aba
    restoreState();
Ivan Mahonin 396d7d
Ivan Mahonin 396d7d
    double mx = mouseTransformedX();
Ivan Mahonin 396d7d
    double my = mouseTransformedY();
Ivan Mahonin 396d7d
    Pos hover = {};
Ivan Mahonin 396d7d
    hover.r = (int)floor(my/cellHeight);
Ivan Mahonin cf0aba
    hover.c = (int)floor(mx/cellWidth);
Ivan Mahonin cf0aba
Ivan Mahonin cf0aba
    if (hover.r < 0 && hover.c < 0 && mouseDown("left"))
Ivan Mahonin cf0aba
      exitTm += dt/EXTIME; else exitTm = 0;
Ivan Mahonin cf0aba
    if (keyWentDown("escape") || exitTm >= 1)
Ivan Mahonin cf0aba
      clearBoard();
Ivan Mahonin cf0aba
Ivan Mahonin 396d7d
    if (mouseWentDown("left")) {
Ivan Mahonin beb95c
      Letter *l = get(hover);
Ivan Mahonin 4958c9
      if (l && l->soundsCount && !mute)
Ivan Mahonin beb95c
        soundPlay( l->sounds[ randomNumber(0, l->soundsCount-1) ], FALSE );
Ivan Mahonin beb95c
Ivan Mahonin 396d7d
      Track t = findTrack(selected, hover);
Ivan Mahonin 396d7d
      if (t.len) {
Ivan Mahonin 396d7d
        t.tm = 1;
Ivan Mahonin 396d7d
        Cell *ca = &board[selected.r][selected.c];
Ivan Mahonin 396d7d
        Cell *cb = &board[hover.r][hover.c];
Ivan Mahonin 396d7d
        ca->l  = cb->l  = NULL;
Ivan Mahonin 396d7d
        ca->tm = cb->tm = t.tm;
Ivan Mahonin 396d7d
        tracks[tracksCount++] = t;
Ivan Mahonin 7362e0
      }
Ivan Mahonin cf0aba
Ivan Mahonin cf0aba
      if (hover.r == selected.r && hover.c == selected.c)
Ivan Mahonin cf0aba
        selected.r = selected.c = -1;
Ivan Mahonin cf0aba
      else
Ivan Mahonin 4958c9
      if ( !(hover.r < 0 && hover.c < 0) // ignore hint, sound or exit pressing
Ivan Mahonin 4958c9
        && !(hover.r >= ROWS && hover.c >= COLS)
Ivan Mahonin 4958c9
        && !(hover.r < 0 && hover.c >= COLS) )
Ivan Mahonin beb95c
        selected = hover;
Ivan Mahonin 4958c9
Ivan Mahonin 4958c9
      if (hover.r < 0 && hover.c >= COLS) mute = !mute;
Ivan Mahonin 7362e0
    }
Ivan Mahonin 7362e0
Ivan Mahonin 396d7d
    Letter *hl = mouseDown("right") ? get(hover) : NULL;
Ivan Mahonin cf0aba
    if (keyWentDown("h") || (!hl && mouseWentDown("right")))
Ivan Mahonin cf0aba
      hint();
Ivan Mahonin cf0aba
    if (hover.r >= ROWS && hover.c >= COLS) {
Ivan Mahonin cf0aba
      hl = mouseDown("left") ? get(selected) : NULL;
Ivan Mahonin cf0aba
      if (!hl && mouseWentDown("left")) hint();
Ivan Mahonin cf0aba
    }
Ivan Mahonin 396d7d
Ivan Mahonin 396d7d
    // draw letters
Ivan Mahonin 396d7d
    for(int r = 0; r < ROWS; ++r)
Ivan Mahonin 396d7d
    for(int c = 0; c < COLS; ++c) {
Ivan Mahonin 396d7d
      Cell *cell = &board[r][c];
Ivan Mahonin 396d7d
      if (!cell->g) continue;
Ivan Mahonin 396d7d
Ivan Mahonin 396d7d
      double a = 1;
Ivan Mahonin 396d7d
      if (cell->tm) {
Ivan Mahonin 396d7d
        cell->tm -= dt/RMTIME;
Ivan Mahonin 396d7d
        if (cell->tm <= 0) {
Ivan Mahonin 396d7d
          cell->g = NULL;
Ivan Mahonin 396d7d
          cell->tm = 0;
Ivan Mahonin 396d7d
          continue;
Ivan Mahonin 396d7d
        }
Ivan Mahonin 396d7d
        a = cell->tm;
Ivan Mahonin 396d7d
      }
Ivan Mahonin 7362e0
Ivan Mahonin 396d7d
      saveState();
Ivan Mahonin 396d7d
      noStroke();
Ivan Mahonin 396d7d
      fill(colorByRGBA(1.0, 1.0, 1.0, a));
Ivan Mahonin 396d7d
      if (a == 1 && hl && hl == cell->l               ) fill(colorByRGBA(0.9, 0.9, 0.9, a));
Ivan Mahonin 396d7d
      if (a == 1 && c ==    hover.c && r ==    hover.r) fill(colorByRGBA(0.9, 0.9, 0.9, a));
Ivan Mahonin 396d7d
      if (a == 1 && c == selected.c && r == selected.r) fill(colorByRGBA(0.8, 0.8, 0.8, a));
Ivan Mahonin 396d7d
      rectTextured(cell->g, c*cellWidth, r*cellHeight, cellWidth, cellHeight);
Ivan Mahonin 396d7d
      restoreState();
Ivan Mahonin 396d7d
    }
Ivan Mahonin 396d7d
Ivan Mahonin 396d7d
    // draw tracks
Ivan Mahonin 396d7d
    int j = 0;
Ivan Mahonin 396d7d
    for(int i = 0; i < tracksCount; ++i) {
Ivan Mahonin 396d7d
      Track *t = &tracks[i];
Ivan Mahonin 396d7d
      t->tm -= dt/RMTIME;
Ivan Mahonin 396d7d
      if (t->tm <= 0) { j = i + 1; continue; }
Ivan Mahonin 396d7d
      saveState();
Ivan Mahonin 396d7d
      stroke(colorByRGBA(1, 0, 0, t->tm));
Ivan Mahonin 396d7d
      for(int k = 0; k < t->len; ++k)
Ivan Mahonin 396d7d
        lineTo((t->p[k].c + 0.5)*cellWidth, (t->p[k].r + 0.5)*cellHeight);
Ivan Mahonin 396d7d
      strokePath();
Ivan Mahonin 396d7d
      restoreState();
Ivan Mahonin 396d7d
    }
Ivan Mahonin 396d7d
    tracksCount -= j;
Ivan Mahonin 396d7d
    memmove(tracks, tracks + j, sizeof(*tracks)*tracksCount);
Ivan Mahonin cf0aba
Ivan Mahonin cf0aba
    // draw exiting
Ivan Mahonin cf0aba
    noFill();
Ivan Mahonin cf0aba
    stroke(colorByRGBA(1, 0, 0, exitTm));
Ivan Mahonin cf0aba
    double r = cellHeight/2;
Ivan Mahonin cf0aba
    arc(mx - r, my - r, 2*r, 2*r, 0, 360*exitTm);
Ivan Mahonin 7362e0
  }
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
  restoreState();
Ivan Mahonin 7362e0
}
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
int main() {
Ivan Mahonin 7362e0
  windowSetResizable(TRUE);
Ivan Mahonin 7362e0
  windowSetVariableFrameRate();
Ivan Mahonin 7362e0
  windowSetInit(&init);
Ivan Mahonin 7362e0
  windowSetDraw(&draw);
Ivan Mahonin 7362e0
  windowRun();
Ivan Mahonin 7362e0
}
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0
Ivan Mahonin 7362e0