public class Cue extends Ball {
	//TODO: Refactor same codes into functions, then reuse the functions.
	//TODO: Partial rewrite... D: (Gamedev.net recommended action.)
	private boolean goalFlag;
	private final float[] defaults = new float[3];
	
	// Jumping variables
	private boolean jumping;
	private float radians = 0f;
	private int multiplier = 0;
	private int radianSpeed = 1;
	private boolean jumpingSound = false;
	
	// Death variables
	private boolean death;
	private float deathSpeed = 0f;
	// private int lives;
	
	public Void voidTile;
	
	// Score
	public final int SCORE;
	
	public Cue(float w, float h, float ratio) {
		super();
		this.screenWidth = w;
		this.screenHeight = h;
		this.radius = 8 * ratio;
		this.bitmap = Art.sprites;
		this.jumping = false;
		this.goalFlag = false;
		this.srcRect.set(0, 0, 16, 16);
		this.dstRect.set(0, 0, 16, 16);
		// -------------
		this.death = false;
		// this.lives = 3;
		// -------------
		SCORE = 0;
	}
	
	public int setScore(int value) {
		return value;
	}
	
	@Override
	public void tick(Stage s) {
		this.setAcceleration(Accelero.X, Accelero.Y, Accelero.Z);
		if (bitmap != null) {
			if (this.checkCollision(s.hole) || goalFlag) {
				if (!jumping) {
					goalFlag = true;
					die();
					reflect(s.hole);
					move();
					position[0] -= speed[0];
					position[1] -= speed[1];
					speed[0] *= 0.6f;
					speed[1] *= 0.6f;
					RenderView.cameraX = position[0];
					RenderView.cameraY = position[1];
					
				}
				else {
					reflectResponse();
					move();
					jump();
					if (position[2] <= 0f) {
						jumpingSound = false;
						jumping = false;
					}
				}
				
			}
			else {
				if (acceleration[2] < 0.0) {
					multiplier = 8;
					jumping = true;
					if (jumpingSound == false)
						jumpingSound = true;
				}
				if (!death) {
					if (!jumping) {
						// Moving - Start
						if (!goalFlag) {
							for (Marble m : s.marbles) {
								if (this.checkCollision(m)) {
									this.collisionResponse(m);
								}
							}
						}
						if (pipe != null)
							reflect(pipe);
						else if (funnel != null)
							reflect(funnel);
						else if (ramp != null)
							reflect(ramp);
						else if (connector != null)
							reflect(connector);
						else if (curvePipe != null)
							reflect(curvePipe);
						else if (bumper != null)
							reflect(bumper);
						else
							reflectResponse();
						move();
						// Moving - End
						
					}
					else {
						// Jumping - Start
						if (jumpingSound) {
							Sound.play(Sound.jumpID);
							jumpingSound = false;
						}
						reflectResponse();
						move();
						jump();
						// Jumping - End
						if (bumper != null)
							reflect(bumper);
					}
				}
				else {
					
					deathAnimation();
				}
				
			}
		}
		else
			bitmap = Art.sprites;
	}
	
	@Override
	public void render(Canvas c, final float centerX, final float centerY) {
		if (bitmap != null) {
			float xOffset = this.position[0] - RenderView.cameraX;
			float yOffset = this.position[1] - RenderView.cameraY;
			move(centerX + xOffset, centerY + yOffset);
			c.drawBitmap(bitmap, srcRect, dstRect, null);
		}
	}
	
	private void move(float f, float g) {
		if (bitmap == null)
			return;
		dstRect.set(f - this.radius, g - this.radius, f + this.radius, g + this.radius);
		if (jumping || death) {
			dstRect.left -= position[2] * RenderView.AspectRatio;
			dstRect.right += position[2] * RenderView.AspectRatio;
			dstRect.top -= position[2] * RenderView.AspectRatio;
			dstRect.bottom += position[2] * RenderView.AspectRatio;
		}
	}
	
	public void setStartingPosition(Tee tee) {
		this.setPosition(tee.x, tee.y, 0f);
		for (int i = 0; i < 3; i++) {
			defaults[i] = position[i];
		}
		move();
		reset();
	}
	
	public void setGoalFlag(boolean value) {
		goalFlag = value;
	}
	
	public boolean hasReachedGoal() {
		return goalFlag;
	}
	
	@Override
	public void reset() {
		for (int i = 0; i < 3; i++) {
			position[i] = defaults[i];
		}
		jumping = false;
		goalFlag = false;
		this.insideHole = false;
		RenderView.cameraX = defaults[0];
		RenderView.cameraY = defaults[1];
		this.deathSpeed = 0f;
		this.death = false;
	}
	
	@Override
	public void reflectResponse() {
		for (int i = 0; i <= 1; i++) {
			speed[i] += acceleration[i];
			position[i] -= speed[i];
			speed[i] *= 0.1f;
		}
		RenderView.cameraX = position[0];
		RenderView.cameraY = position[1];
	}
	
	private void move() {
		if (position[0] > screenWidth + this.radius)
			position[0] = screenWidth + this.radius;
		if (position[1] > screenHeight + this.radius)
			position[1] = screenHeight + this.radius;
		if (position[0] < -radius)
			position[0] = -radius;
		if (position[1] < -radius)
			position[1] = -radius;
	}
	
	private void jump() {
		float pi = (float) Math.PI;
		position[2] = FloatMath.cos((radians + pi)) * multiplier + multiplier;
		radians += 0.2f * radianSpeed;
		if (radians > 2 * Math.PI) {
			if (death) {
				jumping = false;
				return;
			}
			radianSpeed += 2;
			radians = 0f;
			multiplier -= 1;
			if (multiplier <= 0) {
				jumping = false;
				multiplier = 8;
				radians = 0f;
				radianSpeed = 1;
				position[2] = 0f;
			}
		}
	}
	
	@Override
	public void reflect(CurvePipe p) {
		if (p != null) {
			
			float thickness = p.PIPE_SIZE * 3 * RenderView.AspectRatio + radius * 2;
			float thinness = p.PIPE_SIZE * 5;
			
			float vx = this.position[0] - p.x;
			float vy = this.position[1] - p.y;
			float vn = FloatMath.sqrt(vx * vx + vy * vy);
			if (vn == 0)
				vn = 1f;
			vx /= vn;
			vy /= vn;
			
			float pipeRadius = p.dstRect.width() / 2;
			float targetX = vx * pipeRadius;
			float targetY = vy * pipeRadius;
			targetX += p.x;
			targetY += p.y;
			float avx = targetX - this.position[0];
			float avy = targetY - this.position[1];
			float avn = FloatMath.sqrt(avx * avx + avy * avy);
			if (avn == 0)
				avn = 1f;
			avx /= avn;
			avy /= avn;
			
			if (vn - radius > thinness && vn < thickness) {
				//Region B
				//Inside Boundary
				if (Math.abs(targetX - position[0]) > 1f)
					acceleration[0] -= avx;
				if (Math.abs(targetY - position[1]) > 1f)
					acceleration[1] -= avy;
				reflectResponse();
			}
			else if (vn >= thickness) {
				//Region A
				//Outside boundary
				if (vn - radius < thickness) {
					double dotProduct = this.speed[0] * vx + this.speed[1] * vy;
					this.speed[0] += (float) (-2 * dotProduct * vx);
					this.speed[1] += (float) (-2 * dotProduct * vy);
					this.position[0] -= this.speed[0] * 10;
					this.position[1] -= this.speed[1] * 10;
					
				}
				else {
					reflectResponse();
				}
			}
			else if (vn >= thinness) {
				//Region A
				
				if (vn - radius < thinness) {
					if (vn - radius < 0) {
						double dotProduct = this.speed[0] * vx + this.speed[1] * vy;
						this.speed[0] += (float) (-2 * dotProduct * vx);
						this.speed[1] += (float) (-2 * dotProduct * vy);
						this.position[0] -= this.speed[0] * 10;
						this.position[1] -= this.speed[1] * 10;
					}
					else {
						acceleration[0] -= avx * 2;
						acceleration[1] -= avy * 2;
						reflectResponse();
					}
					
				}
				else {
					acceleration[0] -= avx * 2;
					acceleration[1] -= avy * 2;
					reflectResponse();
				}
			}
			else if (vn < thinness) {
				float dx = p.x - position[0];
				float dy = p.y - position[1];
				if (dx >= 0) {
					if (dy >= 0) {
						if (acceleration[0] >= 0) {
							speed[0] += -2 * speed[0];
							if (acceleration[1] >= 0) {
								speed[1] += -2 * speed[1];
							}
							else {
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
						}
						else {
							if (acceleration[1] >= 0) {
								speed[1] += -2 * speed[1];
							}
							else {
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
							this.speed[0] += this.acceleration[0];
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
						}
					}
					else {
						if (acceleration[0] >= 0) {
							speed[0] += -2 * speed[0];
							if (acceleration[1] <= 0) {
								speed[1] += -2 * speed[1];
							}
							else {
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
						}
						else {
							if (acceleration[1] <= 0) {
								speed[1] += -2 * speed[1];
							}
							else {
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
							this.speed[0] += this.acceleration[0];
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
						}
					}
				}
				else {
					if (dy >= 0) {
						if (acceleration[0] <= 0) {
							speed[0] += -2 * speed[0];
							if (acceleration[1] >= 0) {
								speed[1] += -2 * speed[1];
							}
							else {
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
						}
						else {
							if (acceleration[1] >= 0) {
								speed[1] += -2 * speed[1];
							}
							else {
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
							this.speed[0] += this.acceleration[0];
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
						}
					}
					else {
						if (acceleration[0] <= 0) {
							speed[0] += -2 * speed[0];
							if (acceleration[1] <= 0) {
								speed[1] += -2 * speed[1];
							}
							else {
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
						}
						else {
							if (acceleration[1] <= 0) {
								speed[1] += -2 * speed[1];
							}
							else {
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
							this.speed[0] += this.acceleration[0];
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
						}
					}
				}
				
			}
			else
				reflectResponse();
			RenderView.cameraX = this.position[0];
			RenderView.cameraY = this.position[1];
		}
		
	}
	
	@Override
	public void reflect(Pipe p) {
		if (p != null) {
			float pipeRadius = p.PIPE_SIZE * 3 * RenderView.AspectRatio;
			float newTop = p.dstRect.top + pipeRadius;
			float newBottom = p.dstRect.bottom - pipeRadius;
			float newLeft = p.dstRect.left + pipeRadius;
			float newRight = p.dstRect.right - pipeRadius;
			switch (p.orientation) {
				case HORIZONTAL:
					// Region A:
					if (position[1] + radius > newTop && position[1] - radius < newTop) {
						if (position[1] < newTop) {
							if (acceleration[1] > 0) {
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
							else
								this.speed[1] += -2 * this.speed[1];
							this.speed[0] += this.acceleration[0];
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
						else {
							if (acceleration[1] > 0) {
								this.speed[1] += -2 * this.speed[1];
							}
							else {
								float dax = (p.dstRect.left + p.dstRect.width() / 2) - position[0];
								float day = (p.dstRect.top + p.dstRect.height() / 2) - position[1];
								float dan = FloatMath.sqrt(dax * dax + day * day);
								if (dan == 0)
									dan = 1f;
								day /= dan;
								this.acceleration[1] -= day;
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
							this.speed[0] += this.acceleration[0];
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
					}
					else if (position[1] - radius < newBottom && position[1] + radius > newBottom) {
						if (position[1] > newBottom) {
							if (acceleration[1] < 0) {
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
							else
								this.speed[1] += -2 * this.speed[1];
							this.speed[0] += this.acceleration[0];
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
						else {
							if (acceleration[1] < 0) {
								this.speed[1] += -2 * this.speed[1];
							}
							else {
								float dax = (p.dstRect.left + p.dstRect.width() / 2) - position[0];
								float day = (p.dstRect.top + p.dstRect.height() / 2) - position[1];
								float dan = FloatMath.sqrt(dax * dax + day * day);
								if (dan == 0)
									dan = 1f;
								day /= dan;
								this.acceleration[1] -= day;
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
							this.speed[0] += this.acceleration[0];
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
					}
					else if (position[1] + radius < newBottom && position[1] - radius > newTop) {
						// Region B
						float dax = (p.dstRect.left + p.dstRect.width() / 2) - position[0];
						float day = (p.dstRect.top + p.dstRect.height() / 2) - position[1];
						float dan = FloatMath.sqrt(dax * dax + day * day);
						if (dan == 0)
							dan = 1f;
						day /= dan;
						this.acceleration[1] -= day;
						reflectResponse();
					}
					else
						reflectResponse();
					break;
				case VERTICAL:
					// Region A:
					if (position[0] + radius > newLeft && position[0] - radius < newLeft) {
						if (position[0] < newLeft) {
							if (acceleration[0] > 0) {
								this.speed[0] += this.acceleration[0];
								this.position[0] -= this.speed[0];
								this.speed[0] *= 0.1f;
							}
							else
								this.speed[0] += -2 * this.speed[0];
							this.speed[1] += this.acceleration[1];
							this.position[1] -= this.speed[1];
							this.speed[1] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
						else {
							if (acceleration[0] > 0) {
								this.speed[0] += -2 * this.speed[0];
							}
							else {
								float dax = (p.dstRect.left + p.dstRect.width() / 2) - position[0];
								float day = (p.dstRect.top + p.dstRect.height() / 2) - position[1];
								float dan = FloatMath.sqrt(dax * dax + day * day);
								if (dan == 0)
									dan = 1f;
								dax /= dan;
								this.acceleration[0] -= dax;
								this.speed[0] += this.acceleration[0];
								this.position[0] -= this.speed[0];
								this.speed[0] *= 0.1f;
							}
							this.speed[1] += this.acceleration[1];
							this.position[1] -= this.speed[1];
							this.speed[1] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
					}
					else if (position[0] - radius < newRight && position[0] + radius > newRight) {
						if (position[0] > newRight) {
							if (acceleration[0] < 0) {
								this.speed[0] += this.acceleration[0];
								this.position[0] -= this.speed[0];
								this.speed[0] *= 0.1f;
							}
							else
								this.speed[0] += -2 * this.speed[0];
							this.speed[1] += this.acceleration[1];
							this.position[1] -= this.speed[1];
							this.speed[1] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
						else {
							if (acceleration[0] < 0) {
								this.speed[0] += -2 * this.speed[0];
							}
							else {
								float dax = (p.dstRect.left + p.dstRect.width() / 2) - position[0];
								float day = (p.dstRect.top + p.dstRect.height() / 2) - position[1];
								float dan = FloatMath.sqrt(dax * dax + day * day);
								if (dan == 0)
									dan = 1f;
								dax /= dan;
								this.acceleration[0] -= dax;
								this.speed[0] += this.acceleration[0];
								this.position[0] -= this.speed[0];
								this.speed[0] *= 0.1f;
							}
							this.speed[1] += this.acceleration[1];
							this.position[1] -= this.speed[1];
							this.speed[1] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
					}
					else if (position[0] + radius < newRight && position[0] - radius > newLeft) {
						// Region B
						float dax = (p.dstRect.left + p.dstRect.width() / 2) - position[0];
						float day = (p.dstRect.top + p.dstRect.height() / 2) - position[1];
						float dan = FloatMath.sqrt(dax * dax + day * day);
						if (dan == 0)
							dan = 1f;
						dax /= dan;
						this.acceleration[0] -= dax;
						reflectResponse();
					}
					else
						reflectResponse();
					break;
				default:
					break;
			}
		}
	}
	
	public boolean isDead() {
		return death || (position[2] <= -radius);
	}
	
	public void die() {
		if (!death) {
			// lives--;
			death = true;
		}
		if (jumping && radians > 2 * Math.PI) {
			jumping = false;
		}
	}
	
	public void revive() {
		if (death) {
			death = false;
		}
	}
	
	public void deathAnimation() {
		for (int i = 0; i <= 1; i++) {
			position[i] -= speed[i];
			speed[i] *= 0.8f;
		}
		if (position[2] > -radius) {
			position[2] -= 0.1f * deathSpeed;
			deathSpeed += 0.4f;
			if (jumping) {
				jumping = false;
				multiplier = 8;
				radians = 0f;
				radianSpeed = 1;
			}
		}
	}
	
	@Override
	public void reflect(Bumper b) {
		if (b != null) {
			
			if (position[2] <= 0) {
				jumping = false;
				multiplier = 8;
				radians = 0f;
				radianSpeed = 1;
				position[2] = 0f;
			}
			if (jumping) {
				reflectResponse();
			}
			else {
				float dx, dy, dd;
				while (true) {
					dx = position[0] - b.x;
					dy = position[1] - b.y;
					dd = FloatMath.sqrt(dx * dx + dy * dy);
					if (dd <= this.radius + b.radius) {
						if (dd == 0)
							dd = 1;
						dx /= dd;
						dy /= dd;
						position[0] += dx;
						position[1] += dy;
						
						RenderView.cameraX = this.position[0];
						RenderView.cameraY = this.position[1];
					}
					else
						break;
				}
				Log.d("Cue", "It worked...");
			}
			
			RenderView.cameraX = this.position[0];
			RenderView.cameraY = this.position[1];
		}
	}
	
	@Override
	public void reflect(Funnel f) {
		super.reflect(f);
		RenderView.cameraX = this.position[0];
		RenderView.cameraY = this.position[1];
	}
	
	@Override
	public void reflect(Connector c) {
		//aaa
		//FIXME: Need to work on this. Ball needs to have circle->square collision. This is for Square.
		//if (position[0] < c.dstRect.right + radius && position[0] + radius > c.dstRect.right) {
		if (position[0] - radius <= c.dstRect.right && position[0] + radius > c.dstRect.right) {
			if (acceleration[0] > 0)
				speed[0] += -2 * speed[0];
			else {
				speed[0] += acceleration[0];
				position[0] -= speed[0];
				speed[0] *= 0.1f;
			}
			speed[1] += acceleration[1];
			position[1] -= speed[1];
			speed[1] *= 0.1f;
		}
		if (position[0] + radius >= c.dstRect.left && position[0] - radius < c.dstRect.left) {
			if (acceleration[0] < 0)
				speed[0] += -2 * speed[0];
			else {
				speed[0] += acceleration[0];
				position[0] -= speed[0];
				speed[0] *= 0.1f;
			}
			speed[1] += acceleration[1];
			position[1] -= speed[1];
			speed[1] *= 0.1f;
		}
		if (position[1] - radius <= c.dstRect.bottom && position[1] + radius > c.dstRect.bottom) {
			if (acceleration[1] > 0)
				speed[1] += -2 * speed[1];
			else {
				speed[1] += acceleration[1];
				position[1] -= speed[1];
				speed[1] *= 0.1f;
			}
			speed[0] += acceleration[0];
			position[0] -= speed[0];
			speed[0] *= 0.1f;
		}
		if (position[1] + radius >= c.dstRect.top && position[1] - radius < c.dstRect.top) {
			if (acceleration[1] < 0)
				speed[1] += -2 * speed[1];
			else {
				speed[1] += acceleration[1];
				position[1] -= speed[1];
				speed[1] *= 0.1f;
			}
			speed[0] += acceleration[0];
			position[0] -= speed[0];
			speed[0] *= 0.1f;
		}
		
		RenderView.cameraX = this.position[0];
		RenderView.cameraY = this.position[1];
	}
	
	public void reflect2(Connector c) {
		if (lineIntersect(this, c.dstRect.left, c.dstRect.top, c.dstRect.left, c.dstRect.bottom)) {
			Log.d("Connector", "From Left");
			if (acceleration[0] < 0)
				speed[0] += -2 * speed[0];
			else {
				speed[0] += acceleration[0];
				position[0] -= speed[0];
				speed[0] *= 0.1f;
			}
			speed[1] += acceleration[1];
			position[1] -= speed[1];
			speed[1] *= 0.1f;
		}
		if (lineIntersect(this, c.dstRect.right, c.dstRect.top, c.dstRect.right, c.dstRect.bottom)) {
			Log.d("Connector", "From Right");
			reflectResponse();
		}
		if (lineIntersect(this, c.dstRect.left, c.dstRect.top, c.dstRect.right, c.dstRect.top)) {
			Log.d("Connector", "From Up");
			reflectResponse();
		}
		if (lineIntersect(this, c.dstRect.left, c.dstRect.bottom, c.dstRect.right, c.dstRect.bottom)) {
			Log.d("Connector", "From Down");
			reflectResponse();
		}
		
		RenderView.cameraX = this.position[0];
		RenderView.cameraY = this.position[1];
		
	}
	
	private boolean lineIntersect(Ball b, float x1, float y1, float x2, float y2) {
		float acx = b.position[0] - x1;
		float acy = b.position[1] - y1;
		float abx = x2 - x1;
		float aby = y2 - y1;
		float scalar = acx * abx + acy * aby;
		float dx = scalar * abx + x1;
		float dy = scalar * aby + y1;
		float cdx = dx - b.position[0];
		float cdy = dy - b.position[1];
		float dist = FloatMath.sqrt(cdx * cdx + cdy * cdy);
		return dist < b.radius;
	}
	
	@Override
	public void reflect(Ramp r) {
		float halfWidth = r.dstRect.width() / 2;
		float halfHeight = r.dstRect.height() / 2;
		float dx1 = position[0] - r.x1;
		float dx2 = position[0] - r.x2;
		float dy1 = position[1] - r.y1;
		float dy2 = position[1] - r.y2;
		float d1 = FloatMath.sqrt(dx1 * dx1 + dy1 * dy1);
		float d2 = FloatMath.sqrt(dx2 * dx2 + dy2 * dy2);
		float pipeRadius = 3 * r.PIPE_SIZE * RenderView.AspectRatio;
		switch (r.direction) {
			case LEFT:
				if (Math.abs(position[0] - r.dstRect.left) > halfWidth) {
					//Region A
					//Curve Region
					if (d1 < pipeRadius + radius) {
						if (d1 == 0)
							d1 = 1;
						dx1 /= d1;
						dy1 /= d1;
						float dotProduct = (speed[0] * dx1 + speed[1] * dy1);
						speed[0] += (-2 * dotProduct * dx1);
						speed[1] += (-2 * dotProduct * dy1);
						position[0] -= speed[0] * 10;
						position[1] -= speed[1] * 10;
					}
					else if (d2 < pipeRadius + radius) {
						if (d2 == 0)
							d2 = 1;
						dx2 /= d2;
						dy2 /= d2;
						float dotProduct = (speed[0] * dx2 + speed[1] * dy2);
						speed[0] += (-2 * dotProduct * dx2);
						speed[1] += (-2 * dotProduct * dy2);
						position[0] -= speed[0] * 10;
						position[1] -= speed[1] * 10;
						
					}
					else {
						//Non-curve Region
						reflectResponse();
					}
				}
				else if (Math.abs(position[0] - r.dstRect.left) < halfWidth) {
					//Region B
					//MUST NOT ALLOW CUE TO GO ON PIPE AT ALL. CANNOT ALLOW CUE TO MOVE UP/DOWN WHEN
					//INSIDE RAMP ONLY!
					
					//Split into 5 groups of Pipe.
					float newTop = r.dstRect.top + pipeRadius;
					float newBottom = r.dstRect.bottom - pipeRadius;
					if (position[1] + radius > newTop && position[1] - radius < newTop) {
						if (position[1] < newTop) {
							if (acceleration[1] > 0) {
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
							else
								this.speed[1] += -2 * this.speed[1];
							this.speed[0] += this.acceleration[0];
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
						else {
							if (acceleration[1] > 0) {
								this.speed[1] += -2 * this.speed[1];
							}
							else {
								float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
								float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
								float dan = FloatMath.sqrt(dax * dax + day * day);
								if (dan == 0)
									dan = 1f;
								day /= dan;
								this.acceleration[1] -= day;
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
							this.speed[0] += this.acceleration[0];
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
					}
					else if (position[1] - radius < newBottom && position[1] + radius > newBottom) {
						if (position[1] > newBottom) {
							if (acceleration[1] < 0) {
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
							else
								this.speed[1] += -2 * this.speed[1];
							this.speed[0] += this.acceleration[0];
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
						else {
							if (acceleration[1] < 0) {
								this.speed[1] += -2 * this.speed[1];
							}
							else {
								float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
								float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
								float dan = FloatMath.sqrt(dax * dax + day * day);
								if (dan == 0)
									dan = 1f;
								day /= dan;
								this.acceleration[1] -= day;
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
							this.speed[0] += this.acceleration[0];
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
					}
					else if (position[1] + radius < newBottom && position[1] - radius > newTop) {
						// Region B
						float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
						float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
						float dan = FloatMath.sqrt(dax * dax + day * day);
						if (dan == 0)
							dan = 1f;
						day /= dan;
						this.acceleration[1] -= day;
						reflectResponse();
					}
				}
				else
					//Catch everything else.
					reflectResponse();
				break;
			case RIGHT:
				if (Math.abs(position[0] - r.dstRect.left) < halfWidth) {
					//Region A
					//Curve Region
					if (d1 < pipeRadius + radius) {
						if (d1 == 0)
							d1 = 1;
						dx1 /= d1;
						dy1 /= d1;
						float dotProduct = (speed[0] * dx1 + speed[1] * dy1);
						speed[0] += (-2 * dotProduct * dx1);
						speed[1] += (-2 * dotProduct * dy1);
						position[0] -= speed[0] * 10;
						position[1] -= speed[1] * 10;
					}
					else if (d2 < pipeRadius + radius) {
						if (d2 == 0)
							d2 = 1;
						dx2 /= d2;
						dy2 /= d2;
						float dotProduct = (speed[0] * dx2 + speed[1] * dy2);
						speed[0] += (-2 * dotProduct * dx2);
						speed[1] += (-2 * dotProduct * dy2);
						position[0] -= speed[0] * 10;
						position[1] -= speed[1] * 10;
						
					}
					else {
						//Non-curve Region
						reflectResponse();
					}
				}
				else if (Math.abs(position[0] - r.dstRect.left) > halfWidth) {
					//Region B
					//MUST NOT ALLOW CUE TO GO ON PIPE AT ALL. CANNOT ALLOW CUE TO MOVE UP/DOWN WHEN
					//INSIDE RAMP ONLY!
					
					//Split into 5 groups of Pipe.
					float newTop = r.dstRect.top + pipeRadius;
					float newBottom = r.dstRect.bottom - pipeRadius;
					if (position[1] + radius > newTop && position[1] - radius < newTop) {
						if (position[1] < newTop) {
							if (acceleration[1] > 0) {
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
							else
								this.speed[1] += -2 * this.speed[1];
							this.speed[0] += this.acceleration[0];
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
						else {
							if (acceleration[1] > 0) {
								this.speed[1] += -2 * this.speed[1];
							}
							else {
								float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
								float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
								float dan = FloatMath.sqrt(dax * dax + day * day);
								if (dan == 0)
									dan = 1f;
								day /= dan;
								this.acceleration[1] -= day;
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
							this.speed[0] += this.acceleration[0];
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
					}
					else if (position[1] - radius < newBottom && position[1] + radius > newBottom) {
						if (position[1] > newBottom) {
							if (acceleration[1] < 0) {
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
							else
								this.speed[1] += -2 * this.speed[1];
							this.speed[0] += this.acceleration[0];
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
						else {
							if (acceleration[1] < 0) {
								this.speed[1] += -2 * this.speed[1];
							}
							else {
								float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
								float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
								float dan = FloatMath.sqrt(dax * dax + day * day);
								if (dan == 0)
									dan = 1f;
								day /= dan;
								this.acceleration[1] -= day;
								this.speed[1] += this.acceleration[1];
								this.position[1] -= this.speed[1];
								this.speed[1] *= 0.1f;
							}
							this.speed[0] += this.acceleration[0];
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
					}
					else if (position[1] + radius < newBottom && position[1] - radius > newTop) {
						// Region B
						float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
						float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
						float dan = FloatMath.sqrt(dax * dax + day * day);
						if (dan == 0)
							dan = 1f;
						day /= dan;
						this.acceleration[1] -= day;
						reflectResponse();
					}
				}
				else
					//Catch everything else.
					reflectResponse();
				break;
			case UP:
				if (Math.abs(position[1] - r.dstRect.top) > halfHeight) {
					//Region A
					//Curve Region
					if (d1 < pipeRadius + radius) {
						if (d1 == 0)
							d1 = 1;
						dx1 /= d1;
						dy1 /= d1;
						float dotProduct = (speed[0] * dx1 + speed[1] * dy1);
						speed[0] += (-2 * dotProduct * dx1);
						speed[1] += (-2 * dotProduct * dy1);
						position[0] -= speed[0] * 10;
						position[1] -= speed[1] * 10;
					}
					else if (d2 < pipeRadius + radius) {
						if (d2 == 0)
							d2 = 1;
						dx2 /= d2;
						dy2 /= d2;
						float dotProduct = (speed[0] * dx2 + speed[1] * dy2);
						speed[0] += (-2 * dotProduct * dx2);
						speed[1] += (-2 * dotProduct * dy2);
						position[0] -= speed[0] * 10;
						position[1] -= speed[1] * 10;
						
					}
					else {
						//Non-curve Region
						reflectResponse();
					}
				}
				else if (Math.abs(position[1] - r.dstRect.top) < halfHeight) {
					//Region B
					//MUST NOT ALLOW CUE TO GO ON PIPE AT ALL. CANNOT ALLOW CUE TO MOVE UP/DOWN WHEN
					//INSIDE RAMP ONLY!
					
					//Split into 5 groups of Pipe.
					float newLeft = r.dstRect.left + pipeRadius;
					float newRight = r.dstRect.right - pipeRadius;
					if (position[0] + radius > newLeft && position[0] - radius < newLeft) {
						if (position[0] < newLeft) {
							if (acceleration[0] > 0) {
								this.speed[0] += this.acceleration[0];
								this.position[0] -= this.speed[0];
								this.speed[0] *= 0.1f;
							}
							else
								this.speed[0] += -2 * this.speed[0];
							this.speed[1] += this.acceleration[1];
							this.position[1] -= this.speed[1];
							this.speed[1] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
						else {
							if (acceleration[0] > 0) {
								this.speed[0] += -2 * this.speed[0];
							}
							else {
								float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
								float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
								float dan = FloatMath.sqrt(dax * dax + day * day);
								if (dan == 0)
									dan = 1f;
								dax /= dan;
								this.acceleration[0] -= dax;
								this.speed[0] += this.acceleration[0];
								this.position[0] -= this.speed[0];
								this.speed[0] *= 0.1f;
							}
							this.speed[1] += this.acceleration[1];
							this.position[1] -= this.speed[1];
							this.speed[1] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
					}
					else if (position[0] - radius < newRight && position[0] + radius > newRight) {
						if (position[0] > newRight) {
							if (acceleration[0] < 0) {
								this.speed[0] += this.acceleration[0];
								this.position[0] -= this.speed[0];
								this.speed[0] *= 0.1f;
							}
							else
								this.speed[0] += -2 * this.speed[0];
							this.speed[1] += this.acceleration[1];
							this.position[1] -= this.speed[1];
							this.speed[1] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
						else {
							if (acceleration[0] < 0) {
								this.speed[0] += -2 * this.speed[0];
							}
							else {
								float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
								float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
								float dan = FloatMath.sqrt(dax * dax + day * day);
								if (dan == 0)
									dan = 1f;
								dax /= dan;
								this.acceleration[0] -= dax;
								this.speed[0] += this.acceleration[0];
								this.position[0] -= this.speed[0];
								this.speed[0] *= 0.1f;
							}
							this.speed[1] += this.acceleration[1];
							this.position[1] -= this.speed[1];
							this.speed[1] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
					}
					else if (position[0] + radius < newRight && position[0] - radius > newLeft) {
						// Region B
						float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
						float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
						float dan = FloatMath.sqrt(dax * dax + day * day);
						if (dan == 0)
							dan = 1f;
						dax /= dan;
						this.acceleration[0] -= dax;
						reflectResponse();
					}
				}
				else
					//Catch everything else.
					reflectResponse();
				break;
			case DOWN:
				if (Math.abs(position[1] - r.dstRect.top) < halfHeight) {
					//Region A
					//Curve Region
					if (d1 < pipeRadius + radius) {
						if (d1 == 0)
							d1 = 1;
						dx1 /= d1;
						dy1 /= d1;
						float dotProduct = (speed[0] * dx1 + speed[1] * dy1);
						speed[0] += (-2 * dotProduct * dx1);
						speed[1] += (-2 * dotProduct * dy1);
						position[0] -= speed[0] * 10;
						position[1] -= speed[1] * 10;
					}
					else if (d2 < pipeRadius + radius) {
						if (d2 == 0)
							d2 = 1;
						dx2 /= d2;
						dy2 /= d2;
						float dotProduct = (speed[0] * dx2 + speed[1] * dy2);
						speed[0] += (-2 * dotProduct * dx2);
						speed[1] += (-2 * dotProduct * dy2);
						position[0] -= speed[0] * 10;
						position[1] -= speed[1] * 10;
						
					}
					else {
						//Non-curve Region
						reflectResponse();
					}
				}
				else if (Math.abs(position[1] - r.dstRect.top) > halfHeight) {
					//Region B
					//MUST NOT ALLOW CUE TO GO ON PIPE AT ALL. CANNOT ALLOW CUE TO MOVE UP/DOWN WHEN
					//INSIDE RAMP ONLY!
					
					//Split into 5 groups of Pipe.
					float newLeft = r.dstRect.left + pipeRadius;
					float newRight = r.dstRect.right - pipeRadius;
					if (position[0] + radius > newLeft && position[0] - radius < newLeft) {
						if (position[0] < newLeft) {
							if (acceleration[0] > 0) {
								this.speed[0] += this.acceleration[0];
								this.position[0] -= this.speed[0];
								this.speed[0] *= 0.1f;
							}
							else
								this.speed[0] += -2 * this.speed[0];
							this.speed[1] += this.acceleration[1];
							this.position[1] -= this.speed[1];
							this.speed[1] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
						else {
							if (acceleration[0] > 0) {
								this.speed[0] += -2 * this.speed[0];
							}
							else {
								float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
								float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
								float dan = FloatMath.sqrt(dax * dax + day * day);
								if (dan == 0)
									dan = 1f;
								dax /= dan;
								this.acceleration[0] -= dax;
								this.speed[0] += this.acceleration[0];
								this.position[0] -= this.speed[0];
								this.speed[0] *= 0.1f;
							}
							this.speed[1] += this.acceleration[1];
							this.position[1] -= this.speed[1];
							this.speed[1] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
					}
					else if (position[0] - radius < newRight && position[0] + radius > newRight) {
						if (position[0] > newRight) {
							if (acceleration[0] < 0) {
								this.speed[0] += this.acceleration[0];
								this.position[0] -= this.speed[0];
								this.speed[0] *= 0.1f;
							}
							else
								this.speed[0] += -2 * this.speed[0];
							this.speed[1] += this.acceleration[1];
							this.position[1] -= this.speed[1];
							this.speed[1] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
						else {
							if (acceleration[0] < 0) {
								this.speed[0] += -2 * this.speed[0];
							}
							else {
								float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
								float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
								float dan = FloatMath.sqrt(dax * dax + day * day);
								if (dan == 0)
									dan = 1f;
								dax /= dan;
								this.acceleration[0] -= dax;
								this.speed[0] += this.acceleration[0];
								this.position[0] -= this.speed[0];
								this.speed[0] *= 0.1f;
							}
							this.speed[1] += this.acceleration[1];
							this.position[1] -= this.speed[1];
							this.speed[1] *= 0.1f;
							RenderView.cameraX = this.position[0];
							RenderView.cameraY = this.position[1];
						}
					}
					else if (position[0] + radius < newRight && position[0] - radius > newLeft) {
						// Region B
						float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
						float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
						float dan = FloatMath.sqrt(dax * dax + day * day);
						if (dan == 0)
							dan = 1f;
						dax /= dan;
						this.acceleration[0] -= dax;
						reflectResponse();
					}
				}
				else
					//Catch everything else.
					reflectResponse();
				break;
		}
		
		RenderView.cameraX = this.position[0];
		RenderView.cameraY = this.position[1];
	}
} public class Marble extends Ball {
	
	public final int INDEX;
	public boolean renderFlag;
	public float defaultX;
	public float defaultY;
	public float screenHeight;
	public float screenWidth;
	
	private boolean atGoal;
	private boolean inArea;
	public Paint paint;
	
	public final int SCORE;
	public boolean scored;
	
	public Marble(float width, float height, int index, float ratio) {
		super();
		INDEX = index;
		screenWidth = width;
		screenHeight = height;
		paint = new Paint();
		this.radius = 8 * ratio;
		srcRect.set(0, 0, 16, 16);
		dstRect.set(0, 0, 16, 16);
		randomPaint();
		SCORE = 200;
		scored = false;
	}
	
	public int setScore(int value) {
		return value;
	}
	
	public void setScoredFlag(boolean value) {
		scored = value;
	}
	
	public boolean hasBeenScored() {
		return scored;
	}
	
	public void setStartingPosition(float x, float y) {
		defaultX = x;
		defaultY = y;
		reset();
	}
	
	@Override
	public void reset() {
		position[0] = defaultX;
		position[1] = defaultY;
		for (int i = 0; i <= 1; i++) {
			speed[i] = 0;
			acceleration[i] = 0;
		}
		atGoal = false;
		inArea = false;
		renderFlag = true;
		this.insideHole = false;
		this.setScoredFlag(false);
		paint.setAlpha(255);
	}
	
	@Override
	public void tick(Stage level) {
		if (bitmap != null) {
			if (atGoal) {
				checkCollisionFlag(level.hole);
				reflect(level.hole);
				reflectResponse();
				fade();
			}
			else {
				if (checkAreaGravity(level.hole)) {
					this.gravityPull(level.hole);
					if (checkCollision(level.hole)) {
						atGoal = true;
					}
				}
				else {
					if (inArea == true) {
						inArea = false;
					}
					if (checkCollision(level.cue)) {
						collisionResponse(level.cue);
						resolveCollision(level.cue);
					}
					for (Marble m : level.marbles) {
						if (this.INDEX != m.INDEX && checkCollision(m)) {
							collisionResponse(m);
							resolveCollision(m);
						}
					}
				}
				if (curvePipe != null)
					reflect(curvePipe);
				else if (funnel != null)
					reflect(funnel);
				else if (ramp != null)
					reflect(ramp);
				else if (pipe != null)
					reflect(pipe);
				else if (connector != null)
					reflect(connector);
				else if (bumper != null) {
					reflect(bumper);
				}
				else
					reflectResponse();
			}
			move();
		}
		else
			bitmap = Art.sprites;
	}
	
	@Override
	public void render(Canvas c, final float cx, final float cy) {
		if (bitmap != null) {
			float xOffset = this.position[0] - RenderView.cameraX + cx;
			float yOffset = this.position[1] - RenderView.cameraY + cy;
			if (RenderView.bounds.contains(xOffset, yOffset)) {
				move(xOffset, yOffset);
				c.drawBitmap(bitmap, srcRect, dstRect, paint);
			}
		}
	}
	
	private void move(float f, float g) {
		if (bitmap != null) {
			dstRect.set(f - this.radius, g - this.radius, f + this.radius, g + this.radius);
		}
	}
	
	public boolean isAtGoal() {
		return atGoal;
	}
	
	public void setRenderFlag(boolean value) {
		renderFlag = value;
	}
	
	private void randomPaint() {
		Random random = new Random();
		ColorFilter filter = new LightingColorFilter(Color.argb(255, random.nextInt(255), random.nextInt(255), random.nextInt(255)), Color.argb(255, random.nextInt(255), random.nextInt(255), random.nextInt(255)));
		paint.setColorFilter(filter);
	}
	
	private void move() {
		if (position[0] > screenWidth + this.radius)
			position[0] = screenWidth + this.radius;
		if (position[1] > screenHeight + this.radius)
			position[1] = screenHeight + this.radius;
		if (position[0] < -radius)
			position[0] = -radius;
		if (position[1] < -radius)
			position[1] = -radius;
	}
	
	private void wallCollisionResponse() {
		if (position[0] > screenWidth + this.radius)
			speed[0] = -speed[0];
		if (position[1] > screenHeight + this.radius)
			speed[1] = -speed[1];
		for (int i = 0; i <= 1; i++) {
			if (position[i] < -radius)
				speed[i] = -speed[i];
		}
	}
	
	private void fade() {
		int value = paint.getAlpha();
		if (value > 0) {
			value -= 5;
			paint.setAlpha(value);
			if (value < 0) {
				value = 0;
				renderFlag = false;
			}
		}
	}
	
	@Override
	public void reflect(CurvePipe p) {
		
		//TODO: Rewrite this function completely.
		// Split into 5 sections:
		// 			A	OuterRing	B	InnerRing	C
		// A: 			Any balls in this area hits outer ring, use reflection to bounce away.
		// OuterRing:	Any balls on top of outer ring must move back into center, B.
		// B:			Use reflection or something else that allows movement.
		// InnerRing:	Any balls on top of inner ring must move back into center, B.
		// C: 			Any balls in this area hits inner ring, use reflection to bounce away.
		
		// Must declare new OuterRing radius and InnerRing radius.
		
		if (p != null) {
			float multiplier = RenderView.AspectRatio * p.PIPE_SIZE * 2;
			//Comparing all of these regions to ball position. (Test #1)
			//Ball - Center, use "-=" as positive attraction to the center line. "+=" is pushing away from center line.
			float dx = position[0] - p.x;
			float dy = position[1] - p.y;
			float distance = FloatMath.sqrt(dx * dx + dy * dy);
			if (distance > 6 * multiplier) {
				//A
				if (distance - radius < 6 * multiplier) {
					//reflect
					if (distance == 0)
						distance = 1f;
					dx /= distance;
					dy /= distance;
					double dotProduct = this.speed[0] * dx + this.speed[1] * dy;
					this.speed[0] += (float) (-2 * dotProduct * dx);
					this.speed[1] += (float) (-2 * dotProduct * dy);
					reflectResponse();
				}
				else {
					reflectResponse();
				}
			}
			else if (distance <= 6 * multiplier && distance > 4 * multiplier) {
				//B
				if (distance + radius > 6 * multiplier) {
					//reflect
					if (distance == 0)
						distance = 1f;
					dx /= distance;
					dy /= distance;
					double dotProduct = this.speed[0] * dx + this.speed[1] * dy;
					this.speed[0] += (float) (-2 * dotProduct * dx);
					this.speed[1] += (float) (-2 * dotProduct * dy);
					reflectResponse();
				}
				else {
					//pull ball back into center.
					//do something about ball moving counterclockwise or clockwise
					float pipeRadius = p.dstRect.width() / 2;
					float targetX = dx * pipeRadius;
					float targetY = dy * pipeRadius;
					targetX += p.x;
					targetY += p.y;
					targetX = targetX - this.position[0];
					targetY = targetY - this.position[1];
					float avn = FloatMath.sqrt(targetX * targetX + targetY * targetY);
					if (avn == 0)
						avn = 1f;
					targetX /= avn;
					targetY /= avn;
					speed[0] -= targetX;
					speed[1] -= targetY;
					reflectResponse();
				}
			}
			else if (distance <= 4 * multiplier && distance > 2 * multiplier) {
				//C
				if (distance - radius < 2 * multiplier) {
					//reflect
					if (distance == 0)
						distance = 1f;
					dx /= distance;
					dy /= distance;
					double dotProduct = this.speed[0] * dx + this.speed[1] * dy;
					this.speed[0] += (float) (-2 * dotProduct * dx);
					this.speed[1] += (float) (-2 * dotProduct * dy);
					reflectResponse();
				}
				else {
					//push ball back into center.
					//do something about ball moving counterclockwise or clockwise
					float pipeRadius = p.dstRect.width() / 2;
					float targetX = dx * pipeRadius;
					float targetY = dy * pipeRadius;
					targetX += p.x;
					targetY += p.y;
					targetX = targetX - this.position[0];
					targetY = targetY - this.position[1];
					float avn = FloatMath.sqrt(targetX * targetX + targetY * targetY);
					if (avn == 0)
						avn = 1f;
					targetX /= avn;
					targetY /= avn;
					speed[0] -= targetX;
					speed[1] -= targetY;
					reflectResponse();
				}
			}
			else {
				//D
				if (distance + radius > 2 * multiplier) {
					//reflect
					if (distance == 0)
						distance = 1f;
					dx /= distance;
					dy /= distance;
					double dotProduct = this.speed[0] * dx + this.speed[1] * dy;
					this.speed[0] += (float) (-2 * dotProduct * dx);
					this.speed[1] += (float) (-2 * dotProduct * dy);
					reflectResponse();
				}
				else {
					reflectResponse();
				}
			}
			super.reflect(p);
		}
	}
	
	@Override
	public void reflect(Pipe p) {
		if (p != null) {
			float pipeRadius = p.PIPE_SIZE * 3 * RenderView.AspectRatio;
			float newTop = p.dstRect.top + pipeRadius;
			float newBottom = p.dstRect.bottom - pipeRadius;
			float newLeft = p.dstRect.left + pipeRadius;
			float newRight = p.dstRect.right - pipeRadius;
			switch (p.orientation) {
				case HORIZONTAL:
					// Region A:
					if (position[1] - radius < newTop && position[1] + radius > newTop) {
						if (position[1] < newTop) {
							this.speed[1] = -this.speed[1];
							this.position[1] -= this.speed[1] * 10;
							this.speed[1] *= 0.9992f;
						}
						else {
							float dax = (p.dstRect.left + p.dstRect.width() / 2) - position[0];
							float day = (p.dstRect.top + p.dstRect.height() / 2) - position[1];
							float dan = FloatMath.sqrt(dax * dax + day * day);
							if (dan == 0)
								dan = 1f;
							day /= dan;
							this.speed[1] -= day;
							this.position[1] -= this.speed[1];
							this.speed[1] *= 0.1f;
						}
						this.position[0] -= this.speed[0] * 10;
						this.speed[0] *= 0.9992f;
					}
					else if (position[1] + radius > newBottom && position[1] - radius < newBottom) {
						if (position[1] > newBottom) {
							this.speed[1] = -this.speed[1];
							this.position[1] -= this.speed[1] * 10;
							this.speed[1] *= 0.9992f;
						}
						else {
							float dax = (p.dstRect.left + p.dstRect.width() / 2) - position[0];
							float day = (p.dstRect.top + p.dstRect.height() / 2) - position[1];
							float dan = FloatMath.sqrt(dax * dax + day * day);
							if (dan == 0)
								dan = 1f;
							day /= dan;
							this.speed[1] -= day;
							this.position[1] -= this.speed[1];
							this.speed[1] *= 0.1f;
						}
						this.position[0] -= this.speed[0] * 10;
						this.speed[0] *= 0.9992f;
					}
					else if (position[1] < newBottom && position[1] > newTop) {
						// Region B
						float dax = (p.dstRect.left + p.dstRect.width() / 2) - position[0];
						float day = (p.dstRect.top + p.dstRect.height() / 2) - position[1];
						float dan = FloatMath.sqrt(dax * dax + day * day);
						if (dan == 0)
							dan = 1f;
						day /= dan;
						this.speed[1] -= day;
						this.position[0] -= this.speed[0] * 10;
						this.position[1] -= this.speed[1];
						this.speed[0] *= 0.9992f;
						this.speed[1] *= 0.1f;
					}
					else
						reflectResponse();
					break;
				case VERTICAL:
					// Region A:
					if (position[0] - radius < newLeft && position[0] + radius > newLeft) {
						if (position[0] < newLeft) {
							this.speed[0] = -this.speed[0];
							this.position[0] -= this.speed[0] * 10;
							this.speed[0] *= 0.9992f;
						}
						else {
							float dax = (p.dstRect.left + p.dstRect.width() / 2) - position[0];
							float day = (p.dstRect.top + p.dstRect.height() / 2) - position[1];
							float dan = FloatMath.sqrt(dax * dax + day * day);
							if (dan == 0)
								dan = 1f;
							dax /= dan;
							this.speed[0] -= dax;
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
						}
						this.position[1] -= this.speed[1] * 10;
						this.speed[1] *= 0.9992f;
					}
					else if (position[0] + radius > newRight && position[0] - radius < newRight) {
						if (position[0] > newRight) {
							this.speed[0] = -this.speed[0];
							this.position[0] -= this.speed[0] * 10;
							this.speed[0] *= 0.9992f;
						}
						else {
							float dax = (p.dstRect.left + p.dstRect.width() / 2) - position[0];
							float day = (p.dstRect.top + p.dstRect.height() / 2) - position[1];
							float dan = FloatMath.sqrt(dax * dax + day * day);
							if (dan == 0)
								dan = 1f;
							dax /= dan;
							this.speed[0] -= dax;
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
						}
						this.position[1] -= this.speed[1] * 10;
						this.speed[1] *= 0.9992f;
					}
					else if (position[0] < newRight && position[0] > newLeft) {
						// Region B
						float dax = (p.dstRect.left + p.dstRect.width() / 2) - position[0];
						float day = (p.dstRect.top + p.dstRect.height() / 2) - position[1];
						float dan = FloatMath.sqrt(dax * dax + day * day);
						if (dan == 0)
							dan = 1f;
						dax /= dan;
						this.speed[0] -= dax;
						this.position[1] -= this.speed[1] * 10;
						this.position[0] -= this.speed[0];
						this.speed[1] *= 0.9992f;
						this.speed[0] *= 0.1f;
					}
					else
						reflectResponse();
					break;
				default:
					break;
			}
		}
	}
	
	@Override
	public void reflectResponse() {
		for (int i = 0; i <= 1; i++) {
			position[i] -= speed[i] * 10;
			speed[i] *= 0.9992f;
		}
		wallCollisionResponse();
	}
	
	@Override
	public void reflect(Bumper b) {
		if (b != null) {
			double nx = (this.position[0]) - (b.x);
			double ny = (this.position[1]) - (b.y);
			double nd = Math.hypot(nx, ny);
			if (nd < b.radius + radius) {
				if (nd == 0)
					nd = 1;
				nx /= nd;
				ny /= nd;
				double dotProduct = this.speed[0] * nx + this.speed[1] * ny;
				this.speed[0] += (float) (-3 * dotProduct * nx);
				this.speed[1] += (float) (-3 * dotProduct * ny);
				this.position[0] -= this.speed[0] * 6;
				this.position[1] -= this.speed[1] * 6;
				this.speed[0] *= 0.9992;
				this.speed[1] *= 0.9992;
			}
		}
	}
	
	@Override
	public void reflect(Ramp r) {
		float halfWidth = r.dstRect.width() / 2;
		float halfHeight = r.dstRect.height() / 2;
		float dx1 = position[0] - r.x1;
		float dy1 = position[1] - r.y1;
		float dx2 = position[0] - r.x2;
		float dy2 = position[1] - r.y2;
		float funnelRadius = r.PIPE_SIZE * RenderView.AspectRatio;
		float pipeRadius = 3 * funnelRadius;
		final float f = r.PIPE_SIZE * 12;
		switch (r.direction) {
			case LEFT:
				if (Math.abs(position[0] - r.dstRect.left) > halfWidth) {
					if (funnelDetector == false && this.position[0] > r.dstRect.right - funnelRadius)
						funnelDetector = true;
					
					float dist1 = FloatMath.sqrt(dx1 * dx1 + dy1 * dy1);
					float dist2 = FloatMath.sqrt(dx2 * dx2 + dy2 * dy2);
					if (funnelDetector) {
						if (dist1 < f) {
							funnelFlag1 = true;
						}
						else if (dist2 < f) {
							funnelFlag2 = true;
						}
						else
							reflectResponse();
					}
					else {
						if (dist1 < f) {
							funnelResponse(dx1, dy1, dist1);
						}
						else if (dist2 < f) {
							funnelResponse(dx2, dy2, dist2);
						}
						else
							reflectResponse();
						return;
					}
					if (funnelFlag1)
						funnelResponse(dx1, dy1, dist1);
					if (funnelFlag2)
						funnelResponse(dx2, dy2, dist2);
				}
				else {
					float newTop = r.dstRect.top + pipeRadius;
					float newBottom = r.dstRect.bottom - pipeRadius;
					if (position[1] - radius < newTop && position[1] + radius > newTop) {
						if (position[1] < newTop) {
							this.speed[1] = -this.speed[1];
							this.position[1] -= this.speed[1] * 10;
							this.speed[1] *= 0.9992f;
						}
						else {
							float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
							float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
							float dan = FloatMath.sqrt(dax * dax + day * day);
							if (dan == 0)
								dan = 1f;
							day /= dan;
							this.speed[1] -= day;
							this.position[1] -= this.speed[1];
							this.speed[1] *= 0.1f;
						}
						this.position[0] -= this.speed[0] * 10;
						this.speed[0] *= 0.9992f;
					}
					else if (position[1] + radius > newBottom && position[1] - radius < newBottom) {
						if (position[1] > newBottom) {
							this.speed[1] = -this.speed[1];
							this.position[1] -= this.speed[1] * 10;
							this.speed[1] *= 0.9992f;
						}
						else {
							float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
							float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
							float dan = FloatMath.sqrt(dax * dax + day * day);
							if (dan == 0)
								dan = 1f;
							day /= dan;
							this.speed[1] -= day;
							this.position[1] -= this.speed[1];
							this.speed[1] *= 0.1f;
						}
						this.position[0] -= this.speed[0] * 10;
						this.speed[0] *= 0.9992f;
					}
					else if (position[1] < newBottom && position[1] > newTop) {
						// Region B
						float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
						float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
						float dan = FloatMath.sqrt(dax * dax + day * day);
						if (dan == 0)
							dan = 1f;
						day /= dan;
						this.speed[1] -= day;
						this.position[0] -= this.speed[0] * 10;
						this.position[1] -= this.speed[1];
						this.speed[0] *= 0.9992f;
						this.speed[1] *= 0.1f;
					}
					else
						reflectResponse();
				}
				break;
			case RIGHT:
				if (Math.abs(position[0] - r.dstRect.left) < halfWidth) {
					
					if (funnelDetector == false && this.position[0] < r.dstRect.left + funnelRadius)
						funnelDetector = true;
					float dist1 = FloatMath.sqrt(dx1 * dx1 + dy1 * dy1);
					float dist2 = FloatMath.sqrt(dx2 * dx2 + dy2 * dy2);
					
					if (funnelDetector) {
						if (dist1 < f) {
							funnelFlag1 = true;
						}
						else if (dist2 < f) {
							funnelFlag2 = true;
						}
						else
							reflectResponse();
					}
					else {
						if (dist1 < f) {
							funnelResponse(dx1, dy1, dist1);
						}
						else if (dist2 < f) {
							funnelResponse(dx2, dy2, dist2);
						}
						else
							reflectResponse();
						return;
					}
					if (funnelFlag1)
						funnelResponse(dx1, dy1, dist1);
					if (funnelFlag2)
						funnelResponse(dx2, dy2, dist2);
				}
				else {
					float newTop = r.dstRect.top + pipeRadius;
					float newBottom = r.dstRect.bottom - pipeRadius;
					if (position[1] - radius < newTop && position[1] + radius > newTop) {
						if (position[1] < newTop) {
							this.speed[1] = -this.speed[1];
							this.position[1] -= this.speed[1] * 10;
							this.speed[1] *= 0.9992f;
						}
						else {
							float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
							float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
							float dan = FloatMath.sqrt(dax * dax + day * day);
							if (dan == 0)
								dan = 1f;
							day /= dan;
							this.speed[1] -= day;
							this.position[1] -= this.speed[1];
							this.speed[1] *= 0.1f;
						}
						this.position[0] -= this.speed[0] * 10;
						this.speed[0] *= 0.9992f;
					}
					else if (position[1] + radius > newBottom && position[1] - radius < newBottom) {
						if (position[1] > newBottom) {
							this.speed[1] = -this.speed[1];
							this.position[1] -= this.speed[1] * 10;
							this.speed[1] *= 0.9992f;
						}
						else {
							float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
							float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
							float dan = FloatMath.sqrt(dax * dax + day * day);
							if (dan == 0)
								dan = 1f;
							day /= dan;
							this.speed[1] -= day;
							this.position[1] -= this.speed[1];
							this.speed[1] *= 0.1f;
						}
						this.position[0] -= this.speed[0] * 10;
						this.speed[0] *= 0.9992f;
					}
					else if (position[1] < newBottom && position[1] > newTop) {
						// Region B
						float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
						float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
						float dan = FloatMath.sqrt(dax * dax + day * day);
						if (dan == 0)
							dan = 1f;
						day /= dan;
						this.speed[1] -= day;
						this.position[0] -= this.speed[0] * 10;
						this.position[1] -= this.speed[1];
						this.speed[0] *= 0.9992f;
						this.speed[1] *= 0.1f;
					}
					else
						reflectResponse();
				}
				break;
			case UP:
				if (Math.abs(position[1] - r.dstRect.top) > halfHeight) {
					if (funnelDetector == false && this.position[1] > r.dstRect.top + r.dstRect.height() / 2)
						funnelDetector = true;
					float dist1 = FloatMath.sqrt(dx1 * dx1 + dy1 * dy1);
					float dist2 = FloatMath.sqrt(dx2 * dx2 + dy2 * dy2);
					
					if (funnelDetector) {
						if (dist1 < f) {
							funnelFlag1 = true;
						}
						else if (dist2 < f) {
							funnelFlag2 = true;
						}
						else
							reflectResponse();
					}
					else {
						if (dist1 < f) {
							funnelResponse(dx1, dy1, dist1);
						}
						else if (dist2 < f) {
							funnelResponse(dx2, dy2, dist2);
						}
						else
							reflectResponse();
						return;
					}
					if (funnelFlag1)
						funnelResponse(dx1, dy1, dist1);
					if (funnelFlag2)
						funnelResponse(dx2, dy2, dist2);
				}
				else {
					float newLeft = r.dstRect.left + pipeRadius;
					float newRight = r.dstRect.right - pipeRadius;
					if (position[0] - radius < newLeft && position[0] + radius > newLeft) {
						if (position[0] < newLeft) {
							this.speed[0] = -this.speed[0];
							this.position[0] -= this.speed[0] * 10;
							this.speed[0] *= 0.9992f;
						}
						else {
							float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
							float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
							float dan = FloatMath.sqrt(dax * dax + day * day);
							if (dan == 0)
								dan = 1f;
							dax /= dan;
							this.speed[0] -= dax;
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
						}
						this.position[1] -= this.speed[1] * 10;
						this.speed[1] *= 0.9992f;
					}
					else if (position[0] + radius > newRight && position[0] - radius < newRight) {
						if (position[0] > newRight) {
							this.speed[0] = -this.speed[0];
							this.position[0] -= this.speed[0] * 10;
							this.speed[0] *= 0.9992f;
						}
						else {
							float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
							float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
							float dan = FloatMath.sqrt(dax * dax + day * day);
							if (dan == 0)
								dan = 1f;
							dax /= dan;
							this.speed[0] -= dax;
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
						}
						this.position[1] -= this.speed[1] * 10;
						this.speed[1] *= 0.9992f;
					}
					else if (position[0] < newRight && position[0] > newLeft) {
						// Region B
						float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
						float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
						float dan = FloatMath.sqrt(dax * dax + day * day);
						if (dan == 0)
							dan = 1f;
						dax /= dan;
						this.speed[0] -= dax;
						this.position[1] -= this.speed[1] * 10;
						this.position[0] -= this.speed[0];
						this.speed[1] *= 0.9992f;
						this.speed[0] *= 0.1f;
					}
					else
						reflectResponse();
				}
				break;
			case DOWN:
				if (Math.abs(position[1] - r.dstRect.top) < halfHeight) {
					if (funnelDetector == false && this.position[1] < r.dstRect.top + r.dstRect.height() / 2)
						funnelDetector = true;
					float dist1 = FloatMath.sqrt(dx1 * dx1 + dy1 * dy1);
					float dist2 = FloatMath.sqrt(dx2 * dx2 + dy2 * dy2);
					
					if (funnelDetector) {
						if (dist1 < f) {
							funnelFlag1 = true;
						}
						else if (dist2 < f) {
							funnelFlag2 = true;
						}
						else
							reflectResponse();
					}
					else {
						if (dist1 < f) {
							funnelResponse(dx1, dy1, dist1);
						}
						else if (dist2 < f) {
							funnelResponse(dx2, dy2, dist2);
						}
						else
							reflectResponse();
						return;
					}
					if (funnelFlag1)
						funnelResponse(dx1, dy1, dist1);
					if (funnelFlag2)
						funnelResponse(dx2, dy2, dist2);
				}
				else {
					float newLeft = r.dstRect.left + pipeRadius;
					float newRight = r.dstRect.right - pipeRadius;
					if (position[0] - radius < newLeft && position[0] + radius > newLeft) {
						if (position[0] < newLeft) {
							this.speed[0] = -this.speed[0];
							this.position[0] -= this.speed[0] * 10;
							this.speed[0] *= 0.9992f;
						}
						else {
							float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
							float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
							float dan = FloatMath.sqrt(dax * dax + day * day);
							if (dan == 0)
								dan = 1f;
							dax /= dan;
							this.speed[0] -= dax;
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
						}
						this.position[1] -= this.speed[1] * 10;
						this.speed[1] *= 0.9992f;
					}
					else if (position[0] + radius > newRight && position[0] - radius < newRight) {
						if (position[0] > newRight) {
							this.speed[0] = -this.speed[0];
							this.position[0] -= this.speed[0] * 10;
							this.speed[0] *= 0.9992f;
						}
						else {
							float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
							float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
							float dan = FloatMath.sqrt(dax * dax + day * day);
							if (dan == 0)
								dan = 1f;
							dax /= dan;
							this.speed[0] -= dax;
							this.position[0] -= this.speed[0];
							this.speed[0] *= 0.1f;
						}
						this.position[1] -= this.speed[1] * 10;
						this.speed[1] *= 0.9992f;
					}
					else if (position[0] < newRight && position[0] > newLeft) {
						// Region B
						float dax = (r.dstRect.left + r.dstRect.width() / 2) - position[0];
						float day = (r.dstRect.top + r.dstRect.height() / 2) - position[1];
						float dan = FloatMath.sqrt(dax * dax + day * day);
						if (dan == 0)
							dan = 1f;
						dax /= dan;
						this.speed[0] -= dax;
						this.position[1] -= this.speed[1] * 10;
						this.position[0] -= this.speed[0];
						this.speed[1] *= 0.9992f;
						this.speed[0] *= 0.1f;
					}
					else
						reflectResponse();
				}
				break;
		}
	}
} public class Border extends Terrain {
	public float borderWidth;
	public float borderHeight;
	public Paint paint;
	
	public Border(float x, float y) {
		super(x, y);
		move(x, y);
		paint = new Paint();
		paint.setStyle(Style.FILL_AND_STROKE);
		paint.setColor(Color.rgb(79, 234, 255));
	}
	
	@Override
	public void tick(Stage s) {
	}
	
	@Override
	public void render(Canvas c, float centerX, float centerY) {
		if (bitmap != null) {
			float xOffset = x - RenderView.cameraX + centerX;
			float yOffset = y - RenderView.cameraY + centerY;
			move(xOffset, yOffset);
			c.drawRect(dstRect, paint);
		}
		else
			bitmap = Art.sprites;
	}
	
	private void move(float f, float g) {
		if (bitmap != null) {
			float size = 16 * RenderView.AspectRatio;
			dstRect.set(f - size, g - size, f + borderWidth + size, g + borderHeight + size);
		}
	}
	
	public void setNewColor(int r, int g, int b) {
		paint.setColor(Color.rgb(r, g, b));
	}
	
	@Override
	public void reset() {
		x = defaultX;
		y = defaultY;
	}
} public class Bumper extends Terrain {
	
	public float radius;
	
	public Bumper(float x, float y, final float ratio) {
		super(x, y);
		radius = 16f * ratio;
		srcRect.set(0, 32, 16, 48);
		dstRect.set(this.x - radius, this.y - radius, this.x + radius, this.y + radius);
	}
	
	@Override
	public void tick(Stage s) {
		checkCollision(s.cue);
		for (Marble m : s.marbles) {
			checkCollision(m);
		}
	}
	
	@Override
	public void render(Canvas c, final float cx, final float cy) {
		if (bitmap != null) {
			float xOffset = this.x - RenderView.cameraX + cx;
			float yOffset = this.y - RenderView.cameraY + cy;
			if (RenderView.bounds.contains(xOffset, yOffset)) {
				move(xOffset, yOffset);
				c.drawBitmap(bitmap, srcRect, dstRect, null);
			}
		}
	}
	
	private void move(float f, float g) {
		if (bitmap != null) {
			dstRect.set(f - this.radius, g - this.radius, f + this.radius, g + this.radius);
		}
	}
	
	private void checkCollision(Ball b) {
		float dx = b.position[0] - x;
		float dy = b.position[1] - y;
		float distance = FloatMath.sqrt(dx * dx + dy * dy);
		if (distance < radius + b.radius)
			b.bumper = this;
		else if (b.bumper != null && b.bumper == this)
			b.bumper = null;
	}
	
} public class Coin extends Entity {
	public static enum ColorType {
		YELLOW, RED, BLUE, GREEN
	};
	
	private float x, y;
	private ColorType type;
	private boolean getCoin = false;
	private boolean scored = false;
	private boolean flip;
	private byte state;
	private final float radius = 6f * RenderView.AspectRatio;
	private final Paint paint;
	
	public Coin(ColorType t, float f, float g) {
		paint = new Paint();
		switch (t) {
			case YELLOW:
			default:
				paint.setColorFilter(new LightingColorFilter(Color.YELLOW, 0x545400));
				break;
			case RED:
				paint.setColorFilter(new LightingColorFilter(Color.RED, 0x540000));
				break;
			case BLUE:
				paint.setColorFilter(new LightingColorFilter(Color.BLUE, 0x000054));
				break;
			case GREEN:
				paint.setColorFilter(new LightingColorFilter(Color.GREEN, 0x005400));
				break;
		}
		type = t;
		getCoin = false;
		state = 0x0;
		flip = false;
		srcRect.set(0, 0, 16, 16);
		dstRect.set(0, 0, 16, 16);
		bitmap = Art.coin;
		x = f;
		y = g;
	}
	
	@Override
	public void tick(Stage s) {
		dstRect.set(x - radius, y - radius, x + radius, y + radius);
		if (dstRect.contains(s.cue.position[0], s.cue.position[1])) {
			getCoin = true;
			if (!scored) {
				switch (type) {
					default:
					case YELLOW:
						s.addTemporaryScore(200);
						s.addTemporaryTotalScore(200);
						break;
					case RED:
						s.addTemporaryScore(500);
						s.addTemporaryTotalScore(500);
						break;
					case GREEN:
						s.addTemporaryScore(1000);
						s.addTemporaryTotalScore(1000);
						break;
					case BLUE:
						s.addTemporaryScore(2000);
						s.addTemporaryTotalScore(2000);
						break;
				}
				scored = true;
				Sound.play(Sound.coinID);
			}
		}
		checkState();
	}
	
	@Override
	public void render(Canvas c, float centerX, float centerY) {
		if (bitmap != null) {
			if (!getCoin) {
				srcRect.set(state * 16, srcRect.top, (state + 1) * 16, srcRect.bottom);
				float xOffset = this.x - RenderView.cameraX + centerX;
				float yOffset = this.y - RenderView.cameraY + centerY;
				if (RenderView.bounds.contains(xOffset, yOffset)) {
					move(xOffset, yOffset);
					c.drawBitmap(bitmap, srcRect, dstRect, paint);
				}
			}
		}
	}
	
	private void checkState() {
		if (!flip) {
			state++;
			if (state > 5) {
				state = 5;
				flip = true;
			}
		}
		else {
			state--;
			if (state < 0) {
				state = 0;
				flip = false;
			}
		}
	}
	
	private void move(float f, float g) {
		if (bitmap != null) {
			dstRect.set(f - this.radius, g - this.radius, f + this.radius, g + this.radius);
		}
	}
	
	@Override
	public void reset() {
		getCoin = false;
		state = 0x0;
		scored = false;
	}
	
	public boolean gotCoin() {
		return getCoin;
	}
	
	public ColorType getType() {
		return type;
	}
} public static enum ColorType {
	YELLOW, RED, BLUE, GREEN
}; public class Connector extends Path {
	//Connects intersecting Pipes together.
	public final float radius;
	
	public Connector(float x, float y, float z) {
		super.setPlacement(x, y, z);
		srcRect.set(40, 8, 48, 16);
		radius = 8 * PIPE_SIZE * RenderView.AspectRatio;
	}
	
	@Override
	public void tick(Stage s) {
		place();
		checkCollision(s.cue);
		for (Marble m : s.marbles)
			checkCollision(m);
	}
	
	private void checkCollision(Ball b) {
		if (b.position[0] >= dstRect.left && b.position[0] <= dstRect.right && b.position[1] >= dstRect.top && b.position[1] <= dstRect.bottom) {
			b.setConnector(this);
		}
		else if (b.getConnector() != null && b.getConnector() == this) {
			b.setConnector(null);
		}
	}
	
	@Override
	public void render(Canvas c, float centerX, float centerY) {
		if (bitmap != null) {
			float xOffset = position[0] - RenderView.cameraX + centerX;
			float yOffset = position[1] - RenderView.cameraY + centerY;
			if (RenderView.bounds.contains(xOffset, yOffset)) {
				move(xOffset, yOffset);
				c.drawBitmap(bitmap, srcRect, dstRect, null);
			}
		}
		else
			bitmap = Art.sprites;
	}
	
	private void move(float f, float g) {
		if (bitmap != null) {
			dstRect.set(f - radius, g - radius, f + radius, g + radius);
		}
	}
	
	@Override
	public void place() {
		float r = this.radius + 8f;
		dstRect.set(position[0] - r, position[1] - r, position[0] + r, position[1] + r);
	}
} public class CurvePipe extends Path {
	
	public float x, y;
	
	public CurvePipe() {
		super();
	}
	
	public boolean checkCollision(Ball b) {
		switch(this.orientation) {
			case LEFT_TO_UP:
			case LEFT_TO_DOWN:
			case RIGHT_TO_UP:
			case RIGHT_TO_DOWN:
				if (b.position[0] <= dstRect.right && b.position[1] <= dstRect.bottom && b.position[0] >= dstRect.left && b.position[1] >= dstRect.top) {
					b.setCurvePipe(this);
					break;
				}
			default:
				if (b.getCurvePipe() != null && b.getCurvePipe() == this)
					b.setCurvePipe(null);
				return false;
		}
		return true;
	}
	
	public void tick(Stage s) {
		place();
		checkCollision(s.cue);
		for (Marble m: s.marbles)
			checkCollision(m);
	}
	
	public void place() {
		float size = 8 * PIPE_SIZE * ratio;
		dstRect.set(position[0] - size, position[1] - size, position[0] + size, position[1] + size);
		switch(this.orientation) {
			case LEFT_TO_UP:
				x = dstRect.left;
				y = dstRect.top;
				break;
			case LEFT_TO_DOWN:
				x = dstRect.left;
				y = dstRect.bottom;
				break;
			case RIGHT_TO_UP:
				x = dstRect.right;
				y = dstRect.top;
				break;
			case RIGHT_TO_DOWN:
				x = dstRect.right;
				y = dstRect.bottom;
				break;
		}
	}
	
	@Override
	public void render(Canvas c, final float centerX, final float centerY) {
		if (bitmap == null) {
			bitmap = Art.sprites;
			return;
		}
		float xOffset = this.position[0] - RenderView.cameraX + centerX;
		float yOffset = this.position[1] - RenderView.cameraY + centerY;
		if (RenderView.bounds.contains(xOffset, yOffset)){
			move(xOffset, yOffset);
			c.drawBitmap(bitmap, srcRect, dstRect, null);
		}
	}
	
	private void move(float f, float g) {
		if (bitmap == null)
			return;
		float size = 8 * PIPE_SIZE * ratio;
		dstRect.set(f - size, g - size, f + size, g + size);
	}
} public abstract class Funnel extends Path {
	//Makes the ball more easier to move into the Pipe.
	//Corner positions.
	
	public static enum Direction {
		LEFT, UP, RIGHT, DOWN
	}
	
	public float x1, x2, y1, y2;
	public float xPos, yPos;
	public float radius;
	public Direction direction;
	protected Matrix matrix;
	
	public Funnel(final float ratio) {
		radius = ratio * 8 * PIPE_SIZE;
		matrix = new Matrix();
	}
	
	@Override
	public void tick(Stage s) {
		place();
		checkCollision(s.cue);
		for (Marble m : s.marbles)
			checkCollision(m);
	}
	
	protected boolean checkCollision(Ball b) {
		if (b.position[0] > dstRect.left && b.position[0] < dstRect.right && b.position[1] > dstRect.top && b.position[1] < dstRect.bottom) {
			b.setFunnel(this);
			return true;
		}
		else if (b.getFunnel() != null && b.getFunnel() == this) {
			b.setFunnel(null);
			b.funnelDetector = false;
			b.funnelFlag1 = false;
			b.funnelFlag2 = false;
		}
		return false;
	}
	
	@Override
	public void render(Canvas c, float centerX, float centerY) {
		if (bitmap != null) {
			float xOffset = position[0] - RenderView.cameraX + centerX;
			float yOffset = position[1] - RenderView.cameraY + centerY;
			if (RenderView.bounds.contains(xOffset, yOffset)) {
				move(xOffset, yOffset);
				c.drawBitmap(bitmap, srcRect, dstRect, null);
			}
		}
	}
	
	private void move(float f, float g) {
		if (bitmap != null) {
			dstRect.set(f - radius, g - radius, f + radius, g + radius);
		}
	}
	
	@Override
	public void place() {
		dstRect.set(position[0] - radius, position[1] - radius, position[0] + radius, position[1] + radius);
		switch (direction) {
			case LEFT:
			default:
				//Point 1 (Top left)
				x1 = dstRect.left;
				y1 = dstRect.top;
				//Point 2 (Bottom left)
				x2 = dstRect.left;
				y2 = dstRect.bottom;
				break;
			case RIGHT:
				x1 = dstRect.right;
				y1 = dstRect.top;
				x2 = dstRect.right;
				y2 = dstRect.bottom;
				break;
			case UP:
				x1 = dstRect.left;
				y1 = dstRect.top;
				x2 = dstRect.right;
				y2 = dstRect.top;
				break;
			case DOWN:
				x1 = dstRect.left;
				y1 = dstRect.bottom;
				x2 = dstRect.right;
				y2 = dstRect.bottom;
				break;
		}
	}
	
	public void setDirection(Direction d) {
		direction = d;
		switch (d) {
			case LEFT:
			default:
				matrix.reset();
				break;
			case RIGHT:
				matrix.reset();
				matrix.setScale(-1, 1);
				matrix.postRotate(0);
				matrix.postTranslate(-8, 0);
				break;
			case UP:
				matrix.reset();
				matrix.setScale(1, 1);
				matrix.postRotate(90);
				matrix.postTranslate(-8, 0);
				break;
			case DOWN:
				matrix.reset();
				matrix.setScale(1, 1);
				matrix.postRotate(-90);
				matrix.postTranslate(0, -8);
				break;
		}
		bitmap = Bitmap.createBitmap(Art.sprites, 40, 0, 8, 8, matrix, false);
		srcRect.set(0, 0, 8, 8);
	}
} public static enum Direction {
	LEFT, UP, RIGHT, DOWN
} public class Hole extends Terrain {
	public float radius;
	private int slideNumber;
	
	public Hole(float x, float y, float ratio) {
		// (x,y) is the center of the hole.
		//Size of radius = 4.
		//
		super(x, y);
		this.bitmap = Art.animatedHole;
		this.radius = 16 * ratio;
		this.slideNumber = 0;
		srcRect.set(0, 0, 16, 16);
		dstRect.set(this.x - this.radius, this.y - this.radius, this.x + this.radius, this.y + this.radius);
	}
	
	@Override
	public void tick(Stage s) {
		if (bitmap == null) {
			bitmap = Art.animatedHole;
			return;
		}
		dstRect.set(this.x - this.radius, this.y - this.radius, this.x + this.radius, this.y + this.radius);
		slideNumber = slideNumber >= 15 ? 0 : slideNumber + 1;
	}
	
	@Override
	public void render(Canvas c, final float cx, final float cy) {
		if (bitmap != null) {
			int horizontal = (slideNumber % 4 * 16);
			int vertical = (slideNumber / 4 * 16);
			srcRect.set(horizontal, vertical, horizontal + 16, vertical + 16);
			float xOffset = this.x - RenderView.cameraX + cx;
			float yOffset = this.y - RenderView.cameraY + cy;
			if (RenderView.bounds.contains(xOffset, yOffset)) {
				move(xOffset, yOffset);
				c.drawBitmap(bitmap, srcRect, dstRect, null);
			}
		}
	}
	
	private void move(float f, float g) {
		if (bitmap != null) {
			dstRect.set(f - this.radius, g - this.radius, f + this.radius, g + this.radius);
		}
	}
} public abstract class Path extends Entity {
	public static enum Orientation {
		//X axis to Y axis.
		HORIZONTAL, VERTICAL, LEFT_TO_UP, LEFT_TO_DOWN, RIGHT_TO_UP, RIGHT_TO_DOWN
	}
	
	public Orientation orientation;
	public final int PIPE_SIZE = 2;
	public float ratio;
	
	private float defaultX, defaultY, defaultZ;
	
	//Positions are marked from the top left corner (Since they are squares.)
	//Z axis is for height;
	public float[] position = new float[3];
	
	public void setOrientation(Orientation o) {
		this.orientation = o;
		switch (this.orientation) {
			case HORIZONTAL:
			default:
				srcRect.set(16, 8, 24, 16);
				break;
			case VERTICAL:
				srcRect.set(16, 0, 24, 8);
				break;
			case LEFT_TO_UP:
				srcRect.set(32, 8, 40, 16);
				break;
			case LEFT_TO_DOWN:
				srcRect.set(32, 0, 40, 8);
				break;
			case RIGHT_TO_UP:
				srcRect.set(24, 8, 32, 16);
				break;
			case RIGHT_TO_DOWN:
				srcRect.set(24, 0, 32, 8);
				break;
		}
	}
	
	public Orientation getOrientation() {
		return orientation;
	}
	
	public void setPlacement(float x, float y, float z) {
		// Z for above/below placements.
		//(x, y) is the center of a single pipe grid.
		position[0] = x;
		position[1] = y;
		position[2] = z;
		defaultX = x;
		defaultY = y;
		defaultZ = z;
		
	}
	
	public void setAspectRatio(float aspectRatio) {
		ratio = aspectRatio;
	}
	
	public abstract void place();
	
	@Override
	public void reset() {
		position[0] = defaultX;
		position[1] = defaultY;
		position[2] = defaultZ;
	}
} public static enum Orientation {
	//X axis to Y axis.
	HORIZONTAL, VERTICAL, LEFT_TO_UP, LEFT_TO_DOWN, RIGHT_TO_UP, RIGHT_TO_DOWN
} public class Pipe extends Path {
	public Pipe() {
	}
	
	@Override
	public void tick(Stage l) {
		place();
		checkCollision(l.cue);
		for (Marble m : l.marbles)
			checkCollision(m);
	}
	
	@Override
	public void render(Canvas c, final float cx, final float cy) {
		if (bitmap != null) {
			float xOffset = this.position[0] - RenderView.cameraX + cx;
			float yOffset = this.position[1] - RenderView.cameraY + cy;
			if (RenderView.bounds.contains(xOffset, yOffset)) {
				move(xOffset, yOffset);
				c.drawBitmap(bitmap, srcRect, dstRect, null);
			}
		}
		else
			bitmap = Art.sprites;
	}
	
	private void move(float f, float g) {
		if (bitmap != null) {
			float size = 8 * PIPE_SIZE * ratio;
			dstRect.set(f - size, g - size, f + size, g + size);
		}
	}
	
	public boolean checkCollision(Ball b) {
		if (b.position[0] > dstRect.left && b.position[0] < dstRect.right && b.position[1] > dstRect.top && b.position[1] < dstRect.bottom) {
			b.setPipe(this);
			return true;
		}
		else if (b.getPipe() != null && b.getPipe() == this)
			b.setPipe(null);
		return false;
	}
	
	@Override
	public void place() {
		//TODO: ???  Possibly not in need.
		float size = 8 * PIPE_SIZE * ratio;
		dstRect.set(position[0] - size, position[1] - size, position[0] + size, position[1] + size);
	}
} public class Ramp extends Funnel {
	
	public Ramp(float ratio) {
		super(ratio);
	}
	
	@Override
	public boolean checkCollision(Ball b) {
		if (b.position[0] > dstRect.left && b.position[0] < dstRect.right && b.position[1] > dstRect.top && b.position[1] < dstRect.bottom) {
			b.setRamp(this);
			return true;
		}
		else if (b.getRamp() != null && b.getRamp() == this) {
			b.setRamp(null);
			b.funnelDetector = false;
			b.funnelFlag1 = false;
			b.funnelFlag2 = false;
		}
		return false;
	}
	
	@Override
	public void place() {
		dstRect.set(position[0] - radius, position[1] - radius, position[0] + radius, position[1] + radius);
		float size = 10 * PIPE_SIZE * RenderView.AspectRatio;
		switch (direction) {
			case LEFT:
			default:
				//Point 1 (Top left)
				x1 = dstRect.left + size;
				y1 = dstRect.top;
				//Point 2 (Bottom left)
				x2 = dstRect.left + size;
				y2 = dstRect.bottom;
				break;
			case RIGHT:
				x1 = dstRect.right - size;
				y1 = dstRect.top;
				x2 = dstRect.right - size;
				y2 = dstRect.bottom;
				break;
			case UP:
				x1 = dstRect.left;
				y1 = dstRect.top + size;
				x2 = dstRect.right;
				y2 = dstRect.top + size;
				break;
			case DOWN:
				x1 = dstRect.left;
				y1 = dstRect.bottom - size;
				x2 = dstRect.right;
				y2 = dstRect.bottom - size;
				break;
		}
	}
	
	@Override
	public void setDirection(Direction d) {
		direction = d;
		switch (d) {
			case LEFT:
			default:
				matrix.reset();
				break;
			case RIGHT:
				matrix.reset();
				matrix.setScale(-1, 1);
				matrix.postRotate(0);
				matrix.postTranslate(-8, 0);
				break;
			case UP:
				matrix.reset();
				matrix.setScale(1, 1);
				matrix.postRotate(90);
				matrix.postTranslate(-8, 0);
				break;
			case DOWN:
				matrix.reset();
				matrix.setScale(1, 1);
				matrix.postRotate(-90);
				matrix.postTranslate(0, -8);
				break;
		}
		bitmap = Bitmap.createBitmap(Art.sprites, 48, 0, 8, 8, matrix, false);
		srcRect.set(0, 0, 8, 8);
	}
} public class ShortFunnel extends Funnel {
	
	public ShortFunnel(float ratio) {
		super(ratio);
	}
} public class Tee extends Terrain {
	private RectF A;
	private RectF B;
	private RectF C;
	public float radius = 0f;
	
	public Tee(float x, float y, float ratio) {
		super(x, y);
		radius = 16 * ratio;
		A = new RectF();
		B = new RectF();
		C = new RectF();
		srcRect.set(0, 16, 16, 32);
		A.set(this.x - radius, this.y - radius, this.x, this.y);
		B.set(this.x, this.y - radius, this.x + radius, this.y);
		C.set(this.x - radius, this.y, this.x, this.y + radius);
		dstRect.set(this.x, this.y, this.x + radius, this.y + radius);
	}
	
	public void tick(Stage s) {
		if (bitmap == null) {
			bitmap = Art.sprites;
			return;
		}
		A.set(this.x - radius, this.y - radius, this.x, this.y);
		B.set(this.x, this.y - radius, this.x + radius, this.y);
		C.set(this.x - radius, this.y, this.x, this.y + radius);
		dstRect.set(this.x, this.y, this.x + radius, this.y + radius);
	}
	
	@Override
	public void render(Canvas c, float centerX, float centerY) {
		if (bitmap != null) {
			float xOffset = this.x - RenderView.cameraX + centerX;
			float yOffset = this.y - RenderView.cameraY + centerY;
			if (RenderView.bounds.contains(xOffset, yOffset)){
				move(xOffset, yOffset);
				c.drawBitmap(bitmap, srcRect, A, null);
				c.drawBitmap(bitmap, srcRect, B, null);
				c.drawBitmap(bitmap, srcRect, C, null);
				c.drawBitmap(bitmap, srcRect, dstRect, null);
			}
		}
	}
	
	private void move(float f, float g) {
		if (bitmap != null) {
			A.set(f - radius, g - radius, f, g);
			B.set(f, g - radius, f + radius, g);
			C.set(f - radius, g, f, g + radius);
			dstRect.set(f, g, f + radius, g + radius);
		}
	}
} public abstract class Terrain extends Entity {
	public float x;
	public float y;
	protected float defaultX;
	protected float defaultY;
	
	public Terrain(float x, float y){
		this.x = x;
		this.y = y;
		this.defaultX = x;
		this.defaultY = y;
	}
	
	public void reset(){
		this.x = defaultX;
		this.y = defaultY;
	}
} public class Void extends Terrain {
	public float radius = 0f;
	public int direction;
	
	public Void(float x, float y, final float ratio) {
		super(x, y);
		// TODO: Will give it a random background.
		Random random = new Random();
		switch (random.nextInt(20)) {
			case 2:
				srcRect.set(32, 16, 40, 24);
				break;
			case 5:
				srcRect.set(32, 24, 40, 32);
				break;
			case 8:
				srcRect.set(40, 24, 48, 32);
				break;
			default:
				srcRect.set(40, 16, 48, 24);
				break;
		}
		this.radius = 16 * ratio;
		this.direction = random.nextInt(4);
		dstRect.set(this.x - radius, this.y - radius, this.x + radius, this.y + radius);
	}
	
	@Override
	public void tick(Stage s) {
		if (bitmap != Art.sprites) {
			bitmap = Art.sprites;
			return;
		}
		dstRect.set(x - radius, y - radius, x + radius, y + radius);
		if (s.cue.position[0] <= dstRect.right && s.cue.position[1] <= dstRect.bottom && s.cue.position[0] >= dstRect.left && s.cue.position[1] >= dstRect.top) {
			if (s.cue.position[2] < 0.1f)
				s.cue.die();
		}
	}
	
	@Override
	public void render(Canvas c, float centerX, float centerY) {
		if (bitmap != null) {
			float xOffset = this.x - RenderView.cameraX + centerX;
			float yOffset = this.y - RenderView.cameraY + centerY;
			if (RenderView.bounds.contains(xOffset, yOffset)) {
				move(xOffset, yOffset);
				//TODO: Make this bitmap use the matrix, instead of rotating the entire Canvas (takes a long time).
				c.save();
				c.rotate((this.direction * 90), dstRect.centerX(), dstRect.centerY());
				c.drawBitmap(bitmap, srcRect, dstRect, null);
				c.restore();
				
			}
		}
	}
	
	private void move(float f, float g) {
		if (bitmap != null) {
			dstRect.set(f - this.radius, g - this.radius, f + this.radius, g + this.radius);
		}
	}
} public class RenderView extends SurfaceView implements Runnable {
	SurfaceHolder holder;
	Handler tickHandler;
	Handler renderHandler;
	
	PlayActivity parent;
	Stage stage;
	
	//-------------------
	public HUD hud;
	BestScore best;
	public TimeBasedScore timeScore;
	//-------------------
	
	Thread thread = null;
	volatile boolean running = false;
	volatile boolean pausing = false;
	volatile boolean creating = false;
	int width;
	int height;
	AlertDialog dialogLoss;
	AlertDialog dialogWin;
	Dialog dialogPause;
	
	boolean dialogFlag;
	public static float AspectRatio;
	public static RectF bounds;
	public static float cameraX, cameraY;
	
	public RenderView(PlayActivity c) {
		super(c);
		this.parent = c;
		Display d = c.getWindowManager().getDefaultDisplay();
		this.width = d.getWidth();
		this.height = d.getHeight();
		AspectRatio = c.getAspectRatio();
		timeScore = TimeBasedScore.loadTimers(parent, "dialogue/time.txt", timeScore);
	}
	
	public void resume() {
		this.holder = this.getHolder();
		best = new BestScore(Format.loadFormat(parent));
		running = true;
		pausing = false;
		creating = false;
		thread = new Thread(this);
		thread.setName("Game Thread");
		thread.start();
	}
	
	public void pause() {
		running = false;
		pausing = true;
		creating = false;
		
		boolean retry = true;
		if (thread != null) {
			while (retry) {
				try {
					thread.join();
					retry = false;
				}
				catch (InterruptedException e) {
					retry = true;
				}
			}
		}
	}
	
	public void resetGame() {
		pausing = false;
		stage.reset();
		timeScore.reset();
	}
	
	public void pauseGame() {
		pausing = true;
		timeScore.pauseTimer();
		dialogPause.show();
	}
	
	public void unpauseGame() {
		pausing = false;
		timeScore.unpauseTimer();
	}
	
	public boolean isGamePaused() {
		return pausing;
	}
	
	public synchronized void tick() {
		if (tickHandler != null) {
			tickHandler.post(new Runnable() {
				public void run() {
					if (!stage.gameInfoPause)
						timeScore.execute();
					if (!pausing) {
						stage.tick();
						hud.tick(stage);
						best.tick(stage);
						if (!stage.hasWon() && !stage.isGameOver())
							timeScore.unpauseTimer();
					}
					else
						timeScore.pauseTimer();
				}
			});
		}
		if (!stage.isGameDialogEmpty() && stage.gameDialogFlag == false) {
			Thread g = new Thread(new Runnable() {
				public void run() {
					parent.runOnUiThread(new Runnable() {
						public void run() {
							stage.gameDialog.show();
						}
					});
				}
			});
			g.setName("Game Dialog Thread");
			g.start();
			stage.gameDialogFlag = true;
		}
		if (stage.isGameOver() && this.dialogFlag == false) {
			Thread t = new Thread(new Runnable() {
				public void run() {
					parent.runOnUiThread(new Runnable() {
						public void run() {
							// SCORE - This is where we retrieve the score for each individual stage.
							timeScore.pauseTimer();
							stage.resetGameScore();
							parent.setScoreText("Currently Accumulated Score: ", stage.getOldAccumulatedScore(), 0);
							dialogLoss.show();
						}
					});
				}
			});
			t.setName("Losing Dialog Thread");
			t.start();
			this.dialogFlag = true;
		}
		if (stage.hasWon() && this.dialogFlag == false) {
			Thread t = new Thread(new Runnable() {
				public void run() {
					parent.runOnUiThread(new Runnable() {
						public void run() {
							timeScore.pauseTimer();
							stage.addTemporaryScore(timeScore.getScore());
							stage.addTemporaryTotalScore(timeScore.getScore());
							stage.setHighScore();
							stage.setNewAccumulatedScore();
							if (parent.stageNumber <= LevelSelectionActivity.MAX_STAGES) {
								// SCORE - This is where we retrieve the score for each individual stage.
								parent.setScoreText("Accumulated Score: ", stage.getNewAccumulatedScore(), 1);
							}
							dialogWin.show();
						}
					});
				}
			});
			t.setName("Winning Dialog Thread");
			t.start();
			this.dialogFlag = true;
		}
	}
	
	public synchronized void render() {
		if (holder != null) {
			if (renderHandler != null) {
				renderHandler.post(new Runnable() {
					public void run() {
						if (holder.getSurface().isValid()) {
							Canvas c = holder.lockCanvas();
							if (c == null)
								return;
							if (bounds == null) {
								bounds = new RectF(c.getClipBounds());
								bounds.set(bounds.left - 32f, bounds.top - 32f, bounds.right + 32f, bounds.bottom + 32f);
							}
							stage.render(c, AspectRatio, width / 2, height / 2);
							hud.render(c);
							best.render(c);
							timeScore.render(c);
							if (stage.isGameOver()) {
								c.drawBitmap(Art.gameOver, (width - (Art.gameOver.getWidth())) / 2, (height - (Art.gameOver.getHeight())) / 4, null);
								c.drawBitmap(Art.gameOver, (width - (Art.gameOver.getWidth())) / 2, (height - (Art.gameOver.getHeight())) / 4 * 3, null);
							}
							holder.unlockCanvasAndPost(c);
						}
					}
				});
			}
		}
	}
	
	public void run() {
		while (running) {
			if (!creating) {
				tick();
				render();
			}
		}
	}
	
	public boolean isRunning() {
		return running;
	}
	
	public void setHandler(Handler tick, Handler render) {
		tickHandler = tick;
		renderHandler = render;
	}
	
	public Stage getStage() {
		return stage;
	}
	
	public void setLosingDialog(AlertDialog d) {
		dialogLoss = d;
		dialogFlag = false;
	}
	
	public AlertDialog getLosingDialog() {
		return dialogLoss;
	}
	
	public void setWinningDialog(AlertDialog d) {
		dialogWin = d;
		dialogFlag = false;
	}
	
	public AlertDialog getWinningDialog() {
		return dialogWin;
	}
	
	public void setPausingDialog(Dialog pauseDialog) {
		dialogPause = pauseDialog;
		dialogFlag = false;
	}
	
	public Dialog getPausingDialog() {
		return dialogPause;
	}
	
	public void setDialogFlag(boolean value) {
		dialogFlag = value;
	}
	
	public void createStage(PlayActivity a) throws IOException {
		creating = true;
		try {
			if (a.stageNumber <= LevelSelectionActivity.MAX_STAGES)
				this.stage = Art.loadStage(a, this.stage, a.stageNumber);
			else
				throw new IOException("Not allowed to load targeted stage.");
		}
		catch (IOException e) {
			creating = false;
			this.pause();
			throw e;
		}
		this.stage.gameDialog = ImageInfo.createImageDialog(a, stage);
		this.stage.setGameDialogFlag();
		this.stage.generate();
		this.hud = Art.loadHUD(this);
		timeScore.resetAndLoad(a.stageNumber);
		dialogFlag = false;
		creating = false;
		if (Sound.pool != null) {
			Sound.emergencyLoad(parent, parent.getAssets());
		}
		a.getLoadingScreenHandler().sendEmptyMessage(0);
	}
	
	public void clearTotalScore() {
		stage.clearGameScore();
	}
}
import java.util.ArrayList;
import java.util.List;
import nttu.edu.ball.Cue;
import nttu.edu.ball.Marble;
import nttu.edu.entity.Border;
import nttu.edu.entity.Bumper;
import nttu.edu.entity.Coin;
import nttu.edu.entity.Coin.ColorType;
import nttu.edu.entity.Connector;
import nttu.edu.entity.CurvePipe;
import nttu.edu.entity.Funnel;
import nttu.edu.entity.Funnel.Direction;
import nttu.edu.entity.Hole;
import nttu.edu.entity.Path;
import nttu.edu.entity.Path.Orientation;
import nttu.edu.entity.Pipe;
import nttu.edu.entity.Ramp;
import nttu.edu.entity.ShortFunnel;
import nttu.edu.entity.Tee;
import nttu.edu.entity.Terrain;
import nttu.edu.entity.Void;
import nttu.edu.graphics.RenderView;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.graphics.Canvas;
package nttu.edu.level;


public class Stage implements DialogInterface.OnClickListener {
	
	public int width;
	public int height;
	public int[] data;
	public int number;
	
	public final int SIZE;
	
	public static List<Terrain> terrain;
	public static List<Path> paths;
	public List<Marble> marbles;
	public List<Coin> coins;
	public Hole hole;
	public Tee tee;
	public Cue cue;
	
	public AlertDialog gameDialog;
	public boolean gameDialogFlag;
	public boolean gameInfoPause;
	
	private boolean generated;
	public boolean gameOver;
	public boolean gameWin;
	
	/*// This is the local score you have earned in a single stage session.
	public long score;
	//This is the local score single session may have. Once the player reaches the end, add this to score.
	//If a player retries, temporaryScore b\must be set to 0, and score must be reset to its prior value.
	public long temporaryScore;
	// TODO: Total Score goes here. This will go to the High Score screen.
	public long totalScore;
	*/
	
	private long temporaryScore;
	private long highScore;
	private long temporaryTotalScore;
	private long totalScore;
	
	public boolean scored;
	// This is the amount of marbles needed to reach.
	public int marbleCount;
	
	public Stage(int w, int h) {
		width = w;
		height = h;
		data = new int[w * h];
		terrain = new ArrayList<Terrain>();
		paths = new ArrayList<Path>();
		marbles = new ArrayList<Marble>();
		coins = new ArrayList<Coin>();
		
		SIZE = (int) (32 * RenderView.AspectRatio);
		
		gameOver = false;
		gameWin = false;
		
		gameDialogFlag = false;
		gameDialog = null;
		gameInfoPause = true;
		
		synchronized (this) {
			generated = false;
		}
		//score = 0L;
		//temporaryScore = 0L;
		scored = false;
	}
	
	public synchronized void generate() {
		this.generated = false;
		while (!terrain.isEmpty())
			terrain.remove(0);
		while (!marbles.isEmpty())
			marbles.remove(0);
		while (!paths.isEmpty())
			paths.remove(0);
		
		Pipe p = null;
		CurvePipe cp = null;
		Coin c = null;
		Border border = new Border(0, 0);
		Funnel funnel = null;