--require ( "scripts/math.lua" ) ;

CHARACTER_LAYER_NUM = 6 ;

gLightTextR = 0.9 ;
gLightTextG = 0.81 ;
gLightTextB = 0.62 ;
gLightTextA = 1.0 ;
gLightTextShadowR = 0.25 ;
gLightTextShadowG = 0.19 ;
gLightTextShadowB = 0.05 ;
gLightTextShadowA = 1.0 ;

gPlayingTennis = false ;
gPlayingDice = false ;
gCardinalDiceCheat = false ;

-- Card Game
function runCardGame( )
	showChunkedBackground( "game_table" ) ;

	local cardValues = { 
		getString( "card1" ),
		getString( "card2" ),
		getString( "card3" ),
		getString( "card4" ),
		getString( "card5" ),
		getString( "card6" ),
		getString( "card7" ),
		getString( "card8" ),
		getString( "card9" ),
		getString( "card10" ),
		getString( "card11" ),
		getString( "card12" ),
		getString( "card13" ) } ;
	local cardsInSuit = table.getn( cardValues ) ;
	local suitValues = { "spade", "heart", "diamond", "club" } ;
	local suits = table.getn( suitValues ) ;
	local totalCards = cardsInSuit * suits ;
		
	local highCardTitle = addLabel( ) ;
	setLabelText( highCardTitle, getString( "highcard" ) ) ;
	setLabelFont( highCardTitle, "title_font" ) ;
	setLabelColour( highCardTitle, gLightTextR, gLightTextG, gLightTextB, gLightTextA ) ;
	setLabelShadow( highCardTitle, true, 2, 2 ) ;
	setLabelShadowColour( highCardTitle, gLightTextShadowR, gLightTextShadowG, gLightTextShadowB, gLightTextShadowA ) ;
	setWindowAlign( highCardTitle, "center", "top" ) ;
	setWindowPos( highCardTitle, 0, 80 ) ;
	
	local cardTitleY = 150 ;
	local opponentCardTitle = addLabel( ) ;
	setLabelText( opponentCardTitle, getString( "opponentscard" ) ) ;
	setLabelColour( opponentCardTitle, gLightTextR, gLightTextG, gLightTextB, gLightTextA ) ;
	setLabelShadow( opponentCardTitle, true, 1, 1 ) ;
	setLabelShadowColour( opponentCardTitle, gLightTextShadowR, gLightTextShadowG, gLightTextShadowB, gLightTextShadowA ) ;
	setWindowAlign( opponentCardTitle, "left", "top" ) ;
	setWindowPos( opponentCardTitle, 320, cardTitleY ) ;
	
	local playerCardTitle = addLabel( ) ;
	setLabelText( playerCardTitle, getString( "yourcard" ) ) ;
	setLabelColour( playerCardTitle, gLightTextR, gLightTextG, gLightTextB, gLightTextA ) ;
	setLabelShadow( playerCardTitle, true, 1, 1 ) ;
	setLabelShadowColour( playerCardTitle, gLightTextShadowR, gLightTextShadowG, gLightTextShadowB, gLightTextShadowA ) ;
	setWindowAlign( playerCardTitle, "left", "top" ) ;
	setWindowPos( playerCardTitle, 520, cardTitleY ) ;
	
	
	local winTextLabel = -1 ;
	
	local opponentCardLabel1 = -1 ;
	local opponentCardLabel2 = -1 ;
	local opponentCardLabel3 = -1 ;
	local opponentCardLabel4 = -1 ;
	local playerCardLabel1 = -1 ;
	local playerCardLabel2 = -1 ;
	local playerCardLabel3 = -1 ;
	local playerCardLabel4 = -1 ;
	
	local cardPosY = 300 ;
	local stackPosX = 200 ;
	local stackPosY = cardPosY ;
	local opponentCardPosX = 400 ;
	local opponentCardPosY = cardPosY ;
	local playerCardPosX = 600 ;
	local playerCardPosY = cardPosY ;
	
	local cardLayer = 3 ;
	local cardNumberLayer = 4 ;
	local slideCardLayer = 5 ;
	local levelName = "cardgame" ;
	local stackElement = -1 ;
	local opponentCardElement = -1 ;
	local opponentCardSuitElement = -1 ;
	local playerCardElement = -1 ;
	local playerCardSuitElement = -1 ;
	
	
	stackElement = createScriptElement( ) ;
	setScriptElementImageMapCell( stackElement, "card_stack" ) ;
	placeScriptElementInLevel( stackElement, levelName, cardLayer, stackPosX, stackPosY ) ;
	
	local timeElapsed = 0.0 ;
	local timer = 0.0 ;
	
	local cardLabelLeft = -80 ;
	local cardLabelRight = 75 ;
	local cardLabelTop = -115 ;
	local cardLabelBottom = 120 ;
	
	
	local opponentCardNum = randomInt( 1, totalCards ) ;
	local playerCardNum = randomInt( 1, totalCards ) ;
	while ( playerCardNum == opponentCardNum ) do
		-- can't both have the same card
		playerCardNum = randomInt( 1, totalCards ) ;
		DEBUG_OUTPUT( "player same as opponent card" ) ;
	end
	
	
	local opponentCard = ( math.mod( opponentCardNum, cardsInSuit ) ) + 1 ;
	local playerCard = ( math.mod( playerCardNum, cardsInSuit ) ) + 1 ;
	local opponentSuitIndex = math.floor( opponentCardNum / cardsInSuit ) + 1 ;
	local playerSuitIndex = math.floor( playerCardNum / cardsInSuit ) + 1 ;
	local opponentSuit = suitValues[ opponentSuitIndex ] ;
	local playerSuit = suitValues[ playerSuitIndex ] ;
	--DEBUG_OUTPUT( "opponent: " .. opponentCardNum .. " " .. opponentCard .. " " .. opponentSuit ) ;
	--DEBUG_OUTPUT( "player: " .. playerCardNum .. " " .. playerCard .. " " .. playerSuit ) ;
	--local opponentCard = randomInt( 1, numCardValues + 1 ) ;
	--local playerCard = randomInt( 1, numCardValues + 1 ) ;
	local opponentCardVisible = false ;
	local playerCardVisible = false ;
	local winTextVisible = false ;
	
	
	local timeBeforeFirstSlide = 1.0 ;
	local doneFirstSlide = false ;
	local timeBeforeSecondSlide = 0.5 ;
	local doneSecondSlide = false ;
	local timeBeforeFirstReveal = 0.5 ;
	local timeBeforeSecondReveal = 2.0 ;
	local timeBeforeWinText = 1.0 ;
	local timeBeforeExit = 3.0 ;
	
	local showOpponentCard = 50.0 ;
	local showPlayerCard = 100.0 ;
	local showWinText = 120.0 ;
	
	local cardBackSlideX = 0 ;
	local cardBackSlideY = 0 ;
	
	local slideSpeed = 1000.0 ;		
	
	-- calc players win/loss first so that we can save it without player being able to quit
	local francs = "francs" ;
	if ( gBetAmount == 1 ) then
		francs = "franc" ;
	end
	
	local winString = "" ;
	if ( opponentCard == playerCard ) then
		winString = getString( "drawtie" ) ;
	elseif ( opponentCard > playerCard ) then
		if ( gBetAmount == 1 ) then
			winString = getString( "losesingular", gBetAmount ) ;	
		else
			winString = getString( "loseplural", gBetAmount ) ;	
		end
		removeMoney( gBetAmount ) ;
	else
		if ( gBetAmount == 1 ) then
			winString = getString( "winsingular", gBetAmount ) ;	
		else
			winString = getString( "winplural", gBetAmount ) ;	
		end
		addMoney( gBetAmount ) ;
	end		
	
	local playedOpponentDeal = false ;
	local playedOpponentFlip = false ;
	local playedPlayerDeal = false ;
	local playedPlayerFlip = false ;
	local playedWinSound = false ;
	
	local playing = true ;
	while ( playing ) do
	
		timeElapsed = timeElapsed + getDeltaT( ) ;
		timer = timer + getDeltaT( ) ;
		--DEBUG_OUTPUT( "Time Elapsed: " .. timeElapsed ) ;
		
		if ( not doneFirstSlide ) then
			
			if ( timer >= timeBeforeFirstSlide ) then
				-- doing first slide
				
				if ( timer >= ( timeBeforeFirstSlide + 0.2 ) ) then
					if ( not playedOpponentDeal ) then
						playSound( "dealcard", 1.0 ) ;
						playedOpponentDeal = true ;
					end
				end
								
				if ( opponentCardElement == -1 ) then
					cardBackSlideX = stackPosX ;
					cardBackSlideY = cardPosY ;
					opponentCardElement = createScriptElement( ) ;
					setScriptElementImageMapCell( opponentCardElement, "card_back" ) ;
					placeScriptElementInLevel( opponentCardElement, levelName, slideCardLayer, cardBackSlideX, cardBackSlideY ) ;
				else
					cardBackSlideX = cardBackSlideX + slideSpeed * getDeltaT( ) ;
					placeScriptElement( opponentCardElement, cardBackSlideX, cardBackSlideY ) ;
					if ( cardBackSlideX >= opponentCardPosX ) then
						-- put on bottom layer
						opponentCardElement = safeDeleteScriptElement( opponentCardElement ) ;
						opponentCardElement = createScriptElement( ) ;
						setScriptElementImageMapCell( opponentCardElement, "card_back" ) ;
						placeScriptElementInLevel( opponentCardElement, levelName, cardLayer, opponentCardPosX, opponentCardPosY ) ;

						doneFirstSlide = true ;
						timer = 0.0 ;
					end
				end
			end
		elseif ( not doneSecondSlide ) then
			-- first slide is done
			if ( timer >= timeBeforeSecondSlide ) then
				-- doing first slide
				
				if ( timer >= ( timeBeforeSecondSlide + 0.4 ) ) then
					if ( not playedPlayerDeal ) then
						playSound( "dealcard", 1.0 ) ;
						playedPlayerDeal = true ;
					end
				end
				
				if ( playerCardElement == -1 ) then
					cardBackSlideX = stackPosX ;
					cardBackSlideY = cardPosY ;
					playerCardElement = createScriptElement( ) ;
					setScriptElementImageMapCell( playerCardElement, "card_back" ) ;
					placeScriptElementInLevel( playerCardElement, levelName, slideCardLayer, cardBackSlideX, cardBackSlideY ) ;
				else
					cardBackSlideX = cardBackSlideX + slideSpeed * getDeltaT( ) ;
					placeScriptElement( playerCardElement, cardBackSlideX, cardBackSlideY ) ;
					if ( cardBackSlideX >= playerCardPosX ) then
						placeScriptElement( playerCardElement, playerCardPosX, playerCardPosY ) ;
						doneSecondSlide = true ;
						timer = 0.0 ;
					end
				end
			end
		elseif ( not opponentCardVisible ) then
			if ( timer >= timeBeforeFirstReveal ) then
				if ( not playedOpponentFlip ) then
					playSound( "flipcard", 1.0 ) ;
					playedOpponentFlip = true ;
				end
				
				opponentCardElement = safeDeleteScriptElement( opponentCardElement ) ;
								
				opponentCardElement = createScriptElement( ) ;
				setScriptElementImageMapCell( opponentCardElement, "card_front" ) ;
				placeScriptElementInLevel( opponentCardElement, levelName, cardLayer, opponentCardPosX, opponentCardPosY ) ;
				
				--DEBUG_OUTPUT( "opponentCardSuitElement: " .. opponentCardSuitElement ) ;
				--opponentCardSuitElement = safeDeleteScriptElement( opponentCardSuitElement ) ;
				--opponentCardSuitElement = createScriptElement( ) ;
				--setScriptElementImageMapCell( opponentCardSuitElement, "card_" .. opponentSuit ) ;
				--placeScriptElementInLevel( opponentCardSuitElement, levelName, cardNumberLayer, opponentCardPosX, opponentCardPosY ) ;

				--DASSERT( opponentCardSuitElement == -1 ) ;
				opponentCardSuitElement = createScriptElement( ) ;
				setScriptElementImageMapCell( opponentCardSuitElement, "card_" .. opponentSuit ) ;
				placeScriptElementInLevel( opponentCardSuitElement, levelName, cardNumberLayer, opponentCardPosX, opponentCardPosY ) ;
				
				local releaseWidth = 800 ;
				local releaseHeight = 600 ;
											
				opponentCardLabel1 = addCardLabel( cardValues[ opponentCard ], 
					opponentCardPosX + cardLabelLeft,
					opponentCardPosY + cardLabelTop,
					"left",
					"top" ) ;
					
				opponentCardLabel2 = addCardLabel( cardValues[ opponentCard ], 
					releaseWidth - ( opponentCardPosX + cardLabelRight ),
					opponentCardPosY + cardLabelTop,
					"right",
					"top" ) ;
					
				opponentCardLabel3 = addCardLabel( cardValues[ opponentCard ], 
					releaseWidth - ( opponentCardPosX + cardLabelRight ),
					releaseHeight - ( opponentCardPosY + cardLabelBottom ),
					"right",
					"bottom" ) ;
					
				opponentCardLabel4 = addCardLabel( cardValues[ opponentCard ], 
					opponentCardPosX + cardLabelLeft,
					releaseHeight - ( opponentCardPosY + cardLabelBottom ),
					"left",
					"bottom" ) ;
							
				opponentCardVisible = true ;
				timer = 0.0 ;
			end
		elseif ( not playerCardVisible ) then
			if ( timer >= timeBeforeSecondReveal ) then
				if ( not playedPlayerFlip ) then
					playSound( "flipcard", 1.0 ) ;
					playedPlayerFlip = true ;
				end
				
				playerCardElement = safeDeleteScriptElement( playerCardElement ) ;
				
				playerCardElement = createScriptElement( ) ;
				setScriptElementImageMapCell( playerCardElement, "card_front" ) ;
				placeScriptElementInLevel( playerCardElement, levelName, cardLayer, playerCardPosX, playerCardPosY ) ;
						
				playerCardSuitElement = createScriptElement( ) ;
				setScriptElementImageMapCell( playerCardSuitElement, "card_" .. playerSuit ) ;
				placeScriptElementInLevel( playerCardSuitElement, levelName, cardNumberLayer, playerCardPosX, playerCardPosY ) ;
							
				local releaseWidth = 800 ;
				local releaseHeight = 600 ;
											
				playerCardLabel1 = addCardLabel( cardValues[ playerCard ], 
					playerCardPosX + cardLabelLeft,
					playerCardPosY + cardLabelTop,
					"left",
					"top" ) ;
					
				playerCardLabel2 = addCardLabel( cardValues[ playerCard ], 
					releaseWidth - ( playerCardPosX + cardLabelRight ),
					playerCardPosY + cardLabelTop,
					"right",
					"top" ) ;
					
				playerCardLabel3 = addCardLabel( cardValues[ playerCard ], 
					releaseWidth - ( playerCardPosX + cardLabelRight ),
					releaseHeight - ( playerCardPosY + cardLabelBottom ),
					"right",
					"bottom" ) ;
					
				playerCardLabel4 = addCardLabel( cardValues[ playerCard ], 
					playerCardPosX + cardLabelLeft,
					releaseHeight - ( playerCardPosY + cardLabelBottom ),
					"left",
					"bottom" ) ;
							
				playerCardVisible = true ;
				timer = 0.0 ;		
			end
		elseif ( not winTextVisible ) then
			if ( timer >= timeBeforeWinText ) then
				if ( not playedWinSound ) then
					if ( opponentCard > playerCard ) then
						playSound( "gamefailure", 1.0 ) ;
					elseif ( opponentCard < playerCard ) then
						playSound( "gamevictory", 1.0 ) ;
					end
					playedWinSound = true ;
				end		
				
				winTextLabel = addLabel( ) ;
				
				setLabelText( winTextLabel, winString ) ;
				setLabelFont( winTextLabel, "title_font" ) ;
				setLabelColour( winTextLabel, gLightTextR, gLightTextG, gLightTextB, gLightTextA ) ;
				setLabelShadow( winTextLabel, true, 2, 2 ) ;
				setLabelShadowColour( winTextLabel, gLightTextShadowR, gLightTextShadowG, gLightTextShadowB, gLightTextShadowA ) ;
				setWindowAlign( winTextLabel, "center", "top" ) ;
				setWindowPos( winTextLabel, 0, 450 ) ;
								
				winTextVisible = true ;
				timer = 0.0 ;
			end
		else
			if ( timer >= timeBeforeExit ) then
				playing = false ;
			end
		end
		
		coroutine.yield( ) ;
	end
	
	highCardTitle = safeRemoveWindow( highCardTitle ) ;
	winTextLabel = safeRemoveWindow( winTextLabel ) ;
	opponentCardTitle = safeRemoveWindow( opponentCardTitle ) ;
	playerCardTitle = safeRemoveWindow( playerCardTitle ) ;
	opponentCardLabel1 = safeRemoveWindow( opponentCardLabel1 ) ;
	opponentCardLabel2 = safeRemoveWindow( opponentCardLabel2 ) ;
	opponentCardLabel3 = safeRemoveWindow( opponentCardLabel3 ) ;
	opponentCardLabel4 = safeRemoveWindow( opponentCardLabel4 ) ;
	playerCardLabel1 = safeRemoveWindow( playerCardLabel1 ) ;
	playerCardLabel2 = safeRemoveWindow( playerCardLabel2 ) ;
	playerCardLabel3 = safeRemoveWindow( playerCardLabel3 ) ;
	playerCardLabel4 = safeRemoveWindow( playerCardLabel4 ) ;
	
	stackElement = safeDeleteScriptElement( stackElement ) ;
	opponentCardElement = safeDeleteScriptElement( opponentCardElement ) ;
	playerCardElement = safeDeleteScriptElement( playerCardElement ) ;
	opponentCardSuitElement = safeDeleteScriptElement( opponentCardSuitElement ) ;
	playerCardSuitElement = safeDeleteScriptElement( playerCardSuitElement ) ;
	
	hideChunkedBackground( ) ;
	
	exitMiniGame( ) ;
end

function addCardLabel( labelString, posX, posY, halign, valign )
	local id = addLabel( ) ;
	--setLabelText( id, "<font size=\"24\">" .. labelString .. "</font>" ) ;
	setLabelText( id, labelString ) ;
	setLabelFont( id, "title_font" ) ;
	setWindowAlign( id, halign, valign ) ;
	setWindowPos( id, posX, posY ) ;
	
	return id ;
end

-- Dice
function runDiceGame( )
	showChunkedBackground( "game_table" ) ;

	gPlayingDice = true ;

	local diceMax = 6 ;
		
	local diceTitle = addLabel( ) ;
	setLabelText( diceTitle, getString( "dice" ) ) ;
	setLabelFont( diceTitle, "title_font" ) ;
	setLabelColour( diceTitle, gLightTextR, gLightTextG, gLightTextB, gLightTextA ) ;
	setLabelShadow( diceTitle, true, 2, 2 ) ;
	setLabelShadowColour( diceTitle, gLightTextShadowR, gLightTextShadowG, gLightTextShadowB, gLightTextShadowA ) ;
	setWindowAlign( diceTitle, "center", "top" ) ;
	setWindowPos( diceTitle, 0, 80 ) ;
	
	local rollTitleY = 200 ;
	local opponentRollTitle = addLabel( ) ;
	setLabelText( opponentRollTitle, getString( "opponentsroll" ) ) ;
	setLabelColour( opponentRollTitle, gLightTextR, gLightTextG, gLightTextB, gLightTextA ) ;
	setLabelShadow( opponentRollTitle, true, 1, 1 ) ;
	setLabelShadowColour( opponentRollTitle, gLightTextShadowR, gLightTextShadowG, gLightTextShadowB, gLightTextShadowA ) ;
	setWindowAlign( opponentRollTitle, "left", "top" ) ;
	setWindowPos( opponentRollTitle, 150, rollTitleY ) ;
	
	local playerRollTitle = addLabel( ) ;
	setLabelText( playerRollTitle, getString( "yourroll" ) ) ;
	setLabelColour( playerRollTitle, gLightTextR, gLightTextG, gLightTextB, gLightTextA ) ;
	setLabelShadow( playerRollTitle, true, 1, 1 ) ;
	setLabelShadowColour( playerRollTitle, gLightTextShadowR, gLightTextShadowG, gLightTextShadowB, gLightTextShadowA ) ;
	setWindowAlign( playerRollTitle, "left", "top" ) ;
	setWindowPos( playerRollTitle, 550, rollTitleY ) ;
	
	
	local winTextLabel = -1 ;
	
	local screenWidth = 800 ;

	local opponentDiceBoxInitX = -100 ;
	local opponentDiceBoxInitY = 350 ;
	local opponentDiceBoxFinalX = 200 ;
	local opponentDiceBoxFinalY = 350 ;
	local opponentDiceBoxX = opponentDiceBoxInitX ;
	local opponentDiceBoxY = opponentDiceBoxInitY ;
	local opponentDie1X = opponentDiceBoxFinalX - 30 ;
	local opponentDie1Y = opponentDiceBoxFinalY - 40 ;	
	local opponentDie2X = opponentDiceBoxFinalX + 40 ;
	local opponentDie2Y = opponentDiceBoxFinalY + 10 ;
	
	local playerDiceBoxInitX = screenWidth + 100 ;
	local playerDiceBoxInitY = 350 ;
	local playerDiceBoxFinalX = 600 ;
	local playerDiceBoxFinalY = 350 ;
	local playerDiceBoxX = playerDiceBoxInitX ;
	local playerDiceBoxY = playerDiceBoxInitY ;
	local playerDie1X = playerDiceBoxFinalX - 10 ;
	local playerDie1Y = playerDiceBoxFinalY + 40 ;	
	local playerDie2X = playerDiceBoxFinalX + 30 ;
	local playerDie2Y = playerDiceBoxFinalY - 20 ;

	local diceLayer = 3 ;
	local diceBoxLayer = 4 ;
	local levelName = "dicegame" ;
	local opponentDiceBoxElement = -1 ;
	local opponentDie1Element = -1 ;
	local opponentDie2Element = -1 ;
	local playerDiceBoxElement = -1 ;
	local playerDie1Element = -1 ;
	local playerDie2Element = -1 ;
	
	local timeElapsed = 0.0 ;
	local timer = 0.0 ;

	local opponentDie1Angle = randomInt( 0, 360 ) ;
	local opponentDie2Angle = randomInt( 0, 360 ) ;
	local playerDie1Angle = randomInt( 0, 360 ) ;
	local playerDie2Angle = randomInt( 0, 360 ) ;

	local opponentDie1 = randomInt( 1, diceMax + 1 ) ;
	local opponentDie2 = randomInt( 1, diceMax + 1 ) ;
	local playerDie1 = randomInt( 1, diceMax + 1 ) ;
	local playerDie2 = randomInt( 1, diceMax + 1 ) ;

	if ( gCardinalDiceCheat ) then
		opponentDie1 = diceMax ;
		opponentDie2 = diceMax ;
		playerDie1 = 1 ;
		playerDie2 = 1 ;
	end

	if ( gBetAmount == -1 ) then
		-- special bet against de Winter
		opponentDie1 = 1 ;
		opponentDie2 = 1 ;
		playerDie1 = 1 ;
		playerDie2 = 2 ;
	end
	
	local opponentTotal = opponentDie1 + opponentDie2 ;
	local playerTotal = playerDie1 + playerDie2 ;

	DEBUG_OUTPUT( "opponentDie1: " .. opponentDie1 ) ;
	DEBUG_OUTPUT( "opponentDie2: " .. opponentDie2 ) ;
	DEBUG_OUTPUT( "playerDie1: " .. playerDie1 ) ;
	DEBUG_OUTPUT( "playerDie2: " .. playerDie2 ) ;
	DEBUG_OUTPUT( "opponentTotal: " .. opponentTotal ) ;
	DEBUG_OUTPUT( "playerTotal: " .. playerTotal ) ;
		
	local timeBeforeOpponentBoxSlideIn = 1.0 ;
	local doneOpponentBoxSlideIn = false ;
	local timeBeforeOpponentBoxSlideOut = 0.5 ;
	local doneOpponentBoxSlideOut = false ;
	local timeBeforePlayerBoxSlideIn = 2.0 ;
	local donePlayerBoxSlideIn = false ;
	local timeBeforePlayerBoxSlideOut = 0.5 ;
	local donePlayerBoxSlideOut = false ;
	local timeBeforeWinText = 2.0 ;
	local winTextVisible = false ;
	local timeBeforeExit = 3.0 ;
	
	if ( gBetAmount == -1 ) then
		timeBeforePlayerBoxSlideIn = 1.0 ;
		timeBeforeOpponentBoxSlideIn = 3.0 ;
		timeBeforeOpponentBoxSlideOut = 3.0 ;
	end
	
	local slideSpeed = 500.0 ;		
	
	-- calc players win/loss first so that we can save it without player being able to quit
	local francs = "francs" ;
	if ( gBetAmount == 1 ) then
		francs = "franc" ;
	end
	
	local winString = "" ;
	if ( opponentTotal == playerTotal ) then
		winString = getString( "drawtie" ) ;
	elseif ( opponentTotal > playerTotal ) then
		if ( gBetAmount == 1 ) then
			winString = getString( "losesingular", gBetAmount ) ;	
		else
			winString = getString( "loseplural", gBetAmount ) ;	
		end
		
		if ( gBetAmount > 0 ) then
			removeMoney( gBetAmount ) ;
		end
	else
		if ( gBetAmount == 1 ) then
			winString = getString( "winsingular", gBetAmount ) ;	
		else
			winString = getString( "winplural", gBetAmount ) ;	
		end
		
		if ( gBetAmount > 0 ) then
			addMoney( gBetAmount ) ;
		end
	end
	
	if ( gBetAmount == -1 ) then
		winString = getString( "beatdewinter" ) ;
	end
	
	local opponentFirst = true ;
	if ( gBetAmount == -1 ) then
		-- dart rolls a 3 so he should go first for more tension
		opponentFirst = false ;
	end
	
	local opponentSlideIn = false ;
	local opponentSlideOut = false ;
	local playerSlideIn = false ;
	local playerSlideOut = false ;
	
	if ( opponentFirst ) then
		opponentSlideIn = true ;
	else
		playerSlideIn = true ;
	end
	
	local playedOpponentShake = false ;
	local playedOpponentRoll = false ;
	local playedPlayerShake = false ;
	local playedPlayerRoll = false ;
	local playedWinSound = false ;
	
	local playing = true ;
	while ( playing ) do
	
		timeElapsed = timeElapsed + getDeltaT( ) ;
		timer = timer + getDeltaT( ) ;
		--DEBUG_OUTPUT( "Time Elapsed: " .. timeElapsed ) ;
		
		
		if ( opponentSlideIn ) then
			if ( timer >= timeBeforeOpponentBoxSlideIn ) then
				-- doing first slide
				if ( not playedOpponentShake ) then
					playSound( "diceshake", 1.0 ) ;
					playedOpponentShake = true ;
				end
				
				if ( opponentDiceBoxElement == -1 ) then
					opponentDiceBoxElement = createScriptElement( ) ;
					setScriptElementImageMapCell( opponentDiceBoxElement, "dicebox" ) ;
					placeScriptElementInLevel( opponentDiceBoxElement, levelName, diceBoxLayer, opponentDiceBoxX, opponentDiceBoxY ) ;
				else
					opponentDiceBoxX = opponentDiceBoxX + slideSpeed * getDeltaT( ) ;
					if ( opponentDiceBoxX >= opponentDiceBoxFinalX ) then
						opponentDiceBoxX = opponentDiceBoxFinalX ;
						
						doneOpponentBoxSlideIn = true ;
						opponentSlideIn = false ;
						opponentSlideOut = true ;
						timer = 0.0 ;
					end
					
					placeScriptElement( opponentDiceBoxElement, opponentDiceBoxX, opponentDiceBoxY ) ;
				end
			end
		elseif ( opponentSlideOut ) then
			-- box slide in is done
			if ( not playedOpponentRoll ) then
				playSound( "diceroll", 1.0 ) ;
				playedOpponentRoll = true ;
			end
			
			if ( timer >= timeBeforeOpponentBoxSlideOut ) then
				if ( opponentDie1Element == -1 ) then
					opponentDie1Element = createScriptElement( ) ;
					setScriptElementImageMapCell( opponentDie1Element, "dice_" .. opponentDie1 ) ;
					setScriptElementAngle( opponentDie1Element, opponentDie1Angle ) ;
					placeScriptElementInLevel( opponentDie1Element, levelName, diceLayer, opponentDie1X, opponentDie1Y ) ;
				
					opponentDie2Element = createScriptElement( ) ;
					setScriptElementImageMapCell( opponentDie2Element, "dice_" .. opponentDie2 ) ;
					setScriptElementAngle( opponentDie2Element, opponentDie2Angle ) ;
					placeScriptElementInLevel( opponentDie2Element, levelName, diceLayer, opponentDie2X, opponentDie2Y ) ;
				else
					opponentDiceBoxX = opponentDiceBoxX - slideSpeed * getDeltaT( ) ;
					if ( opponentDiceBoxX <= opponentDiceBoxInitX ) then
						opponentDiceBoxX = opponentDiceBoxInitX ;
						
						doneOpponentBoxSlideOut = true ;
						opponentSlideOut = false ;
						if ( opponentFirst ) then
							playerSlideIn = true ;
						end
						timer = 0.0 ;
					end
					
					placeScriptElement( opponentDiceBoxElement, opponentDiceBoxX, opponentDiceBoxY ) ;
				end
			end
		elseif ( playerSlideIn ) then
			if ( timer >= timeBeforePlayerBoxSlideIn ) then
				-- doing first slide
				if ( not playedPlayerShake ) then
					playSound( "diceshake", 1.0 ) ;
					playedPlayerShake = true ;
				end
				
				if ( playerDiceBoxElement == -1 ) then
					playerDiceBoxElement = createScriptElement( ) ;
					setScriptElementImageMapCell( playerDiceBoxElement, "dicebox" ) ;
					placeScriptElementInLevel( playerDiceBoxElement, levelName, diceBoxLayer, playerDiceBoxX, playerDiceBoxY ) ;
				else
					playerDiceBoxX = playerDiceBoxX - slideSpeed * getDeltaT( ) ;
					if ( playerDiceBoxX <= playerDiceBoxFinalX ) then
						playerDiceBoxX = playerDiceBoxFinalX ;
						
						donePlayerBoxSlideIn = true ;
						playerSlideIn = false ;
						playerSlideOut = true ;
						timer = 0.0 ;
					end
					
					placeScriptElement( playerDiceBoxElement, playerDiceBoxX, playerDiceBoxY ) ;
				end
			end
		elseif ( playerSlideOut ) then
			-- box slide in is done
			if ( not playedPlayerRoll ) then
				playSound( "diceroll", 1.0 ) ;
				playedPlayerRoll = true ;
			end
			
			if ( timer >= timeBeforePlayerBoxSlideOut ) then
				if ( playerDie1Element == -1 ) then
					playerDie1Element = createScriptElement( ) ;
					setScriptElementImageMapCell( playerDie1Element, "dice_" .. playerDie1 ) ;
					setScriptElementAngle( playerDie1Element, playerDie1Angle ) ;
					placeScriptElementInLevel( playerDie1Element, levelName, diceLayer, playerDie1X, playerDie1Y ) ;
				
					playerDie2Element = createScriptElement( ) ;
					setScriptElementImageMapCell( playerDie2Element, "dice_" .. playerDie2 ) ;
					setScriptElementAngle( playerDie2Element, playerDie2Angle ) ;
					placeScriptElementInLevel( playerDie2Element, levelName, diceLayer, playerDie2X, playerDie2Y ) ;
				else
					playerDiceBoxX = playerDiceBoxX + slideSpeed * getDeltaT( ) ;
					if ( playerDiceBoxX >= playerDiceBoxInitX ) then
						playerDiceBoxX = playerDiceBoxInitX ;
						
						donePlayerBoxSlideOut = true ;
						playerSlideOut = false ;
						if ( not opponentFirst ) then
							opponentSlideIn = true ;
						end
						timer = 0.0 ;
					end
					
					placeScriptElement( playerDiceBoxElement, playerDiceBoxX, playerDiceBoxY ) ;
				end
			end
		elseif ( not winTextVisible ) then
			if ( timer >= timeBeforeWinText ) then
				if ( not playedWinSound ) then
					if ( opponentTotal > playerTotal ) then
						playSound( "gamefailure", 1.0 ) ;
					elseif ( opponentTotal < playerTotal ) then
						if ( gBetAmount == -1 ) then
							-- horse
							playSound( "neigh", 1.0 ) ;
						else
							playSound( "gamevictory", 1.0 ) ;
						end
					end
					playedWinSound = true ;
				end
				
				winTextLabel = addLabel( ) ;
				
				setLabelText( winTextLabel, winString ) ;
				setLabelFont( winTextLabel, "title_font" ) ;
				setLabelColour( winTextLabel, gLightTextR, gLightTextG, gLightTextB, gLightTextA ) ;
				setLabelShadow( winTextLabel, true, 2, 2 ) ;
				setLabelShadowColour( winTextLabel, gLightTextShadowR, gLightTextShadowG, gLightTextShadowB, gLightTextShadowA ) ;
				setWindowAlign( winTextLabel, "center", "top" ) ;
				setWindowPos( winTextLabel, 0, 450 ) ;
								
				winTextVisible = true ;
				timer = 0.0 ;
			end
		else
			if ( timer >= timeBeforeExit ) then
				playing = false ;
			end
		end
		
		coroutine.yield( ) ;
	end
	
	
	diceTitle = safeRemoveWindow( diceTitle ) ;
	opponentRollTitle = safeRemoveWindow( opponentRollTitle ) ;
	playerRollTitle = safeRemoveWindow( playerRollTitle ) ;
	winTextLabel = safeRemoveWindow( winTextLabel ) ;
	
	opponentDiceBoxElement = safeDeleteScriptElement( opponentDiceBoxElement ) ;
	opponentDie1Element = safeDeleteScriptElement( opponentDie1Element ) ;
	opponentDie2Element = safeDeleteScriptElement( opponentDie2Element ) ;
	playerDiceBoxElement = safeDeleteScriptElement( playerDiceBoxElement ) ;
	playerDie1Element = safeDeleteScriptElement( playerDie1Element ) ;
	playerDie2Element = safeDeleteScriptElement( playerDie2Element ) ;
	
	gPlayingDice = false ;
	
	hideChunkedBackground( ) ;
	
	exitMiniGame( ) ;
end

-- Tennis
function dressTennisPlayer( name, level )
	clearCharacterPieces( name ) ;
	local skinR = 0.9 ;
	local skinG = 0.9 ;
	local skinB = 0.9 ;
	local accentR = 1.0 ;
	local accentG = 0.1 ;
	local accentB = 0.1 ;
	local clothesR = 1.0 ;
	local clothesG = 1.0 ;
	local clothesB = 1.0 ;
	
	if ( level <= 5 ) then
		local intensity = 0.5 + level / 10 ;
		accentR = 1.0 * intensity ;
		accentG = 1.0 * intensity ;
		accentB = 0.0 ;
	else
		local intensity = 0.5 + ( level - 5 ) / 10 ;
		accentR = 1.0 * intensity ;
		accentG = 0.2 * intensity ;
		accentB = 0.2 * intensity ;
	end
	
	if ( level == 1 ) then
		clothesR = 0.7 ;
		clothesG = 0.5 ;
		clothesB = 0.3 ;
	elseif ( level == 2 ) then
		clothesR = 0.9 ;
		clothesG = 0.6 ;
		clothesB = 0.4 ;
	elseif ( level == 3 ) then
		clothesR = 0.9 ;
		clothesG = 0.7 ;
		clothesB = 0.6 ;
	elseif ( level == 4 ) then
		clothesR = 1.0 ;
		clothesG = 0.8 ;
		clothesB = 0.6 ;
	elseif ( level == 5 ) then
		clothesR = 1.0 ;
		clothesG = 0.9 ;
		clothesB = 0.8 ;
	elseif ( level == 6 ) then
		clothesR = 1.0 ;
		clothesG = 1.0 ;
		clothesB = 1.0 ;
	elseif ( level == 7 ) then
		clothesR = 0.6 ;
		clothesG = 0.6 ;
		clothesB = 0.6 ;
	elseif ( level == 8 ) then
		clothesR = 0.5 ;
		clothesG = 0.5 ;
		clothesB = 0.5 ;
	elseif ( level == 9 ) then
		clothesR = 0.4 ;
		clothesG = 0.4 ;
		clothesB = 0.4 ;
	elseif ( level == 10 ) then
		clothesR = 0.2 ;
		clothesG = 0.2 ;
		clothesB = 0.2 ;
	end
	
	addCharacterPiece( name, "nm_head", skinR, skinG, skinB ) ;
	addCharacterPiece( name, "nm_hand_left", skinR, skinG, skinB ) ;
	addCharacterPiece( name, "nm_hand_right", skinR, skinG, skinB ) ;
	
	if ( level > 2 ) then
		addCharacterPiece( name, "feather_hat_small", accentR, accentG, accentB ) ;
		addCharacterPiece( name, "beret", clothesR, clothesG, clothesB ) ;
	end
		
	if ( level < 5 ) then
		addCharacterPiece( name, "boot_plain_right", 0.51, 0.51, 0.51 ) ;
		addCharacterPiece( name, "boot_plain_left", 0.51, 0.51, 0.51 ) ;
	else
		addCharacterPiece( name, "boot_sock_right" ) ;
		addCharacterPiece( name, "boot_sock_left" ) ;
	end
	
	if ( level > 3 ) then 
		addCharacterPiece( name, "sashbelt", 0.58, 0.49, 0.38 ) ;
	end
	addCharacterPiece( name, "pant_musketeer_right", clothesR, clothesG, clothesB ) ;
	addCharacterPiece( name, "pant_musketeer_left", clothesR, clothesG, clothesB ) ;
	addCharacterPiece( name, "doublet_plain_torso", clothesR, clothesG, clothesB ) ;
	addCharacterPiece( name, "shirt_plain_arm_right", clothesR, clothesG, clothesB ) ;
	addCharacterPiece( name, "shirt_plain_arm_left", clothesR, clothesG, clothesB ) ;
	
	randomDressHair( name, 0.0, level * 2 + 3 ) ;
end

function runTennisGame( )
	--DEBUG_OUTPUT( "runTennisGame" ) ;
	gPlayingTennis = true ;
	
	lockPlayerInPlace( ) ;
	local fadeTime = 0.5 ;
	
	local topWall = -150 ;
	local bottomWall = 290 ; -- 250
	local net = 50 ;
	local leftWall = 330 ;
	local rightWall = 750 ; 
	local centerX = ( leftWall + rightWall ) / 2 ;

	-- fade to black	
	setFadeColour( 0.0, 0.0, 0.0, 1.0 ) ;
	fadeToColour( fadeTime ) ;
	while ( isFading( ) ) do
		coroutine.yield( ) ;
	end
	
	local oldLevel = getActiveLevel( ) ;
	DEBUG_OUTPUT( "oldLevel: " .. oldLevel ) ;
	stopCameraFollowCharacter( ) ;

	local levelName = "tennis" ;
	setActiveLevel( levelName ) ;
	setCameraTo( 500, 60 ) ;
	
	setMarkersVisible( false ) ;
	
	-- stop player from leaving tennis area
	local triggerArea1 = addTriggerArea( "", "", true, "tennis", { 180, 160, 330, 305 } ) ;
	local triggerArea2 = addTriggerArea( "", "", true, "tennis", { 300, -180, 770, 45 } ) ;

	local playerBottom = "dartagnan" ;
	
	local playerTop = "" ;
	if ( gTennisOpponentNum == -1 ) then
		-- special case
		playerTop = "porthos" ;
		setWalkSpeed( playerTop, 110 ) ;
	else
		local opponentDisplayIndex = gTennisOpponentNum ;
		if ( opponentDisplayIndex > 10 ) then
			-- already beaten everyone
			opponentDisplayIndex = 10 ;
		end
		
		-- create temp tennis player
		playerTop = createTempCharacter( getString( "tennisopp" .. opponentDisplayIndex ), "tennis", 0, 0 ) ;
		
		-- set speed
		local speed = 60 + 9 * opponentDisplayIndex ; -- was 12 *
		setWalkSpeed( playerTop, speed ) ;
		
		-- dress up
		dressTennisPlayer( playerTop, opponentDisplayIndex ) ;
	end
	
	addCharacterPiece( playerBottom, "tennis_racket" ) ;
	addCharacterPiece( playerTop, "tennis_racket" ) ;
	
	local courtCenterX = ( leftWall + rightWall ) / 2 ;
	local courtCenterY = ( topWall + bottomWall ) / 2 ;
	local startPosOffsetFromCenter = 150 ;
	placeCharacter( playerBottom, levelName, courtCenterX, courtCenterY + startPosOffsetFromCenter ) ;
	placeCharacter( playerTop, levelName, courtCenterX, courtCenterY - startPosOffsetFromCenter ) ;
	
	-- fade from black
	setFadeColour( 0.0, 0.0, 0.0, 1.0 ) ;
	fadeFromColour( fadeTime ) ;
	while ( isFading( ) ) do
		coroutine.yield( ) ;
	end
	
	local topScore = 0 ;
	local bottomScore = 0 ;
	
	-- create score board
	local topScoreLabel = addLabel( ) ;
	setLabelFont( topScoreLabel, "title_font" ) ;
	setLabelColour( topScoreLabel, gLightTextR, gLightTextG, gLightTextB, gLightTextA ) ;
	setLabelShadow( topScoreLabel, true, 2, 2 ) ;
	setLabelShadowColour( topScoreLabel, gLightTextShadowR, gLightTextShadowG, gLightTextShadowB, gLightTextShadowA ) ;
	setWindowAlign( topScoreLabel, "left", "top" ) ;
	setWindowPos( topScoreLabel, 700, 40 ) ;
	
	local bottomScoreLabel = addLabel( ) ;
	setLabelFont( bottomScoreLabel, "title_font" ) ;
	setLabelColour( bottomScoreLabel, gLightTextR, gLightTextG, gLightTextB, gLightTextA ) ;
	setLabelShadow( bottomScoreLabel, true, 2, 2 ) ;
	setLabelShadowColour( bottomScoreLabel, gLightTextShadowR, gLightTextShadowG, gLightTextShadowB, gLightTextShadowA ) ;
	setWindowAlign( bottomScoreLabel, "left", "top" ) ;
	setWindowPos( bottomScoreLabel, 700, 500 ) ;
	
	setScore( topScoreLabel, bottomScoreLabel, topScore, bottomScore ) ;
	
	-- create ball
	local ballElement = createScriptElement( ) ;
	setScriptElementImageMapCell( ballElement, "tennis_ball" ) ;
	placeScriptElementInLevel( ballElement, levelName, CHARACTER_LAYER_NUM, 0, 0 ) ;
		
	local ballShadowElement = createScriptElement( ) ;
	setScriptElementImageMapCell( ballShadowElement, "tennis_ball_shadow" ) ;
	placeScriptElementInLevel( ballShadowElement, levelName, 1, 0, 0 ) ;
	
	local ballHeight = 40 ;
	local ballVelZ = 0 ;
	local ballAccelZ = -160 ;
	local ballPosX = 0 ;
	local ballPosY = 0 ;
	local ballVelX = 0 ;
	local ballVelY = 0 ;
	setBallPos( ballElement, ballShadowElement, ballHeight, ballPosX, ballPosY ) ;


	unlockPlayerInPlace( ) ;

	local STATE_SERVING = 0 ;
	local STATE_PLAYING = 1 ;
	local state = STATE_SERVING ;
	local bottomServing = true ;
	
	local timeSinceStartServe = 0.0 ;
	local SERVE_TIME = 3.0 ;
	
	local ballGoingToTop = false ;
	local ballSpeed = 100.0 ;
	local baseBallSpeed = 100.0 ;
	local speedMult = 1.0 ;

	local aboveNetLast = false ;
	
	local tennisHitSound = 1.0 ;

	local playing = true ;
	while ( playing ) do

		if ( state == STATE_SERVING ) then
			--DEBUG_OUTPUT( "STATE_SERVING" ) ;
			timeSinceStartServe = timeSinceStartServe + getDeltaT( ) ;
			if ( timeSinceStartServe >= SERVE_TIME ) then
				state = STATE_PLAYING ;
				
				playPositionSound( "tennishit", tennisHitSound, "tennis", ballPosX, ballPosY ) ;
					
				ballSpeed = 100.0 ;
				ballVelZ = 10 ;
				if ( bottomServing ) then
					ballVelX, ballVelY = polarToCard( ballSpeed, -90 ) ;
					aboveNetLast = false ;
				else
					ballVelX, ballVelY = polarToCard( ballSpeed, 90 ) ;
					aboveNetLast = true ;
				end
			end
		
			-- set ball pos
			ballHeight = 40 ;
			local servingPlayer = playerBottom ;
			if ( not bottomServing ) then
				servingPlayer = playerTop ;
			end
			local BALL_OFFSET_FROM_PLAYER_X = -10 ;
			local BALL_OFFSET_FROM_PLAYER_Y = 10 ;
			ballPosX, ballPosY = getCharacterPos( servingPlayer  ) ;
			
			if ( bottomServing ) then
				ballPosX = ballPosX - BALL_OFFSET_FROM_PLAYER_X ;
				ballPosY = ballPosY - BALL_OFFSET_FROM_PLAYER_Y ;
			else
				ballPosX = ballPosX + BALL_OFFSET_FROM_PLAYER_X ;
				ballPosY = ballPosY + BALL_OFFSET_FROM_PLAYER_Y ;
			end
			
			if ( bottomServing ) then
				-- match top player to bottom server pos
				local playerTopX = 0 ;
				local playerTopY = 0 ;
				playerTopX, playerTopY = getCharacterPos( playerTop ) ;
				local desiredPosX = ballPosX ;
				desiredPosX = notTooCloseToWall( leftWall, rightWall, desiredPosX ) ;
				
				local minMoveDist = 50 ; -- don't bother moving if distance less than this			
				local distToDesired = playerTopX - desiredPosX ;
				if ( math.abs( distToDesired ) > minMoveDist ) then
					pathfindToPos( playerTop, desiredPosX, courtCenterY - startPosOffsetFromCenter ) ;
				end
			end
			
			setBallPos( ballElement, ballShadowElement, ballHeight,	ballPosX, ballPosY ) ;
		elseif ( state == STATE_PLAYING ) then
			--DEBUG_OUTPUT( "STATE_PLAYING" ) ;
			
			local speedIncreaseRate = 0.1 ; -- 0.05
			speedMult = speedMult + getDeltaT( ) * speedIncreaseRate ;
			local maxSpeedMult = 4.0 ;
			if ( speedMult > maxSpeedMult ) then
				speedMult = maxSpeedMult ;
			end
			ballSpeed = baseBallSpeed * speedMult ;
						
			local angleVariation = 45.0 ;
			
			if ( ballVelY < 0 ) then
				if ( ballIntersectWithCharacter( playerTop, ballPosX, ballPosY, false ) ) then
					playPositionSound( "tennishit", tennisHitSound, "tennis", ballPosX, ballPosY ) ;
				
					local maxAngleAtDist = 20.0 ;
					local maxAngle = 45.0 ;
					local charX = 0.0 ;
					local charY = 0.0 ;
					charX, charY = getCharacterPos( playerTop ) ;
					
					local dist = ballPosX - charX ;
					local fractionOfMax = dist / maxAngleAtDist ;
					if ( fractionOfMax > 1.0 ) then
						fractionOfMax = 1.0 ;
					elseif ( fractionOfMax < -1.0 ) then
						fractionOfMax = -1.0 ;
					end
					
					local angleMod = fractionOfMax * maxAngle ;
										
					ballVelX, ballVelY = polarToCard( ballSpeed, 90 - angleMod ) ;
					--if ( ballHeight < 60 ) then
					--	ballVelZ = 100 ;
					--else
					--	ballVelZ = 0 ;
					--end
				end
			else
				if ( ballIntersectWithCharacter( playerBottom, ballPosX, ballPosY, true ) ) then
					playPositionSound( "tennishit", tennisHitSound, "tennis", ballPosX, ballPosY ) ;
					
					local maxAngleAtDist = 20.0 ;
					local maxAngle = 45.0 ;
					local charX = 0.0 ;
					local charY = 0.0 ;
					charX, charY = getCharacterPos( playerBottom ) ;

					local dist = ballPosX - charX ;
					local fractionOfMax = dist / maxAngleAtDist ;
					if ( fractionOfMax > 1.0 ) then
						fractionOfMax = 1.0 ;
					elseif ( fractionOfMax < -1.0 ) then
						fractionOfMax = -1.0 ;
					end
					
					local angleMod = fractionOfMax * maxAngle ;
										
					ballVelX, ballVelY = polarToCard( ballSpeed, -90 + angleMod ) ;
					--if ( ballHeight < 60 ) then
					--	ballVelZ = 100 ;
					--else
					--	ballVelZ = 0 ;
					--end
				end
			end
			
			ballVelZ = ballVelZ + ballAccelZ * speedMult *  getDeltaT( ) ;
			--local MAX_VEL_Z = -120 * speedMult ;
			--if ( ballVelZ < MAX_VEL_Z ) then
			--	ballVelZ = MAX_VEL_Z ;
			--end
			
			-- don't let ball go down until it has crossed the net
			if ( ballVelY < 0 ) then
				-- going to top
				if ( ballPosY > net ) then
					-- hasn't passed net yet
					if ( ballHeight < 50 ) then
						ballVelZ = 40 * speedMult ;
					else
						ballVelZ = 0 ;
					end
				else
					-- ball over net - make sure it is going down
					if ( not aboveNetLast ) then
						ballVelZ = -40 * speedMult ;
					end
					aboveNetLast = true ;
				end
			elseif ( ballVelY > 0 ) then
				-- going to bottom
				if ( ballPosY < net ) then
					-- hasn't passed net yet
					if ( ballHeight < 50 ) then
						ballVelZ = 40 * speedMult ;
					else
						ballVelZ = 0 ;
					end
				else
					-- ball over net - make sure it is going down
					if ( aboveNetLast ) then
						ballVelZ = -40 * speedMult ;
					end
					
					aboveNetLast = false ;
				end
			end
			
			ballHeight = ballHeight + ballVelZ * getDeltaT( ) ;
			if ( ballHeight <= 0 ) then
				playPositionSound( "tennisbounce", 1.0, "tennis", ballPosX, ballPosY ) ;
				
				ballHeight = 0 ;
				local dampenBounce = 0.75 ;
				ballVelZ = ( -ballVelZ ) * dampenBounce ;
			end
			
			ballPosX = ballPosX + ballVelX * getDeltaT( ) ;
			ballPosY = ballPosY + ballVelY * getDeltaT( ) ;
			
			-- bounce ball off side walls
			if ( ballVelX > 0 ) then
				-- bounce off right wall
				if ( ballPosX > rightWall ) then
					playPositionSound( "tennisbounce", 1.0, "tennis", ballPosX, ballPosY ) ;
					ballPosX = rightWall ;
					ballVelX = -ballVelX ;
				end
			elseif ( ballVelX < 0 ) then
				-- bounce off left wall
				if ( ballPosX < leftWall ) then
					playPositionSound( "tennisbounce", 1.0, "tennis", ballPosX, ballPosY ) ;
					ballPosX = leftWall ;
					ballVelX = -ballVelX ;
				end
			end
			
			-- test for win point condition
			local maxScore = 3 ; -- 5
			local nextServe = false ;
			if ( ballVelY > 0 ) then
				if ( ballPosY > bottomWall ) then
					--ballPosY = bottomWall ;
					--ballVelY = -ballVelY ;
					nextServe = true 
					topScore = topScore + 1 ;
					if ( topScore >= maxScore ) then
						playing = false ;
					end
				end
			elseif ( ballVelY < 0 ) then
				if ( ballPosY < topWall ) then
					--ballPosY = topWall ;
					--ballVelY = -ballVelY ;
					nextServe = true 
					bottomScore = bottomScore + 1 ;
					if ( bottomScore >= maxScore ) then
						playing = false ;
					end
				end
			end
			
			if ( nextServe ) then
				state = STATE_SERVING ;
				bottomServing = not bottomServing ;
				timeSinceStartServe = 0.0 ;
				speedMult = 1.0 ;
				setScore( topScoreLabel, bottomScoreLabel, topScore, bottomScore ) ;
				
				doFanExclamation( ) ;
			end
			
			setBallPos( ballElement, ballShadowElement, ballHeight, ballPosX, ballPosY ) ;
			
			local playerTopX = 0.0 ;
			local playerTopY = 0.0 ;
			local desiredPosX = 0.0 ;
			local desireMove = 10.0 ;
			local desiredPosY = courtCenterY - startPosOffsetFromCenter ;
			playerTopX, playerTopY = getCharacterPos( playerTop ) ;
			if ( ballVelY < 0 ) then
				-- ball going towards top player
				desiredPosX = predictBallPosXAtY( ballPosX, ballPosY, ballVelX, ballVelY,
					leftWall, rightWall, desiredPosY ) ;
			else
				desiredPosX = centerX ;
			end
			--if ( playerTopX > ballPosX ) then
				-- walk to left
			--	desiredPosX = playerTopX - desireMove ;
			--elseif ( playerTopX < ballPosX ) then
				-- walk to right
			--	desiredPosX = playerTopX + desireMove ;
			--end
			
			-- make sure desired pos is not too close to wall
			desiredPosX = notTooCloseToWall( leftWall, rightWall, desiredPosX ) ;
			
			local minMoveDist = 5 ; -- don't bother moving if distance less than this			
			local distToDesired = playerTopX - desiredPosX ;
			if ( math.abs( distToDesired ) > minMoveDist ) then
				pathfindToPos( playerTop, desiredPosX, courtCenterY - startPosOffsetFromCenter ) ;
			end
		end
		
		if ( isCharacterIdle( playerTop ) ) then
			faceDirection( playerTop, 0.0, 10.0 ) ;
		end
		if ( isCharacterIdle( playerBottom ) ) then
			faceDirection( playerBottom, 0.0, -10.0 ) ;
		end
		
		coroutine.yield( ) ;
	end
	
	local victoryString = "" ;
	local playerWon = true ;
	if ( topScore > bottomScore ) then
		-- computer won
		playerWon = false ;
	else
		playerWon = true ;
		gWonTennis = true ;
	end

	if ( playerWon ) then
		victoryString = getString( "tenniswin" ) ;
	else
		victoryString = getString( "tennisotherwin", getCharacterDisplayName( playerTop ) ) ;
	end
	
	local victoryLabel = addLabel( ) ;
	setLabelFont( victoryLabel, "title_font" ) ;
	setLabelText( victoryLabel, victoryString ) ;
	setLabelColour( victoryLabel, gLightTextR, gLightTextG, gLightTextB, gLightTextA ) ;
	setLabelShadow( victoryLabel, true, 2, 2 ) ;
	setLabelShadowColour( victoryLabel, gLightTextShadowR, gLightTextShadowG, gLightTextShadowB, gLightTextShadowA ) ;
	setWindowAlign( victoryLabel, "center", "top" ) ;
	setWindowPos( victoryLabel, 0, 280 ) ;
	
	local victoryDisplayTime = 0.0 ;
	local victoryDisplayTotalTime = 5.0 ;
	while ( victoryDisplayTime <= victoryDisplayTotalTime ) do
		victoryDisplayTime = victoryDisplayTime + getDeltaT( ) ;
		--DEBUG_OUTPUT( "displayTime: " .. victoryDisplayTime ) ;
		coroutine.yield( ) ;
	end

	lockPlayerInPlace( ) ;
	
	-- fade to black	
	setFadeColour( 0.0, 0.0, 0.0, 1.0 ) ;
	fadeToColour( fadeTime ) ;
	while ( isFading( ) ) do
		coroutine.yield( ) ;
	end
	
	wait( 1.0 ) ; -- keep it black for a bit

	-- set everything back to normal
	removeCharacterPiece( playerBottom, "tennis_racket" ) ;
	removeCharacterPiece( playerTop, "tennis_racket" ) ;

	removeTriggerArea( triggerArea1 ) ;
	removeTriggerArea( triggerArea2 ) ;
	
	ballElement = safeDeleteScriptElement( ballElement ) ;
	ballShadowElement = safeDeleteScriptElement( ballShadowElement ) ;
	
	topScoreLabel = safeRemoveWindow( topScoreLabel ) ;
	bottomScoreLabel = safeRemoveWindow( bottomScoreLabel ) ;
	victoryLabel = safeRemoveWindow( victoryLabel ) ;
	
	if ( gTennisOpponentNum == -1 ) then
		-- porthos match
		
		-- reset porthos speed
		setWalkSpeed( playerTop, 60 ) ;
		
		-- put dartagnan off court, to be insulted by bernajoux
		placeCharacter( "dartagnan", "tennis", 74, 151 ) ;
		placeCharacter( "bernajoux", "tennis", 116, 151 ) ;
		
		-- put musketeers on court, discussing who will play next
		placeCharacter( "porthos", "tennis", 530, 122 ) ;
		placeCharacter( "athos", "tennis", 570, 122 ) ;
		placeCharacter( "aramis", "tennis", 550, 160 ) ;
		faceDirection( "porthos", 1, 0 ) ;
		faceDirection( "athos", -1, 0 ) ;
		faceDirection( "aramis", 0, -1 ) ;
	else
		-- normal match
		if ( playerWon ) then
			setInt( "tennisMatch", gTennisOpponentNum + 1 ) ;
		end
		deleteTempCharacter( playerTop ) ;
	end
	
	setMarkersVisible( true ) ;
	
	setActiveLevel( oldLevel ) ;
	setCameraFollowCharacter( "dartagnan", true ) ;
	moveCameraToDest( ) ;
		
	-- fade from black
	setFadeColour( 0.0, 0.0, 0.0, 1.0 ) ;
	fadeFromColour( fadeTime ) ;
	while ( isFading( ) ) do
		coroutine.yield( ) ;
	end
	
	unlockPlayerInPlace( ) ;
	
	gPlayingTennis = false ;
end

function setBallPos( ballElement, ballShadowElement, height, ballPosX, ballPosY )
	local ballOverShadowOffsetY = -5 ;
	local drawPointYExtraOffset = 15 ;
	placeScriptElement( ballElement, ballPosX, ballPosY - height + ballOverShadowOffsetY ) ;
	setScriptElementDrawPointY( ballElement, height + drawPointYExtraOffset ) ;
	placeScriptElement( ballShadowElement, ballPosX, ballPosY ) ;
end

function ballIntersectWithCharacter( character, ballPosX, ballPosY, bottom )
	local charX = 0 ;
	local charY = 0 ;
	charX, charY = getCharacterPos( character ) ;

	local INTERSECT_DIST_X = 30.0 ;
	local INTERSECT_DIST_Y = 20.0 ;
	
	local distX = charX - ballPosX ;
	local distY = 0.0 ;
	
	-- collision area is behine the character
	if ( bottom ) then
		distY = ( charY + 10 ) - ballPosY ;
	else
		distY = ( charY - 10 ) - ballPosY ;
	end	
	
	local INTERSECT_DIST_X = 30.0 ;
	local INTERSECT_DIST_Y = 20.0 ;
	if ( math.abs( distX ) < INTERSECT_DIST_X
		and math.abs( distY ) < INTERSECT_DIST_Y ) then
		return true ;
	else
		return false ;
	end
end

function predictBallPosXAtY( ballPosX, ballPosY, ballVelX, ballVelY,
				leftWall, rightWall, desiredPosY )
	--DEBUG_OUTPUT( "ballVelX: " .. ballVelX ) ;
	local EPSILON = 0.3 ;
	if ( math.abs( ballVelX ) <= EPSILON ) then
		-- moving essentially vertically
		return ballPosX ;
	end
	
	local slope = ballVelY / ballVelX ;
	--DEBUG_OUTPUT( "slope: " .. slope ) ;
	local yIntercept = ballPosY - slope * ballPosX ;
	--DEBUG_OUTPUT( "yIntercept: " .. yIntercept ) ;
	--DEBUG_OUTPUT( "desiredPosY: " .. desiredPosY ) ;
	local predictX = ( desiredPosY - yIntercept ) / slope ;
	--DEBUG_OUTPUT( "predictX: " .. predictX ) ;
	if ( predictX < leftWall or predictX > rightWall ) then
		-- need to bounce off a wall
		
		-- calc where ball hit the wall
		if ( predictX < leftWall ) then
			-- bounce off left wall
			ballPosX = leftWall ;	
		elseif ( predictX > rightWall ) then
			-- bounce off right wall
			ballPosX = rightWall ;	
		end
		ballPosY = slope * ballPosX + yIntercept ;
		
		-- calc new line equation after wall hit
		ballVelX = -ballVelX ;
		slope = ballVelY / ballVelX ;
		yIntercept = ballPosY - slope * ballPosX ;
		predictX = ( desiredPosY - yIntercept ) / slope ;
		
		--DEBUG_OUTPUT( "bounce slope: " .. slope ) ;
		--DEBUG_OUTPUT( "bounce yIntercept: " .. yIntercept ) ;
		--DEBUG_OUTPUT( "bounce predictX: " .. predictX ) ;
	end
	
	return predictX ;
end

function setScore( topScoreLabel, bottomScoreLabel, topScore, bottomScore )
	setLabelText( topScoreLabel, topScore ) ;
	setLabelText( bottomScoreLabel, bottomScore ) ;
end

function notTooCloseToWall( leftWall, rightWall, desiredPosX )
	local allowDistFromWall = 20 ;
	local minDesiPosX = leftWall + allowDistFromWall ;
	local maxDesiPosX = rightWall - allowDistFromWall ;
	if ( desiredPosX < minDesiPosX ) then
		desiredPosX = minDesiPosX ;
	elseif ( desiredPosX > maxDesiPosX ) then
		desiredPosX = maxDesiPosX ;
	end
	
	return desiredPosX ;
end

function doFanExclamation( )
	local randomSayingNum = randomInt( 1, 12 ) ; --math.random( 3 ) ;
	local randomPersonNum = randomInt( 1, 4 ) ; --math.random( 3 ) ;
	local randomPerson = "" ;
	local randomSaying = "" ;
	if ( randomPersonNum == 1 ) then
		randomPerson = "tennisfan1" ;
	elseif ( randomPersonNum == 2 ) then
		randomPerson = "tennisfan2" ;
	else
		randomPerson = "tennisfan3" ;
	end
	
	randomSaying = getString( "tennisrandom" .. randomSayingNum ) ;
	
	local bubInd = characterSay( randomPerson, false, 4, -1, true, randomSaying ) ;
	autoDeleteBubble( bubInd ) ;
end

-- Helper functions
function exitMiniGame( )
	setActiveLevel( gOldLevel ) ;
	setCameraFollowCharacter( "dartagnan", false ) ;
	removeMusic( gGameTempMusicID ) ;
end

function polarToCard( r, theta )
	local radianTheta = ( theta * math.pi ) / 180.0 ;
	local x = r * math.cos( radianTheta ) ;
	local y = r * math.sin( radianTheta ) ;
	
	return x, y ;
end

function safeRemoveWindow( windowID )
	if ( windowID >= 0 ) then
		removeWindow( windowID ) ;
	end
	
	return -1 ;
end

function safeDeleteScriptElement( elementID )
	if ( elementID >= 0 ) then
		deleteScriptElement( elementID ) ;
	end
	
	return -1 ;
end