// Credit : https://forum.processing.org/two/discussion/18478/deltatime.html, https://gamedev.stackexchange.com/questions/73727/how-do-i-implement-friction-in-a-billiards-game, https://www.youtube.com/watch?v=69IuP6uOmRA, https://www.youtube.com/watch?v=jknX9ci9jec, https://discourse.processing.org/t/how-do-you-detect-stop-someone-from-closing-the-sketch/26864/2, https://forum.processing.org/one/topic/ignore-escape-key-do-other-action.html, https://forum.processing.org/two/discussion/6457/how-to-use-arguments-with-an-application-built-with-processing.html // Based of the collision_detect code /* Things to implement * Image Loader * Main Menu * Normal Mode * Line Dragging * Slowing down when dragging * Show the fps when launched with the args '-showfps 1' * The friction implementation is still frame-dependant * Fix the line dragging that's kinda broken * Check so the corona and the enemy don't spawn inside the player's hitbox * Add a restart game button * Don't draw the object if it's out of bounds * Win menu * Win menu Background * Make it Christmas themed? * Fix that you can drag in the escape menu */ /* Fixed * No more "seam" where the player could go out of bounds when colliding with an object :) * Fixed the getting more points glitch if you go out of bounds slightly to the top left * Fixed the line dragging when mouse is only pressed (the line can start where the endMouseX and endMouseY used to be) */ // All sounds used is from sfxr, get sfxr at http://drpetter.se/project_sfxr.html // Dependancy : The minim Sound Library // App used for obtaining colors for the game : https://play.google.com/store/apps/details?id=com.odrowaz.robert.rgb2 //import ddf.minim.*; // default vars int defPlayerPoint = 0; int defPlayerLive = 3; int defGameFrameContinue = 30; // game vars float ballX; float ballY; float lastFrameTime = 0; float deltaTime = 0; boolean mouseHold = false; float startMouseX = 0; float startMouseY = 0; float endMouseX = 0; float endMouseY = 0; float powerX = 0; float powerY = 0; float dampFactor = 0.94; int ballSize = 50; int enemySize = 50; PImage corona; PImage heart; PImage damagedHeart; PImage coronaLose; PImage inGamebg; float ballSpeedMul = 7; int coronaSizeFactor = 7; float boxGUIheight = 100; int playerPoint = defPlayerPoint; int playerLive = defPlayerLive; int gameStatus = 0; // 0 is main menu, 1 is the normal gamemode, 2 is the game over screen, 3 is the win screen, 4 is the stupid mode //float coronaRandX; //float coronaRandY; //float enemyRandX; //float enemyRandY; int coronaNum = 10; // 25 on hard int[][] coronaPos = new int[coronaNum][2]; int[][] enemyPos = new int[6][2]; // 10 for hard int[][] coronaLosePos = new int[50][2]; int avgFPStrack = 1; float avgFPStemp = 0; float avgFPS = 0; int allowGameContinue = 1; int gameFrameContinue = defGameFrameContinue; int coronaLoseSize = 50; int escMenuStatus = 0; //boolean mmMusicIsPlaying = false; //Minim minim = new Minim(this); //AudioPlayer mmMusic; //AudioPlayer ballLaunch; //AudioPlayer coronaSound; // float frictionTargetFrameRate = 120; // translations by translate.yandex.com void setup() { size(1280, 720, P2D); // 1280, 720 / 420, 520 frameRate(120); ellipseMode(CENTER); imageMode(CENTER); rectMode(CORNER); corona = loadImage("./data/corona.png"); heart = loadImage("./data/heart.png"); damagedHeart = loadImage("./data/damaged-heart.png"); coronaLose = loadImage("./data/coronalose.png"); inGamebg = loadImage("./data/in-game-bg.jpeg"); //coronaSound = minim.loadFile("./data/coronacollide.wav"); surface.setTitle("Stay Safe Recreation - Remade With Processing"); ballX = width * 0.5; ballY = height * 0.5; textSize(26); // caching //mmMusic = minim.loadFile("./data/portal2_background01.wav"); //mmMusic.setGain(5); } void draw() { deltaTime = (millis() - lastFrameTime) / 1000; lastFrameTime = millis(); if (avgFPStemp + (1 / deltaTime) > 1000000000) { avgFPStemp = 0; // maybe reset this to last fps and then lowering the reset treshold so it gives out more accurate values? avgFPStrack = 1; } avgFPStemp += (1 / deltaTime); avgFPS = avgFPStemp / avgFPStrack; avgFPStrack++; // to make the draw tidy, unlike the rest of the code... switch (gameStatus) { case 0: menuLoop(); break; case 1: normalMLoop(); break; case 2: loseLoop(); break; case 3: winLoop(); break; case 4: stupidMLoop(); break; } // check if args is not null // doesn't work? /*if (args != null && args.length > 0) { for (int r = 0; r < args.length; r++) { if (args[r].equals("-debuginfo")) { println("startMouseX : " + startMouseX); println("startMouseY : " + startMouseY); println("endMouseX : " + endMouseX); println("endMouseY : " + endMouseY); println("powerX : " + powerX); println("powerY : " + powerY); println("ballX : " + ballX); println("ballY : " + ballY); println("mouseX : " + mouseX); println("mouseY : " + mouseY); println("mouseHold : " + mouseHold); println("playerPoint : " + playerPoint); println("avgFPStemp : " + avgFPStemp); println("allowGameContinue : " + allowGameContinue); println("gameFrameContinue : " + gameFrameContinue + "\n"); } } }*/ } /*void dispose() { println("bruh"); super.dispose(); }*/ void mousePressed() { switch (gameStatus) { case 0: if (mouseX > 94 && mouseY > 247 && mouseX < 389 && mouseY < 308) { entityInit(); playerPoint = defPlayerPoint; playerLive = defPlayerLive; startMouseX = 0; startMouseY = 0; endMouseX = 0; endMouseY = 0; ballX = width * 0.5; ballY = height * 0.5; allowGameContinue = 0; gameStatus = 1; } else if (mouseX > 94 && mouseY > 335 && mouseX < 389 && mouseY < 396) { entityInit(); playerPoint = defPlayerPoint; playerLive = defPlayerLive; startMouseX = 0; startMouseY = 0; endMouseX = 0; endMouseY = 0; ballX = width * 0.5; ballY = height * 0.5; allowGameContinue = 0; gameStatus = 4; } else if (mouseX > 94 && mouseY > 425 && mouseX < 389 && mouseY < 486) { exit(); } break; case 1: startMouseX = mouseX; startMouseY = mouseY; endMouseX = ballX - (mouseX - startMouseX); endMouseY = ballY - (mouseY - startMouseY); endMouseX = ballX - (mouseX - startMouseX); endMouseY = ballY - (mouseY - startMouseY); mouseHold = true; switch (escMenuStatus) { case 1: if (mouseX > 44 && mouseY > 479 && mouseX < 301 && mouseY < 542) { escMenuStatus = 0; gameStatus = 0; } else if (mouseX > 44 && mouseY > 389 && mouseX < 301 && mouseY < 452) { escMenuStatus = 0; } break; } break; case 2: entityInit(); playerPoint = defPlayerPoint; playerLive = defPlayerLive; startMouseX = 0; startMouseY = 0; endMouseX = 0; endMouseY = 0; ballX = width * 0.5; ballY = height * 0.5; allowGameContinue = 0; gameStatus = 0; // this must be last! break; case 3: entityInit(); playerPoint = defPlayerPoint; playerLive = defPlayerLive; startMouseX = 0; startMouseY = 0; endMouseX = 0; endMouseY = 0; ballX = width * 0.5; ballY = height * 0.5; allowGameContinue = 0; gameStatus = 0; break; case 4: startMouseX = mouseX; startMouseY = mouseY; endMouseX = ballX - (mouseX - startMouseX); endMouseY = ballY - (mouseY - startMouseY); endMouseX = ballX - (mouseX - startMouseX); endMouseY = ballY - (mouseY - startMouseY); mouseHold = true; switch (escMenuStatus) { case 1: if (mouseX > 44 && mouseY > 479 && mouseX < 301 && mouseY < 542) { escMenuStatus = 0; gameStatus = 0; } else if (mouseX > 44 && mouseY > 389 && mouseX < 301 && mouseY < 452) { escMenuStatus = 0; } break; } break; } } void mouseDragged() { switch (gameStatus) { case 1: endMouseX = ballX - (mouseX - startMouseX); endMouseY = ballY - (mouseY - startMouseY); mouseHold = true; break; case 4: endMouseX = ballX - (mouseX - startMouseX); endMouseY = ballY - (mouseY - startMouseY); mouseHold = true; break; } } void mouseReleased() { switch (gameStatus) { case 1: powerX = (mouseX - startMouseX) * -1; powerY = (mouseY - startMouseY) * -1; mouseHold = false; startMouseX = 0; startMouseY = 0; break; case 4: powerX = (mouseX - startMouseX) * -1; powerY = (mouseY - startMouseY) * -1; mouseHold = false; startMouseX = 0; startMouseY = 0; break; } } void keyPressed() { // char type? /*if (key == 'r' || key == 'R') { gameStatus = 1; entityInit(); playerPoint = defPlayerPoint; playerLive = defPlayerLive; startMouseX = 0; startMouseY = 0; endMouseX = 0; endMouseY = 0; powerX = 0; powerY = 0; ballX = width * 0.5; ballY = height * 0.5; println("For debugging only, remove in final release!"); }*/ // prevent escape key from closing the sketch if (keyCode == ESC) { key = 0; // aaaaaa if (gameStatus != 0 && escMenuStatus == 0) { escMenuStatus = 1; } else if (gameStatus != 0 && escMenuStatus == 1) { escMenuStatus = 0; } } } void normalMLoop() { // bruh if (allowGameContinue == 0 && gameFrameContinue > 0) { gameFrameContinue -= 1; } else { gameFrameContinue = defGameFrameContinue; allowGameContinue = 1; } background(210, 212, 199); //image(inGamebg, width / 2, height / 2, width, height); fill(255, 236, 223); circle(ballX, ballY, ballSize); if (mouseHold) { line(ballX, ballY, endMouseX, endMouseY); } thread("collision"); thread("ballApplyForce"); gameDraw(); // thread can't get access to any draw method drawGUI(); // this needs to be the last so it gets priority to draw over other stuff } void loseLoop() { background(210, 212, 199); for (int p = 0; p < coronaLosePos.length; p++) { image(coronaLose, coronaLosePos[p][0], coronaLosePos[p][1], coronaLoseSize, coronaLoseSize); } drawGUI(); } void menuLoop() { background(210, 212, 199); /*if (!mmMusic.isPlaying()) { //mmMusic.play(); }*/ drawGUI(); } void winLoop() { background(210, 212, 199); drawGUI(); } void stupidMLoop() { if (allowGameContinue == 0 && gameFrameContinue > 0) { gameFrameContinue -= 1; } else { gameFrameContinue = defGameFrameContinue; allowGameContinue = 1; } background(210, 212, 199); fill(255, 236, 223); circle(ballX, ballY, ballSize); if (mouseHold) { line(ballX, ballY, endMouseX, endMouseY); } thread("collision"); thread("ballApplyForce"); gameDraw(); drawGUI(); } void collision() { // just a crappy square collider, nothing to see here switch (gameStatus) { case 1: if (ballY > (height - boxGUIheight) - (ballSize / 2)) { // bottom window collision powerY -= ballY - ((height - boxGUIheight) - (ballSize / 2)); } if (ballY < 0 + (ballSize / 2)) { // top window collision powerY -= ballY - (ballSize / 2); } if (ballX > width - (ballSize / 2)) { // right window collision powerX -= ballX - (width - (ballSize / 2)); } if (ballX < 0 + (ballSize / 2)) { // left window collision powerX -= ballX - (ballSize / 2); } // corona collision for (int l = 0; l < coronaPos.length; l++) { if (ballY > coronaPos[l][1] - (9 * coronaSizeFactor) && ballY < coronaPos[l][1] + (9 * coronaSizeFactor) && ballX > coronaPos[l][0] - (9 * coronaSizeFactor) && ballX < coronaPos[l][0] + (9 * coronaSizeFactor)) { coronaPos[l][0] = 2147483646; coronaPos[l][1] = 2147483646; playerPoint++; if (playerPoint >= coronaNum) { gameStatus = 3; } //coronaSound.play(0); annoying lol } } // enemy collision for (int n = 0; n < enemyPos.length; n++) { if (ballY > enemyPos[n][1] - enemySize && ballY < enemyPos[n][1] + enemySize && ballX > enemyPos[n][0] - enemySize && ballX < enemyPos[n][0] + enemySize) { enemyPos[n][0] = 2147483646; enemyPos[n][1] = 2147483646; playerLive--; if (playerLive <= 0) { gameStatus = 2; } } } break; case 4: if (ballY > (height - boxGUIheight) - (ballSize / 2)) { // bottom window collision powerY -= ballY - ((height - boxGUIheight) - (ballSize / 2)); } if (ballY < 0 + (ballSize / 2)) { // top window collision powerY -= ballY - (ballSize / 2); } if (ballX > width - (ballSize / 2)) { // right window collision powerX -= ballX - (width - (ballSize / 2)); } if (ballX < 0 + (ballSize / 2)) { // left window collision powerX -= ballX - (ballSize / 2); } // corona collision for (int l = 0; l < coronaPos.length; l++) { if (ballY > coronaPos[l][1] - (9 * coronaSizeFactor) && ballY < coronaPos[l][1] + (9 * coronaSizeFactor) && ballX > coronaPos[l][0] - (9 * coronaSizeFactor) && ballX < coronaPos[l][0] + (9 * coronaSizeFactor)) { coronaPos[l][0] = 2147483646; coronaPos[l][1] = 2147483646; playerPoint++; powerX *= random(3, 15); powerY *= random(3, 15); if (playerPoint >= coronaNum) { gameStatus = 3; } //coronaSound.play(0); annoying lol } } // enemy collision for (int n = 0; n < enemyPos.length; n++) { if (ballY > enemyPos[n][1] - enemySize && ballY < enemyPos[n][1] + enemySize && ballX > enemyPos[n][0] - enemySize && ballX < enemyPos[n][0] + enemySize) { enemyPos[n][0] = 2147483646; enemyPos[n][1] = 2147483646; playerLive--; powerX *= random(5, 15); powerY *= random(5, 15); if (playerLive <= 0) { gameStatus = 2; } } } } } void ballApplyForce() { switch (escMenuStatus) { case 0: switch (allowGameContinue) { case 0: powerX = 0; powerY = 0; break; } ballX += (powerX * ballSpeedMul) * deltaTime; ballY += (powerY * ballSpeedMul) * deltaTime; powerX *= dampFactor; powerY *= dampFactor; if (powerX < 0.7 && powerX > -0.7) { powerX = 0; } // why not else if, why does this work now?? if (powerY < 0.7 && powerY > -0.7) { powerY = 0; } break; } } void gameDraw() { for (int i = 0; i < coronaPos.length; i++) { image(corona, coronaPos[i][0], coronaPos[i][1], 16 * coronaSizeFactor, 9 * coronaSizeFactor); } for (int m = 0; m < enemyPos.length; m++) { fill(74, 75, 70); circle(enemyPos[m][0], enemyPos[m][1], enemySize); } } void entityInit() { for (int j = 0; j < coronaPos.length; j++) { //coronaRandX = int(random(0 + (16 * coronaSizeFactor) / 2, width - ((16 * coronaSizeFactor) / 2))); //coronaRandY = int(random(0 + (9 * coronaSizeFactor) / 2, (height - boxHUDheight) - ((9 * coronaSizeFactor) / 2))); coronaPos[j][0] = int(random(0 + (16 * coronaSizeFactor) / 2, width - ((16 * coronaSizeFactor) / 2))); coronaPos[j][1] = int(random(0 + (9 * coronaSizeFactor) / 2, (height - boxGUIheight) - ((9 * coronaSizeFactor) / 2))); } for (int k = 0; k < enemyPos.length; k++) { enemyPos[k][0] = int(random(0 + (enemySize / 2), width - (enemySize / 2))); enemyPos[k][1] = int(random(0 + (enemySize / 2), (height - boxGUIheight) - (enemySize / 2))); } for (int q = 0; q < coronaLosePos.length; q++) { coronaLosePos[q][0] = int(random(0 + (coronaLoseSize / 2), width - (coronaLoseSize / 2))); coronaLosePos[q][1] = int(random(0 + (coronaLoseSize / 2), height - (coronaLoseSize / 2))); } } void drawGUI() { switch (gameStatus) { case 0: // trying to imitate the portal 2 main menu here fill(255, 255, 255); if (mouseX > 94 && mouseY > 247 && mouseX < 389 && mouseY < 308) { rect(94, 247, 296, 61); } // we don't need it to highlight multiple parts of the button else if (mouseX > 94 && mouseY > 335 && mouseX < 389 && mouseY < 396) { rect(94, 335, 296, 61); } else if (mouseX > 94 && mouseY > 425 && mouseX < 389 && mouseY < 486) { rect(94, 425, 296, 61); } fill(0, 0, 0); text("Play on Normal Mode", 120, 285); text("Play on Stupid Mode", 120, 375); text("Quit", 120, 465); text("please ignore the portal2_background01.wav aka the portal 2\nmain menu music, it's just for placeholder", 120, 600); break; case 1: if (int((1 / deltaTime)) > 59) { fill(3, 201, 0); } else if (int((1 / deltaTime)) > 29 && int((1 / deltaTime)) < 60) { fill(221, 221, 0); } else if (int((1 / deltaTime)) < 30) { fill(255, 0, 0); } text(int((1 / deltaTime)) + " fps dt: " + (deltaTime * 1000) + " ms avg: " + int(avgFPS) + " fps", 26, int(height / 22.5)); fill(243, 211, 189); // so the bottom, left and right outline is not visible rect(-1, height - boxGUIheight, width + 1, height); fill(0, 0, 0); text("You are now playing :\n Normal Mode", 1000, int(height / 1.099236641221374)); text("Point : " + playerPoint, 30, int(height / 1.058823529411765)); fill(255, 0, 0); text("Lives : " + playerLive, 180, int(height / 1.058823529411765)); switch (allowGameContinue) { case 0: fill(0, 0, 0); text("Resetting powerX and powerY...", 480, height / 2); break; } // overlaying everything switch (escMenuStatus) { case 1: fill(210, 212, 199); // so none of the edges are visible rect(-1, -1, width + 1, height + 1); fill(255, 255, 255); if (mouseX > 44 && mouseY > 479 && mouseX < 301 && mouseY < 542) { rect(44, 480, 256, 61); } else if (mouseX > 44 && mouseY > 389 && mouseX < 301 && mouseY < 452) { rect(44, 390, 256, 61); } fill(0, 0, 0); text("The Game Is Now Paused", 70, 100); text("Return To Game", 70, 430); text("Exit To Main Menu", 70, 520); break; } break; case 2: fill(0, 0, 0); text("Oh no, the white blood cells have weakened.", int(width / 3.657142857142857), (height / 2) - 70); text("Now the virus is taking over your lungs and making you sick.", 270, (height / 2) - 20); text("Press anywhere on the screen to be able to go back in time and prevent the covid virus from defeating you.", 70, (height / 2) + 30); if (int((1 / deltaTime)) > 59) { fill(3, 201, 0); } else if (int((1 / deltaTime)) > 29 && int((1 / deltaTime)) < 60) { fill(221, 221, 0); } else if (int((1 / deltaTime)) < 30) { fill(255, 0, 0); } text(int((1 / deltaTime)) + " fps dt: " + (deltaTime * 1000) + " ms avg: " + int(avgFPS) + " fps", 26, int(height / 22.5)); break; case 3: fill(0, 0, 0); text("you win, this is a placeholder", width / 2, height / 2); break; case 4: if (int((1 / deltaTime)) > 59) { fill(3, 201, 0); } else if (int((1 / deltaTime)) > 29 && int((1 / deltaTime)) < 60) { fill(221, 221, 0); } else if (int((1 / deltaTime)) < 30) { fill(255, 0, 0); } text(int((1 / deltaTime)) + " fps dt: " + (deltaTime * 1000) + " ms avg: " + int(avgFPS) + " fps", 26, int(height / 22.5)); fill(243, 211, 189); // so the bottom, left and right outline is not visible rect(-1, height - boxGUIheight, width + 1, height); fill(0, 0, 0); text("You are now playing :\n Stupid Mode", 1000, int(height / 1.099236641221374)); text("Point : " + playerPoint, 30, int(height / 1.058823529411765)); fill(255, 0, 0); text("Lives : " + playerLive, 180, int(height / 1.058823529411765)); switch (allowGameContinue) { case 0: fill(0, 0, 0); text("Resetting powerX and powerY...", 480, height / 2); break; } // overlaying everything switch (escMenuStatus) { case 1: fill(210, 212, 199); // so none of the edges are visible rect(-1, -1, width + 1, height + 1); fill(255, 255, 255); if (mouseX > 44 && mouseY > 479 && mouseX < 301 && mouseY < 542) { rect(44, 480, 256, 61); } else if (mouseX > 44 && mouseY > 389 && mouseX < 301 && mouseY < 452) { rect(44, 390, 256, 61); } fill(0, 0, 0); text("The Game Is Now Paused", 70, 100); text("Return To Game", 70, 430); text("Exit To Main Menu", 70, 520); break; } break; } }