Manic miner game for the Gameduino

Dependencies:   Gameduino mbed

Committer:
TheChrisyd
Date:
Fri Dec 21 14:00:03 2012 +0000
Revision:
1:e047847f1cda
Parent:
0:a2d36977aec3
sound on title screen not working, otherwise ok

Who changed what in which revision?

UserRevisionLine numberNew contents of line
TheChrisyd 0:a2d36977aec3 1 #include "mbed.h"
TheChrisyd 0:a2d36977aec3 2 #include "GD.h"
TheChrisyd 0:a2d36977aec3 3 #include "arduino.h"
TheChrisyd 1:e047847f1cda 4 #include "shield.h"
TheChrisyd 0:a2d36977aec3 5
TheChrisyd 1:e047847f1cda 6 GDClass GD(ARD_MOSI, ARD_MISO, ARD_SCK, ARD_D9, USBTX, USBRX) ;
TheChrisyd 0:a2d36977aec3 7 SPI spi(ARD_MOSI, ARD_MISO, ARD_SCK);// mosi, miso, sclk
TheChrisyd 0:a2d36977aec3 8 DigitalOut cs(ARD_D9);
TheChrisyd 0:a2d36977aec3 9 Serial pc(USBTX, USBRX);
TheChrisyd 0:a2d36977aec3 10
TheChrisyd 0:a2d36977aec3 11 #define CHEAT_INVINCIBLE 0 // means Willy unaffected by nasties
TheChrisyd 0:a2d36977aec3 12 #define START_LEVEL 0 // level to start on, 0-18
TheChrisyd 0:a2d36977aec3 13 #define CHEAT_OPEN_PORTAL 0 // Portal always open
TheChrisyd 0:a2d36977aec3 14
TheChrisyd 0:a2d36977aec3 15 // Game has three controls: LEFT, RIGHT, JUMP. You can map them
TheChrisyd 0:a2d36977aec3 16 // to any pins by changing the definitions of PIN_L, PIN_R, PIN_J
TheChrisyd 0:a2d36977aec3 17 // below.
TheChrisyd 0:a2d36977aec3 18
TheChrisyd 0:a2d36977aec3 19 #if 1 // SPARKFUN_JOYSTICK
TheChrisyd 1:e047847f1cda 20 #define PIN_L ARD_A0
TheChrisyd 1:e047847f1cda 21 #define PIN_R ARD_A1
TheChrisyd 1:e047847f1cda 22 #define PIN_J ARD_D6
TheChrisyd 0:a2d36977aec3 23 #else
TheChrisyd 0:a2d36977aec3 24 #define PIN_L ARD_A2
TheChrisyd 0:a2d36977aec3 25 #define PIN_R ARD_A3
TheChrisyd 0:a2d36977aec3 26 #define PIN_J ARD_A5
TheChrisyd 0:a2d36977aec3 27 #endif
TheChrisyd 0:a2d36977aec3 28
TheChrisyd 0:a2d36977aec3 29 #define CONTROL_LEFT 1
TheChrisyd 0:a2d36977aec3 30 #define CONTROL_RIGHT 2
TheChrisyd 0:a2d36977aec3 31 #define CONTROL_JUMP 4
TheChrisyd 0:a2d36977aec3 32
TheChrisyd 1:e047847f1cda 33 DigitalIn jump(ARD_D4);
TheChrisyd 1:e047847f1cda 34 DigitalIn jumpa(ARD_D5);
TheChrisyd 1:e047847f1cda 35 DigitalIn jumpb(ARD_D6);
TheChrisyd 1:e047847f1cda 36 DigitalIn jumpc(ARD_D7);
TheChrisyd 1:e047847f1cda 37 DigitalIn jumpd(ARD_A4);
TheChrisyd 1:e047847f1cda 38 DigitalIn left(ARD_A1);
TheChrisyd 1:e047847f1cda 39 DigitalIn right(ARD_A0);
TheChrisyd 0:a2d36977aec3 40
TheChrisyd 0:a2d36977aec3 41 static byte setup_control()
TheChrisyd 0:a2d36977aec3 42 {
TheChrisyd 1:e047847f1cda 43 jump.mode(PullUp);
TheChrisyd 1:e047847f1cda 44 jumpa.mode(PullUp);
TheChrisyd 1:e047847f1cda 45 jumpb.mode(PullUp);
TheChrisyd 1:e047847f1cda 46 jumpc.mode(PullUp);
TheChrisyd 1:e047847f1cda 47 jumpd.mode(PullUp);
TheChrisyd 1:e047847f1cda 48 left.mode(PullUp);
TheChrisyd 1:e047847f1cda 49 right.mode(PullUp);
TheChrisyd 0:a2d36977aec3 50 }
TheChrisyd 0:a2d36977aec3 51
TheChrisyd 0:a2d36977aec3 52 static byte control()
TheChrisyd 0:a2d36977aec3 53 {
TheChrisyd 0:a2d36977aec3 54 byte r = 0;
TheChrisyd 1:e047847f1cda 55 //if (jump)
TheChrisyd 1:e047847f1cda 56 // r |= CONTROL_JUMP;
TheChrisyd 1:e047847f1cda 57 if (!jumpa)
TheChrisyd 1:e047847f1cda 58 r |= CONTROL_JUMP;
TheChrisyd 1:e047847f1cda 59 if (!jumpb)
TheChrisyd 0:a2d36977aec3 60 r |= CONTROL_JUMP;
TheChrisyd 1:e047847f1cda 61 if (!jumpc)
TheChrisyd 1:e047847f1cda 62 r |= CONTROL_JUMP;
TheChrisyd 1:e047847f1cda 63 if (!jumpd)
TheChrisyd 1:e047847f1cda 64 r |= CONTROL_JUMP;
TheChrisyd 1:e047847f1cda 65 if (!left)
TheChrisyd 0:a2d36977aec3 66 r |= CONTROL_LEFT;
TheChrisyd 1:e047847f1cda 67 if (!right)
TheChrisyd 0:a2d36977aec3 68 r |= CONTROL_RIGHT;
TheChrisyd 0:a2d36977aec3 69 return r;
TheChrisyd 0:a2d36977aec3 70 }
TheChrisyd 0:a2d36977aec3 71
TheChrisyd 0:a2d36977aec3 72 #define BLACK RGB(0,0,0)
TheChrisyd 0:a2d36977aec3 73 #define RED RGB(255,0,0)
TheChrisyd 0:a2d36977aec3 74 #define YELLOW RGB(255,255,0)
TheChrisyd 0:a2d36977aec3 75 #define CYAN RGB(0,255,255)
TheChrisyd 0:a2d36977aec3 76 #define GREEN RGB(0,255,0)
TheChrisyd 0:a2d36977aec3 77 #define MAGENTA RGB(255,0,255)
TheChrisyd 0:a2d36977aec3 78 #define WHITE RGB(255,255,255)
TheChrisyd 0:a2d36977aec3 79
TheChrisyd 0:a2d36977aec3 80 #define CHR_BORDER 31 // hw char used for all of the border
TheChrisyd 0:a2d36977aec3 81 #define CHR_AIR 128 // AIR line
TheChrisyd 0:a2d36977aec3 82
TheChrisyd 0:a2d36977aec3 83 // assigned sprite images and sprite numbers
TheChrisyd 0:a2d36977aec3 84 #define IMG_ITEM 0 // items 0-4
TheChrisyd 0:a2d36977aec3 85 #define IMG_PORTAL 5 // room exit
TheChrisyd 0:a2d36977aec3 86 #define IMG_SWITCH1 6 // the switches for the Kong Beast levels
TheChrisyd 0:a2d36977aec3 87 #define IMG_SWITCH2 7
TheChrisyd 0:a2d36977aec3 88 #define IMG_GUARD 8 // All guardians, 8 max
TheChrisyd 0:a2d36977aec3 89 #define IMG_NASTY1 16 // Nasty blocks
TheChrisyd 0:a2d36977aec3 90 #define IMG_NASTY2 17
TheChrisyd 0:a2d36977aec3 91 #define IMG_WILLYC 56
TheChrisyd 0:a2d36977aec3 92 #define IMG_WILLY 63
TheChrisyd 0:a2d36977aec3 93
TheChrisyd 0:a2d36977aec3 94 #define ELEM_AIR 0
TheChrisyd 0:a2d36977aec3 95 #define ELEM_FLOOR 1
TheChrisyd 0:a2d36977aec3 96 #define ELEM_CRUMBLE 2
TheChrisyd 0:a2d36977aec3 97 #define ELEM_WALL 3
TheChrisyd 0:a2d36977aec3 98 #define ELEM_CONVEYOR 4
TheChrisyd 0:a2d36977aec3 99 #define ELEM_NASTY1 5
TheChrisyd 0:a2d36977aec3 100 #define ELEM_NASTY2 6
TheChrisyd 0:a2d36977aec3 101
TheChrisyd 0:a2d36977aec3 102 struct level {
TheChrisyd 0:a2d36977aec3 103 byte name[32];
TheChrisyd 0:a2d36977aec3 104 prog_uchar *background;
TheChrisyd 0:a2d36977aec3 105 byte border;
TheChrisyd 0:a2d36977aec3 106 prog_uchar bgchars[64];
TheChrisyd 0:a2d36977aec3 107 byte bgattr[8];
TheChrisyd 0:a2d36977aec3 108 byte item[8];
TheChrisyd 0:a2d36977aec3 109 struct { byte x, y; } items[5];
TheChrisyd 0:a2d36977aec3 110 byte portal[32];
TheChrisyd 0:a2d36977aec3 111 byte air;
TheChrisyd 0:a2d36977aec3 112 byte conveyordir;
TheChrisyd 0:a2d36977aec3 113 byte portalattr, portalx, portaly;
TheChrisyd 0:a2d36977aec3 114 byte guardian[8 * 32];
TheChrisyd 0:a2d36977aec3 115 struct { byte a, x, y, d, x0, x1; } hguard[8];
TheChrisyd 0:a2d36977aec3 116 byte wx, wy, wd, wf;
TheChrisyd 0:a2d36977aec3 117 byte bidir;
TheChrisyd 0:a2d36977aec3 118 };
TheChrisyd 0:a2d36977aec3 119 #include "manicminer.h"
TheChrisyd 0:a2d36977aec3 120 #include "spectrum.h"
TheChrisyd 0:a2d36977aec3 121 #include "spectrum_data.h" // title screen
TheChrisyd 0:a2d36977aec3 122
TheChrisyd 0:a2d36977aec3 123 #define COLOR(i) (pgm_read_word_near(specpal + (2 * (i))))
TheChrisyd 0:a2d36977aec3 124 #define BRIGHT(x) (((x) & 64) >> 3)
TheChrisyd 0:a2d36977aec3 125 #define PAPER(a) ((((a) >> 3) & 7) | BRIGHT(a))
TheChrisyd 0:a2d36977aec3 126 #define INK(a) (((a) & 7) | BRIGHT(a))
TheChrisyd 0:a2d36977aec3 127
TheChrisyd 0:a2d36977aec3 128 // screen coordinate - Spectrum 256x192 screen is centered in Gameduino screen
TheChrisyd 0:a2d36977aec3 129 static uint16_t atxy(byte x, byte y)
TheChrisyd 0:a2d36977aec3 130 {
TheChrisyd 0:a2d36977aec3 131 return RAM_PIC + 64 * (y + 6) + (x + 9);
TheChrisyd 0:a2d36977aec3 132 }
TheChrisyd 0:a2d36977aec3 133
TheChrisyd 0:a2d36977aec3 134 // unpack 8 monochrome pixels into Gameduino character RAM
TheChrisyd 0:a2d36977aec3 135 void unpack8(byte b)
TheChrisyd 0:a2d36977aec3 136 {
TheChrisyd 0:a2d36977aec3 137 static byte stretch[16] = {
TheChrisyd 0:a2d36977aec3 138 0x00, 0x03, 0x0c, 0x0f,
TheChrisyd 0:a2d36977aec3 139 0x30, 0x33, 0x3c, 0x3f,
TheChrisyd 0:a2d36977aec3 140 0xc0, 0xc3, 0xcc, 0xcf,
TheChrisyd 0:a2d36977aec3 141 0xf0, 0xf3, 0xfc, 0xff
TheChrisyd 0:a2d36977aec3 142 };
TheChrisyd 0:a2d36977aec3 143 spi.write(stretch[b >> 4]);
TheChrisyd 0:a2d36977aec3 144 spi.write(stretch[b & 15]);
TheChrisyd 0:a2d36977aec3 145 }
TheChrisyd 0:a2d36977aec3 146
TheChrisyd 0:a2d36977aec3 147 // unpack monochrome bitmap from flash to Gameduino character RAM at dst
TheChrisyd 0:a2d36977aec3 148 void unpack8xn(uint16_t dst, PROGMEM prog_uchar *src, uint16_t size)
TheChrisyd 0:a2d36977aec3 149 {
TheChrisyd 0:a2d36977aec3 150 GD.__wstart(dst);
TheChrisyd 0:a2d36977aec3 151 while (size--)
TheChrisyd 0:a2d36977aec3 152 unpack8(pgm_read_byte_near(src++));
TheChrisyd 0:a2d36977aec3 153 GD.__end();
TheChrisyd 0:a2d36977aec3 154 }
TheChrisyd 0:a2d36977aec3 155
TheChrisyd 0:a2d36977aec3 156 // Load 8x8 1-bit sprite from src into slot (0-63)
TheChrisyd 0:a2d36977aec3 157 // zero and one are the two colors
TheChrisyd 0:a2d36977aec3 158
TheChrisyd 0:a2d36977aec3 159 void send8(byte b, byte zero, byte one)
TheChrisyd 0:a2d36977aec3 160 {
TheChrisyd 0:a2d36977aec3 161 for (byte j = 8; j; j--) {
TheChrisyd 0:a2d36977aec3 162 spi.write((b & 0x80) ? one : zero);
TheChrisyd 0:a2d36977aec3 163 b <<= 1;
TheChrisyd 0:a2d36977aec3 164 }
TheChrisyd 0:a2d36977aec3 165 }
TheChrisyd 0:a2d36977aec3 166
TheChrisyd 0:a2d36977aec3 167 // load an 8x8 into a hw 256-color sprite, padding with transparent (color 255)
TheChrisyd 0:a2d36977aec3 168 void loadspr8(byte slot, PROGMEM prog_uchar *src, byte zero, byte one)
TheChrisyd 0:a2d36977aec3 169 {
TheChrisyd 0:a2d36977aec3 170 GD.__wstart(RAM_SPRIMG + (slot << 8));
TheChrisyd 0:a2d36977aec3 171 for (byte i = 8; i; i--) {
TheChrisyd 0:a2d36977aec3 172 send8(pgm_read_byte_near(src++), zero, one);
TheChrisyd 0:a2d36977aec3 173 send8(0, 255, 255);
TheChrisyd 0:a2d36977aec3 174 }
TheChrisyd 0:a2d36977aec3 175 for (byte i = 128; i; i--)
TheChrisyd 0:a2d36977aec3 176 spi.write(255);
TheChrisyd 0:a2d36977aec3 177 GD.__end();
TheChrisyd 0:a2d36977aec3 178 }
TheChrisyd 0:a2d36977aec3 179
TheChrisyd 0:a2d36977aec3 180 // load an 16x16 into a hw 256-color sprite
TheChrisyd 0:a2d36977aec3 181 void loadspr16(byte slot, PROGMEM prog_uchar *src, byte zero, byte one)
TheChrisyd 0:a2d36977aec3 182 {
TheChrisyd 0:a2d36977aec3 183 GD.__wstart(RAM_SPRIMG + (slot << 8));
TheChrisyd 0:a2d36977aec3 184 for (byte i = 32; i; i--)
TheChrisyd 0:a2d36977aec3 185 send8(pgm_read_byte_near(src++), zero, one);
TheChrisyd 0:a2d36977aec3 186 GD.__end();
TheChrisyd 0:a2d36977aec3 187 }
TheChrisyd 0:a2d36977aec3 188
TheChrisyd 0:a2d36977aec3 189
TheChrisyd 0:a2d36977aec3 190 static void sprite(byte spr, byte x, byte y, byte img, byte rot = 0)
TheChrisyd 0:a2d36977aec3 191 {
TheChrisyd 0:a2d36977aec3 192 GD.sprite(spr, x + 9*8, y + 6*8, img, 0, rot);
TheChrisyd 0:a2d36977aec3 193 }
TheChrisyd 0:a2d36977aec3 194
TheChrisyd 0:a2d36977aec3 195 static void hide(byte spr)
TheChrisyd 0:a2d36977aec3 196 {
TheChrisyd 0:a2d36977aec3 197 GD.sprite(spr, 400, 400, 0, 0, 0);
TheChrisyd 0:a2d36977aec3 198 }
TheChrisyd 0:a2d36977aec3 199
TheChrisyd 0:a2d36977aec3 200 struct guardian {
TheChrisyd 0:a2d36977aec3 201 byte a;
TheChrisyd 0:a2d36977aec3 202 byte x, y;
TheChrisyd 0:a2d36977aec3 203 signed char d;
TheChrisyd 0:a2d36977aec3 204 byte x0, x1;
TheChrisyd 0:a2d36977aec3 205 byte f;
TheChrisyd 0:a2d36977aec3 206 } guards[8];
TheChrisyd 0:a2d36977aec3 207
TheChrisyd 0:a2d36977aec3 208 #define MAXRAY 40
TheChrisyd 0:a2d36977aec3 209
TheChrisyd 0:a2d36977aec3 210 struct state_t {
TheChrisyd 0:a2d36977aec3 211 byte level;
TheChrisyd 0:a2d36977aec3 212 byte lives;
TheChrisyd 0:a2d36977aec3 213 uint32_t score;
TheChrisyd 0:a2d36977aec3 214 uint32_t hiscore;
TheChrisyd 0:a2d36977aec3 215
TheChrisyd 0:a2d36977aec3 216 byte bidir;
TheChrisyd 0:a2d36977aec3 217 byte air;
TheChrisyd 0:a2d36977aec3 218 byte conveyordir;
TheChrisyd 0:a2d36977aec3 219 byte portalattr;
TheChrisyd 0:a2d36977aec3 220 byte nitems;
TheChrisyd 0:a2d36977aec3 221 byte wx, wy; // Willy x,y
TheChrisyd 0:a2d36977aec3 222 byte wd, wf; // Willy dir and frame
TheChrisyd 0:a2d36977aec3 223 byte lastdx; // Willy last x movement
TheChrisyd 0:a2d36977aec3 224 byte convey; // Willy caught on conveyor
TheChrisyd 0:a2d36977aec3 225 byte jumping;
TheChrisyd 0:a2d36977aec3 226 signed char wyv;
TheChrisyd 0:a2d36977aec3 227 byte conveyor[2];
TheChrisyd 0:a2d36977aec3 228 PROGMEM prog_uchar *guardian;
TheChrisyd 0:a2d36977aec3 229 uint16_t prevray[MAXRAY];
TheChrisyd 0:a2d36977aec3 230 byte switch1, switch2;
TheChrisyd 0:a2d36977aec3 231 } state;
TheChrisyd 0:a2d36977aec3 232
TheChrisyd 0:a2d36977aec3 233 // All this is taken from
TheChrisyd 0:a2d36977aec3 234 // http://jswremakes.emuunlim.com/Mmt/Manic%20Miner%20Room%20Format.htm
TheChrisyd 0:a2d36977aec3 235
TheChrisyd 0:a2d36977aec3 236 static void plot_air()
TheChrisyd 0:a2d36977aec3 237 {
TheChrisyd 0:a2d36977aec3 238 // Starting at CHR_AIR+4, draw a line of n/8 solid, n%8, then 27-(n/8)
TheChrisyd 0:a2d36977aec3 239 uint16_t addr = RAM_CHR + (16 * (CHR_AIR + 4)); // line 2 of (CHR_AIR+4)
TheChrisyd 0:a2d36977aec3 240 for (byte i = 0; i < 28; i++) {
TheChrisyd 0:a2d36977aec3 241 byte v;
TheChrisyd 0:a2d36977aec3 242 if (i < (state.air >> 3))
TheChrisyd 0:a2d36977aec3 243 v = 8;
TheChrisyd 0:a2d36977aec3 244 else if (i == (state.air >> 3))
TheChrisyd 0:a2d36977aec3 245 v = state.air & 7;
TheChrisyd 0:a2d36977aec3 246 else
TheChrisyd 0:a2d36977aec3 247 v = 0;
TheChrisyd 0:a2d36977aec3 248 unpack8xn(addr + i * 16, airs + (v << 3), 8);
TheChrisyd 0:a2d36977aec3 249 }
TheChrisyd 0:a2d36977aec3 250 }
TheChrisyd 0:a2d36977aec3 251
TheChrisyd 0:a2d36977aec3 252 static void plot_score()
TheChrisyd 0:a2d36977aec3 253 {
TheChrisyd 0:a2d36977aec3 254 uint32_t n = state.score;
TheChrisyd 0:a2d36977aec3 255
TheChrisyd 0:a2d36977aec3 256 GD.__wstart(atxy(26, 19));
TheChrisyd 0:a2d36977aec3 257 spi.write('0' + (n / 100000) % 10);
TheChrisyd 0:a2d36977aec3 258 spi.write('0' + (n / 10000) % 10);
TheChrisyd 0:a2d36977aec3 259 spi.write('0' + (n / 1000) % 10);
TheChrisyd 0:a2d36977aec3 260 spi.write('0' + (n / 100) % 10);
TheChrisyd 0:a2d36977aec3 261 spi.write('0' + (n / 10) % 10);
TheChrisyd 0:a2d36977aec3 262 spi.write('0' + n % 10);
TheChrisyd 0:a2d36977aec3 263 GD.__end();
TheChrisyd 0:a2d36977aec3 264 }
TheChrisyd 0:a2d36977aec3 265
TheChrisyd 0:a2d36977aec3 266 static void bump_score(byte n)
TheChrisyd 0:a2d36977aec3 267 {
TheChrisyd 0:a2d36977aec3 268 if ((state.score < 10000) && (10000 <= (state.score + n )))
TheChrisyd 0:a2d36977aec3 269 state.lives++;
TheChrisyd 0:a2d36977aec3 270 state.score += n;
TheChrisyd 0:a2d36977aec3 271 plot_score();
TheChrisyd 0:a2d36977aec3 272 }
TheChrisyd 0:a2d36977aec3 273
TheChrisyd 0:a2d36977aec3 274 static void pause(byte n)
TheChrisyd 0:a2d36977aec3 275 {
TheChrisyd 0:a2d36977aec3 276 while (n--)
TheChrisyd 0:a2d36977aec3 277 GD.waitvblank();
TheChrisyd 0:a2d36977aec3 278 }
TheChrisyd 0:a2d36977aec3 279
TheChrisyd 0:a2d36977aec3 280 static void clear_screen(byte yclear = 16)
TheChrisyd 0:a2d36977aec3 281 {
TheChrisyd 0:a2d36977aec3 282 // blank out top 16 lines
TheChrisyd 0:a2d36977aec3 283 GD.fill(RAM_CHR, 0, 16);
TheChrisyd 0:a2d36977aec3 284 GD.wr16(RAM_PAL, BLACK);
TheChrisyd 0:a2d36977aec3 285 for (byte y = 0; y < yclear; y++)
TheChrisyd 0:a2d36977aec3 286 GD.fill(atxy(0, y), 0, 32);
TheChrisyd 0:a2d36977aec3 287 for (int i = 0; i < 256; i++)
TheChrisyd 0:a2d36977aec3 288 hide(i);
TheChrisyd 0:a2d36977aec3 289 for (int i = 0; i < 64; i++)
TheChrisyd 0:a2d36977aec3 290 GD.voice(i, 0, 0, 0, 0);
TheChrisyd 0:a2d36977aec3 291 pause(6);
TheChrisyd 0:a2d36977aec3 292 }
TheChrisyd 0:a2d36977aec3 293
TheChrisyd 0:a2d36977aec3 294 static void loadlevel(struct level *l)
TheChrisyd 0:a2d36977aec3 295 {
TheChrisyd 0:a2d36977aec3 296 clear_screen();
TheChrisyd 0:a2d36977aec3 297
TheChrisyd 0:a2d36977aec3 298 // border
TheChrisyd 0:a2d36977aec3 299 GD.wr16(RAM_PAL + 8 * CHR_BORDER, COLOR(pgm_read_byte_near(&l->border)));
TheChrisyd 0:a2d36977aec3 300
TheChrisyd 0:a2d36977aec3 301 // background picture: uncompress into scratch then copy to screen
TheChrisyd 0:a2d36977aec3 302 prog_uchar *background = (prog_uchar*)pgm_read_word_near(&l->background);
TheChrisyd 0:a2d36977aec3 303 uint16_t scratch = 4096 - 512; // offscreen scratch
TheChrisyd 0:a2d36977aec3 304 GD.uncompress(scratch, background);
TheChrisyd 0:a2d36977aec3 305 for (byte y = 0; y < 16; y++)
TheChrisyd 0:a2d36977aec3 306 for (byte x = 0; x < 32; x++)
TheChrisyd 0:a2d36977aec3 307 GD.wr(atxy(x, y), GD.rd(scratch + (y << 5) + x));
TheChrisyd 0:a2d36977aec3 308 for (byte y = 16; y < 24; y++)
TheChrisyd 0:a2d36977aec3 309 GD.fill(atxy(0, y), ' ', 32);
TheChrisyd 0:a2d36977aec3 310
TheChrisyd 0:a2d36977aec3 311 // bg characters
TheChrisyd 0:a2d36977aec3 312 unpack8xn(RAM_CHR, l->bgchars, sizeof(l->bgchars));
TheChrisyd 0:a2d36977aec3 313 state.conveyor[0] = pgm_read_byte_near(&l->bgchars[8 * 4 + 0]);
TheChrisyd 0:a2d36977aec3 314 state.conveyor[1] = pgm_read_byte_near(&l->bgchars[8 * 4 + 3]);
TheChrisyd 0:a2d36977aec3 315
TheChrisyd 0:a2d36977aec3 316 // bg character palettes
TheChrisyd 0:a2d36977aec3 317 for (byte c = 0; c < 8; c++) {
TheChrisyd 0:a2d36977aec3 318 byte attr = pgm_read_byte_near(l->bgattr + c);
TheChrisyd 0:a2d36977aec3 319 GD.wr16(RAM_PAL + 8 * c, COLOR(PAPER(attr)));
TheChrisyd 0:a2d36977aec3 320 GD.wr16(RAM_PAL + 8 * c + 6, COLOR(INK(attr)));
TheChrisyd 0:a2d36977aec3 321 }
TheChrisyd 0:a2d36977aec3 322
TheChrisyd 0:a2d36977aec3 323 // block 2 is the crumbling floor
TheChrisyd 0:a2d36977aec3 324 // put the 7 anims of crumbling floor in chars 8-14
TheChrisyd 0:a2d36977aec3 325 GD.fill(RAM_CHR + 16 * 8, 0, 16 * 7);
TheChrisyd 0:a2d36977aec3 326 prog_uchar *src = l->bgchars + 2 * 8;
TheChrisyd 0:a2d36977aec3 327 for (byte i = 0; i < 7; i++) {
TheChrisyd 0:a2d36977aec3 328 byte ch = 8 + i;
TheChrisyd 0:a2d36977aec3 329 unpack8xn(RAM_CHR + 16 * ch + 2 * (i + 1), src, 7 - i);
TheChrisyd 0:a2d36977aec3 330 byte attr = pgm_read_byte_near(l->bgattr + 2);
TheChrisyd 0:a2d36977aec3 331 GD.wr16(RAM_PAL + 8 * ch, COLOR(PAPER(attr)));
TheChrisyd 0:a2d36977aec3 332 GD.wr16(RAM_PAL + 8 * ch + 6, COLOR(INK(attr)));
TheChrisyd 0:a2d36977aec3 333 }
TheChrisyd 0:a2d36977aec3 334
TheChrisyd 0:a2d36977aec3 335 // level name and score line
TheChrisyd 0:a2d36977aec3 336 for (byte x = 0; x < 32; x++) {
TheChrisyd 0:a2d36977aec3 337 char scoreline[] = "High Score 000000 Score 000000";
TheChrisyd 0:a2d36977aec3 338 GD.wr(atxy(x, 16), 0x80 + pgm_read_byte_near(l->name + x));
TheChrisyd 0:a2d36977aec3 339 GD.wr(atxy(x, 19), scoreline[x]);
TheChrisyd 0:a2d36977aec3 340 }
TheChrisyd 0:a2d36977aec3 341 plot_score();
TheChrisyd 0:a2d36977aec3 342
TheChrisyd 0:a2d36977aec3 343 // AIR line characters
TheChrisyd 0:a2d36977aec3 344 for (byte i = 0; i < 32; i++)
TheChrisyd 0:a2d36977aec3 345 GD.wr(atxy(i, 17), CHR_AIR + i);
TheChrisyd 0:a2d36977aec3 346
TheChrisyd 0:a2d36977aec3 347 // the items are 5 sprites, using colors 16 up
TheChrisyd 0:a2d36977aec3 348 state.nitems = 0;
TheChrisyd 0:a2d36977aec3 349 for (byte i = 0; i < 5; i++) {
TheChrisyd 0:a2d36977aec3 350 byte x = pgm_read_byte_near(&l->items[i].x);
TheChrisyd 0:a2d36977aec3 351 byte y = pgm_read_byte_near(&l->items[i].y);
TheChrisyd 0:a2d36977aec3 352 if (x || y) {
TheChrisyd 0:a2d36977aec3 353 loadspr8(IMG_ITEM + i, l->item, 255, 16 + i); // items have color 16 up
TheChrisyd 0:a2d36977aec3 354 sprite(IMG_ITEM + i, x, y, IMG_ITEM + i);
TheChrisyd 0:a2d36977aec3 355 state.nitems++;
TheChrisyd 0:a2d36977aec3 356 } else
TheChrisyd 0:a2d36977aec3 357 hide(IMG_ITEM + i);
TheChrisyd 0:a2d36977aec3 358 }
TheChrisyd 0:a2d36977aec3 359
TheChrisyd 0:a2d36977aec3 360 // the portal is a sprite, using colors 32,33
TheChrisyd 0:a2d36977aec3 361 state.portalattr = pgm_read_byte_near(&l->portalattr);
TheChrisyd 0:a2d36977aec3 362 loadspr16(IMG_PORTAL, l->portal, 32, 33);
TheChrisyd 0:a2d36977aec3 363 byte portalx = pgm_read_byte_near(&l->portalx);
TheChrisyd 0:a2d36977aec3 364 byte portaly = pgm_read_byte_near(&l->portaly);
TheChrisyd 0:a2d36977aec3 365 sprite(IMG_PORTAL, portalx, portaly, IMG_PORTAL);
TheChrisyd 0:a2d36977aec3 366
TheChrisyd 0:a2d36977aec3 367 // use sprites for blocks NASTY1 and NASTY2
TheChrisyd 0:a2d36977aec3 368 byte nc1 = pgm_read_byte_near(l->bgattr + ELEM_NASTY1);
TheChrisyd 0:a2d36977aec3 369 byte nc2 = pgm_read_byte_near(l->bgattr + ELEM_NASTY2);
TheChrisyd 0:a2d36977aec3 370 loadspr8(IMG_NASTY1, l->bgchars + ELEM_NASTY1 * 8, 255, INK(nc1));
TheChrisyd 0:a2d36977aec3 371 loadspr8(IMG_NASTY2, l->bgchars + ELEM_NASTY2 * 8, 255, INK(nc2));
TheChrisyd 0:a2d36977aec3 372 // now paint sprites over all the NASTY blocks
TheChrisyd 0:a2d36977aec3 373 {
TheChrisyd 0:a2d36977aec3 374 byte spr = IMG_NASTY1;
TheChrisyd 0:a2d36977aec3 375 for (byte y = 0; y < 16; y++)
TheChrisyd 0:a2d36977aec3 376 for (byte x = 0; x < 32; x++) {
TheChrisyd 0:a2d36977aec3 377 byte blk = GD.rd(atxy(x, y));
TheChrisyd 0:a2d36977aec3 378 if (blk == ELEM_NASTY1)
TheChrisyd 0:a2d36977aec3 379 sprite(spr++, x << 3, y << 3, IMG_NASTY1);
TheChrisyd 0:a2d36977aec3 380 if (blk == ELEM_NASTY2)
TheChrisyd 0:a2d36977aec3 381 sprite(spr++, x << 3, y << 3, IMG_NASTY2);
TheChrisyd 0:a2d36977aec3 382 }
TheChrisyd 0:a2d36977aec3 383 }
TheChrisyd 0:a2d36977aec3 384
TheChrisyd 0:a2d36977aec3 385 // air
TheChrisyd 0:a2d36977aec3 386 state.air = pgm_read_byte_near(&l->air);
TheChrisyd 0:a2d36977aec3 387 plot_air();
TheChrisyd 0:a2d36977aec3 388
TheChrisyd 0:a2d36977aec3 389 // Conveyor direction
TheChrisyd 0:a2d36977aec3 390 state.conveyordir = pgm_read_byte_near(&l->conveyordir);
TheChrisyd 0:a2d36977aec3 391
TheChrisyd 0:a2d36977aec3 392 // the hguardians
TheChrisyd 0:a2d36977aec3 393 state.bidir = pgm_read_byte_near(&l->bidir);
TheChrisyd 0:a2d36977aec3 394 state.guardian = l->guardian;
TheChrisyd 0:a2d36977aec3 395 guardian *pg;
TheChrisyd 0:a2d36977aec3 396 for (byte i = 0; i < 8; i++) {
TheChrisyd 0:a2d36977aec3 397 byte a = pgm_read_byte_near(&l->hguard[i].a);
TheChrisyd 0:a2d36977aec3 398 guards[i].a = a;
TheChrisyd 0:a2d36977aec3 399 if (a) {
TheChrisyd 0:a2d36977aec3 400 byte x = pgm_read_byte_near(&l->hguard[i].x);
TheChrisyd 0:a2d36977aec3 401 byte y = pgm_read_byte_near(&l->hguard[i].y);
TheChrisyd 0:a2d36977aec3 402 guards[i].x = x;
TheChrisyd 0:a2d36977aec3 403 guards[i].y = y;
TheChrisyd 0:a2d36977aec3 404 guards[i].d = pgm_read_byte_near(&l->hguard[i].d);
TheChrisyd 0:a2d36977aec3 405 guards[i].x0 = pgm_read_byte_near(&l->hguard[i].x0);
TheChrisyd 0:a2d36977aec3 406 guards[i].x1 = pgm_read_byte_near(&l->hguard[i].x1);
TheChrisyd 0:a2d36977aec3 407 guards[i].f = 0;
TheChrisyd 0:a2d36977aec3 408 } else {
TheChrisyd 0:a2d36977aec3 409 hide(6 + i);
TheChrisyd 0:a2d36977aec3 410 }
TheChrisyd 0:a2d36977aec3 411 }
TheChrisyd 0:a2d36977aec3 412
TheChrisyd 0:a2d36977aec3 413 pg = &guards[4]; // Use slot 4 for special guardians
TheChrisyd 0:a2d36977aec3 414 switch (state.level) { // Special level handling
TheChrisyd 0:a2d36977aec3 415 case 4: // Eugene's lair
TheChrisyd 0:a2d36977aec3 416 pg->a = 0; // prevent normal guardian logic
TheChrisyd 0:a2d36977aec3 417 pg->x = 120;
TheChrisyd 0:a2d36977aec3 418 pg->y = 0;
TheChrisyd 0:a2d36977aec3 419 pg->d = -1;
TheChrisyd 0:a2d36977aec3 420 pg->x0 = 0;
TheChrisyd 0:a2d36977aec3 421 pg->x1 = 88;
TheChrisyd 0:a2d36977aec3 422 loadspr16(IMG_GUARD + 4, eugene, 255, 15);
TheChrisyd 0:a2d36977aec3 423 break;
TheChrisyd 0:a2d36977aec3 424 case 7: // Miner Willy meets the Kong Beast
TheChrisyd 0:a2d36977aec3 425 case 11: // Return of the Alien Kong Beast
TheChrisyd 0:a2d36977aec3 426 pg->a = 0;
TheChrisyd 0:a2d36977aec3 427 pg->x = 120;
TheChrisyd 0:a2d36977aec3 428 pg->y = 0;
TheChrisyd 0:a2d36977aec3 429 state.switch1 = 0;
TheChrisyd 0:a2d36977aec3 430 loadspr8(IMG_SWITCH1, lightswitch, 0, 6);
TheChrisyd 0:a2d36977aec3 431 sprite(IMG_SWITCH1, 48, 0, IMG_SWITCH1);
TheChrisyd 0:a2d36977aec3 432 state.switch2 = 0;
TheChrisyd 0:a2d36977aec3 433 loadspr8(IMG_SWITCH2, lightswitch, 0, 6);
TheChrisyd 0:a2d36977aec3 434 sprite(IMG_SWITCH2, 144, 0, IMG_SWITCH2);
TheChrisyd 0:a2d36977aec3 435 break;
TheChrisyd 0:a2d36977aec3 436 }
TheChrisyd 0:a2d36977aec3 437
TheChrisyd 0:a2d36977aec3 438 for (byte i = 0; i < MAXRAY; i++)
TheChrisyd 0:a2d36977aec3 439 state.prevray[i] = 4095;
TheChrisyd 0:a2d36977aec3 440
TheChrisyd 0:a2d36977aec3 441 // Willy
TheChrisyd 0:a2d36977aec3 442 state.wx = pgm_read_byte_near(&l->wx);
TheChrisyd 0:a2d36977aec3 443 state.wy = pgm_read_byte_near(&l->wy);
TheChrisyd 0:a2d36977aec3 444 state.wf = pgm_read_byte_near(&l->wf);
TheChrisyd 0:a2d36977aec3 445 state.wd = pgm_read_byte_near(&l->wd);
TheChrisyd 0:a2d36977aec3 446 state.jumping = 0;
TheChrisyd 0:a2d36977aec3 447 state.convey = 0;
TheChrisyd 0:a2d36977aec3 448 state.wyv = 0;
TheChrisyd 0:a2d36977aec3 449 }
TheChrisyd 0:a2d36977aec3 450
TheChrisyd 0:a2d36977aec3 451 static byte rol8(byte b, byte d) // 8-bit byte rotate left
TheChrisyd 0:a2d36977aec3 452 {
TheChrisyd 0:a2d36977aec3 453 d &= 7;
TheChrisyd 0:a2d36977aec3 454 return ((b << d) | (b >> (8 - d))) & 0xff;
TheChrisyd 0:a2d36977aec3 455 }
TheChrisyd 0:a2d36977aec3 456
TheChrisyd 0:a2d36977aec3 457
TheChrisyd 0:a2d36977aec3 458 void setup()
TheChrisyd 0:a2d36977aec3 459 {
TheChrisyd 0:a2d36977aec3 460 GD.begin();
TheChrisyd 0:a2d36977aec3 461 setup_control();
TheChrisyd 0:a2d36977aec3 462 }
TheChrisyd 0:a2d36977aec3 463
TheChrisyd 0:a2d36977aec3 464 void game_graphics()
TheChrisyd 0:a2d36977aec3 465 {
TheChrisyd 0:a2d36977aec3 466 // Load the Spectrum font: regular in 32-127 reverse in 160-255
TheChrisyd 0:a2d36977aec3 467 unpack8xn(RAM_CHR + 16 * ' ', specfont, sizeof(specfont));
TheChrisyd 0:a2d36977aec3 468 unpack8xn(RAM_CHR + 16 * (128 + ' '), specfont, sizeof(specfont));
TheChrisyd 0:a2d36977aec3 469 for (byte i = 32; i < 128; i++) {
TheChrisyd 0:a2d36977aec3 470 GD.setpal(4 * i, BLACK);
TheChrisyd 0:a2d36977aec3 471 GD.setpal(4 * i + 3, YELLOW);
TheChrisyd 0:a2d36977aec3 472 GD.setpal(4 * (128 + i), COLOR(6)); // dark yellow
TheChrisyd 0:a2d36977aec3 473 GD.setpal(4 * (128 + i) + 3, BLACK);
TheChrisyd 0:a2d36977aec3 474 }
TheChrisyd 0:a2d36977aec3 475
TheChrisyd 0:a2d36977aec3 476 // AIR display in 32 chars starting at CHR_AIR
TheChrisyd 0:a2d36977aec3 477 unpack8xn(RAM_CHR + 16 * CHR_AIR, specfont + 8 * ('A' - ' '), 8);
TheChrisyd 0:a2d36977aec3 478 unpack8xn(RAM_CHR + 16 * (CHR_AIR+1), specfont + 8 * ('I' - ' '), 8);
TheChrisyd 0:a2d36977aec3 479 unpack8xn(RAM_CHR + 16 * (CHR_AIR+2), specfont + 8 * ('R' - ' '), 8);
TheChrisyd 0:a2d36977aec3 480 for (byte i = 0; i < 32; i++) {
TheChrisyd 0:a2d36977aec3 481 uint16_t color = (i < 10) ? RED : GREEN;
TheChrisyd 0:a2d36977aec3 482 GD.setpal(4 * (CHR_AIR + i), color);
TheChrisyd 0:a2d36977aec3 483 GD.setpal(4 * (CHR_AIR + i) + 3, WHITE);
TheChrisyd 0:a2d36977aec3 484 }
TheChrisyd 0:a2d36977aec3 485
TheChrisyd 0:a2d36977aec3 486 // Load the Spectrum's palette into PALETTE256A
TheChrisyd 0:a2d36977aec3 487 GD.copy(RAM_SPRPAL, specpal, sizeof(specpal));
TheChrisyd 0:a2d36977aec3 488 GD.wr16(RAM_SPRPAL + 2 * 255, TRANSPARENT); // color 255 is transparent
TheChrisyd 0:a2d36977aec3 489
TheChrisyd 0:a2d36977aec3 490 // fill screen with char CHR_BORDER
TheChrisyd 0:a2d36977aec3 491 GD.fill(RAM_CHR + 16 * CHR_BORDER, 0, 16);
TheChrisyd 0:a2d36977aec3 492 GD.fill(RAM_PIC, CHR_BORDER, 64 * 64);
TheChrisyd 0:a2d36977aec3 493
TheChrisyd 0:a2d36977aec3 494 // Load Willy in bright cyan and white
TheChrisyd 0:a2d36977aec3 495 for (byte i = 0; i < 4; i++) {
TheChrisyd 0:a2d36977aec3 496 loadspr16(IMG_WILLYC + i, willy + (i << 5), 255, 8 + 5);
TheChrisyd 0:a2d36977aec3 497 }
TheChrisyd 0:a2d36977aec3 498 }
TheChrisyd 0:a2d36977aec3 499
TheChrisyd 0:a2d36977aec3 500 // midi frequency table
TheChrisyd 0:a2d36977aec3 501 static PROGMEM prog_uint16_t midifreq[128] = {
TheChrisyd 0:a2d36977aec3 502 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
TheChrisyd 0:a2d36977aec3 503 };
TheChrisyd 0:a2d36977aec3 504 #define MIDI(n) pgm_read_word(midifreq + (n))
TheChrisyd 0:a2d36977aec3 505
TheChrisyd 0:a2d36977aec3 506 static void squarewave(byte voice, uint16_t f0, byte amp)
TheChrisyd 0:a2d36977aec3 507 {
TheChrisyd 0:a2d36977aec3 508 GD.voice(8*voice + 0, 0, f0, amp, amp);
TheChrisyd 0:a2d36977aec3 509 GD.voice(8*voice + 1, 0, 3 * f0, amp/3, amp/3);
TheChrisyd 0:a2d36977aec3 510 GD.voice(8*voice + 2, 0, 5 * f0, amp/5, amp/5);
TheChrisyd 0:a2d36977aec3 511 GD.voice(8*voice + 3, 0, 7 * f0, amp/7, amp/7);
TheChrisyd 0:a2d36977aec3 512 GD.voice(8*voice + 4, 0, 9 * f0, amp/9, amp/9);
TheChrisyd 0:a2d36977aec3 513 GD.voice(8*voice + 5, 0, 11 * f0, amp/11, amp/11);
TheChrisyd 0:a2d36977aec3 514 }
TheChrisyd 0:a2d36977aec3 515
TheChrisyd 0:a2d36977aec3 516 static void drive_level(byte t);
TheChrisyd 0:a2d36977aec3 517
TheChrisyd 0:a2d36977aec3 518 static int title_screen() // returns 1 if player pressed something
TheChrisyd 0:a2d36977aec3 519 {
TheChrisyd 0:a2d36977aec3 520 for (;;) {
TheChrisyd 0:a2d36977aec3 521 display:
TheChrisyd 0:a2d36977aec3 522 clear_screen();
TheChrisyd 0:a2d36977aec3 523 GD.fill(0x4000, 0, 6144); // clear emulated Spectrum video RAM
TheChrisyd 0:a2d36977aec3 524 GD.uncompress(0x7000, spectrum_tables);
TheChrisyd 0:a2d36977aec3 525
TheChrisyd 0:a2d36977aec3 526 // fill screen with char 0x80 for border
TheChrisyd 0:a2d36977aec3 527 GD.setpal(4 * 0x80, COLOR(2));
TheChrisyd 0:a2d36977aec3 528 GD.fill(RAM_CHR, 0, 4096);
TheChrisyd 0:a2d36977aec3 529 GD.fill(RAM_PIC, 0x80, 4096);
TheChrisyd 0:a2d36977aec3 530
TheChrisyd 0:a2d36977aec3 531 // paint the 256x192 window as alternating lines of
TheChrisyd 0:a2d36977aec3 532 // chars 0-31 and 32-63
TheChrisyd 0:a2d36977aec3 533 for (byte y = 0; y < 24; y += 2) {
TheChrisyd 0:a2d36977aec3 534 for (byte x = 0; x < 32; x++) {
TheChrisyd 0:a2d36977aec3 535 GD.wr(atxy(x, y), x);
TheChrisyd 0:a2d36977aec3 536 GD.wr(atxy(x, y + 1), 32 + x);
TheChrisyd 0:a2d36977aec3 537 }
TheChrisyd 0:a2d36977aec3 538 }
TheChrisyd 0:a2d36977aec3 539 GD.microcode(spectrum_code, sizeof(spectrum_code));
TheChrisyd 0:a2d36977aec3 540 GD.uncompress(0x4000, screen_mm1);
TheChrisyd 0:a2d36977aec3 541
TheChrisyd 0:a2d36977aec3 542 for (prog_uint32_t *tune = blue_danube;
TheChrisyd 0:a2d36977aec3 543 (size_t)tune < ((size_t)blue_danube + sizeof(blue_danube));
TheChrisyd 0:a2d36977aec3 544 tune++) {
TheChrisyd 0:a2d36977aec3 545 uint32_t cmd = pgm_read_dword_near(tune);
TheChrisyd 0:a2d36977aec3 546 for (int t = 60 * (cmd >> 28); t; t--) {
TheChrisyd 0:a2d36977aec3 547 delay(1);
TheChrisyd 0:a2d36977aec3 548 if (control())
TheChrisyd 0:a2d36977aec3 549 goto escape;
TheChrisyd 0:a2d36977aec3 550 }
TheChrisyd 0:a2d36977aec3 551 byte n0 = 127 & (cmd >> 21);
TheChrisyd 0:a2d36977aec3 552 byte a0 = 127 & (cmd >> 14);
TheChrisyd 0:a2d36977aec3 553 byte n1 = 127 & (cmd >> 7);
TheChrisyd 0:a2d36977aec3 554 byte a1 = 127 & cmd;
TheChrisyd 0:a2d36977aec3 555 squarewave(0, MIDI(n0), a0 >> 1);
TheChrisyd 0:a2d36977aec3 556 squarewave(1, MIDI(n1), a1 >> 1);
TheChrisyd 0:a2d36977aec3 557 GD.setpal(4 * 0x80, COLOR((n1 ^ n0) & 7));
TheChrisyd 0:a2d36977aec3 558 }
TheChrisyd 0:a2d36977aec3 559 GD.setpal(4 * 0x80, COLOR(7));
TheChrisyd 0:a2d36977aec3 560
TheChrisyd 0:a2d36977aec3 561 for (byte i = 0; i < ((sizeof(message) - 1) - 32); i++) {
TheChrisyd 0:a2d36977aec3 562 prog_uchar *src = message + i;
TheChrisyd 0:a2d36977aec3 563 for (byte j = 0; j < 32; j++) {
TheChrisyd 0:a2d36977aec3 564 byte ch = pgm_read_byte_near(src + j);
TheChrisyd 0:a2d36977aec3 565 prog_uchar *glyph = specfont + ((ch - ' ') << 3);
TheChrisyd 0:a2d36977aec3 566 for (byte k = 0; k < 8; k++)
TheChrisyd 0:a2d36977aec3 567 GD.wr(0x5060 + j + (k << 8), pgm_read_byte_near(glyph++));
TheChrisyd 0:a2d36977aec3 568 }
TheChrisyd 0:a2d36977aec3 569 for (byte t = 80; t; t--) {
TheChrisyd 0:a2d36977aec3 570 delay(1);
TheChrisyd 0:a2d36977aec3 571 if (control())
TheChrisyd 0:a2d36977aec3 572 goto escape;
TheChrisyd 0:a2d36977aec3 573 }
TheChrisyd 0:a2d36977aec3 574 }
TheChrisyd 0:a2d36977aec3 575
TheChrisyd 0:a2d36977aec3 576 GD.wr(J1_RESET, 1); // stop coprocessor
TheChrisyd 0:a2d36977aec3 577 clear_screen(24);
TheChrisyd 0:a2d36977aec3 578 game_graphics();
TheChrisyd 0:a2d36977aec3 579
TheChrisyd 0:a2d36977aec3 580 for (state.level = 0; state.level < 19; state.level++) {
TheChrisyd 0:a2d36977aec3 581 loadlevel(&levels[state.level]);
TheChrisyd 0:a2d36977aec3 582 for (byte t = 0; t < 128; t++) {
TheChrisyd 0:a2d36977aec3 583 drive_level(t);
TheChrisyd 0:a2d36977aec3 584 GD.waitvblank();
TheChrisyd 0:a2d36977aec3 585 if (control())
TheChrisyd 0:a2d36977aec3 586 goto display;
TheChrisyd 0:a2d36977aec3 587 }
TheChrisyd 0:a2d36977aec3 588 }
TheChrisyd 0:a2d36977aec3 589 }
TheChrisyd 0:a2d36977aec3 590
TheChrisyd 0:a2d36977aec3 591 escape:
TheChrisyd 0:a2d36977aec3 592 GD.wr(J1_RESET, 1); // stop coprocessor
TheChrisyd 0:a2d36977aec3 593 clear_screen(24);
TheChrisyd 0:a2d36977aec3 594 game_graphics();
TheChrisyd 0:a2d36977aec3 595 }
TheChrisyd 0:a2d36977aec3 596
TheChrisyd 0:a2d36977aec3 597 static void game_over()
TheChrisyd 0:a2d36977aec3 598 {
TheChrisyd 0:a2d36977aec3 599 clear_screen();
TheChrisyd 0:a2d36977aec3 600 // Willy
TheChrisyd 0:a2d36977aec3 601 loadspr16(0, willy, 255, 8 + 7);
TheChrisyd 0:a2d36977aec3 602 sprite(0, 124, 128 - 32, 0);
TheChrisyd 0:a2d36977aec3 603
TheChrisyd 0:a2d36977aec3 604 // plinth
TheChrisyd 0:a2d36977aec3 605 loadspr16(2, plinth, 255, 8 + 7);
TheChrisyd 0:a2d36977aec3 606 sprite(2, 120, 128 - 16, 2);
TheChrisyd 0:a2d36977aec3 607
TheChrisyd 0:a2d36977aec3 608 // Boot
TheChrisyd 0:a2d36977aec3 609 loadspr16(1, boot, 255, 8 + 7);
TheChrisyd 0:a2d36977aec3 610 loadspr16(3, boot, 255, 8 + 7); // sprite 3 is top 2 lines of boot
TheChrisyd 0:a2d36977aec3 611 // erase the bottom 14 lines of boot
TheChrisyd 0:a2d36977aec3 612 GD.fill(RAM_SPRIMG + 3 * 256 + 2 * 16, 255, 14 * 16);
TheChrisyd 0:a2d36977aec3 613
TheChrisyd 0:a2d36977aec3 614 for (byte i = 0; i <= 48; i++) {
TheChrisyd 0:a2d36977aec3 615 sprite(1, 120, 2 * i, 1); // boot in sprite 1
TheChrisyd 0:a2d36977aec3 616 sprite(3 + i, 120, 2 * i, 3); // leg in sprites 3,4, etc
TheChrisyd 0:a2d36977aec3 617 if (41 <= i) // erase Willy as boot covers him
TheChrisyd 0:a2d36977aec3 618 GD.fill(RAM_SPRIMG + 32 * (i - 41), 255, 32);
TheChrisyd 0:a2d36977aec3 619 if ((i & 1) == 0)
TheChrisyd 0:a2d36977aec3 620 GD.wr16(RAM_PAL, COLOR(random(7)));
TheChrisyd 0:a2d36977aec3 621 squarewave(0, 400 + 4 * i, 150);
TheChrisyd 0:a2d36977aec3 622 pause(2);
TheChrisyd 0:a2d36977aec3 623 }
TheChrisyd 0:a2d36977aec3 624 GD.wr16(RAM_PAL, BLACK);
TheChrisyd 0:a2d36977aec3 625 squarewave(0, 0, 0);
TheChrisyd 0:a2d36977aec3 626
TheChrisyd 0:a2d36977aec3 627 // "Game Over" text in sprite images 16-23, color 16-23
TheChrisyd 0:a2d36977aec3 628 char msg[] = "GameOver";
TheChrisyd 0:a2d36977aec3 629 for (byte i = 0; i < 8; i++)
TheChrisyd 0:a2d36977aec3 630 loadspr8(16 + i, specfont + (msg[i] - 32) * 8, 255, 16 + i);
TheChrisyd 0:a2d36977aec3 631 for (byte i = 0; i < 4; i++) {
TheChrisyd 0:a2d36977aec3 632 sprite(100 + i, 80 + i * 8, 48, 16 + i);
TheChrisyd 0:a2d36977aec3 633 sprite(104 + i, 140 + i * 8, 48, 20 + i);
TheChrisyd 0:a2d36977aec3 634 }
TheChrisyd 0:a2d36977aec3 635 for (byte t = 120; t; t--) {
TheChrisyd 0:a2d36977aec3 636 for (byte i = 0; i < 8; i++)
TheChrisyd 0:a2d36977aec3 637 GD.wr16(RAM_SPRPAL + (16 + i) * 2, COLOR(9 + random(7)));
TheChrisyd 0:a2d36977aec3 638 pause(2);
TheChrisyd 0:a2d36977aec3 639 }
TheChrisyd 0:a2d36977aec3 640 clear_screen();
TheChrisyd 0:a2d36977aec3 641 }
TheChrisyd 0:a2d36977aec3 642
TheChrisyd 0:a2d36977aec3 643 // crumble block at s, which is the sequence
TheChrisyd 0:a2d36977aec3 644 // 2->8->9->10->11->12->13->14->0
TheChrisyd 0:a2d36977aec3 645
TheChrisyd 0:a2d36977aec3 646 static void crumble(uint16_t s)
TheChrisyd 0:a2d36977aec3 647 {
TheChrisyd 0:a2d36977aec3 648 byte r = GD.rd(s);
TheChrisyd 0:a2d36977aec3 649 signed char nexts[] = {
TheChrisyd 0:a2d36977aec3 650 -1, -1, 8, -1, -1, -1, -1, -1,
TheChrisyd 0:a2d36977aec3 651 9, 10, 11, 12, 13, 14, 0 };
TheChrisyd 0:a2d36977aec3 652 if (r < sizeof(nexts) && nexts[r] != -1)
TheChrisyd 0:a2d36977aec3 653 GD.wr(s, nexts[r]);
TheChrisyd 0:a2d36977aec3 654 }
TheChrisyd 0:a2d36977aec3 655
TheChrisyd 0:a2d36977aec3 656 // is it legal for willy to be at (x,y)
TheChrisyd 0:a2d36977aec3 657 static int canbe(byte x, byte y)
TheChrisyd 0:a2d36977aec3 658 {
TheChrisyd 0:a2d36977aec3 659 uint16_t addr = atxy(x / 8, y / 8);
TheChrisyd 0:a2d36977aec3 660 return
TheChrisyd 0:a2d36977aec3 661 (GD.rd(addr) != ELEM_WALL) &&
TheChrisyd 0:a2d36977aec3 662 (GD.rd(addr+1) != ELEM_WALL) &&
TheChrisyd 0:a2d36977aec3 663 (GD.rd(addr+64) != ELEM_WALL) &&
TheChrisyd 0:a2d36977aec3 664 (GD.rd(addr+65) != ELEM_WALL);
TheChrisyd 0:a2d36977aec3 665 }
TheChrisyd 0:a2d36977aec3 666
TheChrisyd 0:a2d36977aec3 667 // is Willy standing in a solar ray?
TheChrisyd 0:a2d36977aec3 668 static int inray(byte x, byte y)
TheChrisyd 0:a2d36977aec3 669 {
TheChrisyd 0:a2d36977aec3 670 uint16_t addr = atxy(x / 8, y / 8);
TheChrisyd 0:a2d36977aec3 671 return
TheChrisyd 0:a2d36977aec3 672 (GD.rd(addr) > 0x80 ) ||
TheChrisyd 0:a2d36977aec3 673 (GD.rd(addr+1) > 0x80) ||
TheChrisyd 0:a2d36977aec3 674 (GD.rd(addr+64) > 0x80) ||
TheChrisyd 0:a2d36977aec3 675 (GD.rd(addr+65) > 0x80);
TheChrisyd 0:a2d36977aec3 676 }
TheChrisyd 0:a2d36977aec3 677
TheChrisyd 0:a2d36977aec3 678 static void drive_level(byte t)
TheChrisyd 0:a2d36977aec3 679 {
TheChrisyd 0:a2d36977aec3 680 uint16_t freq = 133955L / pgm_read_byte_near(music1 + ((t >> 1) & 63));
TheChrisyd 0:a2d36977aec3 681 GD.waitvblank();
TheChrisyd 0:a2d36977aec3 682 squarewave(0, freq, 128);
TheChrisyd 0:a2d36977aec3 683 GD.waitvblank();
TheChrisyd 0:a2d36977aec3 684 squarewave(0, freq, 0);
TheChrisyd 0:a2d36977aec3 685 GD.waitvblank();
TheChrisyd 0:a2d36977aec3 686 GD.waitvblank();
TheChrisyd 0:a2d36977aec3 687
TheChrisyd 0:a2d36977aec3 688 guardian *pg;
TheChrisyd 0:a2d36977aec3 689 byte lt; // local time, for slow hguardians
TheChrisyd 0:a2d36977aec3 690 for (byte i = 0; i < 8; i++) {
TheChrisyd 0:a2d36977aec3 691 pg = &guards[i];
TheChrisyd 0:a2d36977aec3 692 if (pg->a) {
TheChrisyd 0:a2d36977aec3 693 byte vertical = (i >= 4);
TheChrisyd 0:a2d36977aec3 694 byte frame;
TheChrisyd 0:a2d36977aec3 695 switch (state.level) {
TheChrisyd 0:a2d36977aec3 696 case 13: // Skylabs
TheChrisyd 0:a2d36977aec3 697 if (pg->y != pg->x1) {
TheChrisyd 0:a2d36977aec3 698 pg->f = 0;
TheChrisyd 0:a2d36977aec3 699 pg->y += pg->d;
TheChrisyd 0:a2d36977aec3 700 } else {
TheChrisyd 0:a2d36977aec3 701 pg->f++;
TheChrisyd 0:a2d36977aec3 702 }
TheChrisyd 0:a2d36977aec3 703 if (pg->f == 8) {
TheChrisyd 0:a2d36977aec3 704 pg->f = 0;
TheChrisyd 0:a2d36977aec3 705 pg->x += 64;
TheChrisyd 0:a2d36977aec3 706 pg->y = pg->x0;
TheChrisyd 0:a2d36977aec3 707 }
TheChrisyd 0:a2d36977aec3 708 loadspr16(IMG_GUARD + i, state.guardian + (pg->f << 5), 255, INK(pg->a));
TheChrisyd 0:a2d36977aec3 709 sprite(IMG_GUARD + i, pg->x, pg->y, IMG_GUARD + i);
TheChrisyd 0:a2d36977aec3 710 break;
TheChrisyd 0:a2d36977aec3 711 default:
TheChrisyd 0:a2d36977aec3 712 lt = t;
TheChrisyd 0:a2d36977aec3 713 if (!vertical && (pg->a & 0x80)) {
TheChrisyd 0:a2d36977aec3 714 if (t & 1)
TheChrisyd 0:a2d36977aec3 715 lt = t >> 1;
TheChrisyd 0:a2d36977aec3 716 else
TheChrisyd 0:a2d36977aec3 717 break;
TheChrisyd 0:a2d36977aec3 718 }
TheChrisyd 0:a2d36977aec3 719 if (state.bidir)
TheChrisyd 0:a2d36977aec3 720 frame = (lt & 3) ^ (pg->d ? 7 : 0);
TheChrisyd 0:a2d36977aec3 721 else
TheChrisyd 0:a2d36977aec3 722 if (vertical)
TheChrisyd 0:a2d36977aec3 723 frame = lt & 3;
TheChrisyd 0:a2d36977aec3 724 else
TheChrisyd 0:a2d36977aec3 725 frame = 4 + (lt & 3) ^ (pg->d ? 3 : 0);
TheChrisyd 0:a2d36977aec3 726 loadspr16(IMG_GUARD + i, state.guardian + (frame << 5), 255, INK(pg->a));
TheChrisyd 0:a2d36977aec3 727 sprite(IMG_GUARD + i, pg->x, pg->y, IMG_GUARD + i);
TheChrisyd 0:a2d36977aec3 728 if (!vertical) {
TheChrisyd 0:a2d36977aec3 729 if ((lt & 3) == 3) {
TheChrisyd 0:a2d36977aec3 730 if (pg->x == pg->x0 && pg->d)
TheChrisyd 0:a2d36977aec3 731 pg->d = 0;
TheChrisyd 0:a2d36977aec3 732 else if (pg->x == pg->x1 && !pg->d)
TheChrisyd 0:a2d36977aec3 733 pg->d = 1;
TheChrisyd 0:a2d36977aec3 734 else
TheChrisyd 0:a2d36977aec3 735 pg->x += pg->d ? -8 : 8;
TheChrisyd 0:a2d36977aec3 736 }
TheChrisyd 0:a2d36977aec3 737 } else {
TheChrisyd 0:a2d36977aec3 738 if (pg->y <= pg->x0 && pg->d < 0)
TheChrisyd 0:a2d36977aec3 739 pg->d = -pg->d;
TheChrisyd 0:a2d36977aec3 740 else if (pg->y >= pg->x1 && pg->d > 0)
TheChrisyd 0:a2d36977aec3 741 pg->d = -pg->d;
TheChrisyd 0:a2d36977aec3 742 else
TheChrisyd 0:a2d36977aec3 743 pg->y += pg->d;
TheChrisyd 0:a2d36977aec3 744 }
TheChrisyd 0:a2d36977aec3 745 }
TheChrisyd 0:a2d36977aec3 746 }
TheChrisyd 0:a2d36977aec3 747 }
TheChrisyd 0:a2d36977aec3 748 pg = &guards[4];
TheChrisyd 0:a2d36977aec3 749 switch (state.level) { // Special level handling
TheChrisyd 0:a2d36977aec3 750 case 4: // Eugene's lair
TheChrisyd 0:a2d36977aec3 751 sprite(IMG_GUARD + 4, pg->x, pg->y, IMG_GUARD + 4);
TheChrisyd 0:a2d36977aec3 752 if (pg->y == pg->x0 && pg->d < 0)
TheChrisyd 0:a2d36977aec3 753 pg->d = 1;
TheChrisyd 0:a2d36977aec3 754 if (pg->y == pg->x1 && pg->d > 0)
TheChrisyd 0:a2d36977aec3 755 pg->d = -1;
TheChrisyd 0:a2d36977aec3 756 if (state.nitems == 0) { // all collected -> descend and camp
TheChrisyd 0:a2d36977aec3 757 if (pg->d == -1)
TheChrisyd 0:a2d36977aec3 758 pg->d = 1;
TheChrisyd 0:a2d36977aec3 759 if (pg->y == pg->x1)
TheChrisyd 0:a2d36977aec3 760 pg->d = 0;
TheChrisyd 0:a2d36977aec3 761 }
TheChrisyd 0:a2d36977aec3 762 pg->y += pg->d;
TheChrisyd 0:a2d36977aec3 763 break;
TheChrisyd 0:a2d36977aec3 764 case 7: // Miner Willy meets the Kong Beast
TheChrisyd 0:a2d36977aec3 765 case 11: // Return of the Alien Kong Beast
TheChrisyd 0:a2d36977aec3 766 byte frame, color;
TheChrisyd 0:a2d36977aec3 767 if (!state.switch2) {
TheChrisyd 0:a2d36977aec3 768 frame = (t >> 3) & 1;
TheChrisyd 0:a2d36977aec3 769 color = 8 + 4;
TheChrisyd 0:a2d36977aec3 770 } else {
TheChrisyd 0:a2d36977aec3 771 frame = 2 + ((t >> 1) & 1);
TheChrisyd 0:a2d36977aec3 772 color = 8 + 6;
TheChrisyd 0:a2d36977aec3 773 if (pg->y < 104) {
TheChrisyd 0:a2d36977aec3 774 pg->y += 4;
TheChrisyd 0:a2d36977aec3 775 bump_score(100);
TheChrisyd 0:a2d36977aec3 776 }
TheChrisyd 0:a2d36977aec3 777 }
TheChrisyd 0:a2d36977aec3 778 if (pg->y != 104) {
TheChrisyd 0:a2d36977aec3 779 loadspr16(IMG_GUARD + 4, state.guardian + (frame << 5), 255, color);
TheChrisyd 0:a2d36977aec3 780 sprite(IMG_GUARD + 4, pg->x, pg->y, IMG_GUARD + 4);
TheChrisyd 0:a2d36977aec3 781 } else {
TheChrisyd 0:a2d36977aec3 782 hide(IMG_GUARD + 4);
TheChrisyd 0:a2d36977aec3 783 }
TheChrisyd 0:a2d36977aec3 784 }
TheChrisyd 0:a2d36977aec3 785
TheChrisyd 0:a2d36977aec3 786 // Animate conveyors:
TheChrisyd 0:a2d36977aec3 787 signed char convt = state.conveyordir ? t : -t;
TheChrisyd 0:a2d36977aec3 788 GD.__wstart(RAM_CHR + 4 * 16 + 2 * 0); // character 4 line 0
TheChrisyd 0:a2d36977aec3 789 unpack8(rol8(state.conveyor[0], convt));
TheChrisyd 0:a2d36977aec3 790 GD.__end();
TheChrisyd 0:a2d36977aec3 791 GD.__wstart(RAM_CHR + 4 * 16 + 2 * 3); // character 4 line 3
TheChrisyd 0:a2d36977aec3 792 unpack8(rol8(state.conveyor[0], -convt));
TheChrisyd 0:a2d36977aec3 793 GD.__end();
TheChrisyd 0:a2d36977aec3 794
TheChrisyd 0:a2d36977aec3 795 // Solar rays
TheChrisyd 0:a2d36977aec3 796 if (state.level == 18) {
TheChrisyd 0:a2d36977aec3 797 // Draw new ray, turning 0x00 to 0x80
TheChrisyd 0:a2d36977aec3 798 byte sx = 23;
TheChrisyd 0:a2d36977aec3 799 byte sy = 0;
TheChrisyd 0:a2d36977aec3 800 byte sd = 0; // down
TheChrisyd 0:a2d36977aec3 801 byte ri; // ray index
TheChrisyd 0:a2d36977aec3 802 for (ri = 0; ri < MAXRAY; ri++) {
TheChrisyd 0:a2d36977aec3 803 uint16_t a = atxy(sx, sy);
TheChrisyd 0:a2d36977aec3 804 if (state.prevray[ri] != a) {
TheChrisyd 0:a2d36977aec3 805 GD.wr(state.prevray[ri], 0);
TheChrisyd 0:a2d36977aec3 806 if (GD.rd(a) != 0)
TheChrisyd 0:a2d36977aec3 807 break; // absorbed
TheChrisyd 0:a2d36977aec3 808 GD.wr(a, 0x80 + ' ');
TheChrisyd 0:a2d36977aec3 809 state.prevray[ri] = a;
TheChrisyd 0:a2d36977aec3 810 }
TheChrisyd 0:a2d36977aec3 811 for (byte i = 0; i < 8; i++)
TheChrisyd 0:a2d36977aec3 812 if (guards[i].a && (guards[i].x >> 3) == sx && (guards[i].y >> 3) == sy)
TheChrisyd 0:a2d36977aec3 813 sd ^= 1;
TheChrisyd 0:a2d36977aec3 814 if (sd == 0)
TheChrisyd 0:a2d36977aec3 815 sy++;
TheChrisyd 0:a2d36977aec3 816 else
TheChrisyd 0:a2d36977aec3 817 sx--;
TheChrisyd 0:a2d36977aec3 818 }
TheChrisyd 0:a2d36977aec3 819 while (ri < MAXRAY) {
TheChrisyd 0:a2d36977aec3 820 GD.wr(state.prevray[ri], 0);
TheChrisyd 0:a2d36977aec3 821 state.prevray[ri++] = 4095;
TheChrisyd 0:a2d36977aec3 822 }
TheChrisyd 0:a2d36977aec3 823 }
TheChrisyd 0:a2d36977aec3 824
TheChrisyd 0:a2d36977aec3 825 // animate item colors starting at 16
TheChrisyd 0:a2d36977aec3 826 uint16_t colors[4] = { MAGENTA, YELLOW, CYAN, GREEN };
TheChrisyd 0:a2d36977aec3 827 for (byte i = 0; i < 5; i++)
TheChrisyd 0:a2d36977aec3 828 GD.wr16(RAM_SPRPAL + 2 * (i + 16), colors[(i + t) & 3]);
TheChrisyd 0:a2d36977aec3 829
TheChrisyd 0:a2d36977aec3 830 // when all items collected,
TheChrisyd 0:a2d36977aec3 831 // flash portal in colors 32,33 - the thing the Spectrum *can* do in hardware
TheChrisyd 0:a2d36977aec3 832 byte flip = (t >> 3) & (CHEAT_OPEN_PORTAL || state.nitems == 0);
TheChrisyd 0:a2d36977aec3 833 GD.wr16(RAM_SPRPAL + 2 * (32 ^ flip), COLOR(PAPER(state.portalattr)));
TheChrisyd 0:a2d36977aec3 834 GD.wr16(RAM_SPRPAL + 2 * (33 ^ flip), COLOR(INK(state.portalattr)));
TheChrisyd 0:a2d36977aec3 835
TheChrisyd 0:a2d36977aec3 836 // animated lives count, draw up to seven
TheChrisyd 0:a2d36977aec3 837 for (byte i = 0; i < 7; i++)
TheChrisyd 0:a2d36977aec3 838 if (i < (state.lives - 1))
TheChrisyd 0:a2d36977aec3 839 sprite(IMG_WILLYC + i, i << 4, 168, IMG_WILLYC + ((t >> 2) & 3));
TheChrisyd 0:a2d36977aec3 840 else
TheChrisyd 0:a2d36977aec3 841 hide(IMG_WILLYC + i);
TheChrisyd 0:a2d36977aec3 842
TheChrisyd 0:a2d36977aec3 843 GD.waitvblank();
TheChrisyd 0:a2d36977aec3 844 }
TheChrisyd 0:a2d36977aec3 845
TheChrisyd 0:a2d36977aec3 846 // play a complete game
TheChrisyd 0:a2d36977aec3 847 void loop()
TheChrisyd 0:a2d36977aec3 848 {
TheChrisyd 0:a2d36977aec3 849 title_screen();
TheChrisyd 0:a2d36977aec3 850 game_graphics();
TheChrisyd 0:a2d36977aec3 851
TheChrisyd 0:a2d36977aec3 852 state.level = START_LEVEL;
TheChrisyd 0:a2d36977aec3 853 state.score = 0;
TheChrisyd 0:a2d36977aec3 854 for (state.lives = 3; state.lives; state.lives--) {
TheChrisyd 0:a2d36977aec3 855 loadlevel(&levels[state.level]);
TheChrisyd 0:a2d36977aec3 856 // state.wy = 16;
TheChrisyd 0:a2d36977aec3 857 byte alive = 1;
TheChrisyd 0:a2d36977aec3 858 byte t = 0;
TheChrisyd 0:a2d36977aec3 859 while (alive) {
TheChrisyd 0:a2d36977aec3 860 drive_level(t);
TheChrisyd 0:a2d36977aec3 861
TheChrisyd 0:a2d36977aec3 862 // Willy draw
TheChrisyd 0:a2d36977aec3 863 byte frame = state.wf ^ (state.wd ? 7 : 0);
TheChrisyd 0:a2d36977aec3 864 loadspr16(IMG_WILLY, willy + (frame << 5), 255, 8 + 7);
TheChrisyd 0:a2d36977aec3 865 sprite(IMG_WILLY, state.wx, state.wy, IMG_WILLY);
TheChrisyd 0:a2d36977aec3 866
TheChrisyd 0:a2d36977aec3 867 // Willy movement
TheChrisyd 0:a2d36977aec3 868 // See http://www.seasip.demon.co.uk/Jsw/manic.mac
TheChrisyd 0:a2d36977aec3 869 // and http://jetsetwilly.jodi.org/poke.html
TheChrisyd 0:a2d36977aec3 870
TheChrisyd 0:a2d36977aec3 871 byte con = control();
TheChrisyd 0:a2d36977aec3 872 byte ychanged = 0; // if Y block changes must check for landing later
TheChrisyd 0:a2d36977aec3 873 if (state.jumping) {
TheChrisyd 0:a2d36977aec3 874 #define JUMP_APEX 9
TheChrisyd 0:a2d36977aec3 875 #define JUMP_FALL 11 // 1 2 3 4 5 6 7 8 9 10 11
TheChrisyd 0:a2d36977aec3 876 signed char moves[] = { -4, -4, -3, -3, -2, -2, -1, -1, 0, 0, 1, 1, 2, 2, 3, 3, 4 };
TheChrisyd 0:a2d36977aec3 877 byte index = min(sizeof(moves) - 1, state.jumping - 1);
TheChrisyd 0:a2d36977aec3 878 byte newy = state.wy + moves[index];
TheChrisyd 0:a2d36977aec3 879 state.jumping++;
TheChrisyd 0:a2d36977aec3 880 if (canbe(state.wx, newy)) {
TheChrisyd 0:a2d36977aec3 881 ychanged = (state.wy >> 3) != (newy >> 3);
TheChrisyd 0:a2d36977aec3 882 state.wy = newy;
TheChrisyd 0:a2d36977aec3 883 } else {
TheChrisyd 0:a2d36977aec3 884 state.jumping = max(state.jumping, JUMP_FALL); // halt ascent
TheChrisyd 0:a2d36977aec3 885 ychanged = 1;
TheChrisyd 0:a2d36977aec3 886 }
TheChrisyd 0:a2d36977aec3 887 }
TheChrisyd 0:a2d36977aec3 888 uint16_t feet_addr = atxy(state.wx >> 3, (state.wy + 16) >> 3);
TheChrisyd 0:a2d36977aec3 889 byte elem0 = GD.rd(feet_addr);
TheChrisyd 0:a2d36977aec3 890 byte elem1 = GD.rd(feet_addr + 1);
TheChrisyd 0:a2d36977aec3 891 byte elem = ((1 <= elem0) && (elem0 <= 31)) ? elem0 : elem1;
TheChrisyd 0:a2d36977aec3 892
TheChrisyd 0:a2d36977aec3 893 byte dx = 0xff;
TheChrisyd 0:a2d36977aec3 894 byte first_jump = (con & CONTROL_JUMP) && state.lastdx == 0xff;
TheChrisyd 0:a2d36977aec3 895 if (state.jumping) {
TheChrisyd 0:a2d36977aec3 896 dx = state.lastdx;
TheChrisyd 0:a2d36977aec3 897 } else if (!first_jump && (elem == ELEM_CONVEYOR) && (state.wd == state.conveyordir)) {
TheChrisyd 0:a2d36977aec3 898 dx = state.conveyordir;
TheChrisyd 0:a2d36977aec3 899 } else {
TheChrisyd 0:a2d36977aec3 900 if (con & CONTROL_RIGHT) {
TheChrisyd 0:a2d36977aec3 901 if (state.wd != 0) {
TheChrisyd 0:a2d36977aec3 902 state.wf ^= 3;
TheChrisyd 0:a2d36977aec3 903 state.wd = 0;
TheChrisyd 0:a2d36977aec3 904 } else {
TheChrisyd 0:a2d36977aec3 905 dx = 0;
TheChrisyd 0:a2d36977aec3 906 }
TheChrisyd 0:a2d36977aec3 907 } else if (con & CONTROL_LEFT) {
TheChrisyd 0:a2d36977aec3 908 if (state.wd == 0) {
TheChrisyd 0:a2d36977aec3 909 state.wf ^= 3;
TheChrisyd 0:a2d36977aec3 910 state.wd = 1;
TheChrisyd 0:a2d36977aec3 911 } else {
TheChrisyd 0:a2d36977aec3 912 dx = 1;
TheChrisyd 0:a2d36977aec3 913 }
TheChrisyd 0:a2d36977aec3 914 }
TheChrisyd 0:a2d36977aec3 915 }
TheChrisyd 0:a2d36977aec3 916 if (dx != 0xff) {
TheChrisyd 0:a2d36977aec3 917 if (state.wf != 3)
TheChrisyd 0:a2d36977aec3 918 state.wf++;
TheChrisyd 0:a2d36977aec3 919 else {
TheChrisyd 0:a2d36977aec3 920 byte newx = state.wx + (dx ? -8 : 8);
TheChrisyd 0:a2d36977aec3 921 if (canbe(newx, state.wy)) {
TheChrisyd 0:a2d36977aec3 922 state.wf = 0;
TheChrisyd 0:a2d36977aec3 923 state.wx = newx;
TheChrisyd 0:a2d36977aec3 924 }
TheChrisyd 0:a2d36977aec3 925 }
TheChrisyd 0:a2d36977aec3 926 }
TheChrisyd 0:a2d36977aec3 927 state.lastdx = dx;
TheChrisyd 0:a2d36977aec3 928
TheChrisyd 0:a2d36977aec3 929 if ((elem == ELEM_CONVEYOR) && (dx == 0xff) && !state.jumping)
TheChrisyd 0:a2d36977aec3 930 state.wd = state.conveyordir;
TheChrisyd 0:a2d36977aec3 931
TheChrisyd 0:a2d36977aec3 932 if (!state.jumping && (con & CONTROL_JUMP)) {
TheChrisyd 0:a2d36977aec3 933 if (canbe(state.wx, state.wy - 3)) {
TheChrisyd 0:a2d36977aec3 934 state.jumping = 1;
TheChrisyd 0:a2d36977aec3 935 state.wy -= 0;
TheChrisyd 0:a2d36977aec3 936 }
TheChrisyd 0:a2d36977aec3 937 }
TheChrisyd 0:a2d36977aec3 938
TheChrisyd 0:a2d36977aec3 939 byte onground = ((1 <= elem) && (elem <= 4)) || ((7 <= elem) && (elem <= 16));
TheChrisyd 0:a2d36977aec3 940 if (state.jumping) {
TheChrisyd 0:a2d36977aec3 941 if ((JUMP_APEX <= state.jumping) && ychanged && onground) {
TheChrisyd 0:a2d36977aec3 942 state.jumping = 0;
TheChrisyd 0:a2d36977aec3 943 state.wy &= ~7;
TheChrisyd 0:a2d36977aec3 944 }
TheChrisyd 0:a2d36977aec3 945 if (state.jumping >= 19) // stop x movement late in the jump
TheChrisyd 0:a2d36977aec3 946 state.lastdx = 0xff;
TheChrisyd 0:a2d36977aec3 947 } else {
TheChrisyd 0:a2d36977aec3 948 if (!onground) { // nothing to stand on, start falling
TheChrisyd 0:a2d36977aec3 949 state.jumping = JUMP_FALL;
TheChrisyd 0:a2d36977aec3 950 state.lastdx = 0xff;
TheChrisyd 0:a2d36977aec3 951 }
TheChrisyd 0:a2d36977aec3 952 }
TheChrisyd 0:a2d36977aec3 953 if (!state.jumping) {
TheChrisyd 0:a2d36977aec3 954 crumble(feet_addr);
TheChrisyd 0:a2d36977aec3 955 crumble(feet_addr + 1);
TheChrisyd 0:a2d36977aec3 956 }
TheChrisyd 0:a2d36977aec3 957
TheChrisyd 0:a2d36977aec3 958 if (((t & 7) == 0) ||
TheChrisyd 0:a2d36977aec3 959 ((state.level == 18) && inray(state.wx, state.wy))) {
TheChrisyd 0:a2d36977aec3 960 state.air--;
TheChrisyd 0:a2d36977aec3 961 plot_air();
TheChrisyd 0:a2d36977aec3 962 if (state.air == 0) {
TheChrisyd 0:a2d36977aec3 963 alive = 0;
TheChrisyd 0:a2d36977aec3 964 }
TheChrisyd 0:a2d36977aec3 965 }
TheChrisyd 0:a2d36977aec3 966 GD.waitvblank(); // let the screen display, then check for collisions
TheChrisyd 0:a2d36977aec3 967 byte coll = GD.rd(COLLISION + IMG_WILLY);
TheChrisyd 0:a2d36977aec3 968 if (coll <= (IMG_ITEM + 4)) {
TheChrisyd 0:a2d36977aec3 969 bump_score(100);
TheChrisyd 0:a2d36977aec3 970 hide(coll - IMG_ITEM);
TheChrisyd 0:a2d36977aec3 971 --state.nitems;
TheChrisyd 0:a2d36977aec3 972 } else if (coll == IMG_PORTAL) {
TheChrisyd 0:a2d36977aec3 973 if (CHEAT_OPEN_PORTAL || state.nitems == 0) {
TheChrisyd 0:a2d36977aec3 974 while (state.air) {
TheChrisyd 0:a2d36977aec3 975 squarewave(0, 800 + 2 * state.air, 100);
TheChrisyd 0:a2d36977aec3 976 state.air--;
TheChrisyd 0:a2d36977aec3 977 plot_air();
TheChrisyd 0:a2d36977aec3 978 bump_score(7);
TheChrisyd 0:a2d36977aec3 979 GD.waitvblank();
TheChrisyd 0:a2d36977aec3 980 }
TheChrisyd 0:a2d36977aec3 981 state.level = (state.level + 1) % 18;
TheChrisyd 0:a2d36977aec3 982 loadlevel(&levels[state.level]);
TheChrisyd 0:a2d36977aec3 983 }
TheChrisyd 0:a2d36977aec3 984 } else if (coll == IMG_SWITCH1) {
TheChrisyd 0:a2d36977aec3 985 if (!state.switch1) {
TheChrisyd 0:a2d36977aec3 986 state.switch1 = 1;
TheChrisyd 0:a2d36977aec3 987 sprite(IMG_SWITCH1, 48 - 8, 0, IMG_SWITCH1, 2);
TheChrisyd 0:a2d36977aec3 988 GD.wr(atxy(17, 11), 0);
TheChrisyd 0:a2d36977aec3 989 GD.wr(atxy(17, 12), 0);
TheChrisyd 0:a2d36977aec3 990 }
TheChrisyd 0:a2d36977aec3 991 } else if (coll == IMG_SWITCH2) {
TheChrisyd 0:a2d36977aec3 992 if (coll == IMG_SWITCH2) {
TheChrisyd 0:a2d36977aec3 993 state.switch2 = 1;
TheChrisyd 0:a2d36977aec3 994 sprite(IMG_SWITCH2, 144 - 8, 0, IMG_SWITCH2, 2);
TheChrisyd 0:a2d36977aec3 995 GD.wr(atxy(15, 2), 0);
TheChrisyd 0:a2d36977aec3 996 GD.wr(atxy(16, 2), 0);
TheChrisyd 0:a2d36977aec3 997 }
TheChrisyd 0:a2d36977aec3 998 } else if (coll != 0xff && !CHEAT_INVINCIBLE) {
TheChrisyd 0:a2d36977aec3 999 alive = 0;
TheChrisyd 0:a2d36977aec3 1000 }
TheChrisyd 0:a2d36977aec3 1001 t++;
TheChrisyd 0:a2d36977aec3 1002 }
TheChrisyd 0:a2d36977aec3 1003 // player died
TheChrisyd 0:a2d36977aec3 1004 clear_screen();
TheChrisyd 0:a2d36977aec3 1005 }
TheChrisyd 0:a2d36977aec3 1006 game_over();
TheChrisyd 0:a2d36977aec3 1007 }
TheChrisyd 0:a2d36977aec3 1008
TheChrisyd 0:a2d36977aec3 1009
TheChrisyd 0:a2d36977aec3 1010
TheChrisyd 0:a2d36977aec3 1011
TheChrisyd 0:a2d36977aec3 1012
TheChrisyd 0:a2d36977aec3 1013
TheChrisyd 0:a2d36977aec3 1014
TheChrisyd 0:a2d36977aec3 1015 int main(){
TheChrisyd 0:a2d36977aec3 1016 setup();
TheChrisyd 0:a2d36977aec3 1017 while(1){
TheChrisyd 0:a2d36977aec3 1018 loop();
TheChrisyd 0:a2d36977aec3 1019 }
TheChrisyd 0:a2d36977aec3 1020 }