class Ball { Vector2D m_Position; float m_StartX; float m_StartY; float m_DestinationX; float m_DestinationY; Vector2D m_MovementVector; int m_BallState; //0 - held in hand //1 - in air //2 - in cup //3 - off table //4 - bouncing int m_CupIndex; int m_CupRow; int m_NumBounces; float m_BallWidth; float m_BallHeight; float m_DrawScale; void Initialize() { m_BallWidth = IMAGE_POOL.m_Images[3].width; m_BallHeight = IMAGE_POOL.m_Images[3].height; m_DrawScale = 1.0; m_BallState = 0; m_CupIndex = -1; m_CupRow = -1; m_Position = new Vector2D(); m_MovementVector = new Vector2D(); ResetToHand(); } void Reset() { m_DrawScale = 1.0; m_BallState = 0; m_NumBounces = 0; ResetToHand(); } void ResetToHand() { m_CupIndex = -1; m_CupRow = -1; m_Position.x = PLAYER.m_Hand.m_X + PLAYER.m_Hand.m_BallOffsetX; m_Position.y = PLAYER.m_Hand.m_Y + PLAYER.m_Hand.m_BallOffsetY; } void Draw( float opacity, float offsetX, float offsetY ) { tint( 255.0, opacity ); float thisWidth = m_BallWidth * m_DrawScale; float thisHeight = m_BallHeight * m_DrawScale; float xOffset = (thisWidth - m_BallWidth) / 2.0; float yOffset = (thisHeight - m_BallHeight) / 2.0; image( IMAGE_POOL.m_Images[3], m_Position.x - xOffset + offsetX, m_Position.y - yOffset + offsetY, thisWidth, thisHeight ); } void Update() { switch( m_BallState ) { case 0: ResetToHand(); break; case 1: case 4: UpdateInAirMovement(); break; case 2: UpdateInCup(); break; default: break; } } void UpdateInAirMovement() { m_Position.x += m_MovementVector.x; m_Position.y += m_MovementVector.y; //calculate midpoint of start-destination line segment Vector2D travelPath = new Vector2D(); travelPath.x = m_DestinationX - m_StartX; travelPath.y = m_DestinationY - m_StartY; //determine half length of travel path float pathHalfLength = travelPath.Magnitude() / 2.0; Vector2D pathMidpoint = new Vector2D(); pathMidpoint.x = m_StartX + (travelPath.x / 2.0); pathMidpoint.y = m_StartY + (travelPath.y / 2.0); //determine distance from current position to the midpoint of the travel path Vector2D toMidpoint = new Vector2D(); toMidpoint.x = pathMidpoint.x - m_Position.x; toMidpoint.y = pathMidpoint.y - m_Position.y; float distanceToMidpoint = toMidpoint.Magnitude(); //determine scale of ball from ratio of distance to midpoint over half distance float travelRatio = (pathHalfLength - distanceToMidpoint) / pathHalfLength; travelRatio *= travelRatio; if( m_BallState == 1 ) { m_DrawScale = 1.0 + travelRatio; } else { m_DrawScale = 1.0 + (travelRatio / 2.0); } if( m_Position.y <= m_DestinationY ) { ResolveGameTableCollision(); } } void UpdateInCup() { m_Position.Add( m_MovementVector ); Vector2D cupCenter = new Vector2D( GAME_TABLE.m_Cups[m_CupIndex].m_X + (GAME_TABLE.m_CupWidth / 2.0), GAME_TABLE.m_Cups[m_CupIndex].m_Y + (GAME_TABLE.m_CupHeight / 2.0) ); cupCenter.x -= (m_Position.x + (m_BallWidth / 2.0)); cupCenter.y -= (m_Position.y + (m_BallHeight / 2.0)); if( cupCenter.MagnitudeSquared() > 5.0 ) { m_MovementVector.x = -( m_MovementVector.x * 0.8 ); m_MovementVector.y = -( m_MovementVector.y * 0.8 ); m_Position.Add( m_MovementVector ); } } void SetInCup( int cupIndex ) { m_DrawScale = 1.0; m_CupIndex = cupIndex; switch( cupIndex ) { case 0: case 1: case 2: case 3: m_CupRow = 0; break; case 4: case 5: case 6: m_CupRow = 1; break; case 7: case 8: m_CupRow = 2; break; case 9: m_CupRow = 3; break; default: m_CupRow = -1; break; } Vector2D cupCenter = new Vector2D( GAME_TABLE.m_Cups[cupIndex].m_X + (GAME_TABLE.m_CupWidth / 2.0), GAME_TABLE.m_Cups[cupIndex].m_Y + (GAME_TABLE.m_CupHeight / 2.0) ); m_Position.x = cupCenter.x - (m_BallWidth / 2.0); m_Position.y = cupCenter.y - (m_BallHeight / 2.0); m_MovementVector.x = (random(0.5) - 0.25); m_MovementVector.y = (random(0.5) - 0.25); } void SetBall() { m_BallState = 1; m_Position.x = mouseX; m_Position.y = mouseY; m_StartX = 0.0; m_StartY = 0.0; m_DestinationX = mouseX; m_DestinationY = mouseY; m_MovementVector.x = 0.0; m_MovementVector.y = 0.0; } void ReleaseBall( float power ) { m_NumBounces = 0; m_DrawScale = 1.0; m_BallState = 1; //make sure power is in between 0.0 and 1.0 if( power < 0.0 ) { power = 0.0; } if( power > 1.0 ) { power = 1.0; } //calculate destination m_StartX = m_Position.x; m_StartY = m_Position.y; m_DestinationX = m_Position.x; m_DestinationY = 100.0 - (100.0 * power) + (PLAYER.m_Hand.m_Y - PLAYER.m_Hand.m_MovementAreaY); m_MovementVector.x = 0.0; m_MovementVector.y = -(15.0 + (4.0 * power)); } void BounceBall( boolean bounceIn, float toCupCenterX, float toCupCenterY, float bouncePercent ) { ++m_NumBounces; m_BallState = 4; m_StartX = m_Position.x; m_StartY = m_Position.y; Vector2D bounceVec = new Vector2D( toCupCenterX, toCupCenterY ); bounceVec.Normalize(); if( bounceIn == true ) { bounceVec.Multiply( bouncePercent*50.0 ); } else { bounceVec.Multiply( (1.0 - bouncePercent) * -50.0 ); } m_DestinationX = m_Position.x + bounceVec.x; m_DestinationY = m_Position.y + bounceVec.y; m_MovementVector.x = (m_DestinationX - m_StartX) / 15.0; m_MovementVector.y = (m_DestinationY - m_StartY) / 15.0; } void FallInCup( float cupCenterX, float cupCenterY ) { m_StartX = m_Position.x; m_StartY = m_Position.y; m_DestinationX = cupCenterX; m_DestinationY = cupCenterY; m_MovementVector.x = (m_DestinationX - m_StartX) / 8.0; m_MovementVector.y = (m_DestinationY - m_StartY) / 8.0; } void ResolveGameTableCollision() { int i; for( i = 0; i < GAME_TABLE.m_NumCups; ++i ) { if( GAME_TABLE.m_Cups[i].m_Eliminated == true ) { continue; } Vector2D ballCenter = new Vector2D( m_Position.x + (m_BallWidth / 2.0), m_Position.y + (m_BallHeight / 2.0) ); Vector2D cupCenter = new Vector2D( GAME_TABLE.m_Cups[i].m_X + (GAME_TABLE.m_CupWidth / 2.0), GAME_TABLE.m_Cups[i].m_Y + (GAME_TABLE.m_CupHeight / 4.0) ); Vector2D cupToBall = new Vector2D( ballCenter.x - cupCenter.x, ballCenter.y - cupCenter.y ); cupToBall.x /= 2.0; float cupToBallDist = cupToBall.Magnitude(); if( cupToBallDist < IN_CUP_DIST ) { //print( "in" + '\n' ); m_BallState = 2; SetInCup( i ); GAME_TABLE.m_Cups[i].SetBigSplash(); MENUS.SetDrink(); return; } else if( cupToBallDist < FALL_IN_CUP_DIST ) { //print( "fall in" + '\n' ); FallInCup( cupCenter.x, cupCenter.y ); /* m_BallState = 2; SetInCup( i ); GAME_TABLE.m_Cups[i].SetBigSplash(); MENUS.SetDrink(); */ return; } else if( cupToBallDist < BOUNCE_TOWARD_CENTER_DIST ) { //print( "bounce in" + '\n' ); //third parameter approaches 1.0 as we get closer to falling in the cup // likewise, it approaches 0.0 as we approach bouncing out BounceBall( true, -cupToBall.x, -cupToBall.y, (BOUNCE_TOWARD_CENTER_DIST-cupToBallDist) / (BOUNCE_TOWARD_CENTER_DIST-FALL_IN_CUP_DIST) ); return; } else if( cupToBallDist < BOUNCE_AWAY_DIST ) { //print( "bounce out" + '\n' ); //third parameter approaches 1.0 as we get closer to bouncing inward off the cup // likewise, it approaches 0.0 as we approach missing BounceBall( false, -cupToBall.x, -cupToBall.y, (BOUNCE_AWAY_DIST-cupToBallDist) / (BOUNCE_AWAY_DIST-BOUNCE_TOWARD_CENTER_DIST) ); return; } } MENUS.SetMiss(); m_BallState = 3; return; } }