Great game by Aurelién Rodot for Gamebuin. Ported by Jonne
Dependencies: PokittoLib
Fork of Asterocks by
Diff: Crabator.cpp
- Revision:
- 5:42a7d8c6c8fd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Crabator.cpp Fri Oct 13 21:38:43 2017 +0000 @@ -0,0 +1,1398 @@ +//#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; +} +