// **************************************************************************** // // Main code // // **************************************************************************** #include "main.h" #include "pico/printf.h" #include u8 Text[TEXTMAX]; // row of text ALIGNED u8 TextCol[TEXTMAX*8]; // text color gradient ALIGNED u8 Font_Copy[4096]; // copy of font // copy of skyline ALIGNED u8 SkylineImg_Copy[sizeof(SkylineImg)]; // copy of tiles ALIGNED u8 TilesImg_Copy[sizeof(TilesImg)]; // copy of cars ALIGNED u8 Car1Img_Copy[sizeof(Car1Img)]; ALIGNED u8 Car2Img_Copy[sizeof(Car2Img)]; ALIGNED u8 Car3Img_Copy[sizeof(Car3Img)]; // copy of ghosts ALIGNED u8 Ghost1Img_Copy[sizeof(Ghost1Img)]; ALIGNED u8 Ghost2Img_Copy[sizeof(Ghost2Img)]; ALIGNED u8 Ghost3Img_Copy[sizeof(Ghost3Img)]; ALIGNED u8 Ghost4Img_Copy[sizeof(Ghost4Img)]; // game state float Speed = 0; // current speed int Gear = 0; // current gear float CarX = 8.5*TILESIZE; // car X coordinate float CarY = 15.5*TILESIZE; // car Y coordinate float CarDir = 0; // car direction int CarTurn = 0; // car turn -1 left, 0, +1 right int CurTile = 0; // current tile under car u64 StartTime; // start time int ThisTime = 0; // this time int LastTime = 0; // last time int BestTime = 0; // best time Bool NewBest = False; // new best time int Rank = 0; // current rank 1..3 (0 = start new game) int CheckNext = START; // next check point (START = search start) Bool CheckOK[CHECKNUM]; // check points processed u8 CheckCol[CHECKNUM] = { (B0+B1+B4+B6), COL_YELLOW, COL_RED }; // check point color int Hist[HISTNUM] = { 1000000000, 1000000000, 1000000000 }; // history of best times u32 RecordStep; int RecordInx[3] = { 0, -1, -1 }; // record index (-1 = not initialized) float RecordX[RECORDMAX]; // record of X coordinates float RecordY[RECORDMAX]; // record of Y coordinates float RecordDir[RECORDMAX]; // record of direction // tile map // grass 0 ... GRASS // | start 1 ... START // | check A (blue) 2 ... CHECKA // | check B (yellow) 3 ... CHECKB // | check C (red) 4 ... CHECKC // | straight 5 // -- straight 6 // // -- 7 8 // | 9 10 // -- 11 12 // | 13 14 // | 15 16 // -- 17 18 // | 19 20 // -- 21 22 // do not use "const" prefix, to use faster RAM u8 TileMap[MAPSIZE] = { // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,11,12, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 9,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,13,14, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, // 10 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 7, 8, 6, 6, 6,11,12, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, // 11 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 9,10, 0, 0, 0,13,14, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, // 12 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 5, 0, 0, 0, 0, 0,15,16, 0, 0, 0,19,20, 0, 0, 0, 0, 0, 0, 0, 0, // 13 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 3, 0, 0, 0, 0, 0,17,18, 6, 6, 6,21,22, 0, 0, 0, 0, 0, 0, 0, 0, // 14 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 15 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0,15,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0,17,18, 6, 6, 6, 6, 6, 6, 6, 6,11,12, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 17 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,13,14, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 18 0, 0, 0, 0, 0, 0, 0, 0,15,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 19 0, 0, 0, 0, 0, 0, 0, 0,17,18, 6, 6, 6, 6, 6, 6, 6,11,12, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,13,14, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 21 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,15,16, 0,19,20, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 22 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,17,18, 6,21,22, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 23 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 24 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 25 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 26 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 27 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 28 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 29 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 30 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 31 }; // projection matrix - terrain cMat2Df Mat; int MatInt[6]; // projection matrix - car cMat2Df Mat2; int Mat2Int[6]; // projection matrix - ghost 1 cMat2Df Mat3; int Mat3Int[6]; // projection matrix - ghost 2 cMat2Df Mat4; int Mat4Int[6]; u8 InfoX = 0; // info X position // clear info row void InfoClear() { memset(Text, ' ', sizeof(Text)); memset(TextCol, COL_BLACK, sizeof(TextCol)); } // display info character void InfoDispChar(char ch, u8 col) { u8 x = InfoX; Text[x] = ch; for (ch = 0; ch < 8; ch++) TextCol[x*8+ch] = col; InfoX = x + 1; } // display text to info row void InfoDispText(const char* txt, u8 col) { char ch; while ((ch = *txt++) != 0) { InfoDispChar(ch, col); } } // shift info text position (clear with spaces void InfoPos(u8 x) { while (InfoX < x) InfoDispChar(' ', COL_BLACK); } const char SpeedTxt[] = "SPEED:"; const char TimeTxt[] = "TIME:"; const char LastTxt[] = "LAST:"; const char BestTxt[] = "BEST:"; const char RankTxt[] = "RANK:"; const char CheckTxt[] = "CHECK:"; char NumBuf[20]; // display time void InfoTime(int t, u8 col) { int min = t/60; int sec = t - min*60; int n = DecNum(NumBuf, min); NumBuf[n] = ':'; n++; if (sec < 10) { NumBuf[n] = '0'; n++; } DecNum(&NumBuf[n], sec); InfoDispText(NumBuf, col); } // display info row void InfoRow() { InfoX = 0; u8 x = 2; InfoPos(x); #define COLTIT COL_YELLOW #define COLVAL COL_WHITE #define COLBEST COL_GREEN // display Speed InfoDispText(SpeedTxt, COLTIT); x += 7; InfoPos(x); DecNum(NumBuf, (int)(Speed/SPEEDCOEF)); InfoDispText(NumBuf, COLVAL); InfoDispChar('/', COLTIT); InfoDispChar((Gear < 0) ? 'R' : (Gear + '0'), COLVAL); x += 7; InfoPos(x); // display this time InfoDispText(TimeTxt, COLTIT); x += 6; InfoPos(x); InfoTime(ThisTime, COLVAL); x += 7; InfoPos(x); // display last time InfoDispText(LastTxt, COLTIT); x += 6; InfoPos(x); InfoTime(LastTime, NewBest ? COLBEST : COLVAL); x += 7; InfoPos(x); // display best time InfoDispText(BestTxt, COLTIT); x += 6; InfoPos(x); InfoTime(BestTime, NewBest ? COLBEST : COLVAL); x += 7; InfoPos(x); // display rank InfoDispText(RankTxt, COLTIT); x += 6; InfoPos(x); DecNum(NumBuf, Rank); InfoDispText(NumBuf, COLVAL); x += 4; InfoPos(x); // display check InfoDispText(CheckTxt, COLTIT); x += 7; InfoPos(x); int i; for (i = 0; i < CHECKNUM; i++) { InfoDispChar(CheckOK[i] ? 4 : 2, CheckCol[i]); x += 2; InfoPos(x); } } // initialize videomode void VideoInit(u32 freq) { // terminate current driver VgaInitReq(NULL); // setup videomode VgaCfgDef(&Cfg); // get default configuration Cfg.video = &DRV; // video timings Cfg.width = WIDTH; // screen width Cfg.height = HEIGHT; // screen height Cfg.dbly = DBLY; // double Y Cfg.freq = freq; // minimal system clock Cfg.mode[CARLAYER] = LAYERMODE_PERSP2WHITE; // car Cfg.mode[GHOST1LAYER] = LAYERMODE_PERSP2WHITE; // ghost 1 Cfg.mode[GHOST2LAYER] = LAYERMODE_PERSP2WHITE; // ghost 2 VgaCfg(&Cfg, &Vmode); // calculate videomode setup // initialize base layer 0 ScreenClear(pScreen); // info row sStrip* t = ScreenAddStrip(pScreen, TEXTH); sSegm* g = ScreenAddSegm(t, WIDTH); ScreenSegmGText(g, Text, Font_Copy, 16, COL_BLACK, TextCol, TEXTMAX); InfoClear(); // skyline t = ScreenAddStrip(pScreen, SKYLINEH); g = ScreenAddSegm(t, WIDTH); ScreenSegmGraph8(g, SkylineImg_Copy, SKYLINEW); // terrain t = ScreenAddStrip(pScreen, TERRAINH); g = ScreenAddSegm(t, WIDTH); ScreenSegmTilePersp4(g, TileMap, TilesImg_Copy, MatInt, MAPWBITS, MAPHBITS, TILEBITS, HORIZ); // car LayerPerspSetup(CARLAYER, Car1Img_Copy, &Vmode, CARW2, CARH2, CARWBITS, CARHBITS, 0, Mat2Int); LayerSetX(CARLAYER, (WIDTH-CARW2)/2); LayerSetY(CARLAYER, HEIGHT-CARH2); LayerOn(CARLAYER); Mat2.PrepDrawImg(CARW, CARH, 0, 0, CARW2, CARH2, 0, 0, 0, CARW/2, CARH/2); Mat2.ExportInt(Mat2Int); // ghost 1 LayerPerspSetup(GHOST1LAYER, Ghost1Img_Copy, &Vmode, GHOSTW, GHOSTH, GHOSTWBITS, GHOSTHBITS, 0, Mat3Int); LayerSetX(GHOST1LAYER, 100); LayerSetY(GHOST1LAYER, 100); Mat3.PrepDrawImg(GHOSTW, GHOSTH, 0, 0, GHOSTW, GHOSTH, 0, 0, 0, GHOSTW/2, GHOSTH/2); Mat3.ExportInt(Mat3Int); // ghost 2 LayerPerspSetup(GHOST2LAYER, Ghost1Img_Copy, &Vmode, GHOSTW, GHOSTH, GHOSTWBITS, GHOSTHBITS, 0, Mat4Int); LayerSetX(GHOST2LAYER, 300); LayerSetY(GHOST2LAYER, 150); Mat4.PrepDrawImg(GHOSTW, GHOSTH, 0, 0, GHOSTW, GHOSTH, 0, 0, 0, GHOSTW/2, GHOSTH/2); Mat4.ExportInt(Mat4Int); // initialize system clock set_sys_clock_pll(Vmode.vco*1000, Vmode.pd1, Vmode.pd2); // initialize videomode VgaInitReq(&Vmode); } // get character from keyboard (0 = no key) char GetChar() { char c = getchar_timeout_us(0); if (c == (char)PICO_ERROR_TIMEOUT) c = 0; if ((c >= 'a') && (c <= 'z')) c -= 32; return c; } // flush characters from keyboard void FlushChar() { while (GetChar() != 0) {} } int main() { int t0, t2; float dt; // copy images memcpy(SkylineImg_Copy, SkylineImg, sizeof(SkylineImg)); memcpy(Font_Copy, FontBold8x16, sizeof(FontBold8x16)); memcpy(TilesImg_Copy, TilesImg, sizeof(TilesImg)); CopyWhiteImg(Car1Img_Copy, Car1Img, sizeof(Car1Img)); CopyWhiteImg(Car2Img_Copy, Car2Img, sizeof(Car2Img)); CopyWhiteImg(Car3Img_Copy, Car3Img, sizeof(Car3Img)); CopyWhiteImg(Ghost1Img_Copy, Ghost1Img, sizeof(Ghost1Img)); CopyWhiteImg(Ghost2Img_Copy, Ghost2Img, sizeof(Ghost2Img)); CopyWhiteImg(Ghost3Img_Copy, Ghost3Img, sizeof(Ghost3Img)); CopyWhiteImg(Ghost4Img_Copy, Ghost4Img, sizeof(Ghost4Img)); // run VGA core StartVgaCore(); // initialize videomode VideoInit(170000); // initialize stdio stdio_init_all(); // initialize sound output PWMSndInit(); // display info row InfoRow(); // time mark t0 = (int)time_us_64(); // main loop while (true) { switch (GetChar()) { // forward case 'I': Gear++; if (Gear > GEARMAX) Gear = GEARMAX; FlushChar(); break; // back case 'K': Gear--; if (Gear < GEARMIN) Gear = GEARMIN; FlushChar(); break; // left case 'J': if (CarTurn > -1) CarTurn--; FlushChar(); break; // right case 'L': if (CarTurn < 1) CarTurn++; FlushChar(); break; // no key case 0: break; // flush another keys default: printf("\nKeys:\n"); printf("I ... gear up\n"); printf("K ... gear down\n"); printf("J ... turn left\n"); printf("L ... turn right\n"); FlushChar(); break; } // delta time (at seconds) t2 = (int)time_us_64(); dt = (t2 - t0)/1000000.f; t0 = t2; // update speed float s = (float)Gear*SPEEDCOEF*10; // required speed float s2 = Speed; // current speed Bool grass = (TileMap[CurTile] == GRASS); // car on grass if (grass && (s > SPEEDCOEF*10)) s = SPEEDCOEF*10; // slow down on grass if (s > s2) // required speed up { s2 += dt*ACCEL; if (s <= s2) s2 = s; } if (s < s2) // required speed down { s2 -= dt*ACCEL; if (grass) s2 -= 3*dt*ACCEL; if (s >= s2) s2 = s; } Speed = s2; // update turning if (Speed != 0) { float c = CarTurn*TURNSPEED * (6 - Speed/(SPEEDCOEF*10)); if (Speed > 0) CarDir += c; else CarDir -= c; // normalize direction do range 0..2PI while (CarDir < 0) CarDir += PI2; while (CarDir >= PI2) CarDir -= PI2; } // update sound if (Speed == 0) { StopSound(); } else { if (!PlayingSound()) { PlaySound(EngineSnd, sizeof(EngineSnd), True, 1.0f); } SpeedSound(Speed/(SPEEDCOEF*10)/5); } // update car image switch (CarTurn) { case 1: LayerScreen[CARLAYER].img = Car3Img_Copy; break; case -1: LayerScreen[CARLAYER].img = Car2Img_Copy; break; default: LayerScreen[CARLAYER].img = Car1Img_Copy; break; } // update position CarY += - s2 * dt * cos(CarDir); CarX += s2 * dt * sin(CarDir); // update terrain Mat.PrepDrawImg(TILESIZE, TILESIZE, 0, TILESIZE*3, WIDTH, WIDTH, 0, 0, CarDir, CarX, CarY); Mat.ExportInt(MatInt); // update skyline pScreen->strip[1].seg[0].offx = CarDir*2*SKYLINEW/PI; // update time counter if (Rank > 0) // game started { ThisTime = (int)((time_us_64() - StartTime)/1000000); } // current tile int x = (int)((CarX+1024*TILESIZE)/TILESIZE) & (MAPW-1); int y = (int)((CarY+1024*TILESIZE)/TILESIZE) & (MAPH-1); CurTile = x + y*MAPW; // game goal u8 tile = TileMap[CurTile]; // get current tile if (tile == CheckNext) { switch (tile) { // start tile case START: if (Rank > 0) // game started { // process last lap LastTime = ThisTime; // new best time NewBest = False; if ((LastTime < BestTime) || (BestTime == 0)) { BestTime = LastTime; NewBest = True; } // check rank if (LastTime < Hist[0]) { Hist[2] = Hist[1]; Hist[1] = Hist[0]; Hist[0] = LastTime; Rank = 1; } else { if (LastTime < Hist[1]) { Hist[2] = Hist[1]; Hist[1] = LastTime; Rank = 2; } else Rank = 3; } // start ghost if (RecordInx[1] < 0) RecordInx[1] = 0; else if (RecordInx[2] < 0) RecordInx[2] = 0; } else { // start game Rank = 1; RecordStep = (u32)time_us_64();; } // start new lap StartTime = time_us_64(); CheckNext = CHECKA; CheckOK[0] = False; CheckOK[1] = False; CheckOK[2] = False; break; // check point A case CHECKA: CheckOK[0] = True; CheckNext = CHECKB; break; // check point B case CHECKB: CheckOK[1] = True; CheckNext = CHECKC; break; // check pojnt C case CHECKC: CheckOK[2] = True; CheckNext = START; break; } } // record step delta int i; int dif = (int)((u32)time_us_64() - RecordStep); // display ghost 1 int ghost; for (ghost = 1; ghost <= 2; ghost++) { i = RecordInx[1]; if (i >= 0) { // X coordinate float x1 = RecordX[i]; float x2 = RecordX[(i+1)&(RECORDMAX-1)]; float x = (x2 - x1)*dif/1000000 + x1; x = x - CarX; // Y coordinate float y1 = RecordY[i]; float y2 = RecordY[(i+1)&(RECORDMAX-1)]; float y = (y2 - y1)*dif/1000000 + y1; y = y - CarY; // direction float d = RecordDir[i]; // angle of vector to ghost float a = atan2(y, x); float a0 = a; // distane of vector to ghost float k = sqrt(y*y + x*x); if (k < 1) k = 1; // view angle a = a - CarDir + PI/2; while (a < -PI) a += PI2; while (a > PI) a -= PI2; // check if view angle is out of view frustum (frustrum is +- 45 deg) if ((a < -PI/3) || (a > PI/3)) { // not visible LayerOff(ghost+1); } else { // relative coordinates to the viewer x2 = sin(a)*k; y2 = cos(a)*k; // ghost size (@TODO: incorrect calculations!) float s = 400/(k+200); int wd = (int)(GHOSTW*s); int hd = (int)(GHOSTH*s); if (wd < 50) wd = 0; wd = ALIGN4(wd); // update ghost image if (ghost == 1) { Mat3.PrepDrawImg(GHOSTW, GHOSTH, 0, 0, wd, hd, 0, 0, 0, GHOSTW/2, GHOSTH/2); Mat3.ExportInt(Mat3Int); } else { Mat4.PrepDrawImg(GHOSTW, GHOSTH, 0, 0, wd, hd, 0, 0, 0, GHOSTW/2, GHOSTH/2); Mat4.ExportInt(Mat4Int); } LayerSetW(ghost+1, wd); LayerScreen[ghost+1].wb = GHOSTW; LayerSetH(ghost+1, hd); // ghost direction d -= a0 + PI/2; while (d < -PI) d += PI2; while (d > PI) d -= PI2; if ((d >= -PI/4) && (d < PI/4)) LayerScreen[ghost+1].img = Ghost1Img_Copy; // back else if ((d < -PI*3/4) || (d >= PI*3/4)) LayerScreen[ghost+1].img = Ghost3Img_Copy; // front else if (d < 0) LayerScreen[ghost+1].img = Ghost2Img_Copy; // left else LayerScreen[ghost+1].img = Ghost4Img_Copy; // right // ghost position on the screen (@TODO: incorrect calculations!) int x0 = (int)((tan(a) + 1)*WIDTH/2) - wd/2; int y0 = (int)(TERRAINH/(y2+10)/(y2+10)/(y2+10)) + TEXTH + SKYLINEH; // check if coordinates are valid if ((wd > 0) && (hd > 0) && (x0 >= 0) && ((x0 + wd) < WIDTH) && (y0 >= 0) && ((y0 + hd) < TERRAINH)) { // set ghost position on the screen LayerSetX(ghost+1, x0); LayerSetY(ghost+1, y0); // set visible LayerOn(ghost+1); } else { // not visible LayerOff(ghost+1); } } } } // record game (every 1 second) if ((Rank > 0) && (dif >= 1000000)) { RecordStep += 1000000; i = RecordInx[0]; RecordX[i] = CarX; RecordY[i] = CarY; RecordDir[i] = CarDir; i = (i + 1) & (RECORDMAX-1); if ((i != RecordInx[1]) && (i != RecordInx[0])) // check buffer overflow { RecordInx[0] = i; } // shift ghosts if (RecordInx[1] >= 0) { RecordInx[1] = (RecordInx[1] + 1) & (RECORDMAX-1); } if (RecordInx[2] >= 0) { RecordInx[2] = (RecordInx[2] + 1) & (RECORDMAX-1); } } // display info InfoRow(); // some delay sleep_ms(20); /* // check tile coordinates (visible as blinking tile under car) TileMap[CurTile] ^= 1; sleep_ms(100); TileMap[CurTile] ^= 1; sleep_ms(100); */ } }