Great game by Aurelién Rodot for Gamebuin. Ported by Jonne
Dependencies: PokittoLib
Fork of Asterocks by
Crabator.cpp
- Committer:
- Pokitto
- Date:
- 2018-05-02
- Revision:
- 13:9bf683c077df
- Parent:
- 5:42a7d8c6c8fd
File content as of revision 13:9bf683c077df:
//#include <SPI.h> #include <Pokitto.h> #include "Crabator.h" Pokitto::Core gb; //#include <EEPROM.h> //#include <avr/pgmspace.h> uint16_t hazepalette[16]; extern const byte font3x5[]; extern const byte font5x7[]; #define WORLD_W 16 #define WORLD_H 12 uint8_t byteWidth = (WORLD_H + 7) / 8; //declare all the sprites which are in the "sprites" tab extern const byte PROGMEM logo[]; extern const byte PROGMEM world[]; extern const byte PROGMEM tiles[]; extern const byte PROGMEM mobSprite[]; extern const byte PROGMEM bossSprite[]; extern const byte PROGMEM playerSprite[]; extern const byte PROGMEM splashSprite[]; extern const byte PROGMEM crateSprite[]; extern const byte PROGMEM fullHeart[]; extern const byte PROGMEM halfHeart[]; extern const byte PROGMEM emptyHeart[]; #define playerW 6 #define playerH 6 byte playerSpeed; int playerX; int playerY; int playerLife; #define playerLifeMax 6 byte playerDir; int cameraX; int cameraY; byte shake_magnitude; byte shake_timeLeft; const PROGMEM uint16_t player_damage_sound[] = {0x0045,0x564,0x0000}; ///////////////////////////////////// MOBS #define NUMMOBS 16 #define INITNUMMOBS 4 #define MOBSRATE 6 //how often is the mob number increased (every X kills) #define BOSSFREQ 16//one boss every X kills (initially) #define BOSSRATE 1 //every boss killed, the next one will spawn X kills earlier byte boss_nextSpawn; byte boss_freq; byte activeMobs; int mobs_x[NUMMOBS]; int mobs_y[NUMMOBS]; byte mobs_dir[NUMMOBS]; int8_t mobs_life[NUMMOBS]; // was char - Jonne byte mobs_size[NUMMOBS]; byte mob_maxLife = 10; byte boss_maxLife = 100; #define boss_size 6 #define mob_size 4 //const PROGMEM uint16_t mob_damage_sound[] = {0x5C1F, 0x0000}; const PROGMEM uint16_t mob_death_sound[] = {0x0045,0x184,0x0000}; #define NUMSPLASH 16 boolean splash_active[NUMSPLASH]; int splash_x[NUMSPLASH]; int splash_y[NUMSPLASH]; int splash_dir[NUMSPLASH]; ///////////////////////////////////// WEAPONS #define NUMBULLETS 10 int bullets_x[NUMBULLETS]; int bullets_y[NUMBULLETS]; byte bullets_dir[NUMBULLETS]; boolean bullets_active[NUMBULLETS]; byte bullets_weapon[NUMBULLETS]; int blast_x; int blast_y; byte blast_lifespan; byte blast_bullet; #define NUMWEAPONS 5 byte currentWeapon; byte nextShot; const char str357[] PROGMEM = ".357"; const char strP90[] PROGMEM = "P90"; const char strAK47[] PROGMEM = "AK47"; const char strRPG[] PROGMEM = "RPG"; const char strMG42[] PROGMEM = "MG42"; const char* const weapon_name[NUMWEAPONS] PROGMEM = { str357,strP90, strAK47, strRPG, strMG42}; const byte weapon_size[NUMWEAPONS] = { 2, 1, 2, 3, 2}; const byte weapon_damage[NUMWEAPONS] = { 10, 2, 3, 5, 4}; const byte weapon_rate[NUMWEAPONS] = { 30, 1, 2, 30, 1}; const byte weapon_speed[NUMWEAPONS] = { 4, 5, 3, 2, 5}; const byte weapon_spread[NUMWEAPONS] = { 1, 2, 1, 0, 2}; const byte weapon_ennemyRecoil[NUMWEAPONS] = { 3, 2, 3, 0, 3}; const byte weapon_playerRecoil[NUMWEAPONS] = { 0, 0, 1, 3, 3}; const unsigned int weapon_ammo[NUMWEAPONS] = { 9999, 500, 300, 20, 150}; unsigned int ammo; const uint16_t magnum_sound[] PROGMEM = {0x0045,0x7049,0x17C,0x784D,0x42C,0x0000}; const uint16_t p90_sound[] PROGMEM = {0x0045, 0x0154, 0x0000}; const uint16_t p90_alternative_sound[] PROGMEM = {0x0045, 0x014C, 0x0000}; const uint16_t ak47_sound[] PROGMEM = {0x0045, 0x012C, 0x0000}; const uint16_t mg42_sound[] PROGMEM = {0x0045,0x140,0x8141,0x7849,0x788D,0x52C,0x0000}; const uint16_t rpg_sound[] PROGMEM = {0x0045,0x8101,0x7F30,0x0000}; const uint16_t* const weapons_sounds[NUMWEAPONS] PROGMEM= { magnum_sound, p90_sound, ak47_sound, rpg_sound, mg42_sound}; const uint16_t blast_sound[] PROGMEM = {0x0045,0x7849,0x784D,0xA28,0x0000}; int crate_x, crate_y; const uint16_t power_up[] PROGMEM = {0x0005,0x140,0x150,0x15C,0x170,0x180,0x16C,0x154,0x160,0x174,0x184,0x14C,0x15C,0x168,0x17C,0x18C,0x0000}; ///////////////////////////////////// SCORE #define RANKMAX 5 //number of high scores to save int score = 0; //int lastScore = 0; int kills = 0; int highscore[RANKMAX]; //byte scoreDisplayTimeLeft; #define NAMELENGTH 10 char name[RANKMAX][NAMELENGTH+1]; // predefinitions void loadHighscore(); void initGame(); boolean collideWorld(int16_t, int16_t, uint8_t, uint8_t); void damageMob(byte,byte); boolean checkMobCollisions(byte); boolean collideOtherMobs(byte); void pausem(); void play(); void displayHighScores(); void saveHighscore(); void shakeScreen(); void drawWorld(int16_t, int16_t); void displayScore(); /*int16_t random (int lo,int hi) { return rand() % hi + lo; }*/ ///////////////////////////////////// SETUP void setup() { /** Set colors for this game **/ /** TO ADD COLORS TO YOUR GAMEBUINO GAME, SIMPLY USE VALUES ABOVE 1 !!!! */ gb.display.palette[1] = COLOR_WHITE; gb.display.palette[2] = COLOR_CYAN; gb.display.palette[3] = COLOR_MAGENTA; gb.display.palette[4] = COLOR_RED; gb.display.palette[8] = gb.display.RGBto565(0xff,0xfc,0); // small mob gb.display.palette[7] = gb.display.RGBto565(0x18,0x9a,0x61); // shrub shadow gb.display.palette[5] = gb.display.RGBto565(0x2c,0xff,0x0b); //world (shrubs) gb.display.palette[6] = gb.display.RGBto565(0xf7,0xb2,0);// crate gb.display.palette[9] = gb.display.RGBto565(0xfc,0x14,4);// big mob fc1404 gb.display.palette[10] = gb.display.RGBto565(0,0x53,0xae); // blue shadow gb.display.palette[15] = gb.display.RGBto565(0xff,0xfd,0xbf); // bright sunlight /** YOU HAVE 16 COLORS FREELY SELECTABLE FROM 256000 COLORS !!! **/ /** NOW LETS REBUILD! **/ for (uint16_t k =0 ; k<16; k++) hazepalette[k] = gb.display.interpolateColor(gb.display.palette[k],gb.display.palette[15],0); gb.display.paletteptr = hazepalette; gb.begin(); //while(1) gb.update(); gb.display.setFont(font5x7); gb.titleScreen(logo); gb.pickRandomSeed(); loadHighscore(); initGame(); } ///////////////////////////////////// LOOP void loop() { play(); if (!gb.isRunning()) return; pausem(); if (!gb.isRunning()) return; gb.titleScreen(logo); } ///////////////////////////////////// SCREEN COORD boolean screenCoord(int absoluteX, int absoluteY, int &x, int &y){ x = absoluteX - cameraX + 8; x = (x >= 0) ? x%(WORLD_W*8) : WORLD_W*8 + x%(WORLD_W*8); x -= 8; y = absoluteY - cameraY + 8; y = (y >= 0) ? y%(WORLD_H*8) : WORLD_H*8 + y%(WORLD_H*8); y -= 8; if((x > LCDWIDTH) || (y > LCDHEIGHT)) return false; return true; } #define wrap(i, imax) ((imax+i)%(imax)) ///////////////////////////////////// MOVE XYDS void moveXYDS(int &x, int &y, byte &dir, int8_t speed){ switch(dir){ //switch case depending on the mob's movement direction case 0: //going upward y -= speed; break; case 1: //left x -= speed; break; case 2: //downward y += speed; break; case 3: //right x += speed; break; } x = wrap(x, WORLD_W*8); y = wrap(y, WORLD_H*8); } ///////////////////////////////////// DISTANCE BETWEEN byte distanceBetween(int pos1, int pos2, int worldSize){ byte dist = abs(pos1 - pos2); dist = (dist < worldSize/2) ? dist : worldSize - dist; return dist; } ///////////////////////////////////// ASSIGN ARRAY void assignArray(char *array1, char *array2, byte length){ for(byte i=0; i<length; i++) array1[i] = array2[i]; } ///////////////////////////////////// SHOOT #define min(a,b) (((a)<(b))?(a):(b)) #define max(a,b) (((a)>(b))?(a):(b)) void shoot(){ if(ammo){ if(nextShot == 0){ for(byte thisBullet = 0; thisBullet < NUMBULLETS; thisBullet++){ if(!bullets_active[thisBullet]){ //look for the first inactive bullet bullets_active[thisBullet] = true; //set it to active as it's fired bullets_weapon[thisBullet] = currentWeapon; nextShot = weapon_rate[currentWeapon]; ammo--; //spawn a bullet with some spreading int8_t spreadMax = weapon_spread[currentWeapon]; // this was char, jonne int8_t spreadMin = (weapon_size[currentWeapon]%2==0) ? -spreadMax : -spreadMax-1; bullets_x[thisBullet] = playerX + playerW/2 + random(spreadMin,spreadMax+1) - weapon_size[currentWeapon]/2; bullets_y[thisBullet] = playerY + playerH/2 + random(spreadMin,spreadMax+1) - weapon_size[currentWeapon]/2; bullets_dir[thisBullet] = playerDir; blast_bullet = thisBullet; if(((currentWeapon == 1)||(currentWeapon==4))&&(gb.frameCount%2)) { } else{ gb.sound.playPattern((uint16_t*)pgm_read_word(&(weapons_sounds[currentWeapon])), 0); } if(currentWeapon == 1){//with P90 cancel every two sounds to avoid continuous beep if(rand()%2) gb.sound.playPattern(p90_alternative_sound, 0); } //player recoil byte recoil = weapon_playerRecoil[currentWeapon]; moveXYDS(playerX, playerY, playerDir, -recoil); for(byte i = 0; i<recoil; i++){ if(collideWorld(playerX, playerY, playerW, playerH)) moveXYDS(playerX, playerY, playerDir, 1); else break; } if(currentWeapon == 4){ //MG42 shake_magnitude = 1; shake_timeLeft = 2; } break; } } } } else{ currentWeapon = max(0, currentWeapon-1); //cut... no, magnum finally ammo = weapon_ammo[currentWeapon]; nextShot = 20; gb.popup(("Out of ammo!"), 30); } } ///////////////////////////////////// MOVE BULLETS void moveBullets(){ for(byte thisBullet = 0; thisBullet < NUMBULLETS; thisBullet++){ if(bullets_active[thisBullet]){ byte s = weapon_size[bullets_weapon[thisBullet]]; moveXYDS(bullets_x[thisBullet], bullets_y[thisBullet], bullets_dir[thisBullet], weapon_speed[bullets_weapon[thisBullet]]); //collide world if(collideWorld(bullets_x[thisBullet], bullets_y[thisBullet], s, s)){ bullets_active[thisBullet] = false; if(bullets_weapon[thisBullet] == 3){ //RPG blast_x = bullets_x[thisBullet]; blast_y = bullets_y[thisBullet]; blast_lifespan = 8; gb.sound.playPattern(blast_sound, 0); } else{ } continue; } for(byte thisMob=0; thisMob<activeMobs; thisMob++){ //for each mob if(gb.collideRectRect(bullets_x[thisBullet], bullets_y[thisBullet], s, s, mobs_x[thisMob], mobs_y[thisMob], mobs_size[thisMob], mobs_size[thisMob])){ if(bullets_weapon[thisBullet] == 3){ //RPG blast_x = bullets_x[thisBullet]; blast_y = bullets_y[thisBullet]; blast_lifespan = 8; gb.sound.playPattern(blast_sound, 0); } else { damageMob(thisMob, thisBullet); } bullets_active[thisBullet] = false; break; } } } } } ///////////////////////////////////// EXPLODE void explode(){ if(blast_lifespan){ blast_lifespan--; //gb.buzz(50+random(0,100),40); shake_magnitude = 4; shake_timeLeft = 2; //pick a random blast byte s = 10 + random (0,6); int x = blast_x + random(-4,4) -s/2; int y = blast_y + random(-4,4) -s/2; //damages for(byte thisMob=0; thisMob<activeMobs; thisMob++){ if(gb.collideRectRect(mobs_x[thisMob], mobs_y[thisMob], mobs_size[thisMob], mobs_size[thisMob], x,y,s,s)) damageMob(thisMob,blast_bullet); } //display int x_screen, y_screen; if(screenCoord(x, y, x_screen, y_screen)) gb.display.fillRect(x_screen, y_screen, s, s); } } ///////////////////////////////////// DRAW BULLETS void drawBullets(){ gb.display.setColor(1); for(byte thisBullet = 0; thisBullet < NUMBULLETS; thisBullet++){ if(bullets_active[thisBullet]){ int x, y; if(screenCoord(bullets_x[thisBullet], bullets_y[thisBullet], x, y)){ byte s = weapon_size[bullets_weapon[thisBullet]]; if(s==1) gb.display.drawPixel(x, y); else gb.display.fillRect(x, y, s, s); } } } } ///////////////////////////////////// DRAW AMMO OVERLAY void drawAmmoOverlay(){ if(ammo){ // text shadow /* gb.display.setColor(10); gb.display.cursorX = -1; gb.display.cursorY = LCDHEIGHT-gb.display.fontHeight-1; gb.display.print((const __FlashStringHelper*)pgm_read_word(weapon_name+currentWeapon)); //some crazy casts gb.display.cursorX = -1; gb.display.cursorY = LCDHEIGHT-gb.display.fontHeight+1; gb.display.print((const __FlashStringHelper*)pgm_read_word(weapon_name+currentWeapon)); //some crazy casts gb.display.cursorX = 1; gb.display.cursorY = LCDHEIGHT-gb.display.fontHeight-1; gb.display.print((const __FlashStringHelper*)pgm_read_word(weapon_name+currentWeapon)); //some crazy casts gb.display.cursorX = +1; gb.display.cursorY = LCDHEIGHT-gb.display.fontHeight+1; gb.display.print((const __FlashStringHelper*)pgm_read_word(weapon_name+currentWeapon)); //some crazy casts */ // white on top gb.display.setColor(2); gb.display.cursorX = 0; gb.display.cursorY = LCDHEIGHT-gb.display.fontHeight; gb.display.print((char*)(weapon_name[currentWeapon])); //some crazy casts if(nextShot>2) gb.display.fillRect(-2,LCDHEIGHT-2,nextShot,2); if(currentWeapon > 0){ //don't display the ammo of the cut byte xOffset = 0; if (ammo < 100) xOffset += gb.display.fontWidth; if (ammo < 10) xOffset += gb.display.fontWidth; gb.display.cursorX = LCDWIDTH-3*gb.display.fontWidth+xOffset; gb.display.cursorY = LCDHEIGHT-gb.display.fontHeight; gb.display.print(ammo); } else { gb.display.cursorX = LCDWIDTH-3*gb.display.fontWidth; gb.display.cursorY = LCDHEIGHT-gb.display.fontHeight; gb.display.print(("inf")); } } } ///////////////////////////////////// SET SPLASH void setSplash(int x, int y){ for(byte thisSplash = 0; thisSplash < NUMSPLASH; thisSplash++){ if(!splash_active[thisSplash]){ //look for the first inactive splash splash_active[thisSplash] = true; //set it to active splash_x[thisSplash] = x; splash_y[thisSplash] = y; splash_dir[thisSplash] = random(0,5); break; } } } ///////////////////////////////////// DRAW SPLASHES void drawSplashes(){ for(byte thisSplash = 0; thisSplash < NUMSPLASH; thisSplash++){ if(splash_active[thisSplash]){ int x, y; if(screenCoord(splash_x[thisSplash], splash_y[thisSplash], x, y)){ //if the splash is in the screen //draw it gb.display.drawBitmap(x-2, y-2, splashSprite, splash_dir[thisSplash], NOFLIP); } else{ //erase it if it is out of the screen splash_active[thisSplash] = false; } } } } ///////////////////////////////////// SPAWN CRATE void spawnCrate(){ boolean okay = false; while (okay == false){ //pick a random location crate_x = random(0, WORLD_W) * 8; crate_y = random(0, WORLD_H) * 8; okay = true; //is that in a wall ? if(collideWorld(crate_x, crate_y, 8, 8)){ okay = false; } //is that in the screen ? int x, y; if(screenCoord(crate_x, crate_y, x, y)){ okay = false; } } } ///////////////////////////////////// COLLIDE CRATE void collideCrate(){ if(gb.collideRectRect(crate_x+2, crate_y+2, 4, 4, playerX, playerY, playerW, playerH)){ if (score <5){ gb.popup(("Earn $5 first"), 30); return; } if(currentWeapon<(NUMWEAPONS-1)){ gb.popup(("Upgraded !"), 30); gb.sound.playPattern(power_up,0); } else{ gb.popup(("Refilled !"), 30); } score -= 5; spawnCrate(); currentWeapon = min(NUMWEAPONS-1, currentWeapon+1); //upgrade to the next weapon ammo = weapon_ammo[currentWeapon]; //gb.popup(weapon_name[currentWeapon], 30); //if(random(0,score/10)==0) //the higher is your score, the less life you will find in crates playerLife = min(playerLife+1, playerLifeMax); int hazefactor = (255-42*playerLife)*2/3; for (uint16_t k =0 ; k<16; k++) hazepalette[k] = gb.display.interpolateColor(gb.display.palette[k],gb.display.palette[4],hazefactor); //gb.buzz(2000,40); } } ///////////////////////////////////// DRAW CRATE void drawCrate(){ int x, y; if(screenCoord(crate_x, crate_y, x, y)){ gb.display.setColor(8); gb.display.drawBitmap(x, y, crateSprite); //gb.display.setColor(6); //gb.display.drawBitmap(x-1, y, crateSprite); } } ///////////////////////////////////// SPAWN ONE MOB boolean spawnMob(byte thisMob){ boolean okay = false; byte timout = 0; mobs_size[thisMob] = mob_size; mobs_life[thisMob] = mob_maxLife; if(!boss_nextSpawn){ //spawn big mobs every 20 kills starting from 15 boss_freq = max(boss_freq - BOSSRATE, 1); boss_nextSpawn = boss_freq; mobs_size[thisMob] = boss_size; mobs_life[thisMob] = boss_maxLife; //gb.popup("Boss spawned !", 30); } while(okay == false){ //do the following until it's okay //pick a random location mobs_x[thisMob] = random(0, WORLD_W*2) * 4; mobs_y[thisMob] = random(0, WORLD_H*2) * 4; //and check if that position is okay okay = true; if(checkMobCollisions(thisMob)){ okay = false; continue; } //spawn the mobs out of the player's view if(wrap(mobs_x[thisMob] - cameraX, WORLD_W*8) < LCDWIDTH){ okay = false; continue; } if(wrap(mobs_y[thisMob] - cameraY, WORLD_H*8) < LCDHEIGHT){ okay = false; continue; } } mobs_dir[thisMob] = rand() % 4; //then pick a random direction return true; } ///////////////////////////////////// SPAWN ALL MOBS boolean spawnMobs(){ for(byte thisMob=0; thisMob<activeMobs; thisMob++){ //put mobs far away mobs_x[thisMob] = 9999; mobs_y[thisMob] = 9999; } for(byte thisMob=0; thisMob<activeMobs; thisMob++){ if(!spawnMob(thisMob)) //try to spawn a mob return false; //return false if an error occur } return true; } ///////////////////////////////////// MOVE MOBS void moveMobs(){ for(byte thisMob=0; thisMob<activeMobs; thisMob++){ //for each mob int x = wrap(mobs_x[thisMob] - cameraX, WORLD_W*8); int y = wrap(mobs_y[thisMob] - cameraY, WORLD_H*8); //if the mob is close to the screen if( (distanceBetween(mobs_x[thisMob], playerX, WORLD_W*8) < (LCDWIDTH+32)) && (distanceBetween(mobs_y[thisMob], playerY, WORLD_H*8) < (LCDHEIGHT+32))){ moveXYDS(mobs_x[thisMob], mobs_y[thisMob], mobs_dir[thisMob], 1); //go forward //if there is a collision, move a step backward and pick a new random direction if(checkMobCollisions(thisMob)){ moveXYDS(mobs_x[thisMob], mobs_y[thisMob], mobs_dir[thisMob], -1); mobs_dir[thisMob] = rand()%4; continue; } //go in a random direction if(random(0,32)==0){ mobs_dir[thisMob] = rand()%4; continue; } //go in the direction on the player (randomly choose between X and Y axis) if(random(0,16)==0){ if(random(0,2)){ //get closer to the player on the X axis if((LCDWIDTH/2 - x) > 0){ //go to the left if the player is on the left mobs_dir[thisMob] = 3; } else{ // or go to the right if the player is on the right mobs_dir[thisMob] = 1; } } //if the distance between the player and the mob is larger on the Y axis else { //get closer to the player on the Y axis if((LCDHEIGHT/2 - y) > 0){ //go downward mobs_dir[thisMob] = 2; } else{ //go upward mobs_dir[thisMob] = 0; } } } } } } ///////////////////////////////////// CHECK MOB COLLISIONS boolean checkMobCollisions(byte thisMob){ //check collision with the world if(collideWorld(mobs_x[thisMob], mobs_y[thisMob], mobs_size[thisMob], mobs_size[thisMob])) return true; //check collision with other mobs if(collideOtherMobs(thisMob)) return true; return false; } ///////////////////////////////////// CHECK IF A MOB COLLIDE ANOTHER ONE boolean collideOtherMobs(byte thisMob){ for(byte otherMob=0; otherMob<activeMobs; otherMob++){ if(thisMob == otherMob) //don't check collision with iself >_<' continue; if(gb.collideRectRect(mobs_x[thisMob], mobs_y[thisMob], mobs_size[thisMob], mobs_size[thisMob], mobs_x[otherMob], mobs_y[otherMob], mobs_size[otherMob], mobs_size[otherMob])){ return true; } } return false; } ///////////////////////////////////// DRAW MOBS void drawMobs(){ gb.display.setColor(1); //8 for(byte thisMob=0; thisMob<activeMobs; thisMob++){ //int x = wrap(mobs_x[thisMob] - cameraX + playerW/2, WORLD_W*8); //int y = wrap(mobs_y[thisMob] - cameraY + playerH/2, WORLD_H*8); int x, y; if(screenCoord(mobs_x[thisMob], mobs_y[thisMob], x, y)){ if(mobs_size[thisMob] != boss_size) { gb.display.setColor(8); gb.display.drawBitmap(x-2, y-2, mobSprite, mobs_dir[thisMob], NOFLIP); } else { gb.display.setColor(9); gb.display.drawBitmap(x-1, y-1, bossSprite, mobs_dir[thisMob], NOFLIP); } //gb.fillRect(x, y, mobs_size[thisMob], mobs_size[thisMob], BLACK); } } } ///////////////////////////////////// DAMAGE MOB void damageMob(byte thisMob, byte thisBullet){ mobs_life[thisMob] -= weapon_damage[bullets_weapon[thisBullet]]; //recoil byte recoil = weapon_ennemyRecoil[bullets_weapon[thisBullet]]; if(mobs_size[thisMob] == boss_size) recoil /= 4; moveXYDS(mobs_x[thisMob], mobs_y[thisMob], bullets_dir[thisBullet], recoil); if(checkMobCollisions(thisMob)) moveXYDS(mobs_x[thisMob], mobs_y[thisMob], bullets_dir[thisBullet], -recoil); mobs_dir[thisMob] = (bullets_dir[thisBullet] + 2) % 4; //gb.buzz(1200,10); if(mobs_life[thisMob] <= 0){ //the mob dies score++; kills++; boss_nextSpawn--; if(bullets_weapon[thisBullet]!=3){ //if it's no the RPG gb.sound.playPattern(mob_death_sound,0); } if(mobs_size[thisMob] == boss_size) score += 4; setSplash(mobs_x[thisMob], mobs_y[thisMob]); int x, y; if(screenCoord(mobs_x[thisMob], mobs_y[thisMob], x, y)){ gb.display.fillRect(x-1, y-1, mobs_size[thisMob]+1, mobs_size[thisMob]+1); } //gb.buzz(1400,20); spawnMob(thisMob); if(activeMobs < NUMMOBS){ //if the max isn't reached if(activeMobs < (kills/MOBSRATE)+INITNUMMOBS){ //every 8 mobs killed activeMobs++; //add a mob spawnMob(activeMobs-1); //spawn the mob added } } } else { //the mob survives } } #define PAUSEMENULENGTH 5 const char strPlay[] PROGMEM = "Play"; const char strRestart[] PROGMEM = "Restart"; const char strHighScores[] PROGMEM = "High scores"; const char strSystemInfo[] PROGMEM = "System Info"; const char strMainMenu[] PROGMEM = "Main Menu"; const char* const pauseMenu[PAUSEMENULENGTH] PROGMEM = { strPlay, strRestart, strHighScores, strSystemInfo, strMainMenu }; ///////////////////////////////////// PAUSE void pausem(){ while(gb.isRunning()){ if(gb.update()){ switch(gb.menu(pauseMenu, PAUSEMENULENGTH)){ case 0: //resume gb.wait(100); gb.display.setFont(font3x5); play(); gb.display.setFont(font5x7); gb.battery.show = true; break; case 1: //restart initGame(); gb.display.setFont(font3x5); play(); gb.display.setFont(font5x7); gb.battery.show = true; return; case 2: //high scores displayHighScores(); break; case 3: //System info gb.display.setFont(font3x5); while (1) { if (gb.update()) { if (gb.buttons.pressed(BTN_C)) { gb.display.setFont(font5x7); gb.sound.playCancel(); break; } //gb.display.setCursor(0, 0); gb.display.print(("Bat:")); gb.display.print(gb.battery.voltage); gb.display.println(("mV")); gb.display.print(("Bat lvl:")); gb.display.print(gb.battery.level); gb.display.println(("/4")); gb.display.print(("Light:")); gb.display.println(gb.backlight.ambientLight); gb.display.print(("Backlight:")); gb.display.println(gb.backlight.backlightValue); gb.display.print(("Volume:")); gb.display.print(gb.sound.getVolume()); //gb.display.print(F("/")); //gb.display.println(gb.sound.volumeMax); gb.display.print("Mobs:"); gb.display.print(activeMobs); gb.display.print("/"); gb.display.println(NUMMOBS); gb.display.print("Killed:"); gb.display.println(kills); } } break; case 4: //change game //gb.changeGame(); gb.titleScreen(logo); break; default: return; } } } } ///////////////////////////////////// DISPLAY HIGHSCORES void displayHighScores(){ while(true){ if(gb.update()){ gb.display.cursorX = 9+random(0,2); gb.display.cursorY = 0+random(0,2); gb.display.println(("HIGH SCORES")); gb.display.textWrap = false; gb.display.cursorX = 0; gb.display.cursorY = gb.display.fontHeight; for(byte thisScore=0; thisScore<RANKMAX; thisScore++){ if(highscore[thisScore]==0) gb.display.print('-'); else gb.display.print(name[thisScore]); gb.display.cursorX = LCDWIDTH-3*gb.display.fontWidth; gb.display.cursorY = gb.display.fontHeight+gb.display.fontHeight*thisScore; gb.display.println(highscore[thisScore]); } if(gb.buttons.pressed(BTN_A) || gb.buttons.pressed(BTN_B) || gb.buttons.pressed(BTN_C)){ gb.sound.playOK(); break; } } } } ///////////////////////////////////// INIT GAME void initGame(){ //lastScore = score; if(score > highscore[RANKMAX-1]){ //if the score is better than the worse high score saveHighscore(); } //scoreDisplayTimeLeft = 64; score = 0; kills = 0; currentWeapon = 0; //magnum ammo = 9999; nextShot = 0; shake_timeLeft = 0; playerLife = playerLifeMax; boss_freq = BOSSFREQ; boss_nextSpawn = boss_freq; activeMobs = INITNUMMOBS; //6 initial mobs do{ do{ playerX = random(0, WORLD_W) * 8; playerY = random(0, WORLD_H) * 8; } while(collideWorld(playerX, playerY, playerW, playerH)); cameraX = playerX - LCDWIDTH/2 + playerW/2; cameraY = playerY - LCDHEIGHT/2 + playerW/2; } while(!spawnMobs()); //do that until mobs are spawned without error //spawn crate spawnCrate(); //reset bullets for(byte thisBullet = 0; thisBullet < NUMBULLETS; thisBullet++){ bullets_active[thisBullet] = false; } //reset splashes for(byte thisSplash = 0; thisSplash < NUMSPLASH; thisSplash++){ splash_active[thisSplash] = false; } blast_lifespan = 0; //reset explosion } ///////////////////////////////////// PLAY void play(){ gb.battery.show = false; byte i = 0; while(i < 10 && gb.isRunning()){ if(gb.update()){ gb.display.fontSize = 2; gb.display.cursorX = 6; gb.display.cursorY = 16; gb.display.print(("LET'S GO!")); i++; } } gb.display.fontSize = 1; gb.popup(("\x15:shoot \x16:run"), 60); while(gb.isRunning()){ if(gb.update()){ if(gb.buttons.pressed(BTN_C)){ gb.sound.playCancel(); return; //gb.battery.show = true; //pause(); //gb.battery.show = false; } boolean moved = false; if(gb.buttons.repeat(BTN_RIGHT, 1)){ playerDir = 3; moved = true; } else{ if(gb.buttons.repeat(BTN_LEFT, 1)){ playerDir = 1; moved = true; } } if(gb.buttons.repeat(BTN_DOWN, 1)){ playerDir = 2; moved = true; } else{ if(gb.buttons.repeat(BTN_UP, 1)){ playerDir = 0; moved = true; } } if(moved){ moveXYDS(playerX, playerY, playerDir, playerSpeed); if(collideWorld(playerX, playerY, playerW, playerH)) moveXYDS(playerX, playerY, playerDir, -playerSpeed); } cameraX = playerX + playerW/2 - LCDWIDTH/2; cameraY = playerY + playerH/2 - LCDHEIGHT/2; shakeScreen(); /* gb.display.setColor(7); // shrub shadow drawWorld(cameraX-3, cameraY-3); gb.display.setColor(5); // shrubs drawWorld(cameraX, cameraY); */ int x, y; screenCoord(playerX, playerY, x, y); //gb.display.setColor(10); //shadow int8_t ox=0,oy=0; ox--; //gb.display.drawBitmap(x+ox, y+oy, playerSprite, playerDir, NOFLIP); gb.display.setColor(3); //magenta gb.display.drawBitmap(x, y, playerSprite, playerDir, NOFLIP); byte thisSprite = 0; moveMobs(); drawMobs(); if(nextShot) nextShot--; if(gb.buttons.repeat(BTN_A, 1) && !gb.buttons.repeat(BTN_B, 1)){ shoot(); } if (gb.buttons.repeat(BTN_B, 1)){ playerSpeed = 2; } else { playerSpeed = 1; } moveBullets(); drawBullets(); explode(); gb.display.setColor(4); drawSplashes(); collideCrate(); drawCrate(); /** DRAW WORLD**/ //gb.display.setColor(7); // shrub shadow //drawWorld(cameraX-2, cameraY-2); gb.display.setColor(5); // shrubs drawWorld(cameraX, cameraY); //life remaining for(byte i=0; i<=playerLifeMax/2; i+=1){ if((i*2)<=playerLife){ gb.display.setColor(4); gb.display.drawBitmap(LCDWIDTH-i*9+2, 0, fullHeart); } else{ gb.display.setColor(0,0); gb.display.drawBitmap(LCDWIDTH-i*9+2, 0, fullHeart); gb.display.setColor(4,0); gb.display.drawBitmap(LCDWIDTH-i*9+2, 0, emptyHeart); } } if(!playerLife){ if((gb.frameCount%2)==0){ shake_magnitude = 2; shake_timeLeft = 1; } } else{ if(playerLife == 1){ shake_magnitude = 1; shake_timeLeft = 1; } } if(playerLife%2){ //odd number gb.display.setColor(4,0); gb.display.drawBitmap(LCDWIDTH-(playerLife/2+1)*9+2, 0, halfHeart); } drawAmmoOverlay(); displayScore(); for(byte thisMob=0; thisMob<activeMobs; thisMob++){ if(gb.collideRectRect(mobs_x[thisMob],mobs_y[thisMob], mobs_size[thisMob], mobs_size[thisMob], playerX, playerY, playerW, playerH)){ playerLife--; //pokConsoleAddMessage(MSG_PRINT,V_INT16,playerLife); shake_magnitude = 2; shake_timeLeft = 4; if(mobs_size[thisMob] == boss_size){ playerLife--; shake_magnitude = 3; shake_timeLeft = 4; } //int hazefactor = (255-42*playerLife)*2/3; //for (uint16_t k =0 ; k<16; k++) hazepalette[k] = pokInterpolateColor(palette[k],palette[4],hazefactor); gb.sound.playPattern(player_damage_sound, 0); spawnMob(thisMob); if(playerLife < 0){ byte timer=0; while(1){ if(gb.update()){ //gb.display.setColor(1); drawMobs(); drawBullets(); drawSplashes(); drawCrate(); drawAmmoOverlay(); displayScore(); drawWorld(cameraX, cameraY); gb.display.drawBitmap(x-1, y-1, playerSprite, playerDir, NOFLIP); gb.display.setColor(WHITE); gb.display.fillRect(0,0,timer*2,LCDHEIGHT); gb.display.fillRect(LCDWIDTH-timer*2,0,timer*2,LCDHEIGHT); gb.display.setColor(BLACK, WHITE); gb.display.cursorX = 12; gb.display.cursorY = 1; gb.display.print(("GAME OVER!")); timer++; if(timer==((LCDWIDTH/4)+10)) break; } //int hazefactor = 0; //for (uint16_t k =0 ; k<16; k++) hazepalette[k] = pokInterpolateColor(palette[k],palette[4],hazefactor); } while(1){ if(gb.update()){ if(score > highscore[RANKMAX-1]){ //if the score is better than the worse high score gb.display.cursorX = 2+rand()%2; gb.display.cursorY = 0+rand()%2; gb.display.print(("NEW HIGHSCORE")); } else{ gb.display.cursorX = 12; gb.display.cursorY = 1; gb.display.print(("GAME OVER!")); } gb.display.cursorX = 0; gb.display.cursorY = 12; gb.display.print(("You made $")); gb.display.print(score); gb.display.print(("\nby killing\n")); gb.display.print(kills); gb.display.print((" crabs.")); gb.display.cursorX = 0; gb.display.cursorY = 40; gb.display.print(("\x15:accept")); if(gb.buttons.pressed(BTN_A)){ gb.sound.playOK(); break; } } } initGame(); break; } } } } } } ///////////////////////////////////// DISPLAY SCORE void displayScore(){ // blue on bottom /* gb.display.setColor(10); gb.display.cursorX = -1; gb.display.cursorY = -1; gb.display.print('$'); gb.display.println(score); gb.display.cursorX = 0; gb.display.cursorY = -1; gb.display.print('$'); gb.display.println(score); gb.display.cursorX = 1; gb.display.cursorY = -1; gb.display.print('$'); gb.display.println(score); gb.display.cursorX = -1; gb.display.cursorY = 0; gb.display.print('$'); gb.display.println(score); gb.display.cursorX = 0; gb.display.cursorY = 1; gb.display.print('$'); gb.display.println(score); gb.display.cursorX = -1; gb.display.cursorY = 1; gb.display.print('$'); gb.display.println(score); gb.display.cursorX = 0; gb.display.cursorY = 1; gb.display.print('$'); gb.display.println(score); gb.display.cursorX = 1; gb.display.cursorY = 1; gb.display.print('$'); gb.display.println(score); gb.display.cursorX = 1; gb.display.cursorY = 0; gb.display.print('$'); gb.display.println(score);*/ // white on top gb.display.setColor(8); gb.display.cursorX = 0; gb.display.cursorY = 0; gb.display.print('$'); gb.display.println(score); } ///////////////////////////////////// SHAKE SCREEN void shakeScreen(){ if(shake_timeLeft){ shake_timeLeft--; cameraX += random(-shake_magnitude,shake_magnitude+1); cameraY += random(-shake_magnitude,shake_magnitude+1); byte backlightStep = gb.backlight.backlightMax / 4; gb.backlight.set(gb.backlight.backlightValue-random(0,backlightStep*shake_magnitude)); } } ///////////////////////////////////// LOAD HIGHSCORE void loadHighscore(){ /* for(byte thisScore = 0; thisScore < RANKMAX; thisScore++){ for(byte i=0; i<NAMELENGTH; i++){ name[thisScore][i] = EEPROM.read(i + thisScore*(NAMELENGTH+2)); } highscore[thisScore] = EEPROM.read(NAMELENGTH + thisScore*(NAMELENGTH+2)) & 0x00FF; //LSB highscore[thisScore] += (EEPROM.read(NAMELENGTH+1 + thisScore*(NAMELENGTH+2)) << 8) & 0xFF00; //MSB highscore[thisScore] = (highscore[thisScore]==0xFFFF) ? 0 : highscore[thisScore]; }*/ //jonnehw } ///////////////////////////////////// SAVE HIGHSCORE void saveHighscore(){ //gb.getDefaultName(name[RANKMAX-1]); gb.display.setFont(font5x7); gb.keyboard(name[RANKMAX-1], NAMELENGTH+1); highscore[RANKMAX-1] = score; for(byte i=RANKMAX-1; i>0; i--){ //bubble sorting FTW if(highscore[i-1] < highscore[i]){ char tempName[NAMELENGTH]; strcpy(tempName, name[i-1]); strcpy(name[i-1], name[i]); strcpy(name[i], tempName); unsigned int tempScore; tempScore = highscore[i-1]; highscore[i-1] = highscore[i]; highscore[i] = tempScore; } else{ break; } } /* for(byte thisScore = 0; thisScore < RANKMAX; thisScore++){ for(byte i=0; i<NAMELENGTH; i++){ EEPROM.write(i + thisScore*(NAMELENGTH+2), name[thisScore][i]); } EEPROM.write(NAMELENGTH + thisScore*(NAMELENGTH+2), highscore[thisScore] & 0x00FF); //LSB EEPROM.write(NAMELENGTH+1 + thisScore*(NAMELENGTH+2), (highscore[thisScore] >> 8) & 0x00FF); //MSB }*/// jonnehw displayHighScores(); } const byte PROGMEM logo[] = { 64,36, //width and height B11111111, B11111111, B11111111, B11111111, B11111111, B11111111, B11111111, B11111111, B10000000, B00000000, B00000000, B00000000, B00000000, B00000001, B01000001, B01000001, B10000000, B00000000, B00000001, B10000000, B00000110, B00000100, B00010100, B00010001, B10000000, B00000000, B00000001, B10000000, B00000110, B00000000, B10000000, B10000001, B10000000, B00000000, B00000001, B10000000, B00000110, B00000000, B00000000, B01000001, B10001111, B00111111, B01111001, B10111001, B11101111, B10011110, B01111110, B00010001, B10011001, B10111011, B11001101, B11111011, B00110110, B00110011, B01110110, B00100001, B10011001, B10110011, B00001101, B10011000, B00110110, B00110011, B01100110, B01000101, B10011000, B00110000, B01111101, B10011001, B11110110, B00110011, B01100000, B00000011, B10011000, B00110000, B11001101, B10011011, B00110110, B00110011, B01100001, B01000001, B10011000, B00110000, B11001101, B10011011, B00110110, B00110011, B01100100, B00010101, B10011001, B10110000, B11011101, B10011011, B01110110, B00110011, B01100000, B10001001, B11001111, B00110000, B11111101, B11110011, B11110011, B10011110, B01100000, B01000001, B10000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000101, B00010101, B10100000, B00000000, B00000000, B00000000, B00000000, B00100000, B10000000, B10100001, B11000000, B00000000, B00011000, B00001100, B00000000, B00000000, B00001111, B11110101, B10010000, B00000000, B00100100, B01010010, B10000000, B11110000, B00001000, B00010011, B11010000, B00000010, B10101100, B01010110, B10000001, B11111000, B00001111, B11110001, B10011001, B01000011, B00110100, B11011010, B11100001, B11111000, B00001010, B00110101, B10010101, B01000010, B00100101, B01010010, B10000001, B11111000, B00001100, B01011001, B11011101, B11000010, B00011001, B11001100, B01100001, B01101000, B00001111, B11110001, B10000000, B01000000, B00000000, B00000000, B00000000, B11110000, B00001000, B00010101, B10000000, B10000000, B00000000, B00000000, B00000000, B00000000, B00001111, B11110001, B11000000, B00000000, B00000000, B00000000, B00000110, B00000000, B00000000, B00000101, B10000000, B00000000, B00000000, B00000000, B00001111, B00000000, B00000000, B00000011, B11000000, B00000000, B00000000, B00000000, B00000110, B00000000, B00000000, B00000001, B10010000, B00000000, B00000000, B00000000, B00001001, B00000000, B00000000, B00000101, B10000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00001001, B11000001, B01000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000001, B10010011, B11010000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000101, B10100011, B11100000, B00000000, B00000000, B00000000, B00000110, B00000000, B00000001, B10000011, B11100000, B00000101, B01000101, B01000000, B00001111, B00000101, B01000101, B10000011, B11010000, B00000010, B00000010, B00000000, B00000110, B00000010, B00000011, B10000001, B01000000, B00000001, B01000001, B01000000, B00001001, B00000001, B01000001, B10000000, B00000000, B00000100, B00010100, B00010000, B00000000, B00000100, B00010101, B11111111, B11111111, B11111111, B11111111, B11111111, B11111111, B11111111, B11111111, }; const byte PROGMEM world[]= { B11001111,B11001111, B10000000,B01000000, B00000001,B11111100, B00111001,B11100000, B00001100,B00000110, B10000111,B00011111, B11000011,B11000001, B10000001,B00000001, B00111100,B00110011, B00000111,B11100000, B10000100,B00001001, B11000001,B11001111, }; const byte PROGMEM tiles[]= { 8,8, B01010100, B00100000, B00010100, B01000001, B10001000, B00000100, B01010001, B00001010, }; const byte PROGMEM mobSprite[]= { 8,8, B00000000, B00000000, B00011000, B00111100, B00011000, B00100100, B00000000, B00000000, }; const byte PROGMEM bossSprite[]= { 8,8, B00000000, B00100100, B00011000, B01111110, B00111100, B01111110, B00111100, B00000000, }; const byte PROGMEM playerSprite[]= { 8,8, B00000000, B00111100, B01011010, B01111110, B01111110, B01111110, B00111100, B00000000, }; const byte PROGMEM splashSprite[]= { 8,8, B00000000, B00001000, B00000000, B00011000, B00111000, B10110100, B00000000, B00010000, }; const byte PROGMEM crateSprite[]= { 8,8, B11111111, B10000001, B11111111, B10100011, B11000101, B11111111, B10000001, B11111111, }; const byte PROGMEM fullHeart[]= { 8,8, B01101100, B11111110, B11111110, B01111100, B00111000, B00010000, B00000000, B00000000, }; const byte PROGMEM halfHeart[]= { 8,8, B00000000, B00001100, B00011100, B00011000, B00010000, B00000000, B00000000, B00000000, }; const byte PROGMEM emptyHeart[]= { 8,8, B01101100, B10010010, B10000010, B01000100, B00101000, B00010000, B00000000, B00000000, }; ///////////////////////////////////// GET TILE boolean getTile(uint8_t i, uint8_t j){ uint8_t test; test = (pgm_read_byte(world + (j%WORLD_H)*byteWidth + (i%WORLD_W)/8) & (B10000000 >> (i % 8))); if(test) return true; else return false; } ///////////////////////////////////// DRAW WORLD void drawWorld(int16_t x, int16_t y){ int8_t i, j, w = WORLD_W, h = WORLD_H; x = wrap(x,w*8); y = wrap(y,h*8); for(j=y/8; j < (LCDHEIGHT/8 + y/8 + 1); j++) { for(i=x/8; i < (LCDWIDTH/8 + x/8 + 1); i++ ) { if(getTile(i, j)) { gb.display.drawBitmap(i*8 - x, j*8 - y, tiles); } } } } ///////////////////////////////////// COLLIDE WORLD boolean collideWorld(int16_t x, int16_t y, uint8_t w, uint8_t h){ if(getTile(x/8, y/8)) return true; if(getTile((x+w-1)/8, y/8)) return true; if(getTile((x+w-1)/8, (y+h-1)/8)) return true; if(getTile(x/8, (y+h-1)/8)) return true; return false; }