//Version 0.7 released for teaching and learning only, no commerical usage permitted
//code written by Nik Hanselman and Greg Niemeyer
//game designed by Joe McKay and Greg Niemeyer

import processing.opengl.*;
import processing.serial.*;
import javax.media.opengl.*;
import javax.media.opengl.glu.*;
import com.sun.opengl.util.*;
import java.nio.*;
import com.sun.opengl.util.texture.*;
import krister.Ess.*;

GL gl;
GLU glu;
GLUT glut;
PGraphicsOpenGL pgl;
PFont font;

int stash = 160; //how many balls you got?

ball[] beach_ball = new ball[stash];
ball[] joker_ball = new ball[stash];
goal[] ball_goal = new goal[4];

//GAME ENVIROMENT VARIABLES
//light
float[] light_pos = {0,0, -500,500};
float[] spot_direction = { 0.0 , 0.0 , 1.0 };
//fog
float[] fog_color = {.9, .9, .9, 1.0};
float light_cutoff = 80.0;
float fog_start = 1000.0;
float fog_end = 5000.0;
float fog_density = .1;
//camera
float[] camera_pos = {0,0, 1000, 0, 0, -500.0, 0.0, 1.0 , 0.0};
float[] new_camera_pos = {0,0, 1000, 0, 0, -500.0, 0.0, 1.0 , 0.0};

//SENSOR VARIABLES
boolean sensor = true; //false if played with mouse, true if played with two sensors
Serial myPort0, myPort1; // defines the serial port:
//Expand array size to the number of bytes you expect:
byte[] inBuffer = new byte[7];
int lf = 10;//defines a line feed in bytes;
boolean triggered = false;
float effect = 1.5;//multiplies input angle
int inByte; // Input string from serial port:
int data0 = 0;
int data1 = 0;
int data2 = 0;
int data3 = 0;
int data5 = 0;//light data
int threshold = 50;
int threshold_wt = 59;
float tilt_x = 0.0;
float tilt_y = 0.0;
float data02 = 0.0;
float data13 =0.0;
float data01 = 0.0;
float data23 = 0.0;
//get the angles in radians
float hx = 0.0;
float hy = 0.0;
float base = 100.0;
int serialCount = 0;
int offset = -23; //this is sensor dependent
int[] serialInArray = new int[5];
float [] tilt_array = {
0.0, 0.0, 0.0, 0.0};
float [] angle_x = {
0.0, 0.0, 0.0, 0.0};
float [] angle_y = {
0.0, 0.0, 0.0, 0.0};
float [] normalized = {
0.0, 0.0, 0.0, 0.0};
//tilt_array = {0.0, 0.0, 0.0, 0.0};
float factor = 5.0; //this factor scales from data to screen pixels: 400/255-offset

//3D OBJECT TEXTURES AND VARIABLES
GLUquadric sphere_obj;
int texture_wrapvert = 1;
int texture_wraphoriz = 1;
int ball_list, panel_list, goal_list, backring_list, background_list;
float[] panel_pos = {0,0,-400};
float[] backring_pos = {0,0,-2000};
float[] background_pos = {0,300,-2300};
float[] new_background_pos = {0,0,-2300};
Texture ring_black_tex, ring_green_tex, ring_red_tex, ring_blue_tex,
ball_black_tex, ball_green_tex, ball_red_tex, ball_blue_tex,
backring_tex, background_tex, panel_tex, joker_tex, ball_tex,
goal_tex;
String asset_path = "/Users/gregniemeyer/Documents/Processing/balance_rc7/data/";
String ring_black_path = asset_path + "r00.jpg";
String ring_green_path = asset_path + "r01.jpg";
String ring_red_path = asset_path + "r02.jpg";
String ring_blue_path = asset_path + "r03.jpg";
String ball_black_path = asset_path + "g00.jpg";
String ball_green_path = asset_path + "g01.jpg";
String ball_red_path = asset_path + "g02.jpg";
String ball_blue_path = asset_path + "g03.jpg";
String joker_path = asset_path + "g04.jpg";
String background_path = asset_path + "background.jpg";
String panel_path = asset_path + "plane.jpg";
String backring_path = asset_path + "backring.jpg";

//OBJECT ATTRIBUTE VARIABLES
int panel_r = 110;
int ball_r = 40;
int waiting_r = 780;
int ball_div = 10;
int torus_div = 20;
int ring_r1 = 123;
int ring_r2 = 25;
int backring_div = 30;
int backring_r1 = 750;
int backring_r2 = 40;
int accel = 2;
int r_mod = 35;
int goal_offset = 350;
int scored_r = 710;
int backring_shake = 0;
int backring_shake_counter = 0;
float COR = 0.27;
float damper = 0.1;
float ball_wait = 3000;
float amp = 1.75;

//VARIABLES ACTIVE DURING GAME
int current_ball = 0;
int current_joker = 0;
int current_level = 0;
int level_to = 0;
int score = 0;
boolean win = false;
boolean lose = false;
int old_score, cur_score, old_level, cur_level;
float panel_x_deg, panel_y_deg, time_ball, time_between, level_anim_loop,
motion, trans_count;
float[][] old_pos = new float[4][3];
float[][] new_pos = new float[4][3];
int current_score_num;
float game_start;
float cur_time;
int seconds, minutes;
boolean player_state = false;

//SOUNDS
AudioChannel ball_snd0;
AudioChannel ball_snd1;
AudioChannel ball_snd2;
AudioChannel ball_snd3;
AudioChannel ball_snd4;
AudioChannel ball_snd5;
AudioChannel win_snd;
AudioChannel collide_snd0;
AudioChannel collide_snd1;
AudioChannel collide_snd2;
AudioChannel collide_snd3;
AudioChannel collide_snd4;
AudioChannel level_snd;
AudioChannel bad_snd;
AudioChannel very_bad_snd;
AudioChannel joker_snd;
AudioChannel wind_snd;

void setup(){
font = loadFont("Helvetica-Bold-32.vlw");
size(1024, 768, OPENGL);
gl=((PGraphicsOpenGL)g).gl;
glu=((PGraphicsOpenGL)g).glu;
pgl = (PGraphicsOpenGL) g;
glut = new GLUT();
Ess.start(this);
load_sound();
wind_snd.volume(0.1);
wind_snd.play(Ess.FOREVER);
make_panel();
make_balls();
make_goals();
make_background();
make_backring();
time_ball = millis();
game_start = millis();
//FOR SENSORS
if (sensor){
println(Serial.list());
myPort0 = new Serial(this, Serial.list()[3], 9600);
myPort1 = new Serial(this, Serial.list()[5], 9600);
myPort0.clear();
myPort1.clear();
}
}

void draw(){
background(0);
pgl.beginGL();
draw_light();
draw_fog();
set_camera();
draw_panel();
draw_backring();
draw_background();
for(int i = 0; i < ball_goal.length; i++)
draw_goal(ball_goal[i]);
if(player_present()){
if(!win && !lose){
level_calc();
update_time();
draw_balls_in_play();
progress_ball_queue();
check_win_lose();
}
}
if(win && !lose)
draw_win();
if(!win && lose)
draw_lose();
pgl.endGL();
display_text();
}

void draw_balls_in_play(){
for(int i = 0; i < beach_ball.length; i++){
if(i <= current_ball + 12 && i >= current_ball + 1)
draw_ball(waiting_circ_calc(beach_ball[i],i));
if(i > current_ball + 12 && i < current_ball + 19)
draw_ball(waiting_line_calc(beach_ball[i],i));
if(beach_ball[i].in_play == true){
beach_ball[i].calculate_vector();
beach_ball[i].move();
draw_ball(beach_ball[i]);
beach_ball[i] = collision_check(beach_ball[i]);
for(int o = 0; o < ball_goal.length; o++)
beach_ball[i] = collision_goal(beach_ball[i], ball_goal[o]);
}
if(beach_ball[i].position[2] < -2000 && beach_ball[i].in_play == true || beach_ball[i].scored == true && beach_ball[i].in_play == true)
beach_ball[i].in_play = false;
if(beach_ball[i].scored == true)
draw_ball(scored_circ_calc(beach_ball[i]));
}

for(int i = 0; i < current_joker; i++){
if(joker_ball[i].in_play == true){
joker_ball[i].calculate_vector();
joker_ball[i].move();
draw_ball(joker_ball[i]);
joker_ball[i] = collision_check(joker_ball[i]);
for(int o = 0; o < ball_goal.length; o++)
joker_ball[i] = collision_goal(joker_ball[i], ball_goal[o]);
}
if(joker_ball[i].position[2] < -2000 && joker_ball[i].in_play == true || joker_ball[i].scored == true && joker_ball[i].in_play == true)
joker_ball[i].in_play = false;
if(joker_ball[i].scored == true)
draw_ball(scored_circ_calc(joker_ball[i]));
}
}

boolean player_present(){
boolean status = player_state;
int sensor_sum = data0 + data1 + data2 + data3;
//println("sensor_sum is at"+sensor_sum);
if(sensor_sum > threshold_wt && player_state == false){
new_camera_pos[0] = 0;
new_camera_pos[1] = 0;
new_camera_pos[2] = 1000;
new_camera_pos[3] = 0;
new_camera_pos[4] = 0;
new_camera_pos[5] = -500;
new_camera_pos[6] = 0;
new_camera_pos[7] = 1.0;
new_camera_pos[8] = 0;
player_state = true;
reset_game();
status = true;
}
else
if(sensor_sum < threshold_wt && player_state == true){
new_camera_pos[0] = 0;
new_camera_pos[1] = -300;
new_camera_pos[2] = 1000;
new_camera_pos[3] = 0;
new_camera_pos[4] = 0;
new_camera_pos[5] = -500;
new_camera_pos[6] = 0;
new_camera_pos[7] = 1.0;
new_camera_pos[8] = 0;
player_state = false;
status = false;
}
return status;
}

void draw_win(){
for(int i = 0; i <= 3; i++){
for(int j = 0; j <= 2; j++){
if(new_pos[i][j] > ball_goal[i].position[j] + 10)
ball_goal[i].position[j] += 10;
else
if(new_pos[i][j] > ball_goal[i].position[j] + 1)
ball_goal[i].position[j]++;
if(new_pos[i][j] < ball_goal[i].position[j] - 10 )
ball_goal[i].position[j] -= 10;
else
if(new_pos[i][j] < ball_goal[i].position[j] -1)
ball_goal[i].position[j] --;
}
ball_goal[i].orientation[1]++;
draw_goal(ball_goal[i]);
}
if(backring_pos[2] < -1400)
backring_pos[2]+=10;
for(int i = 0; i < beach_ball.length; i++)
win_anim(beach_ball[i], i);
for(int i = 0; i < joker_ball.length; i++)
win_anim(joker_ball[i], i);
}

void win_anim(ball w_ball, int index){
int pos_neg;
if(w_ball.scored == true){
if(w_ball.position[2] < -500 - (75 * w_ball.score_num)){
w_ball.position[2] += 20;
}
draw_ball(w_ball);
}
//if(w_ball.scored == false && index > current_ball && index < current_ball + 19 && w_ball.position[2] < 1200){
// w_ball.position[2]+=30;
// draw_ball(w_ball);
//}
if(w_ball.scored == false && index < current_ball && w_ball.type !=4 && w_ball.position[2] < -400 && w_ball.in_play == false && w_ball.type !=4||
w_ball.scored == false && index > current_ball + 19 && w_ball.type !=4 && w_ball.position[2] < -400 && w_ball.in_play == false && w_ball.type !=4){
w_ball.position[2]+=15;
draw_ball(w_ball);
}
if(w_ball.position[2] >= -400 && w_ball.scored == false && w_ball.in_play == false){
w_ball.in_play = true;
w_ball.ball_start = millis();
pos_neg = int(random(10));
if(pos_neg <= 5) pos_neg = 1;
if(pos_neg > 5) pos_neg = -1;
w_ball.position_vector[0] = pos_neg * random(5);
pos_neg = int(random(10));
if(pos_neg <= 5) pos_neg = 1;
if(pos_neg > 5) pos_neg = -1;
w_ball.position_vector[1] = pos_neg * random(5);
w_ball.position_vector[2] = random(30) - 3;
}
if(w_ball.in_play == true && w_ball.type !=4){
w_ball.calculate_vector();
w_ball.move();
draw_ball(w_ball);
}
if(w_ball.position[2] < -2000 && w_ball.position_vector[2] < 0 && w_ball.type !=4){
w_ball.in_play = false;
w_ball.scored = false;
}
}

void draw_lose(){
for(int i = 0; i <= 3; i++){
for(int j = 0; j <= 2; j++){
if(new_pos[i][j] > ball_goal[i].position[j] + 10)
ball_goal[i].position[j] += 10;
else
if(new_pos[i][j] > ball_goal[i].position[j] + 1)
ball_goal[i].position[j]++;
if(new_pos[i][j] < ball_goal[i].position[j] - 10 )
ball_goal[i].position[j] -= 10;
else
if(new_pos[i][j] < ball_goal[i].position[j] -1)
ball_goal[i].position[j] --;
}
}
for(int i = 0; i < ball_goal.length; i++){
if(ball_goal[i].shake == 0)
ball_goal[i].shake = 20;
//draw_goal(ball_goal[i]);
}
for(int i = 0; i < beach_ball.length; i++){
if(beach_ball[i].scored == true && beach_ball[i].in_play == true){
beach_ball[i].calculate_vector();
beach_ball[i].move();
draw_ball(beach_ball[i]);
}
/*if(beach_ball[i].in_play == false && beach_ball[i].scored == false){
if(beach_ball[i].position[2] >= -500)
beach_ball[i].position[2]-=30;
draw_ball(beach_ball[i]);
if(beach_ball[i].position[2] < -500 && beach_ball[i].in_play == false){
beach_ball[i].in_play = true;
beach_ball[i].ball_start = millis();
beach_ball[i].position_vector[0] = random(5) - 3;
beach_ball[i].position_vector[1] = random(5) - 3;
beach_ball[i].position_vector[2] = -1 * random(5);
}
//if(beach_ball[i].position[2] < -3000)
// beach_ball[i].in_play == false;
}*/
if(beach_ball[i].in_play == true && beach_ball[i].scored == false){
beach_ball[i].calculate_vector();
beach_ball[i].move();
draw_ball(beach_ball[i]);
}
}
for(int i = 0; i < joker_ball.length; i++){
if(joker_ball[i].scored == true && joker_ball[i].in_play == true){
joker_ball[i].calculate_vector();
joker_ball[i].move();
draw_ball(joker_ball[i]);
}
}
}

void keyReleased(){
/////////////////////////////////////////////////////////////////////////////
// for some reason when the current_joker is larger than the current_ball //
// balls start to randomly disappear. it is a mystery. //
// this doesn't let the current joker exceed the current ball index. //
// !! this needs to be fixed !! //
/////////////////////////////////////////////////////////////////////////////
if(key == 'q' && current_joker < joker_ball.length /*&& current_joker < current_ball*/ ){
joker_ball[current_joker].init();
current_joker++;
}
if(key == 's'){
sensor =! sensor;
if (sensor){
println(Serial.list());
myPort0 = new Serial(this, Serial.list()[3], 9600);//port for lights
myPort1 = new Serial(this, Serial.list()[5], 9600);//port for balance pad
myPort0.clear();
myPort1.clear();
}
}
if(key == 'w' && data0 + data1 + data2 + data3 < 100){
data0 = 26;
data1 = 26;
data2 = 26;
data3 = 26;
}
else
if(key == 'w' && data0 + data1 + data2 + data3 > 100){
data0 = 0;
data0 = 0;
data0 = 0;
data3 = 0;
}
if(key == 'e')
ball_wait = 3000;
if(key == 'r')
ball_wait = 200;
}

void check_win_lose(){
int pos_neg;
if(current_ball == stash - 1)
score_calc();
if(beach_ball[stash-1].position[2] < -2000 && cur_score < 36 ||
beach_ball[stash-1].scored == true && cur_score < 36){
lose = true;
new_pos[0] = fill_array(0.0, -1.1 * goal_offset, -500.0);
new_pos[1] = fill_array(1.1*goal_offset, 0.0, -500.0);
new_pos[2] = fill_array(0.0, 1.1*goal_offset, -500.0);
new_pos[3] = fill_array(-1.1 * goal_offset, 0.0, -500.0);
for(int i = 0; i < beach_ball.length; i++){
if(beach_ball[i].scored == true){
beach_ball[i].ball_start = millis();
beach_ball[i].in_play = true;
pos_neg = int(random(10));
if(pos_neg <= 5) pos_neg = 1;
if(pos_neg > 5) pos_neg = -1;
beach_ball[i].position_vector[0] = pos_neg * random(5);
pos_neg = int(random(10));
if(pos_neg <= 5) pos_neg = 1;
if(pos_neg > 5) pos_neg = -1;
beach_ball[i].position_vector[1] = pos_neg * random(5);
beach_ball[i].position_vector[2] = random(50)+10;
}
if(beach_ball[i].scored == false){
switch(beach_ball[i].type){
case 0:
beach_ball[i].position[0] = 0;
beach_ball[i].position[1] = -1.1 * goal_offset;
break;
case 1:
beach_ball[i].position[0] = 1.1 * goal_offset;
beach_ball[i].position[1] = 0;
break;
case 2:
beach_ball[i].position[0] = 0;
beach_ball[i].position[1] = 1.1 * goal_offset;
break;
case 3:
beach_ball[i].position[0] = -1.1 * goal_offset;
beach_ball[i].position[1] = 0;
break;
default:
break;
}
beach_ball[i].position[2] = 500 + (i * 50);
beach_ball[i].in_play = false;
}
}
for(int i = 0; i < joker_ball.length; i++){
if(joker_ball[i].scored == true){
joker_ball[i].ball_start = millis();
joker_ball[i].in_play = true;
pos_neg = int(random(10));
if(pos_neg <= 5) pos_neg = 1;
if(pos_neg > 5) pos_neg = -1;
joker_ball[i].position_vector[0] = pos_neg * random(5);
pos_neg = int(random(10));
if(pos_neg <= 5) pos_neg = 1;
if(pos_neg > 5) pos_neg = -1;
joker_ball[i].position_vector[1] = pos_neg * random(5);
joker_ball[i].position_vector[2] = random(50) + 10;
}
}
backring_shake = 70;
play_snd(9);
for(int i = 0; i < ball_goal.length; i++)
ball_goal[i].shake = 20;
}
if(ball_goal[0].score == 9 && ball_goal[1].score == 9 &&
ball_goal[2].score == 9 && ball_goal[3].score == 9){
win = true;
new_pos[0] = fill_array(0.0, -1.1 * goal_offset, -500.0);
new_pos[1] = fill_array(1.1*goal_offset, 0.0, -500.0);
new_pos[2] = fill_array(0.0, 1.1*goal_offset, -500.0);
new_pos[3] = fill_array(-1.1 * goal_offset, 0.0, -500.0);
for(int i = 0; i < beach_ball.length; i ++){
if(beach_ball[i].scored == false){
beach_ball[i].in_play = false;
switch(beach_ball[i].type){
case 0:
beach_ball[i].position[0] = 0;
beach_ball[i].position[1] = -1.1 * goal_offset;
break;
case 1:
beach_ball[i].position[0] = 1.1 * goal_offset;
beach_ball[i].position[1] = 0;
break;
case 2:
beach_ball[i].position[0] = 0;
beach_ball[i].position[1] = 1.1 * goal_offset;
break;
case 3:
beach_ball[i].position[0] = -1.1 * goal_offset;
beach_ball[i].position[1] = 0;
break;
default:
break;
}
beach_ball[i].position[2] = -2000 - (i * 50);
beach_ball[i].position_vector[0] = 0;
beach_ball[i].position_vector[1] = 0;
beach_ball[i].position_vector[2] = 0;
beach_ball[i].orientation_vector[0] = random(100)/200;
beach_ball[i].orientation_vector[1] = random(100)/200;
beach_ball[i].orientation_vector[2] = random(100)/200;
}
}
}
}

void reset_game(){
cur_score = 0;
old_score = 0;
score = 0;
current_level = 0;
background_pos[2] = -2300;
new_background_pos[2] = -2300;
backring_pos[2] = -2000;
game_start = millis();
win = false;
lose = false;
for(int i = 0; i < beach_ball.length; i++){
beach_ball[i].scored = false;
beach_ball[i].in_play = false;
beach_ball[i].score_num = 0;
beach_ball[i].assumed_type = 0;
beach_ball[i].position = fill_array(0,0,0);
beach_ball[i].orientation = fill_array(0,0,0);
beach_ball[i].bad_ball = false;
}
for(int i = 0; i < joker_ball.length; i++){
joker_ball[i].scored = false;
joker_ball[i].in_play = false;
joker_ball[i].score_num = 0;
joker_ball[i].assumed_type = 0;
joker_ball[i].position = fill_array(0,0,0);
joker_ball[i].orientation = fill_array(0,0,0);
joker_ball[i].bad_ball = false;
}
for(int i = 0; i < ball_goal.length; i++){
ball_goal[i].score = 0;
ball_goal[i].scale_vector = 0;
ball_goal[i].cur_scale = 1.0;
}
current_ball = 0;
current_joker = 0;
}

ball lose_ball(ball l_ball, int type){
if(l_ball.bad_ball == false){
l_ball.bad_ball = true;
for(int i = 0; i < beach_ball.length; i++)
if(beach_ball[i].scored == true && beach_ball[i].score_num == ball_goal[type].score && beach_ball[i].type == type)
beach_ball[i].scored = false;
for(int i = 0; i < joker_ball.length; i++)
if(joker_ball[i].scored == true && joker_ball[i].score_num == ball_goal[type].score && joker_ball[i].assumed_type == type)
joker_ball[i].scored = false;
}
return(l_ball);
}

ball collision_goal(ball col_ball, goal col_goal){
if(col_goal.distance_xy(col_ball) < ring_r1 - ring_r2 &&
col_ball.position[2] < col_goal.position[2] &&
col_ball.position[2] > col_goal.position[2] - 100){
if(col_goal.type == col_ball.type || col_ball.type == 4){
col_ball.scored = true;
col_ball.in_play = false;
if(col_ball.type < 4) play_snd(10);
if(col_ball.type == 4) play_snd(14);
if(col_goal.scale_vector >= 0)
col_goal.scale_vector = .05;
col_ball.assumed_type = col_goal.type;
score_calc();
col_ball.score_num = col_goal.score;
new_background_pos[2] -= 80;
if(col_goal.score > 9){
col_ball = lose_ball(col_ball, col_goal.type);
score_calc();
play_snd(9);
backring_shake = 70;
}
}
else{
col_goal.shake = 20;
play_snd(8);
col_ball = lose_ball(col_ball, col_goal.type);
score_calc();
}
}
if(col_goal.distance_xy(col_ball) >= ring_r1 - ring_r2 - ball_r/2 &&
col_goal.distance_xy(col_ball) <= ring_r1 + ring_r2 + ball_r/2 &&
col_ball.position[2] > col_goal.position[2] + ball_r &&
col_ball.position[2] < col_goal.position[2] + 100){
float choc = (random(1)*0.01)+0.9;
col_ball.position_vector[0] = col_ball.position_vector[0]*damper*choc;
col_ball.position_vector[1] = col_ball.position_vector[1]*damper*choc;
col_ball.position_vector[2] = col_ball.position_vector[2]*damper;
col_ball.position_vector = goal_reflection(col_ball.position_vector, goal_vector(col_ball, col_goal));//col_ball.reflection_vector );
col_ball.ball_start = millis();
switch(col_goal.type){
case 0:
play_snd(6);
break;
case 1:
play_snd(11);
break;
case 2:
play_snd(12);
break;
case 3:
play_snd(13);
break;
default:
break;
}
}
return(col_ball);
}

float[] goal_vector(ball v_ball, goal v_goal){
float x = (v_ball.position[0] - v_goal.position[0]) * -1;
float y = (v_ball.position[1] - v_goal.position[1]) * -1;
float z = (v_ball.position[2] - v_goal.position[2]);
float[] new_vector = new float [3];
new_vector[0] = x;
new_vector[1] = y;
new_vector[2] = z;
return(new_vector);
}


float[] goal_reflection(float[] vector1, float[] vector2){
float[] vector_out = new float[3];
float[] reflecting = new float[3];
float[] incoming = new float[3];
reflecting = normalize(vector1);
incoming = normalize(vector2);
float magnitude = get_magnitude(vector1);
float difference_norm = dot_product(incoming, reflecting);
vector_out[0] = 5 * ((incoming[0]*amp) + 2.0 * (difference_norm * reflecting[0])) * COR * magnitude;
vector_out[1] = 5 * ((incoming[1]*amp) + 2.0 * (difference_norm * reflecting[1])) * COR * magnitude;
vector_out[2] = 5 * ((incoming[2]*amp) - 2.0 * (difference_norm * reflecting[2])) * COR * magnitude;
if(vector_out[2] < 6) vector_out[2] = 6;
return(vector_out);
}

void score_calc(){
old_score = cur_score;
ball_goal[0].score = 0;
ball_goal[1].score = 0;
ball_goal[2].score = 0;
ball_goal[3].score = 0;
for(int i = 0; i < beach_ball.length; i++)
if(beach_ball[i].scored == true)
ball_goal[beach_ball[i].type].score ++;
for(int i = 0; i < joker_ball.length; i++)
if(joker_ball[i].scored == true)
ball_goal[joker_ball[i].assumed_type].score ++;
cur_score = ball_goal[0].score + ball_goal[1].score + ball_goal[2].score + ball_goal[3].score;
if(cur_score != old_score)
which_level();
}

void which_level(){
score = ball_goal[0].score + ball_goal[1].score + ball_goal[2].score + ball_goal[3].score;
int old_level = current_level;
int new_level = floor(score/4);
if(new_level > old_level || old_level > new_level){
trans_count = 0;
current_level = 10;
}
else
current_level = new_level;
}

void draw_goal(goal d_goal){
gl.glPushMatrix();
switch(d_goal.type){
case 0:
goal_tex = ring_black_tex;
break;
case 1:
goal_tex = ring_green_tex;
break;
case 2:
goal_tex = ring_red_tex;
break;
case 3:
goal_tex = ring_blue_tex;
break;
default:
ball_tex = ring_black_tex;
break;
}
goal_tex.enable();
goal_tex.bind();
if(d_goal.shake == 0)
gl.glTranslatef(d_goal.position[0], d_goal.position[1], d_goal.position[2]);
else{
d_goal.shake_count++;
d_goal.shake--;
gl.glTranslatef(d_goal.position[0] + sin((d_goal.shake_count/10) * 2 * PI) * 20, d_goal.position[1], d_goal.position[2]);
}

if(d_goal.scale_vector !=0){
d_goal.cur_scale += d_goal.scale_vector;
gl.glScalef(d_goal.cur_scale, d_goal.cur_scale,d_goal.cur_scale);
if(d_goal.cur_scale > 1.4)
d_goal.scale_vector = d_goal.scale_vector * -1;
if(d_goal.cur_scale < .6)
d_goal.scale_vector = d_goal.scale_vector * -1;
if(d_goal.cur_scale > .9 && d_goal.cur_scale < 1.0 &&
d_goal.scale_vector > 0){
d_goal.cur_scale = 1.0;
d_goal.scale_vector = 0.0;
}
}

gl.glRotatef(d_goal.orientation[1], 0,0,1);
gl.glCallList(goal_list);
goal_tex.disable();
gl.glPopMatrix();
}

void draw_text(String text_string, float x, float y, float z){
gl.glPushMatrix();
gl.glRasterPos3f(x, y, z);
glut.glutBitmapString(GLUT.BITMAP_HELVETICA_18, text_string);
gl.glPopMatrix();
}

void progress_ball_queue(){
time_between = millis() - time_ball;
if(time_between > ball_wait && current_ball >= 0 && current_ball < beach_ball.length - 1 ){
play_snd(current_ball%5);
current_ball++;
beach_ball[current_ball].init();
time_ball = millis();
time_between = millis() - time_ball;
}
}

ball collision_check(ball col_ball){
if(col_ball.next_movement() < -400 + ball_r && col_ball.position[0] > 0 - panel_r - ball_r/2 &&
col_ball.position[0] < 0 + panel_r + ball_r/2 && col_ball.position[1] > 0 - panel_r - ball_r/2 &&
col_ball.position[1] < 0 + panel_r + ball_r/2){

if (sensor){
col_ball.position_vector = reflection(col_ball.position_vector, get_vector(tilt_x, tilt_y, 0));
col_ball.reflection_vector = get_vector(tilt_x, tilt_y, 0);
}else{
col_ball.position_vector = reflection(col_ball.position_vector, get_vector(radians(panel_x_deg), radians(panel_y_deg), 0));
col_ball.reflection_vector = get_vector(radians(panel_x_deg), radians(panel_y_deg), 0);
}
col_ball.ball_start = millis();
play_snd(5);
}
return(col_ball);
}

float[] reflection(float[] vector1, float[] vector2){
float[] vector_out = new float[3];
float[] reflecting = new float[3];
float[] incoming = new float[3];
reflecting = normalize(vector1);
incoming = normalize(vector2);
float magnitude = get_magnitude(vector1);
float difference_norm = dot_product(incoming, reflecting);
vector_out[0] = r_mod * (incoming[0] + 2.0 * (difference_norm * reflecting[0])) * COR * magnitude;
vector_out[1] = r_mod * (incoming[1] + 2.0 * (difference_norm * reflecting[1])) * COR * magnitude;
vector_out[2] = r_mod * (incoming[2] - 2.0 * (difference_norm * reflecting[2])) * COR * magnitude;
if(vector_out[2] < 6) vector_out[2] = 6;
return(vector_out);
}

float dot_product(float[] vector1, float[] vector2){
float sum = 0;
for(int i = 0; i < 2; i++)
sum = sum + vector1[i] * vector2[i];
return(sum);
}

float[] normalize(float[] norm_vector){
float magnitude = get_magnitude(norm_vector);
if(magnitude == 1)
return norm_vector;
for(int i = 0; i < norm_vector.length; i++)
norm_vector[i] = norm_vector[i]/magnitude;
return(norm_vector);
}

float get_magnitude(float[] mag){
float magnitude = sqrt(sq(mag[0]) + sq(mag[1]) + sq(mag[2]));
return( magnitude);
}

float[] get_vector(float x, float y, float z) {
float ch = cos(z);
float sh = sin(z);
float ca = cos(y);
float sa = sin(y);
float cb = cos(x);
float sb = sin(x);
float[] vector = new float[3];
float m00, m01, m02, m10, m11, m12, m20, m21;
m00 = ch * ca;
m01 = sh*sb - ch*sa*cb;
m02 = ch*sa*sb + sh*cb;
m10 = sa;
m11 = ca*cb;
m12 = -ca*sb;
m20 = -sh*ca;
m21 = sh*sa*cb + ch*sb;
vector[0] = m10;
vector[1] = m12;
vector[2] = m11;
return(vector);
}

ball scored_circ_calc(ball c_ball){
scored_r = 710;
float b_off = 7.9; // strange numbers needed to align properly
float ball_circ_y, ball_circ_x;
if(c_ball.assumed_type == 1 || c_ball.type == 1) b_off = 31.6; //more strange numbers
if(c_ball.assumed_type == 3 || c_ball.type == 3) b_off = 32.1;
if(c_ball.type < 4){
ball_circ_y = cos((((c_ball.score_num-1) + b_off) * ((2 * PI)/48)) + ((c_ball.type+1) * (PI/2)));
ball_circ_x = sin((((c_ball.score_num-1) + b_off) * ((2 * PI)/48)) + ((c_ball.type+1) * (PI/2)));
}
else{
ball_circ_y = cos((((c_ball.score_num-1) + b_off) * ((2 * PI)/48)) + ((c_ball.assumed_type+1) * (PI/2)));
ball_circ_x = sin((((c_ball.score_num-1) + b_off) * ((2 * PI)/48)) + ((c_ball.assumed_type+1) * (PI/2)));
}

c_ball.position[0] = scored_r * ball_circ_x;
c_ball.position[1] = scored_r * ball_circ_y;
c_ball.position[2] = -1850;
return(c_ball);
}

ball waiting_circ_calc(ball c_ball, int ball_index){
float ball_circ_y;
float ball_circ_x;
ball_circ_y = cos(((ball_index)-(current_ball)) * .27 - (.27 * ((time_between/ball_wait))));
ball_circ_x = sin(((ball_index)-(current_ball)) * .27 - (.27 * ((time_between/ball_wait))));
c_ball.position[0] = waiting_r * ball_circ_x;
c_ball.position[1] = waiting_r * ball_circ_y + 15;
c_ball.position[2] = -800;
return(c_ball);
}

ball waiting_line_calc(ball c_ball, int ball_index){
float ball_y = ball_index - current_ball;
c_ball.position[0] = ball_y * -180 + 2083 + ((time_between/ball_wait) * 180) ;
c_ball.position[1] = -762;
c_ball.position[2] = -800;
return(c_ball);
}

void draw_ball(ball d_ball){
switch(d_ball.type){
case 0:
ball_tex = ball_black_tex;
break;
case 1:
ball_tex = ball_green_tex;
break;
case 2:
ball_tex = ball_red_tex;
break;
case 3:
ball_tex = ball_blue_tex;
break;
case 4:
ball_tex = joker_tex;
break;
default:
ball_tex = ball_black_tex;
break;
}

gl.glPushMatrix();
gl.glTranslatef(d_ball.position[0], d_ball.position[1],d_ball.position[2]);
if(d_ball.scored != true){
gl.glRotatef(d_ball.orientation[0], 1, 0, 0);
gl.glRotatef(d_ball.orientation[1], 0, 1, 0);
gl.glRotatef(d_ball.orientation[2], 0, 0, 1);
}
ball_tex.enable();
ball_tex.bind();
gl.glCallList(ball_list);
ball_tex.disable();
gl.glPopMatrix();
}

void draw_panel(){
gl.glPushMatrix();
panel_tex.enable();
panel_tex.bind();
gl.glTranslatef(panel_pos[0],panel_pos[1],panel_pos[2]);

if (sensor){
gl.glRotatef(tilt_array[0]*2.0, tilt_array[1], tilt_array[2], tilt_array[3]);

} else {
panel_x_deg = degrees(((mouseY-(height/2))/float(height) * (PI/2)));
panel_y_deg = degrees(((mouseX-(width/2))/float(width) * (PI/2)));
gl.glRotatef(panel_x_deg, 1, 0, 0);
gl.glRotatef(panel_y_deg, 0, 1 ,0);
}
gl.glCallList(panel_list);
panel_tex.disable();
gl.glPopMatrix();
}
void draw_background(){
if(background_pos[2] > new_background_pos[2])
background_pos[2]-=10;
if(background_pos[2] < new_background_pos[2])
background_pos[2]+=10;
gl.glPushMatrix();
background_tex.enable();
background_tex.bind();
gl.glTranslatef(background_pos[0],background_pos[1],background_pos[2]);
if (sensor){
gl.glRotatef(tilt_array[0]*-0.2, tilt_array[1], tilt_array[2], tilt_array[3]);
}
else{
gl.glRotatef(.1 * panel_x_deg, 1,0, 0);
gl.glRotatef(.1 * panel_y_deg, 0,1,0);
}
gl.glCallList(background_list);
background_tex.disable();
gl.glPopMatrix();
}

void draw_backring(){
gl.glPushMatrix();
backring_tex.enable();
backring_tex.bind();

if(backring_shake == 0)
gl.glTranslatef(backring_pos[0], backring_pos[1], backring_pos[2]);
else{
backring_shake_counter++;
backring_shake--;
float m_factor = sin((backring_shake_counter/10.0) * TWO_PI) * 25.0;
gl.glTranslatef(backring_pos[0] + m_factor, backring_pos[1], backring_pos[2]);
}

//gl.glTranslatef(backring_pos[0], backring_pos[1], backring_pos[2]);
gl.glCallList(backring_list);
backring_tex.disable();
gl.glPopMatrix();
}

void make_panel(){
panel_list = ((PGraphicsOpenGL)g).gl.glGenLists(1);
gl.glNewList(panel_list, GL.GL_COMPILE);
gl.glBegin(GL.GL_QUADS);
gl.glNormal3f( 0.0f, 0.0f, 1.0f);
gl.glTexCoord2f(0, 0);
gl.glVertex3f(panel_r, panel_r, 1);
gl.glTexCoord2f(288, 0);
gl.glVertex3f( -panel_r, panel_r, 1);
gl.glTexCoord2f(288, 288);
gl.glVertex3f( -panel_r, -panel_r, 1);
gl.glTexCoord2f(0, 288);
gl.glVertex3f(panel_r, -panel_r, 1);
gl.glEnd();
gl.glEndList();
panel_tex = load_texture(panel_path);
}

void make_balls(){
int ball_type = 6;
int cur_type =0;

sphere_obj = glu.gluNewQuadric();
glu.gluQuadricTexture(sphere_obj, true);
ball_list = ((PGraphicsOpenGL)g).gl.glGenLists(1);
gl.glNewList(ball_list, GL.GL_COMPILE);
glu.gluSphere(sphere_obj, ball_r, ball_div, ball_div);
gl.glEndList();

for(int i = 0; i < beach_ball.length; i++){
beach_ball[i] = new ball();
ball_type --;
beach_ball[i].type = cur_type;
if(ball_type == 0){
cur_type++;
ball_type = 5;
}
if(cur_type > 3)
cur_type = 0;
for(int o = 0; o < beach_ball[i].orientation_vector.length; o++)
beach_ball[i].orientation_vector[o] = random(200)/100;
}

for(int i = 0; i < joker_ball.length; i++){
joker_ball[i] = new ball();
joker_ball[i].type = 4;
for(int o = 0; o < joker_ball[i].orientation_vector.length; o++)
joker_ball[i].orientation_vector[o] = random(200)/100;
}

ball_black_tex = load_texture(ball_black_path);
ball_green_tex = load_texture(ball_green_path);
ball_red_tex = load_texture(ball_red_path);
ball_blue_tex = load_texture(ball_blue_path);
joker_tex = load_texture(joker_path);
}

void make_goals(){
goal_list = ((PGraphicsOpenGL)g).gl.glGenLists(1);
gl.glNewList(goal_list, GL.GL_COMPILE);
gl.glRotatef(90, 1,0,0);
draw_torus(torus_div,torus_div,ring_r2,ring_r1);
gl.glEndList();

for(int i = 0; i < ball_goal.length; i++){
ball_goal[i] = new goal();
ball_goal[i].type = i;
ball_goal[i].init();
}

ring_black_tex = load_texture(ring_black_path);
ring_green_tex = load_texture(ring_green_path);
ring_red_tex = load_texture(ring_red_path);
ring_blue_tex = load_texture(ring_blue_path);
}

void make_background(){
background_list = ((PGraphicsOpenGL)g).gl.glGenLists(1);
gl.glNewList(background_list, GL.GL_COMPILE);
gl.glBegin(GL.GL_QUADS);
gl.glNormal3f( 0.0f, 0.0f, 1.0f);
gl.glTexCoord2f(0, 0); gl.glVertex3f(-3056, -3056, 0); // bottom left
gl.glTexCoord2f(1024, 0); gl.glVertex3f( 3056, -3056, 0); // bottom right
gl.glTexCoord2f(1024, 1024); gl.glVertex3f( 3056, 3056, 0); // top righ
gl.glTexCoord2f(0, 1024); gl.glVertex3f(-3056, 3056, 0); // top left
gl.glEnd();
gl.glEndList();

background_tex = load_texture(background_path);
}

void make_backring(){
backring_list = ((PGraphicsOpenGL)g).gl.glGenLists(1);
gl.glNewList(backring_list, GL.GL_COMPILE);
gl.glRotatef(90, 1,0,0);
gl.glRotatef(48,0,1,0);
draw_torus(backring_div,backring_div,backring_r2,backring_r1);
gl.glEndList();
backring_tex = load_texture(backring_path);
}

void draw_torus(int NumWraps, int NumPerWrap, float MinorRadius, float MajorRadius){
for (int i=0; i<NumWraps; i++ ){
gl.glBegin(GL.GL_QUAD_STRIP);
for (int j=0; j<=NumPerWrap; j++){
put_vert_tex(i, j, MinorRadius, MajorRadius, NumWraps, NumPerWrap);
put_vert_tex(i+1,j, MinorRadius, MajorRadius, NumWraps, NumPerWrap);
}
gl.glEnd();
}
}

void put_vert_tex(int i, int j, float MinorRadius, float MajorRadius, int NumWraps, int NumPerWrap){
float wrapFrac = (j%NumPerWrap)/(float)NumPerWrap;
float wrapFracTex = (float)j/(float)NumPerWrap;
float phi = TWO_PI*wrapFrac;
float thetaFrac = ((float)(i%NumWraps)+wrapFracTex)/(float)NumWraps;
float thetaFracTex = ((float)i+wrapFracTex)/(float)NumWraps;
float theta = TWO_PI*thetaFrac;
float sinphi = sin(phi);
float cosphi = cos(phi);
float sintheta = sin(theta);
float costheta = cos(theta);
float y = MinorRadius*sinphi;
float r = MajorRadius + MinorRadius*cosphi;
float x = sintheta*r;
float z = costheta*r;

gl.glTexCoord2f( wrapFracTex*(float)texture_wrapvert, thetaFracTex*(float)texture_wraphoriz );
gl.glNormal3f(sintheta*cosphi, sinphi, costheta*cosphi);
gl.glVertex3f(x,y,z);
}

Texture load_texture(String fnm){
String fileName = fnm;
Texture tex = null;
try {
tex = TextureIO.newTexture( new File(fileName), false);
tex.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
tex.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
}
catch(Exception e)
{ System.out.println("error loading texture " + fileName); }
return tex;
}

void set_camera(){
gl.glMatrixMode(GL.GL_PROJECTION); gl.glLoadIdentity();
glu.gluPerspective(50.0, 1.333 , 1.0, 10000.0);
gl.glMatrixMode(GL.GL_MODELVIEW); gl.glLoadIdentity();
for(int i = 0; i < camera_pos.length; i++){
if(camera_pos[i] > new_camera_pos[i])
camera_pos[i]-=20;
if(camera_pos[i] < new_camera_pos[i])
camera_pos[i]+=20;
}
glu.gluLookAt(camera_pos[0],camera_pos[1], camera_pos[2],
camera_pos[3],camera_pos[4], camera_pos[5],
camera_pos[6],camera_pos[7], camera_pos[8]);
}

void draw_fog(){
gl.glFogfv(GL.GL_FOG_COLOR, fog_color, 1);
gl.glFogf(GL.GL_FOG_DENSITY,fog_density);
gl.glFogi(GL.GL_FOG_MODE,GL.GL_LINEAR);
gl.glFogf(GL.GL_FOG_START,fog_start);
gl.glFogf(GL.GL_FOG_END,fog_end);
gl.glHint(GL.GL_FOG_HINT, GL.GL_FASTEST);
gl.glEnable(GL.GL_FOG);

}

void draw_light(){
gl.glEnable(GL.GL_LIGHTING);
gl.glEnable(GL.GL_LIGHT0);
gl.glDepthFunc(GL.GL_LEQUAL);
gl.glEnable(GL.GL_DEPTH_TEST);
gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, light_pos,1);
gl.glLightf(GL.GL_LIGHT0, GL.GL_SPOT_CUTOFF, light_cutoff);
gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPOT_DIRECTION, spot_direction, 1);
}

class ball{
float[] position = new float[3];
float[] position_vector = new float[3];
float[] orientation = new float[3];
float[] orientation_vector = new float[3];
float[] reflection_vector = new float[3];
float ball_time;
boolean in_play;
boolean scored;
int score_num;
int type;
int assumed_type;
float ball_dur;
float ball_start;
boolean bad_ball;

void init(){
in_play = true;
scored = false;
score_num = 0;
ball_start = millis();
bad_ball = false;
if(type !=4){
position[0] = 0;
position[1] = 795;
position[2] = -800;
position_vector[0] = 0;
position_vector[1] = -7.5;
position_vector[2] = 17;
}
else{
position[0] = 0;
position[1] = -795;
position[2] = -800;
position_vector[0] = 0;
position_vector[1] = 7.5;
position_vector[2] = 17;
}
}

void calculate_vector(){
ball_dur = (millis() - ball_start)/1000;
position_vector[2] = position_vector[2] + ((ball_dur * ball_dur) * -1 * accel)/4;
}

void move(){
orientation[1] = (orientation[1] + orientation_vector[1]) % 360.0f;
orientation[2] = (orientation[2] + orientation_vector[2]) % 360.0f;
orientation[0] = (orientation[0] + orientation_vector[0]) % 360.0f;
position[0] += position_vector[0];
position[1] += position_vector[1];
position[2] += position_vector[2];
}

float next_movement(){
return(position[2] + position_vector[2]);
}
}

class goal{
float[] position = new float[3];
float[] position_vector = new float[3];
float[] orientation = new float[3];
float[] orientation_vector = new float[3];
float cur_scale;
float scale_vector;
float shake;
float shake_count;
int type;
int score;

void init(){
cur_scale = 1.0;
}

float distance(ball dist_ball){
float distance = sqrt(sq(dist_ball.position[0] - position[0]) +
sq(dist_ball.position[1] - position[1]) +
sq(dist_ball.position[2] - position[2]));
return(distance);
}

float distance_xy(ball dist_ball){
float distance = sqrt(sq(dist_ball.position[0] - position[0]) +
sq(dist_ball.position[1] - position[1]));
return(distance);
}

float distance_z(ball dist_ball){
float distance = sqrt(sq(dist_ball.position[2] - position[2]));
return(distance);
}
}

void level_calc(){
switch(current_level){
case 0:
level_anim_loop = 0.0;
ball_goal[0].position = fill_array(0.0, (-1.1*goal_offset), -500.0);
ball_goal[1].position = fill_array(1.1*goal_offset, 0.0, -500.0);
ball_goal[2].position = fill_array(0.0, 1.1*goal_offset, -500.0);
ball_goal[3].position = fill_array((-1.1*goal_offset), 0.0, -500.0);
break;
case 1:
level_anim_loop ++;
motion = -50 * sin (level_anim_loop/100);
ball_goal[0].position = fill_array(0.0, (-1.1*goal_offset+motion),-500.0 );
ball_goal[1].position = fill_array(1.1 * goal_offset-motion, 0.0, -500.0);
ball_goal[2].position = fill_array(0.0, 1.1 * goal_offset-motion, -500.0);
ball_goal[3].position = fill_array((-1.1*goal_offset+motion), 0.0, -500.0);
break;
case 2:
level_anim_loop ++;
motion = -50 * sin (level_anim_loop/40);
ball_goal[0].position = fill_array(0.0, (-1.0*goal_offset+motion),-500.0 );
ball_goal[1].position = fill_array(goal_offset-motion, 0.0, -500.0);
ball_goal[2].position = fill_array(0.0, goal_offset-motion, -500.0);
ball_goal[3].position = fill_array((-1.0*goal_offset+motion), 0.0, -500.0);
break;
case 3:
level_anim_loop ++;
float goal_move = -2.5* sin(level_anim_loop/100);
ball_goal[0].position = fill_array(goal_offset+goal_move, (-1.0*goal_offset)-goal_move, -500.0);
ball_goal[1].position = fill_array(goal_offset-goal_move, goal_offset-goal_move, -500.0);
ball_goal[2].position = fill_array((goal_offset*-1.0)-goal_move, goal_offset+goal_move, -500.0);
ball_goal[3].position = fill_array((-1.0*goal_offset)+goal_move,-1.0*goal_offset+goal_move, -500.0);
break;
case 4:// moving diagonal
level_anim_loop ++;
goal_move = -50.0 * sin(level_anim_loop/50);
ball_goal[0].position = fill_array(goal_offset+goal_move, (-1.0*goal_offset)-goal_move,-500.0);
ball_goal[1].position = fill_array(goal_offset-goal_move, goal_offset-goal_move, -500.0);
ball_goal[2].position = fill_array((goal_offset*-1.0)-goal_move, goal_offset+goal_move, -500.0);
ball_goal[3].position = fill_array((-1.0*goal_offset)+goal_move, -1.0*goal_offset+goal_move, -500.0);
break;
case 5://rotate goals slowly
level_anim_loop ++;
float goal_move_x = cos(level_anim_loop/1000);
float goal_move_z = sin(level_anim_loop/1000);
float goal_move_x_CC = cos((level_anim_loop/1000)-3.14);
float goal_move_z_CC = sin((level_anim_loop/1000)-3.14);
float goal_move_x_pi2 = cos((level_anim_loop/1000)-1.57);
float goal_move_z_pi2 = sin((level_anim_loop/1000)-1.57);
float goal_move_x_pi4 = cos((level_anim_loop/1000)+1.57);
float goal_move_z_pi4 = sin((level_anim_loop/1000)+1.57);
float rad = 1.1*(goal_offset);
ball_goal[0].position = fill_array(goal_move_x*rad,goal_move_z*rad, -500.0);
ball_goal[2].position = fill_array(rad*goal_move_x_CC,goal_move_z_CC*rad, -500.0);
ball_goal[3].position = fill_array(goal_move_x_pi2*rad,goal_move_z_pi2*rad, -500.0);
ball_goal[1].position = fill_array(goal_move_x_pi4*rad, goal_move_z_pi4*rad, -500.0);
break;
case 6://rotate goals faster
level_anim_loop ++;
goal_move_x = cos(level_anim_loop/500);
goal_move_z = sin(level_anim_loop/500);
goal_move_x_CC = cos((level_anim_loop/500)-3.14);
goal_move_z_CC = sin((level_anim_loop/500)-3.14);
goal_move_x_pi2 = cos((level_anim_loop/500)-1.57);
goal_move_z_pi2 = sin((level_anim_loop/500)-1.57);
goal_move_x_pi4 = cos((level_anim_loop/500)+1.57);
goal_move_z_pi4 = sin((level_anim_loop/500)+1.57);
rad = 1.25*(goal_offset);
ball_goal[0].position = fill_array(goal_move_x*rad,goal_move_z*rad, -500.0);
ball_goal[2].position = fill_array(rad*goal_move_x_CC,goal_move_z_CC*rad, -500.0);
ball_goal[3].position = fill_array(goal_move_x_pi2*rad,goal_move_z_pi2*rad, -500.0);
ball_goal[1].position = fill_array(goal_move_x_pi4*rad, goal_move_z_pi4*rad, -500.0);
break;
case 7://rotate goals slowly
level_anim_loop --;
goal_move_x = cos(level_anim_loop/200);
goal_move_z = sin(level_anim_loop/200);
goal_move_x_CC = cos((level_anim_loop/200)-3.14);
goal_move_z_CC = sin((level_anim_loop/200)-3.14);
goal_move_x_pi2 = cos((level_anim_loop/200)-1.57);
goal_move_z_pi2 = sin((level_anim_loop/200)-1.57);
goal_move_x_pi4 = cos((level_anim_loop/200)+1.57);
goal_move_z_pi4 = sin((level_anim_loop/200)+1.57);
float rad0 = 1.3*(goal_offset);//+(2.0*goal_move_x);
float rad1 = 0.8*goal_offset;//-(2.0*goal_move_x);
float rad2 = 0.8*goal_offset;//+(2.0*goal_move_z);
float rad3 = 1.3*(goal_offset);//-(2.0*goal_move_z);
ball_goal[0].position = fill_array(goal_move_x*rad0, goal_move_z*rad1, -500);
ball_goal[2].position = fill_array(rad0*goal_move_x_CC, goal_move_z_CC*rad1,-500 );
ball_goal[3].position = fill_array(goal_move_x_pi2*rad3, goal_move_z_pi2*rad2,-500);
ball_goal[1].position = fill_array(goal_move_x_pi4*rad3, goal_move_z_pi4*rad2, -500);
break;
case 8://clockwise and counterclockwise animation
level_anim_loop = level_anim_loop+0.6;
goal_move_x = cos(level_anim_loop/100.0);
goal_move_z = sin(level_anim_loop/100.0);
goal_move_x_CC = cos((level_anim_loop/100.0)-3.14);
goal_move_z_CC = sin((level_anim_loop/100.0)-3.14);
goal_move_x_pi2 = cos((level_anim_loop/100.0)-1.57);
goal_move_z_pi2 = sin((level_anim_loop/100.0)-1.57);
goal_move_x_pi4 = cos((level_anim_loop/100)+1.57);
goal_move_z_pi4 = sin((level_anim_loop/100)+1.57);
rad = 1.36*goal_offset;
rad2 = 0.75*goal_offset;
ball_goal[0].position = fill_array(goal_move_x*rad2, goal_move_z*rad2, -500.0);
ball_goal[2].position = fill_array(rad2*goal_move_x_CC, goal_move_z_CC*rad2,-500.0);
ball_goal[3].position = fill_array(-1.0*goal_move_x_pi2*rad, goal_move_z_pi2*rad, -550);
ball_goal[1].position = fill_array(-1.0*goal_move_x_pi4*rad, goal_move_z_pi4*rad, -550);
break;
case 10:
new_background_pos[2] = -2300;
level_anim_loop = 0;
trans_count++;
boolean moved = false;
old_pos[0] = ball_goal[0].position;
old_pos[1] = ball_goal[1].position;
old_pos[2] = ball_goal[2].position;
old_pos[3] = ball_goal[3].position;
int level_to = floor(score/4);
if(level_to == 1 || level_to == 0){
new_pos[0] = fill_array(0.0, -1.1 * goal_offset, -500.0);
new_pos[1] = fill_array(1.1*goal_offset, 0.0, -500.0);
new_pos[2] = fill_array(0.0, 1.1*goal_offset, -500.0);
new_pos[3] = fill_array(-1.1 * goal_offset, 0.0, -500.0);
}
if(level_to == 2){
new_pos[0] = fill_array(0.0, -1 * goal_offset - 50, -500.0);
new_pos[1] = fill_array(goal_offset + 50, 0.0, -500.0);
new_pos[2] = fill_array(0.0, goal_offset + 50, -500.0);
new_pos[3] = fill_array(-1 * goal_offset -50, 0.0, -500);
}
if(level_to == 3){
new_pos[0] = fill_array(goal_offset, -1 * goal_offset, -500.0);
new_pos[1] = fill_array(goal_offset, goal_offset, -500.0);
new_pos[2] = fill_array(-1 * goal_offset, goal_offset, -500.0);
new_pos[3] = fill_array(-1 * goal_offset, -1 * goal_offset, -500);
}
if(level_to == 4){
new_pos[0] = fill_array(goal_offset, -1 * goal_offset, -500);
new_pos[1] = fill_array(goal_offset, goal_offset, -500.0);
new_pos[2] = fill_array(-1 * goal_offset, goal_offset, -500.0);
new_pos[3] = fill_array(-1 * goal_offset, -1 * goal_offset, -500.0);
}
if(level_to == 5){
new_pos[0] = fill_array(1.1 * goal_offset + 2.0, 0.0, -500.0);
new_pos[1] = fill_array(0.0, 1.1 * goal_offset, -500.0);
new_pos[2] = fill_array(-1.1 * goal_offset, 0.0, -500.0);
new_pos[3] = fill_array(0.0, -1.1 * goal_offset, -500.0);
}
if(level_to == 6){
new_pos[0] = fill_array(1.25 * goal_offset + 2.0, 0.0, -500.0);
new_pos[1] = fill_array(0.0, 1.25 * goal_offset, -500.0);
new_pos[2] = fill_array(-1.25 * goal_offset, 0.0, -500.0);
new_pos[3] = fill_array(0.0, -1.25 * goal_offset, -500.0);
}
if(level_to == 7){
new_pos[0] = fill_array(1.3 * goal_offset, 0.0, -500.0);
new_pos[1] = fill_array(0.0, goal_offset * .8, -500.0);
new_pos[2] = fill_array(goal_offset * -1.3, 0.0, -500.0);
new_pos[3] = fill_array(0.0, goal_offset * -.8, -500.0);
}
if(level_to == 8){
new_pos[0] = fill_array(.75 * goal_offset, 0.0, -500.0);
new_pos[1] = fill_array(0.0, goal_offset * 1.36, -550.0);
new_pos[2] = fill_array(goal_offset * -.75, 0.0, -500.0);
new_pos[3] = fill_array(0.0, goal_offset * -1.36, -550.0);
}
for(int i = 0; i <= 3; i++){
for(int j = 0; j <= 2; j++){
if(new_pos[i][j] > ball_goal[i].position[j] + 10){
moved = true;
ball_goal[i].position[j] += 10;
} else if(new_pos[i][j] > ball_goal[i].position[j] + 1){
moved = true;
ball_goal[i].position[j]++;
}
if(new_pos[i][j] < ball_goal[i].position[j] - 10 ){
moved = true;
ball_goal[i].position[j] -= 10;
} else if(new_pos[i][j] < ball_goal[i].position[j] -1){
moved = true;
ball_goal[i].position[j] --;
}
}
}
if(moved = false)
trans_count = 91;
if(level_to == 2)
level_anim_loop = 79;
if(trans_count > 90)
current_level = floor(score/4);
break;
default:
current_level = 0;
}
for(int i = 0; i <=3; i++)
for(int j = 0; j <= 2; j++)
ball_goal[i].position[j] += ball_goal[i].position_vector[j];
}

float[] fill_array(float x, float y, float z){
float[] position = new float[3];
position[0] = x;
position[1] = y;
position[2] = z;
return(position);
}

void load_sound(){
level_snd = new AudioChannel(asset_path+"level.aiff");
bad_snd = new AudioChannel(asset_path+"bad.aiff");
very_bad_snd = new AudioChannel(asset_path+"very_bad.aiff");
ball_snd0 = new AudioChannel(asset_path+"snd0.aiff");
ball_snd1 = new AudioChannel(asset_path+"snd1.aiff");
ball_snd2 = new AudioChannel(asset_path+"snd2.aiff");
ball_snd3 = new AudioChannel(asset_path+"snd3.aiff");
ball_snd4 = new AudioChannel(asset_path+"snd4.aiff");
ball_snd5 = new AudioChannel(asset_path+"snd5.aiff");
win_snd = new AudioChannel(asset_path+"win2.aiff");
collide_snd0 = new AudioChannel(asset_path+"open02.aiff");
collide_snd1 = new AudioChannel(asset_path+"flat.aiff");
collide_snd2 = new AudioChannel(asset_path+"flat02.aiff");
collide_snd3 = new AudioChannel(asset_path+"flat03.aiff");
collide_snd4 = new AudioChannel(asset_path+"flat04.aiff");
joker_snd = new AudioChannel(asset_path + "joker.aiff");
wind_snd = new AudioChannel(asset_path + "windloop.wav");
}

void play_snd(int snd_id){
switch (snd_id){
case (0):
if (ball_snd0.state==Ess.STOPPED){
ball_snd0.volume(1);
ball_snd0.play (1);
}
break;
case (1):
if (ball_snd1.state==Ess.STOPPED){
ball_snd1.volume(1);
ball_snd1.play (1);
}
break;
case (2):
if (ball_snd2.state==Ess.STOPPED){
ball_snd2.volume(1);
ball_snd2.play (1);
}
break;
case (3):
if (ball_snd3.state==Ess.STOPPED){
ball_snd3.volume(1);
ball_snd3.play (1);
}
break;
case (4)://play only if previous playing of this sound ended
if (ball_snd4.state==Ess.STOPPED){
ball_snd4.volume(1);
ball_snd4.play (1);
}
break;
case (5):
if (collide_snd0.state==Ess.STOPPED){
collide_snd0.volume(1);
collide_snd0.play (1);
}
break;
case (6):
if (collide_snd1.state==Ess.STOPPED){
collide_snd1.volume(1);
collide_snd1.play (1);
}
break;
case (7):
if (level_snd.state==Ess.STOPPED){
level_snd.volume(1);
level_snd.play (1);
}
break;
case (8):
if (bad_snd.state==Ess.STOPPED){
bad_snd.volume(1);
bad_snd.play (1);
}
break;
case (9):
if (very_bad_snd.state==Ess.STOPPED){
very_bad_snd.volume(1);
very_bad_snd.play (1);
}
break;
case (10):
if (win_snd.state==Ess.STOPPED){
win_snd.volume(1);
win_snd.play (1);
}
break;
case (11):
if (collide_snd2.state==Ess.STOPPED){
collide_snd2.volume(1);
collide_snd2.play (1);
}
break;
case (12):
if (collide_snd3.state==Ess.STOPPED){
collide_snd3.volume(1);
collide_snd3.play (1);
}
break;
case (13):
if (collide_snd4.state==Ess.STOPPED){
collide_snd4.volume(1);
collide_snd4.play (1);
}
break;
case (14):
if(joker_snd.state == Ess.STOPPED){
joker_snd.volume(1);
joker_snd.play(1);
}
break;

case (15)://fade out all sounds
ball_snd0.fadeTo(0,0010);
ball_snd1.fadeTo(0,0010);
ball_snd2.fadeTo(0,0010);
ball_snd3.fadeTo(0,0010);
ball_snd4.fadeTo(0,0010);
win_snd.fadeTo(0,0010);
collide_snd0.fadeTo(0,0010);
collide_snd1.fadeTo(0,0010);
level_snd.fadeTo(0,0010);
bad_snd.fadeTo(0,0010);
very_bad_snd.fadeTo(0,0010);
break;
}
}

void stop() {
Ess.stop();
super.stop();
}

//FOR SENSORS
void serialEvent(Serial which) {
if (which == myPort0) {
data5 = myPort0.read();

if (data5 < threshold && triggered == false){
println("light sensor is triggered b/c:" +data5);
if (current_joker < joker_ball.length && current_joker < current_ball){
joker_ball[current_joker].init();
current_joker++;
if (ball_snd5.state==Ess.STOPPED){
ball_snd5.volume(1);
ball_snd5.play (1);
}
}

triggered = true;
}
if (data5 > threshold && triggered == true){
triggered = false;
//reset the triggered thing
}

}
else if (which == myPort1) {
myPort1.readBytesUntil(lf, inBuffer);
if (inBuffer[0] != 0) {
data0 = (int)inBuffer[0];
data1 = (int)inBuffer[1];
data2 = (int)inBuffer[2];
data3 = (int)inBuffer[3];
//println ("channel B says the weights are here" + data0+ " ,"+ data1+ " ,"+ data2+ " ,"+ data3);
//sum them up
data02 = (data0+data2)/2.0;
data13 = (data1+data3)/2.0;
data01 = (data0+data1)/2.0;
data23 = (data2+data3)/2.0;
//get the angles in radians
hx = data01-data23;
hy = data02-data13;
tilt_x = atan2(hx, base)*effect;
tilt_y = atan2(hy, base)*effect;
//println ("channel B says the degrees are here" + tilt_x+ " and components are at "+hx + " and base"+base+ " since data0 is "+data0+" and data1 is "+ data1);
//have to do angle addition after all
//step one radians to quat
angle_x = euler2quat(0, 0, tilt_x);
angle_y = euler2quat(0, tilt_y, 0);
//step two add (more like: multiply) quat to final quat
tilt_array = quatsup(angle_x, angle_y);
tilt_array = quat2aa(tilt_array);
//println ("channel B says the degrees are here" + angle_x[0]+ " and y at"+ angle_y[0]);
}

}
}

float[] euler2quat(float x, float y, float z) {
// Assuming the angles are in radians.
double cyaw = Math.cos(x/2.0);
double cpitch = Math.cos(y/2.0);
double croll = Math.cos(z/2.0);
double syaw = Math.sin(x/2.0);
double spitch = Math.sin(y/2.0);
double sroll = Math.sin(z/2.0);

double cyawcpitch = cyaw*cpitch;
double syawspitch = syaw*spitch;
double cyawspitch = cyaw*spitch;
double syawcpitch = syaw*cpitch;

float[] quat = new float [4];

quat[0] = (float)(cyawcpitch*croll + syawspitch * sroll);
quat[1] = (float)(cyawcpitch*sroll - syawspitch *croll);
quat[2] = (float)(cyawspitch*croll + syawcpitch*sroll);
quat[3] = (float)(syawcpitch * croll - cyawspitch*sroll);

quat = qnormalize(quat);
return quat;
}

float[] qnormalize(float[] quat){
float[] norm_vector = new float[4];
norm_vector = quat;
float magnitude = qget_magnitude(quat);
//if(magnitude == 0 )
//??? fix later
if(magnitude == 1)
return norm_vector;
for(int i = 0; i < 4; i++)
norm_vector[i] = norm_vector[i]/magnitude;
return(norm_vector);
}

float qget_magnitude(float[] quat){
float magnitude = sqrt(sq(quat[0]) + sq(quat[1]) + sq(quat[2]) + sq(quat[3]));
return( magnitude);
}

float[] quatsup(float[] q1, float[] q2){//multiplies two quaternions
float q1a = (float)sin(q1[0]/2.0);
q1[0] = (float)cos(q1[0]/2.0);
q1[1] = (float)q1[1]*q1a;
q1[2]= (float)q1[2]*q1a;
q1[3]= (float)q1[3]*q1a; //get first angle
q1 = qnormalize(q1);
float q2a = (float)sin(q2[0]/2.0);
q2[0] = cos(q2[0]/2.0);
q2[1] = (float)q2[1]*q2a;
q2[2] = (float)q2[2]*q2a;
q2[3] = (float)q2[3]*q2a;
q2 = qnormalize(q2);
//now add quaternions by way of quaternion multiplication
float [] q_multiply = new float [4];//w, x y z
q_multiply[0] = q1[0]*q2[0] - q1[1]*q2[1] - q1[2]*q2[2] - q1[3]*q2[3];
q_multiply[1] = q1[0]*q2[1] + q1[1]*q2[0] + q1[2]*q2[3] - q1[3]*q2[2];
q_multiply[2] = q1[0]*q2[2] + q1[2]*q2[0] + q1[3]*q2[1] - q1[1]*q2[3];
q_multiply[3] = q1[0]*q2[3] + q1[3]*q2[0] + q1[1]*q2[2] - q1[2]*q2[1];

q_multiply = qnormalize(q_multiply);
return q_multiply;
}

float[] quat2aa (float [] quaternion){
float [] angle_axis = new float [4];
float angle = 2.0 * acos(quaternion[0])*180/PI;
float factor = sqrt(1-quaternion[0]*quaternion[0]);
if(factor != 0){
angle_axis[0] = angle;
angle_axis[1] = quaternion[1] / factor;
angle_axis[2] = quaternion[2] / factor;
angle_axis[3] = quaternion[3] / factor;
}

return angle_axis;
}

void update_time(){
cur_time = ((millis() - game_start)/1000)/60;
seconds = int((cur_time - int(cur_time)) * 60);
minutes = int(cur_time);
}

void display_text(){
textFont(font, 32);
if(seconds >= 10)
text(minutes + ":" + seconds, 100, 100);
if(seconds < 10)
text(minutes + ":0" + seconds, 100, 100);
}