frogger.ino 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. #include <SPI.h>
  2. #include <GD.h>
  3. // Background http://gameduino.com/results/5f30d40b/
  4. // Sprites http://gameduino.com/results/1d2bd1ca/
  5. #include "froggerbg.h"
  6. #include "sprite.h"
  7. #define CONTROL_LEFT 1
  8. #define CONTROL_RIGHT 2
  9. #define CONTROL_UP 4
  10. #define CONTROL_DOWN 8
  11. class Controller {
  12. public:
  13. void begin() {
  14. byte i;
  15. for (i = 3; i < 7; i++) {
  16. pinMode(i, INPUT);
  17. digitalWrite(i, HIGH);
  18. }
  19. prev = 0;
  20. }
  21. byte read() {
  22. byte r = 0;
  23. if (!digitalRead(5))
  24. r |= CONTROL_DOWN;
  25. if (!digitalRead(4))
  26. r |= CONTROL_UP;
  27. if (!digitalRead(6))
  28. r |= CONTROL_LEFT;
  29. if (!digitalRead(3))
  30. r |= CONTROL_RIGHT;
  31. byte edge = r & ~prev;
  32. prev = r;
  33. return edge;
  34. }
  35. private:
  36. byte prev;
  37. };
  38. static Controller Control;
  39. #define BG_BLUE 0
  40. #define BG_ZERO 12
  41. #define BG_BLACK 34
  42. #define BG_LIFE 35
  43. // JCB-FROG3-ATXY0
  44. static uint16_t atxy(byte x, byte y)
  45. {
  46. return RAM_PIC + 64 * y + (x + 11);
  47. }
  48. // JCB-FROG3-ATXY1
  49. static void draw_score(uint16_t dst, long n)
  50. {
  51. GD.wr(dst + 0, BG_ZERO + (n / 10000) % 10); // ten-thousands
  52. GD.wr(dst + 1, BG_ZERO + (n / 1000) % 10); // thousands
  53. GD.wr(dst + 2, BG_ZERO + (n / 100) % 10); // hundreds
  54. GD.wr(dst + 3, BG_ZERO + (n / 10) % 10); // tens
  55. GD.wr(dst + 4, BG_ZERO + n % 10); // ones
  56. }
  57. // Game variables
  58. static unsigned int t;
  59. // JCB-FROG3-VARSA
  60. static int frogx, frogy; // screen position
  61. static int leaping; // 0 means not leaping, 1-8 animates the leap
  62. static int frogdir; // while leaping, which direction is the leap?
  63. static int frogface; // which way is the frog facing, as a sprite ROT field
  64. static int dying; // 0 means not dying, 1-64 animation counter
  65. // JCB-FROG3-VARSB
  66. static long score;
  67. static long hiscore;
  68. static byte lives;
  69. static byte done[5];
  70. static byte homes[5] = { 24, 72, 120, 168, 216 };
  71. // JCB-FROG3-FROGSTART-0
  72. void frog_start()
  73. {
  74. frogx = 120;
  75. frogy = 232;
  76. leaping = 0;
  77. frogdir = 0;
  78. frogface = 0;
  79. dying = 0;
  80. }
  81. // JCB-FROG3-FROGSTART-1
  82. void level_start()
  83. {
  84. for (byte i = 0; i < 5; i++)
  85. done[i] = 0;
  86. GD.begin();
  87. delay(500);
  88. GD.copy(RAM_CHR, froggerbg_chr, sizeof(froggerbg_chr));
  89. GD.copy(RAM_PAL, froggerbg_pal, sizeof(froggerbg_pal));
  90. GD.fill(RAM_PIC, BG_BLACK, 4096);
  91. for (byte y = 0; y < 32; y++)
  92. GD.copy(atxy(0, y), froggerbg_pic + y * 28, 28);
  93. GD.fill(1 * 64 + 11, BG_BLUE, 28);
  94. // JCB-FROG3-CURTAINS0
  95. byte i = 255;
  96. for (int y = 0; y < 256; y += 16) {
  97. GD.sprite(i--, 72, y, 63, 0);
  98. GD.sprite(i--, 72 + 240, y, 63, 0);
  99. GD.sprite(i--, 72 + 256, y, 63, 0);
  100. }
  101. // JCB-FROG3-CURTAINS1
  102. GD.copy(PALETTE16A, sprite_sprpal, sizeof(sprite_sprpal));
  103. GD.uncompress(RAM_SPRIMG, sprite_sprimg);
  104. }
  105. void game_start()
  106. {
  107. lives = 4;
  108. score = 0;
  109. }
  110. void setup()
  111. {
  112. Serial.begin(1000000); // JCB
  113. Control.begin();
  114. game_start();
  115. level_start();
  116. frog_start();
  117. }
  118. // JCB-FROG3-SPRITE0
  119. static void sprite(byte x, byte y, byte anim, byte rot = 0)
  120. {
  121. draw_sprite(x + 80, y, anim, rot);
  122. }
  123. // JCB-FROG3-SPRITE1
  124. static void turtle3(byte x, byte y)
  125. {
  126. byte anim = 50 + ((t / 32) % 3);
  127. sprite(x, y, anim);
  128. sprite(x + 16, y, anim);
  129. sprite(x + 32, y, anim);
  130. }
  131. static void turtle2(byte x, byte y)
  132. {
  133. byte anim = 50 + ((t / 32) % 3);
  134. sprite(x, y, anim);
  135. sprite(x + 16, y, anim);
  136. }
  137. void log1(byte x, byte y)
  138. {
  139. sprite(x, y, 86);
  140. sprite(x + 16, y, 87);
  141. sprite(x + 32, y, 88);
  142. }
  143. void log(byte length, byte x, byte y)
  144. {
  145. sprite(x, y, 86);
  146. while (length--) {
  147. x += 16;
  148. sprite(x, y, 87);
  149. }
  150. sprite(x + 16, y, 88);
  151. }
  152. static int riverat(byte y, uint16_t tt)
  153. {
  154. switch (y) {
  155. case 120: return -tt;
  156. case 104: return tt;
  157. case 88: return 5 * tt / 4;
  158. case 72: return -tt / 2;
  159. case 56: return tt / 2;
  160. }
  161. }
  162. // JCB-FROG3-SND0
  163. // midi frequency table
  164. static const PROGMEM uint16_t midifreq[128] = {
  165. 32,34,36,38,41,43,46,48,51,55,58,61,65,69,73,77,82,87,92,97,103,110,116,123,130,138,146,155,164,174,184,195,207,220,233,246,261,277,293,311,329,349,369,391,415,440,466,493,523,554,587,622,659,698,739,783,830,880,932,987,1046,1108,1174,1244,1318,1396,1479,1567,1661,1760,1864,1975,2093,2217,2349,2489,2637,2793,2959,3135,3322,3520,3729,3951,4186,4434,4698,4978,5274,5587,5919,6271,6644,7040,7458,7902,8372,8869,9397,9956,10548,11175,11839,12543,13289,14080,14917,15804,16744,17739,18794,19912,21096,22350,23679,25087,26579,28160,29834,31608,33488,35479,37589,39824,42192,44701,47359,50175
  166. };
  167. #define MIDI(n) pgm_read_word(midifreq + (n))
  168. static void squarewave(uint16_t freq, byte amp)
  169. {
  170. GD.voice(0, 0, freq, amp, amp);
  171. GD.voice(1, 0, 3 * freq, amp/3, amp/3);
  172. GD.voice(2, 0, 5 * freq, amp/5, amp/5);
  173. GD.voice(3, 0, 7 * freq, amp/7, amp/7);
  174. GD.voice(4, 0, 9 * freq, amp/9, amp/9);
  175. GD.voice(5, 0, 11 * freq, amp/11, amp/11);
  176. }
  177. static void sound()
  178. {
  179. byte note;
  180. if (dying) {
  181. note = 84 - (dying / 2);
  182. squarewave(MIDI(note), 100);
  183. } else if (leaping) {
  184. if (leaping & 1)
  185. note = 60 + leaping;
  186. else
  187. note = 72 + leaping;
  188. squarewave(MIDI(note), 100);
  189. } else {
  190. squarewave(0, 0); // silence
  191. }
  192. }
  193. // JCB-FROG3-SND1
  194. void loop()
  195. {
  196. GD.__wstartspr(0);
  197. // Completed homes
  198. for (byte i = 0; i < 5; i++) {
  199. if (done[i])
  200. sprite(homes[i], 40, 63);
  201. }
  202. // Yellow cars
  203. sprite(-t, 216, 3);
  204. sprite(-t + 128, 216, 3);
  205. // Dozers
  206. sprite(t, 200, 4);
  207. sprite(t + 50, 200, 4);
  208. sprite(t + 150, 200, 4);
  209. // Purple cars
  210. sprite(-t, 184, 7);
  211. sprite(-t + 75, 184, 7);
  212. sprite(-t + 150, 184, 7);
  213. // Green and white racecars
  214. sprite(2 * t, 168, 8);
  215. // Trucks
  216. sprite(-t/2, 152, 5);
  217. sprite(-t/2 + 16, 152, 6);
  218. sprite(-t/2 + 100, 152, 5);
  219. sprite(-t/2 + 116, 152, 6);
  220. // Turtles
  221. for (int i = 0; i < 256; i += 64)
  222. turtle3(riverat(120, t) + i, 120);
  223. // Short logs
  224. for (int i = 0; i < 240; i += 80)
  225. log(1, riverat(104, t) + i, 104);
  226. // Long logs
  227. for (int i = 0; i < 256; i += 128)
  228. log(5, riverat(88, t) + i, 88);
  229. // Turtles again, but slower
  230. for (int i = 0; i < 250; i += 50)
  231. turtle2(riverat(72, t) + i, 72);
  232. // Top logs
  233. for (int i = 0; i < 210; i += 70)
  234. log(2, riverat(56, t) + i, 56);
  235. // JCB-FROG3-DRAW-0
  236. // The frog himself, or his death animation
  237. byte frogspr = GD.spr; // record which sprite slot frog got, for collision check below
  238. if (!dying) {
  239. static byte frog_anim[] = {2, 1, 0, 0, 2};
  240. sprite(frogx, frogy, frog_anim[leaping / 2], frogface);
  241. } else {
  242. static byte die_anim[] = {31, 32, 33, 30};
  243. sprite(frogx, frogy, die_anim[dying / 16], frogface);
  244. }
  245. // JCB-FROG3-DRAW-1
  246. GD.__end();
  247. // GD.screenshot(t); // JCB
  248. t++;
  249. // JCB-FROG3-E
  250. // player control. If button pressed, start the 'leaping' counter
  251. byte con = Control.read();
  252. if (!dying && (leaping == 0) && con) {
  253. frogdir = con;
  254. leaping = 1;
  255. score += 10;
  256. } else if (leaping > 0) {
  257. if (leaping <= 8) {
  258. if (frogdir == CONTROL_LEFT) {
  259. frogx -= 2;
  260. frogface = 3;
  261. } if (frogdir == CONTROL_RIGHT) {
  262. frogx += 2;
  263. frogface = 5;
  264. } if (frogdir == CONTROL_UP) {
  265. frogy -= 2;
  266. frogface = 0;
  267. } if (frogdir == CONTROL_DOWN) {
  268. frogy += 2;
  269. frogface = 6;
  270. }
  271. leaping++;
  272. } else {
  273. leaping = 0;
  274. }
  275. }
  276. // JCB-FROG3-F
  277. // JCB-FROG3-COLLIDE-0
  278. GD.waitvblank();
  279. byte touching = (GD.rd(COLLISION + frogspr) != 0xff);
  280. // JCB-FROG3-COLLIDE-1
  281. // JCB-FROG3-G
  282. if (dying) {
  283. if (++dying == 64) {
  284. if (--lives == 0) {
  285. game_start();
  286. level_start();
  287. }
  288. frog_start();
  289. }
  290. }
  291. else if (frogx < 8 || frogx > 224) {
  292. dying = 1;
  293. }
  294. // JCB-FROG3-H
  295. // JCB-FROG3-I
  296. else if (frogy >= 136) { // road section
  297. // if touching something, frog dies
  298. if (touching)
  299. dying = 1;
  300. }
  301. // JCB-FROG3-J
  302. else if (frogy > 40) { // river section
  303. if (!leaping) {
  304. // if touching something, frog is safe
  305. if (touching) {
  306. // move frog according to lane speed
  307. int oldx = riverat(frogy, t - 1);
  308. int newx = riverat(frogy, t);
  309. int river_velocity = newx - oldx;
  310. frogx += river_velocity;
  311. } else {
  312. dying = 1;
  313. }
  314. }
  315. }
  316. else
  317. // JCB-FROG3-K
  318. { // riverbank section
  319. if (!leaping) {
  320. byte landed = 0;
  321. for (byte i = 0; i < 5; i ++) {
  322. if (!done[i] && abs(homes[i] - frogx) < 4) {
  323. done[i] = 1;
  324. landed = 1;
  325. score += 10;
  326. }
  327. }
  328. if (landed) {
  329. if (done[0] && done[1] && done[2] && done[3] && done[4])
  330. level_start();
  331. frog_start();
  332. } else // if frog did not land in a home, die!
  333. dying = 1;
  334. }
  335. }
  336. // JCB-FROG3-L
  337. sound();
  338. hiscore = max(score, hiscore);
  339. draw_score(atxy(3, 1), score);
  340. draw_score(atxy(11, 1), hiscore);
  341. for (byte i = 0; i < 16; i++)
  342. GD.wr(atxy(i, 30), (i < lives) ? BG_LIFE : BG_BLACK);
  343. }