frogger.ino 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. #include <EEPROM.h>
  2. #include <SPI.h>
  3. #include <GD2.h>
  4. #include "frogger_assets.h"
  5. #define CONTROL_LEFT 1
  6. #define CONTROL_RIGHT 2
  7. #define CONTROL_UP 4
  8. #define CONTROL_DOWN 8
  9. class Controller {
  10. public:
  11. void begin() {
  12. prev = 0;
  13. }
  14. byte read() {
  15. byte r = GD.inputs.tag & (CONTROL_LEFT | CONTROL_RIGHT | CONTROL_UP | CONTROL_DOWN);
  16. byte edge = r & ~prev;
  17. prev = r;
  18. return edge;
  19. }
  20. private:
  21. byte prev;
  22. };
  23. static Controller Control;
  24. static void draw_score(byte x, byte y, long n)
  25. {
  26. GD.cmd_number(8 * x, 8 * y, FONT_HANDLE, 5, n);
  27. }
  28. // Game variables
  29. static unsigned int t;
  30. static int frogx, frogy; // screen position
  31. static int leaping; // 0 means not leaping, 1-8 animates the leap
  32. static int frogdir; // while leaping, which direction is the leap?
  33. static int frogface; // which way is the frog facing, in furmans for CMD_ROTATE
  34. static int dying; // 0 means not dying, 1-64 animation counter
  35. static long score;
  36. static long hiscore;
  37. static byte lives;
  38. static byte done[5];
  39. static byte homes[5] = { 24, 72, 120, 168, 216 };
  40. static int ti;
  41. void frog_start()
  42. {
  43. frogx = 120;
  44. frogy = 232;
  45. leaping = 0;
  46. frogdir = 0;
  47. frogface = 0x0000;
  48. dying = 0;
  49. ti = 120 << 7;
  50. }
  51. void level_start()
  52. {
  53. for (byte i = 0; i < 5; i++)
  54. done[i] = 0;
  55. }
  56. void game_start()
  57. {
  58. lives = 4;
  59. score = 0;
  60. }
  61. void game_setup()
  62. {
  63. Control.begin();
  64. game_start();
  65. level_start();
  66. frog_start();
  67. }
  68. static void sprite(byte x, byte y, byte anim, uint16_t rot = 0xffff)
  69. {
  70. x -= 16;
  71. y -= 8;
  72. if (rot != 0xffff) {
  73. GD.cmd_loadidentity();
  74. GD.cmd_translate(F16(8),F16(8));
  75. GD.cmd_rotate(rot);
  76. GD.cmd_translate(F16(-8),F16(-8));
  77. GD.cmd_setmatrix();
  78. }
  79. if (x > 224) {
  80. GD.Cell(anim);
  81. GD.Vertex2f(16 * (x - 256), 16 * y);
  82. } else {
  83. GD.Vertex2ii(x, y, SPRITES_HANDLE, anim);
  84. }
  85. }
  86. static void turtle3(byte x, byte y)
  87. {
  88. byte anim = 50 + ((t / 32) % 3);
  89. sprite(x, y, anim);
  90. sprite(x + 16, y, anim);
  91. sprite(x + 32, y, anim);
  92. }
  93. static void turtle2(byte x, byte y)
  94. {
  95. byte anim = 50 + ((t / 32) % 3);
  96. sprite(x, y, anim);
  97. sprite(x + 16, y, anim);
  98. }
  99. void log1(byte x, byte y)
  100. {
  101. sprite(x, y, 86);
  102. sprite(x + 16, y, 87);
  103. sprite(x + 32, y, 88);
  104. }
  105. void log(byte length, byte x, byte y)
  106. {
  107. sprite(x, y, 86);
  108. while (length--) {
  109. x += 16;
  110. sprite(x, y, 87);
  111. }
  112. sprite(x + 16, y, 88);
  113. }
  114. static int riverat(byte y, uint16_t tt)
  115. {
  116. switch (y) {
  117. case 120: return -tt;
  118. case 104: return tt;
  119. case 88: return 5 * tt / 4;
  120. case 72: return -tt / 2;
  121. case 56: return tt / 2;
  122. }
  123. }
  124. static void squarewave(uint16_t freq, byte amp)
  125. {
  126. GD.wr(REG_VOL_SOUND, amp);
  127. GD.wr16(REG_SOUND, (freq << 8) | 0x01);
  128. GD.wr(REG_PLAY, 1);
  129. }
  130. static void sound()
  131. {
  132. byte note;
  133. if (dying) {
  134. note = 84 - (dying / 2);
  135. squarewave(note, 100);
  136. } else if (leaping) {
  137. if (leaping & 1)
  138. note = 60 + leaping;
  139. else
  140. note = 72 + leaping;
  141. squarewave(note, 100);
  142. } else {
  143. squarewave(0, 0); // silence
  144. }
  145. }
  146. static void rotate_around(int x, int y, int a)
  147. {
  148. GD.cmd_loadidentity();
  149. GD.cmd_translate(F16(x),F16(y));
  150. GD.cmd_rotate(a);
  151. GD.cmd_translate(F16(-x),F16(-y));
  152. GD.cmd_setmatrix();
  153. }
  154. void game_over()
  155. {
  156. GD.wr(REG_VOL_SOUND, 0);
  157. for (byte i = 0; i < 60; i++) {
  158. GD.Clear();
  159. // Draw "F R O G G E R" using the sprites 90-94
  160. int x = 160, y = 50, space = 24;
  161. GD.Begin(BITMAPS);
  162. GD.Vertex2ii(x, y, SPRITES_HANDLE, 90); x += space; // F
  163. GD.Vertex2ii(x, y, SPRITES_HANDLE, 91); x += space; // R
  164. GD.Vertex2ii(x, y, SPRITES_HANDLE, 92); x += space; // O
  165. GD.Vertex2ii(x, y, SPRITES_HANDLE, 93); x += space; // G
  166. GD.Vertex2ii(x, y, SPRITES_HANDLE, 93); x += space; // G
  167. GD.Vertex2ii(x, y, SPRITES_HANDLE, 94); x += space; // E
  168. GD.Vertex2ii(x, y, SPRITES_HANDLE, 91); x += space; // R
  169. GD.cmd_text(240, 136, FONT_HANDLE, OPT_CENTER, "GAME OVER");
  170. if (i == 59)
  171. GD.cmd_text(240, 200, FONT_HANDLE, OPT_CENTER, "PRESS TO PLAY");
  172. GD.swap();
  173. }
  174. while (GD.inputs.x == -32768)
  175. GD.get_inputs();
  176. while (GD.inputs.x != -32768)
  177. GD.get_inputs();
  178. }
  179. void loop()
  180. {
  181. static uint32_t counter;
  182. static int prevt;
  183. GD.Clear();
  184. GD.Tag(1);
  185. GD.BitmapHandle(SPRITES_HANDLE);
  186. GD.SaveContext();
  187. GD.ScissorSize(224, 256);
  188. GD.Begin(BITMAPS);
  189. GD.Vertex2ii(0, 0, BACKGROUND_HANDLE, 0); // Background bitmap
  190. GD.wr(REG_TAG_X, frogx - 8);
  191. GD.wr(REG_TAG_Y, frogy);
  192. GD.Tag(2);
  193. GD.AlphaFunc(GREATER, 0); // on road, don't tag transparent pixels
  194. // Completed homes
  195. for (byte i = 0; i < 5; i++) {
  196. if (done[i])
  197. sprite(homes[i], 40, 63);
  198. }
  199. // Yellow cars
  200. sprite(-t, 216, 3);
  201. sprite(-t + 128, 216, 3);
  202. // Dozers
  203. sprite(t, 200, 4);
  204. sprite(t + 50, 200, 4);
  205. sprite(t + 150, 200, 4);
  206. // Purple cars
  207. sprite(-t, 184, 7);
  208. sprite(-t + 75, 184, 7);
  209. sprite(-t + 150, 184, 7);
  210. // Green and white racecars
  211. sprite(2 * t, 168, 8);
  212. // Trucks
  213. sprite(-t/2, 152, 5);
  214. sprite(-t/2 + 16, 152, 6);
  215. sprite(-t/2 + 100, 152, 5);
  216. sprite(-t/2 + 116, 152, 6);
  217. GD.AlphaFunc(GREATER, 0); // on river, tag transparent pixels
  218. // Turtles
  219. for (int i = 0; i < 256; i += 64)
  220. turtle3(riverat(120, t) + i, 120);
  221. // Short logs
  222. for (int i = 0; i < 240; i += 80)
  223. log(1, riverat(104, t) + i, 104);
  224. // Long logs
  225. for (int i = 0; i < 256; i += 128)
  226. log(5, riverat(88, t) + i, 88);
  227. // Turtles again, but slower
  228. for (int i = 0; i < 250; i += 50)
  229. turtle2(riverat(72, t) + i, 72);
  230. // Top logs
  231. for (int i = 0; i < 210; i += 70)
  232. log(2, riverat(56, t) + i, 56);
  233. // The frog himself, or his death animation
  234. GD.TagMask(0);
  235. if (!dying) {
  236. static byte frog_anim[] = {2, 1, 0, 0, 2};
  237. sprite(frogx, frogy, frog_anim[leaping / 2], frogface);
  238. } else {
  239. static byte die_anim[] = {31, 32, 33, 30};
  240. sprite(frogx, frogy, die_anim[dying / 16], frogface);
  241. }
  242. prevt = t;
  243. t++;
  244. ti = max(0, ti - 1);
  245. if ((ti == 0) && (dying == 0))
  246. dying = 1;
  247. // Draw 'time remaining' by clearing a black rectangle
  248. {
  249. byte tw = 120 - (ti >> 7);
  250. byte tx = 72;
  251. GD.SaveContext();
  252. GD.ScissorXY(tx, 248);
  253. GD.ScissorSize(tw, 8);
  254. GD.Clear();
  255. GD.RestoreContext();
  256. }
  257. GD.TagMask(1);
  258. // player control. If button pressed, start the 'leaping' counter
  259. byte con = Control.read();
  260. if (!dying && (leaping == 0) && con) {
  261. frogdir = con;
  262. leaping = 1;
  263. score += 10;
  264. } else if (leaping > 0) {
  265. if (leaping <= 8) {
  266. if (frogdir == CONTROL_LEFT) {
  267. frogx -= 2;
  268. frogface = 0xc000;
  269. } if (frogdir == CONTROL_RIGHT) {
  270. frogx += 2;
  271. frogface = 0x4000;
  272. } if (frogdir == CONTROL_UP) {
  273. frogy -= 2;
  274. frogface = 0x0000;
  275. } if (frogdir == CONTROL_DOWN) {
  276. frogy += 2;
  277. frogface = 0x8000;
  278. }
  279. leaping++;
  280. } else {
  281. leaping = 0;
  282. }
  283. }
  284. GD.RestoreContext();
  285. GD.SaveContext();
  286. GD.Begin(BITMAPS);
  287. #define PADX(x) (480 + (x - 3) * 48)
  288. #define PADY(y) (272 + (y - 3) * 48)
  289. GD.Tag(CONTROL_RIGHT);
  290. GD.Vertex2ii(PADX(2), PADY(1), ARROW_HANDLE, 0);
  291. rotate_around(24, 24, 3 * 0x4000);
  292. GD.Tag(CONTROL_UP);
  293. GD.Vertex2ii(PADX(1), PADY(0), ARROW_HANDLE, 0);
  294. rotate_around(24, 24, 2 * 0x4000);
  295. GD.Tag(CONTROL_LEFT);
  296. GD.Vertex2ii(PADX(0), PADY(1), ARROW_HANDLE, 0);
  297. rotate_around(24, 24, 1 * 0x4000);
  298. GD.Tag(CONTROL_DOWN);
  299. GD.Vertex2ii(PADX(1), PADY(2), ARROW_HANDLE, 0);
  300. GD.RestoreContext();
  301. GD.ColorRGB(255, 85, 0);
  302. draw_score(3, 1, score);
  303. draw_score(11, 1, hiscore);
  304. GD.ColorRGB(255, 255, 255);
  305. for (byte i = 0; i < lives; i++)
  306. GD.Vertex2ii(8 * i, 30 * 8, LIFE_HANDLE, 0);
  307. // for (byte i = 0; i < 16; i++)
  308. // GD.wr(atxy(i, 30), (i < lives) ? BG_LIFE : BG_BLACK);
  309. GD.swap();
  310. GD.get_inputs();
  311. byte tag = GD.rd(REG_TAG);
  312. byte touching = (tag == 2);
  313. if (dying) {
  314. if (++dying == 64) {
  315. if (--lives == 0 || ti == 0) {
  316. game_over();
  317. game_start();
  318. level_start();
  319. }
  320. frog_start();
  321. }
  322. }
  323. else if (frogx < 8 || frogx > 224) {
  324. dying = 1;
  325. }
  326. else if (frogy >= 136) { // road section
  327. // if touching something, frog dies
  328. if (tag == 2)
  329. dying = 1;
  330. }
  331. else if (frogy > 40) { // river section
  332. if (!leaping) {
  333. // if touching something, frog is safe
  334. if (tag != 1) {
  335. // move frog according to lane speed
  336. int oldx = riverat(frogy, prevt);
  337. int newx = riverat(frogy, t);
  338. int river_velocity = newx - oldx;
  339. frogx += river_velocity;
  340. } else {
  341. dying = 1;
  342. }
  343. }
  344. }
  345. else
  346. { // riverbank section
  347. if (!leaping) {
  348. byte landed = 0;
  349. for (byte i = 0; i < 5; i ++) {
  350. if (!done[i] && abs(homes[i] - frogx) < 4) {
  351. done[i] = 1;
  352. landed = 1;
  353. score += 10;
  354. }
  355. }
  356. if (landed) {
  357. if (done[0] && done[1] && done[2] && done[3] && done[4])
  358. level_start();
  359. frog_start();
  360. } else // if frog did not land in a home, die!
  361. dying = 1;
  362. }
  363. }
  364. sound();
  365. hiscore = max(score, hiscore);
  366. }
  367. void setup()
  368. {
  369. Serial.begin(1000000); // JCB
  370. GD.begin(~GD_STORAGE);
  371. LOAD_ASSETS();
  372. game_setup();
  373. }