/**
 * Nametry
 * (c) Kenta Cho (cs8k-cyu@asahi-net.or.jp)
 * http://www.asahi-net.or.jp/~cs8k-cyu/
 *
 * gamemng.c(manage game status)
 */

#include <sys/bios.h>
#include <stdlib.h>
#include "il_sound.h"

#include "nametry.h"
#include "entrytbl.h"

GameStatus gameStatus;

/* button state */
Boolean aPrsd = TRUE;
Boolean bPrsd = TRUE;
Boolean startPrsd = TRUE;

/* size of letter grid */
#define GRID_LENGTH 8

/* letter grid position adjustment */
#define OF_LENGTH 64
#define OF_LGT_SFT 6

/* my cursor man */
static int ltx, lty;
static int spx, spy;
static int fdx, fdy;
static int ofx, ofy;
static int mvx, mvy;
static int myCnt, myd;
static int lpWidth, lpHeight;
int ilIdx;
static int ilChr;
int crsCnt;
long score;
static long spinScore;

/* cursor on letter face */
static int csrx, csry, csrMark;

/* entry table number */
int tableNum;
static int maxTableNum;

/* ranking score */
static long rnkScr;
static int scrLgt;
static int rnkSub;
static int ttlRank;
static int enApItv, enCnt, enNum;

/* center position of letter grid */
static int ltcntx, ltcnty;

/* flying inputted letter */
static int ilsprx, ilspry, ilspraimx, ilspraimy, ilsprcnt;

/* enemy */
#define ENEMY_MAX 10

static Enemy enemy[ENEMY_MAX];
static int fdWidth, fdHeight;

/**
 * Draw over screen lines.
 */
static void drawSc2Line() {
  static int ptn1[] = {
    0, 8, 3, 3, 3, 7,
    0, 2, 0, 0, 0, 4,
    0, 2, 0, 0, 0, 4,
    0, 2, 0, 0, 0, 4,
    8, 5, 1, 1, 1, 9,
  };
  static int ptn2[] = {
    0, 0, 8, 5,
    0, 8, 5, 0,
    8, 5, 0, 0,
  };
  static int ptn3[] = {
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 5,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 5, 0,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 0,
  };
  drawLinePtn(ptn1, 13, 8, 6, 5);
  drawLinePtn(ptn2, 10, 13, 4, 3);
  drawLinePtn(ptn3, 0, 0, 18, 3);
}

/**
 * Initialize game status.
 */
void resetCursor() {
  int i;
  csrx = DISAP_POS;
  for ( i=CSRSPR_IDX ; i<CSRSPR_IDX+18 ; i++ ) {
    sprite_set_location(i, DISAP_POS, DISAP_POS);
  }
}

void resetFlyingLetter() {
  int i;
  ilsprcnt = 0;
  for ( i=FILSPR_IDX ; i<FILSPR_IDX+4 ; i++ ) {
    sprite_set_location(i, DISAP_POS, DISAP_POS);
  }
}

static void drawSpinScore() {
  spinScore += (score-spinScore)>>4;
  if ( spinScore < score ) {
    spinScore++;
    drawBgNum(spinScore, 14, 0);
  }
}

static void drawScore(long score) {
  drawBgNum(score, 14, 0);
}

void initGame() {
  int i;
  ofx = ofy = mvx = mvy = 0;
  myCnt = myd = 0;
  ilIdx = -1;
  csrMark = 0;
  resetCursor();
  gameStatus.atrcStat = NONE;
  maxTableNum = 1;
  lpWidth = -1;
  rnkScr = 100000;
  scrLgt = -1;
  ttlRank = -1;
  rnkSub = 11;
  ltcntx = LTCNT_X; ltcnty = SCREEN_HEIGHT*8-16;
  enApItv = 720;
  crsCnt = 0;
  resetFlyingLetter();

  clearDisplay();
  sprite_set_window(LTCNT_X-8, LTCNT_Y-8, 24, 24);
  for ( i=0 ; i<SPRITE_RANGE ; i++ ) {
    sprite_set_location(i, DISAP_POS, DISAP_POS);
  }
  sprite_set_char(MYSPR_IDX  , 0x1c00+HTO_IDX);
  sprite_set_char(MYSPR_IDX+1, 0xc00+HTO_IDX+1);
  sprite_set_char(MYSPR_IDX+2, 0xc00+HTO_IDX+2);
  sprite_set_char(MYSPR_IDX+3, 0xc00+HTO_IDX+2);
  for ( i=0 ; i<ENEMY_MAX ; i++ ) {
    enemy[i].speed = -1;
    enemy[i].cnt = 0;
    sprite_set_char(ENMSPR_IDX+(i<<2)  , 0x1a00+HTO_IDX);
    sprite_set_char(ENMSPR_IDX+(i<<2)+1, 0x1a00+HTO_IDX+1);
    sprite_set_char(ENMSPR_IDX+(i<<2)+2, 0x1a00+HTO_IDX+2);
    sprite_set_char(ENMSPR_IDX+(i<<2)+3, 0x1a00+HTO_IDX+2);
  }
  drawSc2Line();
  drawScore(0);
}

/**
 * Ready to entry next name.
 */
#define NAME_LENGTH 3
#define NAME_X 21
#define NAME_Y 0

#define RANK_LENGTH 4

static int name[NAME_LENGTH];
static int etrName[RANK_LENGTH][NAME_LENGTH];
int iptlt[NAME_LENGTH];
int nmIdx;

static int rank;
static long etrScr[RANK_LENGTH];

static unsigned int etbl[91];

static void setName(int rank) {
  int i;
  for ( i=0 ; i<NAME_LENGTH ; i++ ) {
    name[i] = etrName[rank][i];
  }
  nmIdx = 0;
}

static void restartEntry();

int rankPtn[] = {
  18,19, 13,3, 17,3, 19,7,
};

void drawBottomRank(int rank, int y) {
  int i, j, x;
  if ( rank < 9 ) {
    drawBgNum(rank+1, 0, y);
    if ( rank < 3 ) {
      j = rank*2;
    } else {
      j = 6;
    }
    x = 2;
  } else {
    drawBgNum(rank+1, 2, y);
    j = 6;
    x = 4;
  }
  for ( i=0 ; i<2 ; i++, j++ ) {
    drawBgAlp(rankPtn[j], i*2+x, y);
  }
}

static void gotoNextEntry() {
  if ( rank >= 0 ) {
    score += rnkScr*(ttlRank+1);
    etrScr[rank] = rnkScr;
    if ( rank < RANK_LENGTH-1 ) {
      playSe(SE_INPUTEND);
    }
  }
  if ( rank >= RANK_LENGTH-1 ) {
    playSe(SE_NEXTENTRY);
    restartEntry();
    return;
  }
  rank++;
  ttlRank++;
  drawBottomRank(ttlRank, IPT_Y);
  setName(rank);
  clearChr(20, IPT_Y, 8, 2);
  if ( gameStatus.atrcStat != START_ENTRY ) {
    gameStatus.atrcStat = START_RANK;
    gameStatus.atrcTime = 18;
  }
}

static void resetEnemy(int i) {
  enemy[i].speed = -1;
  sprite_set_location(ENMSPR_IDX+i*4  , DISAP_POS, DISAP_POS);
  sprite_set_location(ENMSPR_IDX+i*4+1, DISAP_POS, DISAP_POS);
  sprite_set_location(ENMSPR_IDX+i*4+2, DISAP_POS, DISAP_POS);
  sprite_set_location(ENMSPR_IDX+i*4+3, DISAP_POS, DISAP_POS);
}

void setEntryTable() {
  int i, j, k;
  int idx;
  lpWidth  = etrtblSize[tableNum][0];
  lpHeight = etrtblSize[tableNum][1];
  ltx = randN(lpWidth);
  lty = randN(lpHeight);
  spx = spy = 0;
  fdx = fdy = 0;
  fdWidth = lpWidth; fdHeight = lpHeight;
  while ( fdWidth < 8 ) fdWidth += lpWidth;
  while ( fdHeight < 8 ) fdHeight += lpHeight;
  fdWidth <<= OF_LGT_SFT;
  fdHeight <<= OF_LGT_SFT;
  enCnt = 0; enNum = 0;
  for ( i=0 ; i<ENEMY_MAX ; i++ ) {
    resetEnemy(i);
  }
  enApItv -= (enApItv>>2);

  clearChrScr(SCREEN1, 0, 3, SCREEN_WIDTH, SCREEN_HEIGHT-3);
  for ( k=0, i=0 ; i<lpHeight ; i++ ) {
    for ( j=0 ; j<lpWidth ; j++, k++ ) {
      etbl[k] = 0x1800+etrtbl[tableNum][j+i*lpWidth]+ALP_IDX;
      if ( gameStatus.status != TITLE ) {
	screen_fill_char(SCREEN1, j, i+3, 1, 1, etbl[k]-(0x1800-0x0c00));
      }
    }
  }
  for ( k=LTRSPR_IDX, i=0 ; i<GRID_LENGTH ; i++ ) {
    for ( j=0 ; j<GRID_LENGTH ; j++, k++ ) {
      idx = ((ltx+j)%lpWidth)+((lty+i)%lpHeight)*lpWidth;
      sprite_set_char(k, etbl[idx]);
    }
  }
}

/**
 * Change entry table pattern.
 */
static void restartEntry() {
  int i, j, rkp, ptbn;
  rank = -1;
  clearChr(0, IPT_Y, 28, 2);
  clearChr(NAME_X, NAME_Y, 6, 2);
  ptbn = tableNum;
  tableNum = randN(maxTableNum);
  if ( ptbn == tableNum ) {
    tableNum++;
    if ( tableNum >= maxTableNum ) tableNum = 0;
  }
  maxTableNum += 4;
  if ( maxTableNum > ETRTBL_NUM ) maxTableNum = ETRTBL_NUM;
  for ( i=0 ; i<RANK_LENGTH ; i++ ) {
    for ( j=0 ; j<NAME_LENGTH ; j++ ) {
      switch ( etrType[tableNum] ) {
      case 0:
	etrName[i][j] = randN(26);
	rkp = 9;
	break;
      case 1:
	if ( randN(2) == 0 ) {
	  etrName[i][j] = randN(26);
	} else {
	  etrName[i][j] = randN(10)+26;
	}
	rkp = 8;
	break;
      case 2:
	etrName[i][j] = randN(10)+26;
	rkp = 10;
	break;
      case 3:
	if ( j == 1 ) {
	  etrName[i][j] = 36;
	} else {
	  etrName[i][j] = randN(26);
	}
	rkp = 11;
	break;
      case 4:
	switch ( randN(6) ) {
	case 0:
	case 1:
	case 2:
	case 3:
	  etrName[i][j] = randN(26);
	  break;
	case 4:
	  etrName[i][j] = randN(10)+26;
	  break;
	case 5:
	  etrName[i][j] = 36;
	  break;
	}
	rkp = 6;
	break;
      case 5:
	switch ( randN(5) ) {
	case 0:
	case 1:
	case 2:
	case 3:
	  etrName[i][j] = randN(26);
	  break;
	case 4:
	  etrName[i][j] = randN(10)+26;
	  break;
	}
	rkp = 7;
	break;
      }
    }
  }
  gameStatus.atrcStat = START_ENTRY;
  if ( score == 0 ) {
    gameStatus.atrcTime = 9;
  } else {
    gameStatus.atrcTime = 18;
  } 
  gotoNextEntry();
}
  
void initTitleState() {
  ltcntx = SCREEN_WIDTH/2*8-4;
  ltcnty = SCREEN_HEIGHT*8-8;
  nmIdx = 3;
  ilIdx = -1;
  resetCursor();
  gameStatus.atrcStat = NONE;
  tableNum = 0;
  setEntryTable();
  sprite_set_window(0, 0, 0, 0);
  crsCnt = 0;
}

void startGame() {
  gameStatus.status = GAME_IN_PROGRESS;
  score = spinScore = 0;
  timer = 0;
  restartEntry();
  playBgm(BGM_GAME, PLAY_LOOP);
}

static void setCursor(int x, int y) {
  csrx = (x<<3); csry = ((y+3)<<3);
}

/**
 * Draw letter grid.
 */
void moveLetters(int key) {
  Boolean changed;
  int i;
  int tspx, tspy, tltx, tlty;
  int pili;
  int idx;
  if ( gameStatus.status != TITLE ) {
    if ( key & KEY_UP1 ) mvy -= 2;
    if ( key & KEY_LEFT1 ) mvx -= 2;
    if ( key & KEY_RIGHT1 ) mvx += 2;
    if ( key & KEY_DOWN1 ) mvy += 2;
    mvx -= ((mvx+16)>>5); mvy -= ((mvy+16)>>5);
    if ( mvx > 0 ) mvx--;
    else if ( mvx < 0 ) mvx++;
    if ( mvy > 0 ) mvy--;
    else if ( mvy < 0 ) mvy++;
  } else {
    mvx = 8; mvy = 0;
  }
  ofx -= mvx; ofy -= mvy;
  fdx += mvx; fdy += mvy;
  if ( fdx < 0 ) fdx += fdWidth;
  else if ( fdx >= fdWidth ) fdx -= fdWidth;
  if ( fdy < 0 ) fdy += fdHeight;
  else if ( fdy >= fdHeight ) fdy -= fdHeight;
  changed = FALSE;
  if ( ofx < 0 ) {
    ofx += OF_LENGTH;
    tltx = (ltx+GRID_LENGTH)%lpWidth;
    tspx = spx;
    ltx++; if ( ltx >= lpWidth ) ltx -= lpWidth;
    spx++; if ( spx >= GRID_LENGTH ) spx -= GRID_LENGTH;
    changed = TRUE;
  } else if ( ofx >= OF_LENGTH ) {
    ofx -= OF_LENGTH;
    ltx--; if ( ltx < 0 ) ltx += lpWidth;
    spx--; if ( spx < 0 ) spx += GRID_LENGTH;
    tltx = ltx; tspx = spx;
    changed = TRUE;
  }
  if ( changed ) {
    tspy = spy; tlty = lty;
    for ( i=0 ; i<GRID_LENGTH ; i++, tspy++, tlty++ ) {
      if ( tspy >= GRID_LENGTH ) tspy -= GRID_LENGTH;
      if ( tlty >= lpHeight ) tlty -= lpHeight;
      idx = tspx+tspy*GRID_LENGTH+LTRSPR_IDX;
      sprite_set_char(idx, etbl[tltx+tlty*lpWidth]);
    }
  }
  changed = FALSE;
  if ( ofy < 0 ) {
    ofy += OF_LENGTH;
    tlty = (lty+GRID_LENGTH)%lpHeight; 
    tspy = spy;
    lty++; if ( lty >= lpHeight ) lty -= lpHeight;
    spy++; if ( spy >= GRID_LENGTH ) spy -= GRID_LENGTH;
    changed = TRUE;
  } else if ( ofy >= OF_LENGTH ) {
    ofy -= OF_LENGTH;
    lty--; if ( lty < 0 ) lty += lpHeight;
    spy--; if ( spy < 0 ) spy += GRID_LENGTH;
    tlty = lty; tspy = spy;
    changed = TRUE;
  }
  if ( changed ) {
    tspx = spx; tltx = ltx;
    for ( i=0 ; i<GRID_LENGTH ; i++, tspx++, tltx++ ) {
      if ( tspx >= GRID_LENGTH ) tspx -= GRID_LENGTH;
      if ( tltx >= lpWidth ) tltx -= lpWidth;
      idx = tspx+tspy*GRID_LENGTH+LTRSPR_IDX;
      sprite_set_char(idx, etbl[tltx+tlty*lpWidth]);
    }
  }
  pili = ilIdx;
  tltx = ltx+GRID_LENGTH/2; tlty = lty+GRID_LENGTH/2;
  if ( ofx < OF_LENGTH/2 ) {
    tspx = (spx+GRID_LENGTH/2)%GRID_LENGTH;
  } else {
    tspx = (spx+GRID_LENGTH/2-1)%GRID_LENGTH;
    tltx--;
  }
  if ( ofy < OF_LENGTH/2 ) {
    tspy = (spy+GRID_LENGTH/2)%GRID_LENGTH;
  } else {
    tspy = (spy+GRID_LENGTH/2-1)%GRID_LENGTH;
    tlty--;
  }
  if ( gameStatus.status == GAME_IN_PROGRESS ||
       gameStatus.status == RANKING ) {
    ilIdx = tspx+tspy*GRID_LENGTH+LTRSPR_IDX;
    if ( pili != ilIdx ) {
      ilChr = (sprite_get_char(ilIdx)&0x01ff)-ALP_IDX;
      if ( ilChr < 26 ) {
	idx = 0x800+ilChr*2+AB_IDX;
	sprite_set_char(ILSPR_IDX  , idx);
	sprite_set_char(ILSPR_IDX+1, idx+1);
	sprite_set_char(ILSPR_IDX+2, idx+52);
	sprite_set_char(ILSPR_IDX+3, idx+53);
      } else if ( ilChr < 36 ) {
	idx = 0x800+(ilChr-26)*2+NB_IDX;
	sprite_set_char(ILSPR_IDX  , idx);
	sprite_set_char(ILSPR_IDX+1, idx+1);
	sprite_set_char(ILSPR_IDX+2, idx+20);
	sprite_set_char(ILSPR_IDX+3, idx+21);
      } else {
	idx = 0x800+(ilChr-36)*2+SB_IDX;
	sprite_set_char(ILSPR_IDX  , idx);
	sprite_set_char(ILSPR_IDX+1, idx+1);
	sprite_set_char(ILSPR_IDX+2, idx+10);
	sprite_set_char(ILSPR_IDX+3, idx+11);
      }
      tltx %= lpWidth; tlty %= lpHeight;
      setCursor(tltx, tlty);
      if ( !checkSe() ) playSe(SE_MOVE);
    }
  }
}

static void drawCursor(int sx, int sy) {
  int i;
  int sprIdx, sprChr;
  sprite_set_location(CSRSPR_IDX  , sx-8, sy-8);
  sprite_set_location(CSRSPR_IDX+1, sx  , sy-8);
  sprite_set_location(CSRSPR_IDX+2, sx+8, sy-8);
  sprite_set_location(CSRSPR_IDX+3, sx-8, sy);
  sprite_set_location(CSRSPR_IDX+4, sx  , sy);
  sprite_set_location(CSRSPR_IDX+5, sx+8, sy);
  sprite_set_location(CSRSPR_IDX+6, sx-8, sy+8);
  sprite_set_location(CSRSPR_IDX+7, sx  , sy+8);
  sprite_set_location(CSRSPR_IDX+8, sx+8, sy+8);
  sprIdx = CSRSPR_IDX;
  sprChr = 0x1800+csrMark*3+LCK_IDX;
  for ( i=0 ; i<9 ; i++, sprIdx++, sprChr++ ) {
    sprite_set_char(sprIdx, sprChr);
    if ( i==2 || i==5 ) sprChr += 15;
  }
  csrMark++; if ( csrMark >= 6 ) csrMark = 0;
}

static int letpos[] = {
-66,-51, -58,-60, -44,-69, -24,-76, 0,-79, 24,-76, 44,-69, 58,-60, 66,-51, 
-71,-47, -61,-54, -48,-64, -27,-73, 0,-77, 27,-73, 48,-64, 61,-54, 71,-47, 
-75,-41, -66,-49, -49,-55, -29,-65, 0,-70, 29,-65, 49,-55, 66,-49, 75,-41, 
-79,-35, -72,-42, -48,-43, -30,-54, 0,-60, 30,-54, 48,-43, 72,-42, 79,-35, 
-83,-27, -77,-34, -54,-36, -30,-40, 0,-48, 30,-40, 54,-36, 77,-34, 83,-27, 
-86,-19, -82,-24, -61,-27, -26,-23, 0,-33, 26,-23, 61,-27, 82,-24, 86,-19, 
-88,-9, -85,-12, -66,-14, -33,-15, 0,-17, 33,-15, 66,-14, 85,-12, 88,-9, 
-88,0, -86,0, -68,0, -37,0, 0,0, 37,0, 68,0, 86,0, 88,0, 
-88,9, -85,12, -66,14, -33,15, 0,17, 33,15, 66,14, 85,12, 88,9, 
};

void drawLetters() {
  int i, j, k;
  int sx, sy, sx1, sy1, sx2, sy2;
  int tspx, tspy;
  int idx;
  int ctx, cty;
  k = 0;
  tspy = spy;
  if ( crsCnt <= 0 ) {
    ctx = ltcntx; cty = ltcnty;
  } else {
    ctx = ltcntx + randN((crsCnt<<1)) - crsCnt;
    cty = ltcntx + randN((crsCnt<<1)) - crsCnt;
    crsCnt--;
  }
  for ( i=0 ; i<GRID_LENGTH ; i++, tspy++, k+=2 ) {
    if ( tspy >= GRID_LENGTH ) tspy -= GRID_LENGTH;
    tspx = spx;
    for ( j=0 ; j<GRID_LENGTH ; j++, tspx++, k+=2 ) {
      if ( tspx >= GRID_LENGTH ) tspx -= GRID_LENGTH;
      sx1 = ((letpos[k]*(OF_LENGTH-ofx))>>OF_LGT_SFT)
	+((letpos[k+2]*ofx)>>OF_LGT_SFT);
      sx2 = ((letpos[k+GRID_LENGTH*2+2]*(OF_LENGTH-ofx))>>OF_LGT_SFT)
	+((letpos[k+GRID_LENGTH*2+2+2]*ofx)>>OF_LGT_SFT);
      sx = ((sx1*(OF_LENGTH-ofy))>>OF_LGT_SFT) + ((sx2*ofy)>>OF_LGT_SFT);
      sy1 = ((letpos[k+1]*(OF_LENGTH-ofy))>>OF_LGT_SFT)
	+((letpos[k+GRID_LENGTH*2+2+1]*ofy)>>OF_LGT_SFT);
      sy2 = ((letpos[k+2+1]*(OF_LENGTH-ofy))>>OF_LGT_SFT)
	+((letpos[k+GRID_LENGTH*2+2+3]*ofy)>>OF_LGT_SFT);
      sy = ((sy1*(OF_LENGTH-ofx))>>OF_LGT_SFT) + ((sy2*ofx)>>OF_LGT_SFT);
      idx = tspx+tspy*GRID_LENGTH+LTRSPR_IDX;
      if ( gameStatus.atrcStat == START_ENTRY ) {
	int zm = abs(gameStatus.atrcTime-8);
	sx = (sx*zm)>>3; sy = (sy*zm)>>3;
      }
      sx += ctx; sy += cty;
      sprite_set_location(idx, sx, sy);
      if ( idx == ilIdx ) {
	sprite_set_location(ILSPR_IDX  , sx-4, sy-4);
	sprite_set_location(ILSPR_IDX+1, sx+4, sy-4);
	sprite_set_location(ILSPR_IDX+2, sx-4, sy+4);
	sprite_set_location(ILSPR_IDX+3, sx+4, sy+4);
      }
    }
  }
}

/**
 * Count down rank score.
 */
void drawRankScore() {
  int nsl;
  if ( gameStatus.status == GAME_IN_PROGRESS ) {
    rnkScr -= rnkSub;
    nsl = drawBgNum(rnkScr, 17, IPT_Y);
    if ( scrLgt!=nsl && scrLgt!=-1 ) {
      clearChr(19-scrLgt*2, IPT_Y, 2, 2);
    }
    scrLgt = nsl;
  }
  switch ( timer&15 ) {
  case 0:
    drawBgAlp(36+4, nmIdx*2+20, IPT_Y);
    break;
  case 8:
    drawBgAlp(-1, nmIdx*2+20, IPT_Y);
    break;
  }
}

#define MY_DEG_MV 16

/**
 * Move my cursor man and input letter.
 */
void inputLetter() {
  int i, k, ak, c;
  int sx, sy, sx1, sy1, sx2, sy2, cntd, ad, od;
  int key = key_press_check();
  if ( lpWidth == -1 ) return;
  moveLetters(key);
  drawLetters();
  if ( mvx != 0 || mvy != 0 ) {
    ad = getDeg(mvx, mvy);
    od = myd-ad;
    if ( od>=-MY_DEG_MV && od<=MY_DEG_MV ) {
      myd = ad;
    } else if ( (od>0 && od<128) || od<-128 ) {
      myd -= MY_DEG_MV;
    } else {
      myd += MY_DEG_MV;
    }
    myd &= 255;
  }
  sprite_set_location(MYSPR_IDX, LTCNT_X, LTCNT_Y-16);
  sprite_set_location(MYSPR_IDX+1, LTCNT_X, LTCNT_Y-12);
  sx = sx1 = sx2 = LTCNT_X; sy = sy1 = sy2 = LTCNT_Y;
  myCnt += abs(mvx)+abs(mvy); myCnt &= 127;
  if ( myCnt < 64 ) {
    cntd = myCnt>>2;
  } else {
    cntd = (128-myCnt)>>2;
  }
  cntd += myd; cntd &= 255;
  sx1 -= costbl[cntd]>>5;
  sy1 -= sintbl[cntd]>>6;
  sx2 += sx-sx1;
  sy2 += sy-sy1;
  if ( myCnt < 64 ) {
    sy1 -= costbl[((myCnt-32)<<1)&255]>>5;
  } else {
    sy2 -= costbl[((myCnt-96)<<1)&255]>>5;
  }
  sprite_set_location(MYSPR_IDX+2, sx1, sy1);
  sprite_set_location(MYSPR_IDX+3, sx2, sy2);

  if ( gameStatus.atrcStat != NONE ) return;
  if ( csrx != DISAP_POS ) drawCursor(csrx, csry);
  drawRankScore();
  if ( !(key & KEY_A) ) {
    aPrsd = FALSE;
  } else if ( !aPrsd ) { 
    aPrsd = TRUE;
    if ( nmIdx < 3 ) {
      playSe(SE_SET);
      drawBgAlp(ilChr, nmIdx*2+20, IPT_Y);
      iptlt[nmIdx] = ilChr;
      ilsprx = LTCNT_X; ilspry = LTCNT_Y;
      c = iptlt[nmIdx];
      if ( c >= 36 ) {
	k = (c-36)*2 + SB_IDX + 0x1800;
	ak = 10;
      } else if ( c >= 26 ) {
	k = (c-26)*2 + NB_IDX + 0x1800;
	ak = 20;
      } else {
	k = c*2 + AB_IDX + 0x1800;
	ak = 52; 
      }
      sprite_set_char(FILSPR_IDX  , k);
      sprite_set_char(FILSPR_IDX+1, k+1);
      sprite_set_char(FILSPR_IDX+2, k+ak);
      sprite_set_char(FILSPR_IDX+3, k+ak+1);
      ilspraimx = nmIdx*16+20*8; ilspraimy = IPT_Y*8;
      ilsprcnt = 16;
      nmIdx++;
      if ( nmIdx == 3 ) {
	for ( i=0 ; i<3 ; i++ ) {
	  if ( iptlt[i] != name[i] ) break;
	}
	if ( i == 3 ) {
	  gotoNextEntry();
	  return;
	}
      }
    }
  }
  if ( !(key & KEY_B) ) {
    bPrsd = FALSE;
  } else if ( !bPrsd ) { 
    bPrsd = TRUE;
    if ( nmIdx > 0 ) {
      playSe(SE_BACK);
      drawBgAlp(-1, nmIdx*2+20, IPT_Y);
      nmIdx--;
    }
  }
}

/**
 * Move enemy.
 */
static void addEnemy() {
  int ex, ey;
  int i;
  for ( i=0 ; i<ENEMY_MAX ; i++ ) {
    if ( enemy[i].speed == -1 ) break;
  }
  if ( i >= ENEMY_MAX ) return;
  if ( randN(2) == 0 ) {
    ex = fdx - (fdWidth>>1);
    if ( ex < 0 ) ex += fdWidth;
    if ( ex >= fdWidth ) ex -= fdWidth;
    ey = randN(fdHeight);
  } else {
    ex = randN(fdWidth);
    ey = fdy - (fdHeight>>1);
    if ( ey < 0 ) ey += fdHeight;
    if ( ey >= fdHeight ) ey -= fdHeight;
  }
  enemy[i].x = ex; enemy[i].y = ey;
  enemy[i].d = randN(256); enemy[i].speed = 2+randN(5);
  enemy[i].moveCnt = 0;
  enemy[i].moveStat = 0;
  enemy[i].cnt = 0;
  enNum++;
}

#define ENEMY_DEG_MV 4
#define HIT_SUB_SCR 5000

static void moveEnemys() {
  int i, od, ox, oy;
  int k, sx1, sx2, sx, sy1, sy2, sy;
  int cntd;
  Enemy *en;
  for ( i=0 ; i<ENEMY_MAX ; i++ ) {
    if ( enemy[i].speed == -1 ) continue;
    en = &(enemy[i]);
    switch ( en->moveStat ) {
    case 0:
      en->x += (sintbl[en->d]*en->speed)>>8;
      en->y -= (costbl[en->d]*en->speed)>>8;
      en->cnt += en->speed;
      en->cnt &= 127;
      if ( en->x < 0 ) en->x += fdWidth;
      else if ( en->x >= fdWidth ) en->x -= fdWidth;
      if ( en->y < 0 ) en->y += fdHeight;
      else if ( en->y >= fdHeight ) en->y -= fdHeight;
      en->moveCnt--;
      if ( en->moveCnt <= 0 ) {
	en->moveStat = 1;
	en->moveCnt = randN(16)+16;
	ox = fdx - en->x; oy = fdy - en->y;
	if ( ox > (fdWidth>>1) ) ox -= fdWidth;
	else if ( ox < -(fdWidth>>1) ) ox += fdWidth;
	if ( oy > (fdHeight>>1) ) oy -= fdHeight;
	else if ( oy < -(fdHeight>>1) ) oy += fdHeight;
	en->aimd = getDeg(ox, oy)&255;
      }
      break;
    case 1:
      od = en->d - en->aimd;
      if ( od>=-ENEMY_DEG_MV && od<=ENEMY_DEG_MV ) {
	en->d = en->aimd;
      } else if ( (od>0 && od<128) || od<-128 ) {
	en->d -= ENEMY_DEG_MV;
      } else {
	en->d += ENEMY_DEG_MV;
      }
      en->d &= 255;
      en->moveCnt--;
      if ( en->moveCnt <= 0 ) {
	en->moveStat = 0;
	en->moveCnt = randN(128)+64;
      }
      break;
    }
    ox = en->x - fdx;
    oy = en->y - fdy;
    if ( abs(ox)+abs(oy) < (OF_LENGTH>>1) ) {
      playSe(SE_HIT);
      rnkScr -= HIT_SUB_SCR;
      if ( rnkScr < 0 ) rnkScr = 0;
      resetEnemy(i);
      enCnt--;
      crsCnt = 20;
      continue;
    }
    ox += OF_LENGTH*(GRID_LENGTH/2);
    oy += OF_LENGTH*(GRID_LENGTH/2);
    if ( ox < 0 ) ox += fdWidth;
    if ( ox >= fdWidth ) ox -= fdWidth;
    if ( oy < 0 ) oy += fdHeight;
    if ( oy >= fdHeight ) oy -= fdHeight;
    if ( ox>=0 && ox<OF_LENGTH*GRID_LENGTH 
	 && oy>=0 && oy<OF_LENGTH*GRID_LENGTH ) {
      k = (ox>>OF_LGT_SFT)*2 + (oy>>OF_LGT_SFT)*(GRID_LENGTH+1)*2;
      ox &= (OF_LENGTH-1); oy &= (OF_LENGTH-1);
      sx1 = ((letpos[k]*(OF_LENGTH-ox))>>OF_LGT_SFT)
	+((letpos[k+2]*ox)>>OF_LGT_SFT);
      sx2 = ((letpos[k+GRID_LENGTH*2+2]*(OF_LENGTH-ox))>>OF_LGT_SFT)
	+((letpos[k+GRID_LENGTH*2+2+2]*ox)>>OF_LGT_SFT);
      sx = ((sx1*(OF_LENGTH-oy))>>OF_LGT_SFT) + ((sx2*oy)>>OF_LGT_SFT);
      sy1 = ((letpos[k+1]*(OF_LENGTH-oy))>>OF_LGT_SFT)
	+((letpos[k+GRID_LENGTH*2+2+1]*oy)>>OF_LGT_SFT);
      sy2 = ((letpos[k+2+1]*(OF_LENGTH-oy))>>OF_LGT_SFT)
	+((letpos[k+GRID_LENGTH*2+2+3]*oy)>>OF_LGT_SFT);
      sy = ((sy1*(OF_LENGTH-ox))>>OF_LGT_SFT) + ((sy2*ox)>>OF_LGT_SFT);
      sx += ltcntx; sy += ltcnty;
      sprite_set_location(ENMSPR_IDX+(i<<2)  , sx, sy-16);
      sprite_set_location(ENMSPR_IDX+(i<<2)+1, sx, sy-8);
      sx1 = sx2 = sx; sy1 = sy2 = sy;
      if ( en->cnt < 64 ) {
	cntd = en->cnt>>2;
      } else {
	cntd = (128-en->cnt)>>2;
      }
      cntd += en->d; cntd &= 255;
      sx1 -= costbl[cntd]>>6;
      sy1 -= sintbl[cntd]>>7;
      sx2 += (sx-sx1);
      sy2 += (sy-sy1);
      if ( en->cnt < 64 ) {
	sy1 -= costbl[((en->cnt-32)<<1)&255]>>5;
      } else {
	sy2 -= costbl[((en->cnt-96)<<1)&255]>>5;
      }
      sprite_set_location(ENMSPR_IDX+(i<<2)+2, sx1, sy1);
      sprite_set_location(ENMSPR_IDX+(i<<2)+3, sx2, sy2);
    } else {
      sprite_set_location(ENMSPR_IDX+(i<<2)  , DISAP_POS, DISAP_POS);
      sprite_set_location(ENMSPR_IDX+(i<<2)+1, DISAP_POS, DISAP_POS);
      sprite_set_location(ENMSPR_IDX+(i<<2)+2, DISAP_POS, DISAP_POS);
      sprite_set_location(ENMSPR_IDX+(i<<2)+3, DISAP_POS, DISAP_POS);
    }
  }
  enCnt--; 
  if ( enCnt <= 0 ) {
    if ( enNum < ENEMY_MAX ) {
      addEnemy();
      enCnt = enApItv;
    } else {
      enCnt = 9999;
    }
  }
}

/**
 * Move flying inputted letter.
 */
void moveFlyingLetter() {
  int i;
  if ( ilsprcnt > 0 ) {
    ilsprcnt--;
    if ( ilsprcnt <= 0 ) {
      for ( i=FILSPR_IDX ; i<FILSPR_IDX+4 ; i++ ) {
	sprite_set_location(i, DISAP_POS, DISAP_POS);
      }
    } else {
      ilsprx += (ilspraimx-ilsprx)>>3;
      ilspry += (ilspraimy-ilspry)>>3;
      sprite_set_location(FILSPR_IDX  , ilsprx  , ilspry);
      sprite_set_location(FILSPR_IDX+1, ilsprx+8, ilspry);
      sprite_set_location(FILSPR_IDX+2, ilsprx  , ilspry+8);
      sprite_set_location(FILSPR_IDX+3, ilsprx+8, ilspry+8);
    }
  }
}

static int nmltx[] = {
   0, 1, 1, 1, 2, 2, 2, 3, 3,
   3, 4, 4, 3, 2, 1, 0, -1,
};

/**
 * Handle stage effect.
 */
static void handleAtrcStat() {
  int i;
  switch ( gameStatus.atrcStat ) {
  case START_ENTRY:
    if ( gameStatus.atrcTime == 8 ) setEntryTable();
    gameStatus.atrcTime--;
    if ( gameStatus.atrcTime <= 0 ) {
      gameStatus.atrcStat = START_RANK;
      gameStatus.atrcTime = 18;
    }
    break;
  case START_RANK:
    gameStatus.atrcTime--;
    if ( gameStatus.atrcTime < 17 ) {
      clearChrScr(SCREEN1, NAME_X-nmltx[gameStatus.atrcTime+1], NAME_Y, 6, 2);
    }
    for ( i=0 ; i<NAME_LENGTH ; i++ ) {
      drawBgAlpScr
	(SCREEN1, name[i], NAME_X+i*2-nmltx[gameStatus.atrcTime], NAME_Y);
    }
    if ( gameStatus.atrcTime <= 0 ) {
      gameStatus.atrcStat = NONE;
    }
    break;
  }
}

/**
 * Proceed one frame.
 */
void handleGameStatus() {
  int key = key_press_check();
  if ( !(key & KEY_START) ) {
    startPrsd = FALSE;
  } else if ( !startPrsd ) { 
    startPrsd = TRUE;
    startPause();
    return;
  }
  inputLetter();
  moveEnemys();
  moveFlyingLetter();
  handleAtrcStat();
  drawSpinScore();
  if ( rnkScr <= 0 ) {
    drawScore(score);
    startGameGameover();
  }
}


