import ddf.minim.*; /** * Game 37: "Dragondot's Extreme Beach Volleyball"
* Player 1 controls: C=Move Left, V = Move Right, Z = Jump/Hit
* Player 2 controls: LEFT ARROW=Move Left, RIGHT ARROW = Move Right, CTRL = Jump/Hit
* P = pause, M = mute, Q = quit (when paused)
*
No slimes were harmed in the making of this game.
*
Go play more games at NMcCoy.net!
* Creative Commons License
This work by Nathan McCoy is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License. */ color sand_shadow = color(204, 203, 198); boolean debug_aiming = false; String MAIN_CHARACTERS = "Dragondot;Kobold;Hobgoblin"; String ALL_CHARACTERS = MAIN_CHARACTERS + ";Giant;Phoenix;Wavespark"; boolean[] keys = new boolean[256]; boolean title; boolean paused; boolean muted; int drawing_ms_last; int physics_ms_last; int CORE_FPS = 540; int DRAWING_FPS = 60; int PHYSICS_FPS = 180; int DRAW_MS = 1000 / DRAWING_FPS; int PHYS_MS = 1000 / PHYSICS_FPS; Minim minim; float wave_phase = 0; float WAVE_SPEED = 0.01; float debug_left_bound; float debug_right_bound; float GROUND_LEVEL = 440; float NET_TOP = 330; float BALL_GRAVITY = 0.02; float NET_CLEARANCE = 16; float MARGIN = 8; int MAX_POINTS = 5; float expected_landing; Critter p1; Critter p2; Ball ball; boolean p2ai = true; boolean game_on = true; int point_timer; int POINT_TIME = PHYSICS_FPS *5 / 2; boolean serve; Critter server; int anim_frame; color FADE_COLOR = color(0);//color(38, 115, 169); ParticleList fg_particles; ParticleList bg_particles; FlipMenu mode_menu; FlipMenu p1_char_select; FlipMenu p2_char_select; AudioSample point_sound; AudioSample dash_sound; AudioSample claw_sound; AudioSample hit_sound; AudioSample club_sound; AudioSample blip_sound; AudioSample select_sound; AudioSample wave_sound; AudioSample ace_sound; AudioSample secret_sound; PFont font; int game_state; int TITLE = 0; int PLAYING = 1; int menu_state; int MODE_MENU = 0; int ONEPLAYER_SELECT = 1; int TWOPLAYER_SELECT = 2; boolean p1_ready; boolean p2_ready; boolean secret; int gamestart_timer; int GAMESTART_TIME = PHYSICS_FPS * 5/2; int game_frames; int IRIS_TIME = PHYSICS_FPS / 2; int p1score; int p2score; boolean p1s_point; int tourneyround; int NUM_CRITTERS = 6; void audioInit() { minim = new Minim(this); point_sound = minim.loadSample("whistle.wav", 1024); dash_sound = minim.loadSample("dash.wav", 1024); claw_sound = minim.loadSample("claw.wav", 1024); claw_sound.setGain(-8); club_sound = minim.loadSample("club.wav", 1024); club_sound.setGain(-8); hit_sound = minim.loadSample("hit.wav", 1024); hit_sound.setGain(-8); blip_sound = minim.loadSample("menu_blip.wav", 1024); select_sound = minim.loadSample("menu_select.wav", 1024); wave_sound = minim.loadSample("wave.wav", 1024); ace_sound = minim.loadSample("ace.wav", 1024); secret_sound = minim.loadSample("areaclear.wav", 1024); } void audioClose() { point_sound.close(); dash_sound.close(); claw_sound.close(); hit_sound.close(); blip_sound.close(); select_sound.close(); wave_sound.close(); ace_sound.close(); secret_sound.close(); minim.stop(); } void newGame(Critter player1, Critter player2, int level) { game_frames = 0; p1score = 0; p2score = 0; p1 = player1; p2 = player2; if(level == -1) p2ai = false; else p2ai = true; tourneyround = level; game_state = PLAYING; ball = new Ball(); server = p1; serve = true; if(p2ai || random(2)<1) bgmLoop(p2.themesong); else bgmLoop(p1.themesong); gamestart_timer = GAMESTART_TIME; } void doTournamentRound(int level) { int opp_index = level; if(opp_index >= p1_char_select.index) opp_index++; Critter opponent = newCritterByIndex(opp_index, RIGHT); Critter player = newCritterByIndex(p1_char_select.index, LEFT); newGame(player, opponent, level); } void titleSetup() { mode_menu = new FlipMenu("1 Player Tournament;Player 1 vs. Player 2"); p1_char_select = new FlipMenu(MAIN_CHARACTERS); //p1_char_select.base_size = 8; p2_char_select = new FlipMenu(MAIN_CHARACTERS); //p2_char_select.base_size = 8; menu_state = MODE_MENU; game_state = TITLE; bgmLoop("DEBV.mp3"); p1_ready = false; p2_ready = false; secret = false; gamestart_timer = GAMESTART_TIME; paused = false; } void setup() { size(720, 480); frameRate(600); audioInit(); info_font = loadFont("PressStartK-16.vlw"); font = loadFont("PressStartK-16.vlw"); fg_particles = new ParticleList(); bg_particles = new ParticleList(); titleSetup(); //newGame(new Dragondot(LEFT), new Hobgoblin(RIGHT), 0); } void drawBeach() { noStroke(); strokeWeight(1); rectMode(CORNERS); fill(65, 127, 212); rect(0, 0, width, 120); fill(91, 155, 217); rect(0, 120, width, 200); fill(122, 179, 222); rect(0, 200, width, 240); fill(blendColors(color(38, 115, 169), color(255), sin(wave_phase)*0.05)); rect(0, 240, width, 253); fill(blendColors(color(32, 131, 172), color(255), 0.0 + sin(wave_phase - PI/2)*0.05)); rect(0, 253, width, 278); fill(blendColors(color(83, 173, 200), color(255), 0.0 + sin(wave_phase - PI)/10)); rect(0, 278, width, 309); fill(blendColors(color(216, 201, 194), color(255), 0.30+ sin(wave_phase - PI*3/2)*0.25)); rect(0, 309, width, 311); fill(216, 201, 194); rect(0, 311, width, 360); } void drawNet() { stroke(255); strokeWeight(5); line(width/2, NET_TOP, width/2, GROUND_LEVEL); stroke(0); strokeWeight(1); line(width/2, NET_TOP, width/2, height); } void drawScore() { float INSET = 32; float SPACING = 32; float RAD = 12; float TOPMARGIN = 40; fill(255); textFont(font, 16); textAlign(LEFT, TOP); text(p1.name, INSET - RAD, 8); textAlign(RIGHT, TOP); text(p2.name, width-INSET + RAD, 8); if(point_timer > 0) { fill(255, 255, 400*point_timer/POINT_TIME, 500*point_timer/POINT_TIME); textAlign(CENTER, CENTER); if(p1s_point) text("<-- POINT!", width/2, TOPMARGIN); else text("POINT! -->", width/2, TOPMARGIN); if(p1score >= MAX_POINTS || p2score >= MAX_POINTS) { textFont(font, 32); text("GAME!", width/2, height/2); } } if(p1score >= MAX_POINTS) { textFont(font, 16); textAlign(LEFT, TOP); fill(210, 160, 0); if((anim_frame/2) % 30 == MAX_POINTS+1) fill(255); text("Winner!", INSET - RAD + 4, TOPMARGIN + RAD * 2 + 4); fill(255, 210, 0); if((anim_frame/2) % 30 == MAX_POINTS) fill(255); text("Winner!", INSET - RAD + 2, TOPMARGIN + RAD * 2 + 2); } if(p2score >= MAX_POINTS) { textFont(font, 16); textAlign(RIGHT, TOP); fill(210, 160, 0); if((anim_frame/2) % 30 == MAX_POINTS+1) fill(255); text("Winner!", width - INSET + RAD + 2, TOPMARGIN + RAD * 2 + 4); fill(255, 210, 0); if((anim_frame/2) % 30 == MAX_POINTS) fill(255); text("Winner!", width - INSET + RAD, TOPMARGIN + RAD * 2 + 2); } for(int i = 0; i < MAX_POINTS; i++) { if(p1score > i) { strokeWeight(2); fill(255, 210, 0); if((anim_frame/2) % 30 == i) fill(255); stroke(210, 160, 0); ellipse(INSET + SPACING*i, TOPMARGIN, RAD, RAD); stroke(255); if((anim_frame/2 + 1) % 30 == i) arc(INSET + SPACING*i, TOPMARGIN, RAD, RAD, TWO_PI*3/8, TWO_PI*7/8); if((anim_frame/2 + 29) % 30 == i) arc(INSET + SPACING*i, TOPMARGIN, RAD, RAD, TWO_PI*7/8, TWO_PI*11/8); } else { strokeWeight(1); noFill(); stroke(160); ellipse(INSET + SPACING*i, TOPMARGIN, RAD, RAD); } if(p2score > i) { strokeWeight(2); fill(255, 210, 0); if((anim_frame/2) % 30 == 4-i) fill(255); stroke(210, 160, 0); ellipse(width - INSET - SPACING*i, TOPMARGIN, RAD, RAD); stroke(255); if((anim_frame/2 + 1) % 30 == 4-i) arc(width - INSET - SPACING*i, TOPMARGIN, RAD, RAD, TWO_PI*3/8, TWO_PI*7/8); if((anim_frame/2 + 29) % 30 == 4-i) arc(width - INSET - SPACING*i, TOPMARGIN, RAD, RAD, TWO_PI*7/8, TWO_PI*11/8); } else { strokeWeight(1); noFill(); stroke(160); ellipse(width - INSET - SPACING*i, TOPMARGIN, RAD, RAD); } } } void stop() { audioClose(); super.stop(); } void physicsStep() { game_frames++; wave_phase += WAVE_SPEED; fg_particles.tick(); bg_particles.tick(); if(game_state == TITLE) { if(menu_state == TWOPLAYER_SELECT && p1_ready && p2_ready) { if(gamestart_timer == GAMESTART_TIME) { bgmStop(); wave_sound.trigger(); } gamestart_timer --; if(gamestart_timer <= 0) newGame(newCritterByIndex(p1_char_select.index, LEFT), newCritterByIndex(p2_char_select.index, RIGHT), -1); } if(menu_state == ONEPLAYER_SELECT && p1_ready) { if(gamestart_timer == GAMESTART_TIME) { bgmStop(); wave_sound.trigger(); } gamestart_timer --; if(gamestart_timer <= 0) doTournamentRound(0); } } if(game_state == PLAYING) { if(p2ai) doAI(); if(p1score >= MAX_POINTS || p2score >= MAX_POINTS) { if(point_timer == POINT_TIME -1) { bgmStop(); } if(point_timer == -POINT_TIME && gamestart_timer == GAMESTART_TIME) { if(!(p2ai && p2score >= MAX_POINTS)) bgmLoop("ddbeach.mp3"); } p1.control_action = false; p1.control_left = false; p1.control_right = false; p2.control_action = false; p2.control_left = false; p2.control_right = false; } p1.tick(); p2.tick(); ball.tick(); if(serve) { ball.pos.x = server.pos.x; ball.pos.y = server.pos.y; ball.vel.x = 0; ball.vel.y = 0; } point_timer--; if((p1score >= MAX_POINTS || p2score >= MAX_POINTS) && gamestart_timer != GAMESTART_TIME) { gamestart_timer --; if(gamestart_timer <= 0) { if(tourneyround == -1 || p2score == MAX_POINTS || tourneyround >= NUM_CRITTERS -2) titleSetup(); else doTournamentRound(tourneyround +1); } } } //jukebox stuff info_time--; } float AI_FUZZ = 10; float random_serve_point; float PANIC_LEVEL = 30; void doAI() { if(ball.pos.x < width/2 || ball.vel.x < -0.001) { p2.control_left = (p2.pos.x > width*7/8 - p1.pos.x/2 + 10); p2.control_right = (p2.pos.x < width*7/8 - p1.pos.x/2 - 10); p2.control_action = false; } else { p2.control_left = (expected_landing + AI_FUZZ < p2.pos.x); p2.control_right = (expected_landing - AI_FUZZ > p2.pos.x); if(p2.state == p2.STATE_GROUND) { if(ball.pos.y < p2.pos.y || inRadius(ball.pos, p2.pos, p2.reach) && point_timer <= 0) p2.control_action = !p2.control_action; } if(p2.state == p2.STATE_RISE) { if(ball.vel.y < 0 && ball.pos.y < p2.pos.y) p2.control_action = true; else p2.control_action = false; if(PVector.angleBetween(ball.vel, PVector.sub(p2.pos, ball.pos)) < 0.5) p2.control_action = true; } if(p2.state == p2.STATE_FALL) { if(PVector.angleBetween(ball.vel, PVector.sub(p2.pos, ball.pos)) < 0.5 || (ball.pos.y > p2.pos.y) ) p2.control_action = true; } if(p2.state == p2.STATE_FOCUS) { if(inRadius(ball.pos, p2.pos, p2.reach) || ball.pos.y > p2.pos.y || ball.pos.y > GROUND_LEVEL - PANIC_LEVEL) p2.control_action = false; p2.control_left = p2.control_right = false; if(p1.pos.x < width/4) p2.control_right = true; else p2.control_left = true; } } } void ballLand(float xpos) { if(game_on) { game_on = false; point_timer = POINT_TIME; if(!muted) point_sound.trigger(); if(ball.pos.x > width/2) { p1score ++; server = p2; p1s_point = true; } else { p2score ++; server = p1; p1s_point = false; } } } void drawTitle() { color fill_c = color(192, 16, 64); color stroke_c = color(148, 0, 32); textFont(font, 32); textAlign(CENTER, TOP); fill(stroke_c); text("Dragondot's", width/2+3, 32+3); fill(fill_c); text("Dragondot's", width/2, 32); fill(255); textFont(font, 16); text("EXTREME BEACH VOLLEYBALL", width/2, 72); if(menu_state == MODE_MENU) { mode_menu.drawAt(width/2, 260); } if(menu_state == ONEPLAYER_SELECT || menu_state == TWOPLAYER_SELECT) { fill(255, 128+64*sin(anim_frame*0.1)); textFont(font, 16); textAlign(LEFT, TOP); if(gamestart_timer == GAMESTART_TIME) text("Q = back", 8, 8); fill(0, 128); rectMode(CORNERS); noStroke(); rect(30, 320, width/2-30, 450); textAlign(CENTER, TOP); textFont(font, 8); fill(128, 192, 255); text("Player 1", width/4, 326); p1_char_select.drawAt(width/4, 336); if(p1_ready) { textFont(font, 8); fill(255, 255 - (anim_frame%16)*4, 0); text("READY!", width/4, 438); } } if(menu_state == TWOPLAYER_SELECT) { fill(0, 128); rectMode(CORNERS); noStroke(); rect(width/2+30, 320, width-30, 450); textAlign(CENTER, TOP); textFont(font, 8); fill(128, 192, 255); text("Player 2", width*3/4, 326); p2_char_select.drawAt(width*3/4, 336); if(p2_ready) { textFont(font, 8); fill(255, 255 - (anim_frame%16)*4, 0); text("READY!", width*3/4, 438); } } if(gamestart_timer != GAMESTART_TIME) { mosaic((GAMESTART_TIME - gamestart_timer)/3+1); fill(FADE_COLOR, 255-255*gamestart_timer/GAMESTART_TIME); noStroke(); rect(0, 0, width, height); } } void drawingStep() { if(!paused) anim_frame++; background(234,233, 228); drawBeach(); if(game_state == TITLE) { drawTitle(); } if(game_state == PLAYING) { bg_particles.draw(); p1.draw(); p2.draw(); ball.draw(); drawNet(); fg_particles.draw(); //debug physics if(debug_aiming) { strokeWeight(1); stroke(0, 0, 255); line(debug_left_bound, GROUND_LEVEL, debug_right_bound, GROUND_LEVEL); stroke(255, 0, 0); line(expected_landing, GROUND_LEVEL - 5, expected_landing, GROUND_LEVEL + 5); } drawScore(); if(game_frames < IRIS_TIME) { diamonds(game_frames * 100.0 / IRIS_TIME); fill(255, 255 - (255 * game_frames / IRIS_TIME)); textFont(font, 32); textAlign(CENTER, CENTER); if(tourneyround >= 0) { if(tourneyround < NUM_CRITTERS - 2) text("ROUND "+(tourneyround+1), width/2, height/2); else text("FINAL", width/2, height/2); } } if(p1score >= MAX_POINTS && tourneyround == NUM_CRITTERS - 1) { fill(0, min(128 * (1 - 1.0 * point_timer / POINT_TIME), 128)); noStroke(); rect(0, 0, width, height); fill(255, min(255 * (0 - 1.0 * point_timer / POINT_TIME), 255)); textFont(font, 32); textAlign(CENTER, TOP); text("CONGRATULATIONS!", width/2, 100); fill(255, min(255 * (-1 - 1.0 * point_timer / POINT_TIME), 255)); textFont(font, 16); text("Title screen music by John Axon\nEverything else by Nathan McCoy\n\nInspired by Slime Volleyball\nand Mario Tennis\n\nDedicated to my awesome fans\n\nTo unlock extra characters,\npress \"!\" on title screen.", width/2, 160); } if(gamestart_timer != GAMESTART_TIME) { mosaic((GAMESTART_TIME - gamestart_timer)/3+1); fill(FADE_COLOR, 255-255*gamestart_timer/GAMESTART_TIME); noStroke(); rect(0, 0, width, height); } } if(paused) { fill(0, 128); noStroke(); strokeWeight(1); rectMode(CORNERS); rect(0, 0, width, height); fill(255); textAlign(CENTER, CENTER); textFont(font, 32); text("PAUSED", width/2, height/2); textFont(font, 8); text("Q = exit game", width/2, height/2 + 32); } drawSongInfo(); } void draw() { while(physics_ms_last + PHYS_MS < millis()) { if(!paused) { physicsStep(); } physics_ms_last += PHYS_MS; } if(drawing_ms_last + DRAW_MS < millis()) { drawingStep(); drawing_ms_last = millis(); } } void keyPressed() { if(!keys[keyCode]) { keys[keyCode] = true; down(keyCode); } } void keyReleased() { if(keys[keyCode]) { keys[keyCode] = false; up(keyCode); } } void down(int theKey) { //println(theKey + " down"); if(theKey == 'M') jbToggleMute(); if(theKey == 'P' && game_state != TITLE) { paused = !paused; if(!muted) blip_sound.trigger(); if(paused) bgmDuck(-10); else bgmDuck(0); } if(game_state == PLAYING) { if(!paused) { if(theKey == 'C') p1.control_left = true; if(theKey == 'V') p1.control_right = true; if(theKey == 'Z') p1.control_action = true; if(!p2ai) { if(theKey == LEFT) p2.control_left = true; if(theKey == RIGHT) p2.control_right = true; if(theKey == CONTROL) p2.control_action = true; } } else if(theKey == 'Q') { titleSetup(); if(!muted) select_sound.trigger(); } if((p1score >= MAX_POINTS || p2score >= MAX_POINTS) && gamestart_timer == GAMESTART_TIME && point_timer <= 0) { if(theKey == 'Z' || theKey == CONTROL) { gamestart_timer--; if(!muted)wave_sound.trigger(); bgmStop(); } } } if(game_state == TITLE) { if(!secret && keys[SHIFT] && theKey == '1') { secret = true; p1_char_select = new FlipMenu(ALL_CHARACTERS); //p1_char_select.base_size = 8; p2_char_select = new FlipMenu(ALL_CHARACTERS); //p2_char_select.base_size = 8; if(!muted) secret_sound.trigger(); } if(menu_state == MODE_MENU) { if(theKey == 'C' || theKey == LEFT) { if(!muted) blip_sound.trigger(); mode_menu.prev(); } if(theKey == 'V' || theKey == RIGHT) { if(!muted) blip_sound.trigger(); mode_menu.next(); } if(theKey == 'Z' || theKey == CONTROL) { if(!muted) select_sound.trigger(); if(mode_menu.index == 0) menu_state = ONEPLAYER_SELECT; if(mode_menu.index == 1) menu_state = TWOPLAYER_SELECT; } } else { if(gamestart_timer == GAMESTART_TIME) { if(menu_state == ONEPLAYER_SELECT || menu_state == TWOPLAYER_SELECT) { if(theKey == 'Q') { menu_state = MODE_MENU; p1_ready = false; p2_ready = false; if(!muted) blip_sound.trigger(); } if(theKey == 'C') { if(!muted) blip_sound.trigger(); p1_char_select.prev(); p1_ready = false; } if(theKey == 'V') { if(!muted) blip_sound.trigger(); p1_char_select.next(); p1_ready = false; } if(theKey == 'Z' && !p1_ready) { if(!muted) select_sound.trigger(); p1_ready = true; } } if(menu_state == TWOPLAYER_SELECT) { if(theKey == LEFT) { if(!muted) blip_sound.trigger(); p2_char_select.prev(); p2_ready = false; } if(theKey == RIGHT) { if(!muted) blip_sound.trigger(); p2_char_select.next(); p2_ready = false; } if(theKey == CONTROL && !p2_ready) { if(!muted) select_sound.trigger(); p2_ready = true; } } } } } } void up(int theKey) { //println(theKey + " up"); if(game_state == PLAYING) { if(!paused) { if(theKey == 'C') p1.control_left = false; if(theKey == 'V') p1.control_right = false; if(theKey == 'Z') p1.control_action = false; if(!p2ai) { if(theKey == LEFT) p2.control_left = false; if(theKey == RIGHT) p2.control_right = false; if(theKey == CONTROL) p2.control_action = false; } } } }