// // ONE LEVEL.. Controls : Cursors Left,Right,Up,Down and Left Alt = shoot bubble. // // enum flag2{PLAYER1,PLAYER2,LEFT,RIGHT}; enum bubblestates{SHOT,FLOATUP,FLOAT}; enum aistates{ROAMING,TRAPPED,DAMAGED,TRAJECTORY}; enum flag{SCROLLLEVELDOWN,INGAME}; #define CHEATMODE false #define MAX_JUMP 4.0f #define MAX_PLAYERS 2 // second player not fully implemented yet. #define MAX_FALL_SPEED 2.0f #define MAX_BUBBLES 256 #define BUBBLE_SHOOTFORCE 10.0f //how fast does the bubble shoot away from the player #define BUBBLE_LIFE 60*20 //how long does a empty bubble stay alive #define CAPTURED_TIMEOUT 60*7 //how long does a ai stay inside the bubble #define RESTOREFROMDAMAGED 200 //time a ai stays damaged #define BUBBLE_PUSHTIME 4 //how long do we push the ai holding bubble before it pops #define MAX_AI 32 #define AI_SPEED 3 #define AI_LAUNCHFORCE 4.5 #define AI_TRAJTIME 60*2 #define MAX_PICKUP 128 #define PICKUP_TIMEDISAPPEAR 60*6 #define MAX_PICKUPEFFECT 128 #define PICKUPEFFECT_MY 2 #define PICKUPEFFECT_DISAPPEAR 60*1.5 #define POINT1 200 #define POINT2 500 #define POINT3 1000 #define POINT4 10000 #define POINT5 20000 #include "raylib.h" #include "math.h" static const int screenWidth=800; static const int screenHeight=450; static int mapWidth; static int mapHeight; static float tileWidth; static float tileHeight; static int map[512][512]; static Color c64color[16]; //' our colors typedef struct player{ bool active; float facing; float x; float y; int shotfiredtime; int w; int h; int score; bool canjump; float jumpforce; }player; static struct player p[MAX_PLAYERS]; typedef struct bubble{ bool active; bool contains; int timeout; int timeoutcaptured; float x; float y; int state; int r; float mx; float my; int shakex; int shakey; int pushtime; }bubble; static struct bubble arr_bubble[MAX_BUBBLES]; typedef struct ai{ bool active; float rotation; int state; int animframetime; int facing; int timedamaged; float x; float y; int trajtime; float mx; float my; int w; int h; int lastjump; bool canjump; float jumpforce; }ai; static struct ai arr_ai[MAX_AI]; typedef struct pickup{ bool active; int x; int y; int w; int h; int type; Color col; int timedisappear; }pickup; static struct pickup arr_pickup[MAX_PICKUP]; typedef struct pickupeffect{ bool active; float x; float y; float my; int type; float alpha; float timedisappear; int points; }pickupeffect; static struct pickupeffect arr_pueffect[MAX_PICKUPEFFECT]; static void inilevel(void); static void drawmap(int offsetx,int offsety); static void drawplayers(void); static void updateplayers(void); static bool rectsoverlap(int x1,int y1,int w1,int h1,int x2,int y2,int w2,int h2); static bool playertilecollide(int num,int tile, int offsetx,int offsety); static void updateplayergravity(); static void shootbubble(int player, int direction); static void drawbubbles(void); static void updatebubbles(void); static bool circlerectcollide(int cx,int cy,int cr, int rx, int ry,int rw,int rh); static bool bubbletilecollide(int num,int offsetx,int offsety); static float Clamp(float value, float min, float max); static void drawai(void); static void addai(int x,int y); static void updateai(void); static bool aitilecollide(int num,int tile,int offsetx,int offsety); static void bubbleaicollision(void); static void playeraicollision(void); static void playerbubblecollision(void); static void inigfx(void); static void createaitrajectory(int x,int y,int facing); static float getangle(float x1,float y1,float x2,float y2); static void drawpickups(void); static void updatepickups(void); static void addpickup(int x,int y); static void playerpickupcollision(); static void updatepickupeffects(); static void drawpickupeffects(); static void addpickupeffect(int type,int x, int y); static int getscore(int type); static void drawplayerbar(void); static void inigame(void); static void inic64colors(void); static void drawplayerscolling(void); // Here the gfx are defined. static RenderTexture2D tilepurple; static RenderTexture2D spritebobble1; static RenderTexture2D spritebobble2; static RenderTexture2D spriteai1; static RenderTexture2D spriteai2; static RenderTexture2D spritefruit1; static RenderTexture2D spritefruit2; static RenderTexture2D spritefruit3; static RenderTexture2D spritefruit4; static RenderTexture2D spritefruit5; static bool gameover = false; static int fx=0; // for moving the playr in a bubble to the level. static int fy=0; int main(void) { // Initialization //-------------------------------------------------------------------------------------- InitWindow(screenWidth, screenHeight, "raylib example."); // Create a Image in memory tilepurple = LoadRenderTexture(32, 32); spritebobble1 = LoadRenderTexture(32, 32); spritebobble2 = LoadRenderTexture(32, 32); spriteai1 = LoadRenderTexture(32, 32); spriteai2 = LoadRenderTexture(32, 32); spritefruit1 = LoadRenderTexture(32, 32); spritefruit2 = LoadRenderTexture(32, 32); spritefruit3 = LoadRenderTexture(32, 32); spritefruit4 = LoadRenderTexture(32, 32); spritefruit5 = LoadRenderTexture(32, 32); inic64colors(); inigfx(); SetTargetFPS(60); // Set our game to run at 60 frames-per-second inilevel(); inigame(); //-------------------------------------------------------------------------------------- static int offsety; offsety = -(mapHeight*tileHeight); static int gamestate=SCROLLLEVELDOWN; //static int gamestate=INGAME; static int aiaddtime=10; static int aimax = 3; static int restarttime; fx = screenWidth/2; fy = -200; // Main game loop while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- switch (gamestate){ case INGAME: if(aimax>0)aiaddtime-=1; if(aiaddtime==0 && aimax>0){ addai(10*tileWidth,tileHeight+5); aiaddtime=20; aimax-=1; } updateplayers(); updateplayers(); updateplayers(); updateplayergravity(); updatebubbles(); bubbleaicollision(); updateai(); playeraicollision(); playerbubblecollision(); updatepickups(); updatepickupeffects(); playerpickupcollision(); //see if there are no more ai then countdown and reset. restarttime+=1; for(int i=0;i60*10){ fx = p[PLAYER1].x; fy = p[PLAYER1].y; inilevel(); inigame(); aiaddtime=10; aimax = 3; restarttime=0; offsety = -(mapHeight*tileHeight); gamestate = SCROLLLEVELDOWN; gameover = false; } break; } //---------------------------------------------------------------------------------- // Draw //---------------------------------------------------------------------------------- BeginDrawing(); ClearBackground(BLACK); switch (gamestate){ case SCROLLLEVELDOWN: offsety+=4; drawmap(0,offsety); drawplayerscolling(); for(int i=0;i<5;i++){ if(fx>p[PLAYER1].x)fx-=1; if(fyp[PLAYER1].y)fy-=1; } if(offsety>=0)gamestate=INGAME; break; case INGAME: drawmap(0,0); drawplayerbar(); drawpickups(); drawpickupeffects(); drawplayers(); drawai(); drawbubbles(); break; } // DrawTexturePro(spritebobble1.texture, (Rectangle){0,0,spritebobble1.texture.width, // spritebobble1.texture.height}, // (Rectangle){50, // 50, // tileWidth,tileHeight}, // (Vector2){0,0},0,WHITE); EndDrawing(); //---------------------------------------------------------------------------------- } // De-Initialization //-------------------------------------------------------------------------------------- UnloadRenderTexture(tilepurple); UnloadRenderTexture(spritebobble1); UnloadRenderTexture(spritebobble2); UnloadRenderTexture(spriteai1); UnloadRenderTexture(spriteai2); UnloadRenderTexture(spritefruit1); UnloadRenderTexture(spritefruit2); UnloadRenderTexture(spritefruit3); UnloadRenderTexture(spritefruit4); UnloadRenderTexture(spritefruit5); CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } void drawmap(int offsetx,int offsety){ for(int y=0;y-2)p[i].shotfiredtime-=1; if(p[i].facing==RIGHT){ if(p[i].shotfiredtime>0){ DrawTexturePro(spritebobble2.texture, (Rectangle){0,0,spritebobble2.texture.width, spritebobble2.texture.height}, (Rectangle){p[i].x, p[i].y, p[i].w,p[i].h}, (Vector2){0,0},0,WHITE); }else{ DrawTexturePro(spritebobble1.texture, (Rectangle){0,0,spritebobble1.texture.width, spritebobble1.texture.height}, (Rectangle){p[i].x, p[i].y, p[i].w,p[i].h}, (Vector2){0,0},0,WHITE); } }else{ if(p[i].shotfiredtime>0){ DrawTexturePro(spritebobble2.texture, (Rectangle){0,0,-spritebobble2.texture.width, spritebobble2.texture.height}, (Rectangle){p[i].x, p[i].y, p[i].w,p[i].h}, (Vector2){0,0},0,WHITE); }else{ DrawTexturePro(spritebobble1.texture, (Rectangle){0,0,-spritebobble1.texture.width, spritebobble1.texture.height}, (Rectangle){p[i].x, p[i].y, p[i].w,p[i].h}, (Vector2){0,0},0,WHITE); } } } } //Unit collide with solid blocks true/false // num = player bool playertilecollide(int num,int tile,int offsetx,int offsety){ if(p[num].active==false)return false; int cx = (p[num].x+offsetx)/tileWidth; int cy = (p[num].y+offsety)/tileHeight; for(int y2=cy-1; y2=0 && x2=0 && y2 0){ int x3 = (x2)*tileWidth; int y3 = (y2)*tileHeight; if(rectsoverlap(p[num].x+offsetx,p[num].y+offsety,p[num].w,p[num].h,x3,y3,tileWidth,tileHeight)){ return true; } } } } }} return false; } //Unit collide with solid blocks true/false // num = player bool bubbletilecollide(int num,int offsetx,int offsety){ if(arr_bubble[num].active==false)return false; int cx = (arr_bubble[num].x+offsetx)/tileWidth; int cy = (arr_bubble[num].y+offsety)/tileHeight; for(int y2=cy-1; y2=0 && x2=0 && y2 0 ){ int x3 = (x2)*tileWidth; int y3 = (y2)*tileHeight; if(circlerectcollide( arr_bubble[num].x+offsetx, arr_bubble[num].y+offsety, arr_bubble[num].r, x3,y3,tileWidth,tileHeight)){ return true; } } } }} return false; } // Rectangles overlap bool rectsoverlap(int x1,int y1,int w1,int h1,int x2,int y2,int w2,int h2){ if(x1 >= (x2 + w2) || (x1 + w1) <= x2) return false; if(y1 >= (y2 + h2) || (y1 + h1) <= y2) return false; return true; } void updateplayers(void){ for(int num=0;num0 && IsKeyDown(KEY_LEFT) && playertilecollide(num,99,-1,0)==false){ p[num].x-=1; p[num].facing=LEFT; p[num].facing=RIGHT; } if(p[num].canjump==false && p[num].jumpforce>0 && IsKeyDown(KEY_RIGHT) && playertilecollide(num,99,1,0)==false){ p[num].x+=1; p[num].facing=RIGHT; } //move left and right normal if(p[num].canjump && IsKeyDown(KEY_LEFT) && playertilecollide(num,99,-1,0)==false){ p[num].x-=1; p[num].facing=LEFT; } if(p[num].canjump && IsKeyDown(KEY_RIGHT) && playertilecollide(num,99,1,0)==false){ p[num].x+=1; p[num].facing=RIGHT; } if(IsKeyDown(KEY_SPACE) && p[num].canjump){ p[num].canjump = false; // We are now jumping. p[num].jumpforce = -MAX_JUMP; // How high we jump. } } } } void updateplayergravity(){ for(int num=0;num0 && playertilecollide(num,99,0,1)==true){ p[num].y = p[num].y-2; p[num].canjump = true; break; } } // Here we add to the jumpforce. It will go from - to +. p[num].jumpforce += 0.2f; if(p[num].jumpforce>MAX_FALL_SPEED)p[num].jumpforce=MAX_FALL_SPEED; } } } void shootbubble(int player, int direction){ for(int i=0;i-1)arr_bubble[i].pushtime--; //timeout arr_bubble[i].timeout-=1; if(arr_bubble[i].timeout<0){ arr_bubble[i].active=false; if(arr_bubble[i].contains==true){ int newai; for(newai=0;newai-2)arr_bubble[i].timeout-=1; // release the ai again but faster and killable. if(arr_bubble[i].timeout<0){ arr_bubble[i].active=false; int newai; for(newai=0;newai2.5*tileHeight){ arr_bubble[i].y-=1; } if(arr_bubble[i].y<3*tileHeight){ if(arr_bubble[i].xscreenWidth/2){ arr_bubble[i].x-=1; } if(rectsoverlap( arr_bubble[i].x,arr_bubble[i].y,arr_bubble[i].r,arr_bubble[i].r, screenWidth/2-5,0,10,100)){ for(int z=0;z0.0f){ arr_bubble[i].x += 1; if(bubbletilecollide(i,1,0)){ arr_bubble[i].mx=0; arr_bubble[i].state = FLOATUP; ex=true; } } if(ex)break; } if(arr_bubble[i].mx<0){ arr_bubble[i].mx+=0.2f; }else{ arr_bubble[i].mx-=0.2f; } // If the bubble slows down enough then change state to FLOAT; if(arr_bubble[i].mx>-0.3f && arr_bubble[i].mx < 0.3f){ arr_bubble[i].mx = 0; arr_bubble[i].state = FLOATUP; } } } } bool circlerectcollide(int cx,int cy,int cr, int rx, int ry,int rw,int rh){ float closestx = Clamp(cx, rx, rx+rw); float closesty = Clamp(cy, ry, ry+rh); float distancex = cx - closestx; float distancey = cy - closesty; float distancesquared = (distancex * distancex) + (distancey * distancey); return distancesquared < (cr * cr); } // Clamp float value float Clamp(float value, float min, float max) { const float res = value < min ? min : value; return res > max ? max : res; } void drawai(){ for(int i=0;iscreenWidth-(tileWidth*2)){ arr_ai[num].facing=LEFT; } } } // gravity on the trajectory if(arr_ai[num].trajtime>-2)arr_ai[num].trajtime-=1; for(int z=0;ztileHeight){ if(arr_ai[num].my<0 && arr_ai[num].y>screenHeight-(tileHeight*3))arr_ai[num].trajtime=-1; if(arr_ai[num].trajtime<0){ if( aitilecollide(num,99,0,1)==true && aitilecollide(num,99,0,0)==false && aitilecollide(num,99,-1,0)==false && aitilecollide(num,99,1,0)==false){ arr_ai[num].active=false; addpickup(arr_ai[num].x,arr_ai[num].y); break; } } } } arr_ai[num].my-=0.07f; // } // If ai is roaming or damaged (Moving around) if(arr_ai[num].state==ROAMING || arr_ai[num].state==DAMAGED){ // restore from damages if time up. if(arr_ai[num].timedamaged>-2)arr_ai[num].timedamaged-=1; if(arr_ai[num].timedamaged<0 && bubbletilecollide(num,0,0)==false){ arr_ai[num].state = ROAMING; } if(arr_ai[num].canjump==true){ int speed=AI_SPEED; //If the ai is damaged than speed it it. if(arr_ai[num].state==DAMAGED)speed+=1; for(int z=0;z p[PLAYER1].y){ arr_ai[num].jumpforce = -MAX_JUMP; arr_ai[num].canjump = false; arr_ai[num].lastjump=0; } } // Jump randomly if a platform is reachable. if(GetRandomValue(0,400)<2){ if(aitilecollide(num,99,0,-(tileHeight*1.1))){ arr_ai[num].jumpforce = -MAX_JUMP; arr_ai[num].canjump = false; arr_ai[num].lastjump=0; } } // This is a jump fast after the first jump if(arr_ai[num].lastjump<100)arr_ai[num].lastjump+=1; if(arr_ai[num].lastjump<100 && GetRandomValue(0,100)<2){ if(aitilecollide(num,99,0,-(tileHeight*1.1))){ arr_ai[num].jumpforce = -MAX_JUMP; arr_ai[num].canjump = false; arr_ai[num].lastjump=101; } } } // if hanging in the air then fall down if(aitilecollide(num,99,0,1)==false)arr_ai[num].canjump=false; // If we are currently not jumping and if the player presses space. // If we are currently in a jump. if(arr_ai[num].canjump==false){ // Here we update the player y position. // We want to move 1 pixel at a time. This for collision detection(Not going through tiles.) for(int i=0;i0 && aitilecollide(num,99,0,1)==true){ arr_ai[num].y = arr_ai[num].y-1; arr_ai[num].canjump = true; // look if the player is left or right and adjust direction. if(arr_ai[num].state==DAMAGED){ if(arr_ai[num].xp[PLAYER1].x){ arr_ai[num].facing=LEFT; }else{ arr_ai[num].facing=RIGHT; } if(GetRandomValue(0,10)<5){ if(GetRandomValue(0,10)<5){ arr_ai[num].facing=LEFT; }else{ arr_ai[num].facing=RIGHT; } } } exit=true; break; } if(exit)break; } // Here we add to the jumpforce. It will go from - to +. arr_ai[num].jumpforce += 0.2f; if(arr_ai[num].jumpforce>MAX_FALL_SPEED)arr_ai[num].jumpforce=MAX_FALL_SPEED; } } } } //Unit collide with solid blocks true/false // num = player bool aitilecollide(int num,int tile,int offsetx,int offsety){ if(arr_ai[num].active==false)return false; int cx = (arr_ai[num].x+offsetx)/tileWidth; int cy = (arr_ai[num].y+offsety)/tileHeight; for(int y2=cy-1; y2=0 && x2=0 && y2 0){ int x3 = (x2)*tileWidth; int y3 = (y2)*tileHeight; if(rectsoverlap(arr_ai[num].x+offsetx,arr_ai[num].y+offsety,arr_ai[num].w,arr_ai[num].h,x3,y3,tileWidth,tileHeight)){ return true; } } } } }} return false; } void bubbleaicollision(){ // If bubble touches ai for(int i=0;iBUBBLE_PUSHTIME){//frametime used arr_bubble[ii].active=false; createaitrajectory(arr_bubble[ii].x,arr_bubble[ii].y,p[i].facing); }else{//push bubble away from player float an=getangle( arr_bubble[ii].x, arr_bubble[ii].y, p[i].x, p[i].y); int oldx = arr_bubble[ii].x; int oldy = arr_bubble[ii].y; arr_bubble[ii].x -= cos(an)*3; arr_bubble[ii].y -= sin(an)*3; if(bubbletilecollide(ii,0,0)){ arr_bubble[ii].x = oldx; arr_bubble[ii].y = oldy; } } } } } } void createaitrajectory(int x,int y,int facing){ for(int i=0;iPICKUP_TIMEDISAPPEAR){ arr_pickup[i].active=false; } } } void addpickup(int x, int y){ for(int i=0;i-1)arr_pueffect[i].alpha -= 1; } } void drawpickupeffects(){ for(int i=0;i