REM
REM Emeritus Pong - A clone of the oldschool Pong game
REM Author: Mateusz Viste
REM License: GNU/GPL
REM
REM This program is free software: you can redistribute it and/or modify
REM it under the terms of the GNU General Public License as published by
REM the Free Software Foundation, either version 3 of the License, or
REM (at your option) any later version.
REM
REM This program is distributed in the hope that it will be useful,
REM but WITHOUT ANY WARRANTY; without even the implied warranty of
REM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
REM GNU General Public License for more details.
REM
REM You should have received a copy of the GNU General Public License
REM along with this program. If not, see <
http://www.gnu.org/licenses/>.
REM
#INCLUDE ONCE "vbcompat.bi" ' Needed for time handling
#IFNDEF __FB_DOS__ ' If not under DOS, include SDL libs
#INCLUDE ONCE "SDL/SDL.bi" ' Required for sound support (Linux/Windows)
#INCLUDE ONCE "SDL/SDL_mixer.bi" ' Required for sound support (Linux/Windows)
'#INCLUDE ONCE "SDL/SDL_net.bi" ' This program requires the SDL_net library
#ENDIF
#INCLUDE ONCE "checktsc.bi" ' CheckTSC()
DECLARE SUB FlushKeyb()
DECLARE SUB RefreshScreen()
DECLARE SUB DrawTable()
DECLARE SUB UpdateBallPosition()
DECLARE SUB ShowScore()
DECLARE SUB InitBall()
DECLARE SUB MainMenu()
DECLARE SUB QuitGame()
DECLARE SUB PlayGame()
DECLARE SUB PlaySound(SndType AS BYTE)
DECLARE SUB Setup()
DECLARE SUB Pause()
DECLARE SUB LoadConfig()
DECLARE SUB Sound(BYVAL freq AS INTEGER, dur AS UINTEGER)
DECLARE SUB CheckPlayersInputs()
DECLARE SUB UpdatePlayersPositions()
DECLARE FUNCTION GenRnd(BYVAL NewGen AS BYTE = 0) AS DOUBLE
DECLARE FUNCTION ReadCFG(CFGfile AS STRING, CFGField AS STRING) AS STRING
DECLARE FUNCTION RDTSC() AS ULONGINT ' Only used in DOS port
DECLARE FUNCTION uTIMER() AS ULONGINT
'DECLARE SUB InitNetworkSocket()
'DECLARE FUNCTION IPaddressToString(sdlIP AS UINT32) AS STRING
TYPE BallParams
Speed AS INTEGER
Angle AS INTEGER
X AS DOUBLE
Y AS DOUBLE
END TYPE
CONST Degr2Rad AS DOUBLE = 0.01745329251994330
CONST Mega AS INTEGER = 1000000
DIM SHARED AS UBYTE PalWidth, PalTick, ColorMode, SyncCounter, BallMissed, Score1, Score2, GameMode, Snd, FullScreen, SoundError, EscPressed
DIM SHARED AS INTEGER xRes, yRes, hBorder, vBorder, InitSpeed
DIM SHARED AS DOUBLE AiSpeed, CurPos, CurPos2
DIM SHARED AS ULONGINT LastRefresh, LastMissedBall, LastUpdate, Player1LastMove, Player2LastMove, AiLastDecision, LastClockRefresh, LastAiSpeedChange, LastKeybPolling
DIM SHARED AS BYTE Player1Cmd, Player2Cmd
DIM SHARED AS STRING LastKey, Delimiter
DIM SHARED AS BallParams Ball
DIM SHARED BigNumbers(0 TO 9, 1 TO 5, 1 TO 3) AS BYTE => {{{1,1,1}, {1,0,1}, {1,0,1}, {1,0,1}, {1,1,1}},_
{{0,0,1}, {0,1,1}, {0,0,1}, {0,0,1}, {0,0,1}},_
{{1,1,1}, {0,0,1}, {1,1,1}, {1,0,0}, {1,1,1}},_
{{1,1,1}, {0,0,1}, {1,1,1}, {0,0,1}, {1,1,1}},_
{{1,0,0}, {1,0,1}, {1,1,1}, {0,0,1}, {0,0,1}},_
{{1,1,1}, {1,0,0}, {1,1,1}, {0,0,1}, {1,1,1}},_
{{1,1,1}, {1,0,0}, {1,1,1}, {1,0,1}, {1,1,1}},_
{{1,1,1}, {0,0,1}, {0,0,1}, {0,0,1}, {0,0,1}},_
{{1,1,1}, {1,0,1}, {1,1,1}, {1,0,1}, {1,1,1}},_
{{1,1,1}, {1,0,1}, {1,1,1}, {0,0,1}, {1,1,1}}}
CONST AS STRING pVer = "0.92"
CONST AS STRING pDate = "2008-2012"
#IFNDEF __FB_DOS__
Delimiter = "/"
DIM SHARED SndFilePong AS Mix_Chunk POINTER
DIM SHARED SndFileBounce AS Mix_Chunk POINTER
DIM SHARED SndFileMissed AS Mix_Chunk POINTER
SDL_Init(SDL_INIT_AUDIO) ' SDL sound initialization
IF (Mix_OpenAudio(44100, AUDIO_S16, 1, 2048)) = -1 THEN
PRINT "Error: Unable to open the SDL audio mixer! SDL says: " & Mix_GetError()
SLEEP 1000, 1
SoundError = 1
END IF
SndFilePong = Mix_LoadWAV(EXEPATH & Delimiter & "pong.dat")
SndFileBounce = Mix_LoadWAV(EXEPATH & Delimiter & "bounce.dat")
SndFileMissed = Mix_LoadWAV(EXEPATH & Delimiter & "missed.dat")
' TYPE NetGameProperties
' State AS BYTE ' 1 (yes) or 0 (no)
' RemoteIP AS STRING ' Remote player's IP
' LocalMode AS STRING ' "server" or "client"
' RemotePktCounter AS UINTEGER
' LocalPktCounter AS UINTEGER
' LocalPort AS UINTEGER
' RemotePort AS UINTEGER
' END TYPE
' DIM SHARED NetGame AS NetGameProperties
' DIM SHARED DataPacketRX AS UDPpacket POINTER
' DIM SHARED DataPacketTX AS UDPpacket POINTER
' DIM SHARED UdpSrvSocket AS udpsocket
#ENDIF
#IFDEF __FB_DOS__ ' If under DOS, check if RDTSC is supported
IF CheckTSC() = 0 THEN
PRINT "Error: Your CPU doesn't support the RDTSC instruction! The DOS version"
PRINT " of Emeritus Pong can run on a RDTSC-aware CPU only. The RDTSC"
PRINT " instruction is supported on all pentium-compatible processors"
PRINT " since 1993."
END IF
#ENDIF
LoadConfig()
MainMenu()
REM *** END OF THE MAIN PROGRAM ** SUBS AND FUNCTIONS BELOW ***
SUB PlayGame()
SCREENRES xRes, yRes, 8, 2, FullScreen
SETMOUSE ,,0 ' Hide the mouse cursor
SCREENSET 1, 0 ' Displaying 1st video page, but working on the 2nd.
IF ColorMode = 1 THEN PALETTE 0, &hAA0000 ' (BGR format)
CLS
SLEEP 1200, 1
Ball.X = GenRnd(1) ' Generate new random numbers
IF GameMode <> 3 THEN
Ball.X = xRes * INT(GenRnd*2) ' Randomizing "who plays first"
ELSE
Ball.X = 0 ' In "Solo" mode, the ball must come from the left
END IF
InitBall()
LastUpdate = uTIMER
LastMissedBall = uTIMER
DO
CheckPlayersInputs()
UpdatePlayersPositions()
IF GameMode = 3 THEN Score2 = Ball.Speed
IF GameMode = 4 AND ABS(uTIMER - LastClockRefresh) > 3 * Mega THEN
Score1 = HOUR(NOW)
Score2 = MINUTE(NOW)
LastClockRefresh = uTIMER
END IF
UpdateBallPosition()
IF ABS(uTIMER - LastRefresh) > 20000 THEN RefreshScreen() ' Refresh screen at 50 FPS (1000000 / 20000)
IF ABS(uTIMER - LastAiSpeedChange) > 5 * Mega THEN
AiSpeed = 0.2 + GenRnd() * 0.3
LastAiSpeedChange = uTIMER
END IF
SLEEP 1, 1 ' Give some CPU time away
IF BallMissed = 1 THEN
RefreshScreen()
PlaySound(3)
SLEEP 1000, 1
IF GameMode <> 4 THEN
IF Ball.X > xRes \ 2 THEN Score1 += 1 ELSE Score2 += 1
END IF
Ball.X = GenRnd(1) ' The Ball.X is just a fake var. Could be anything.
InitBall()
BallMissed = 0
LastMissedBall = uTIMER
LastUpdate = uTIMER
Player1LastMove = uTIMER
Player2LastMove = uTIMER
END IF
IF ABS(uTIMER - LastMissedBall) > 10 * Mega AND Ball.Speed < 12 THEN Ball.Speed += 1: LastMissedBall = uTIMER
IF ABS(uTIMER - LastKeybPolling) > Mega THEN ' Poll keyb 3 times in a second
LastKey = INKEY
FlushKeyb()
LastKeybPolling = uTIMER
IF LastKey = CHR(255) + "k" THEN QuitGame()
END IF
#IFDEF __FB_DOS__
IF Snd = 1 THEN Sound(-1, 0) ' Checking/processing sound output
#ENDIF
LOOP UNTIL EscPressed = 1
EscPressed = 0
SLEEP 100, 1
#IFDEF __FB_DOS__
IF Snd = 1 THEN Sound(0, 0) ' Turns off the sound (if any)
#ENDIF
FlushKeyb()
END SUB
SUB RefreshScreen()
CLS
DrawTable()
ShowScore()
LINE (vBorder, CurPos - PalWidth \ 2) - (vBorder + PalTick, CurPos + PalWidth / 2), 15, BF ' Player 1
IF GameMode <> 3 THEN LINE (xRes - vBorder - PalTick, CurPos2 - PalWidth \ 2) - (xRes - vBorder, CurPos2 + PalWidth / 2), 15, BF ' Player 2
LINE (Ball.X - PalTick \ 2, Ball.Y - PalTick \ 2) - (Ball.X + PalTick \ 2, Ball.Y + PalTick \ 2), 15, BF ' Ball
SCREENCOPY 1, 0
LastRefresh = uTIMER
END SUB
SUB FlushKeyb()
WHILE LEN(INKEY) > 0: WEND
END SUB
SUB DrawTable()
DIM AS INTEGER i
FOR i = hBorder TO yRes - hBorder STEP 2 * PalTick
LINE (xRes \ 2 - PalTick \ 2, i) - (xRes \ 2 + PalTick \ 2, i + PalTick), 7, BF ' Central line
NEXT i
LINE (vBorder, hBorder - PalTick) - (xRes - vBorder, hBorder), 7, BF ' Up line
LINE (vBorder, yRes - hBorder + PalTick) - (xRes - vBorder, yRes - hBorder), 7, BF ' Down line
IF GameMode = 3 THEN LINE (xRes - vBorder - PalTick, hBorder) - (xRes - vBorder, yRes - hBorder), 7, BF ' Back line (Solo mode)
END SUB
SUB UpdateBallPosition()
DIM AS DOUBLE a, b, c
IF ABS(uTIMER - LastUpdate) > Mega THEN LastUpdate = uTIMER ' The machine could be paused, or anything...
c = xRes * (Ball.Speed + 5) * ABS(uTIMER - LastUpdate) * 0.000000055
LastUpdate = uTIMER
SELECT CASE Ball.Angle
CASE IS < 90
b = COS((Ball.Angle MOD 90) * Degr2Rad) * c
a = SQR(c^2 - b^2)
Ball.X += a
Ball.Y -= b
CASE IS < 180
a = COS((Ball.Angle MOD 90) * Degr2Rad) * c
b = SQR(c^2 - a^2)
Ball.X += a
Ball.Y += b
CASE IS < 270
b = COS((Ball.Angle MOD 90) * Degr2Rad) * c
a = SQR(c^2 - b^2)
Ball.X -= a
Ball.Y += b
CASE ELSE
a = COS((Ball.Angle MOD 90) * Degr2Rad) * c
b = SQR(c^2 - a^2)
Ball.X -= a
Ball.Y -= b
END SELECT
REM Solo mode handling...
IF (Ball.X > xRes - vBorder - PalTick - PalTick \ 2) AND (Ball.Angle < 180) AND GameMode = 3 THEN
Ball.Angle = 360 - Ball.Angle
PlaySound(1)
END IF
REM Player 1 handling...
IF (Ball.X < vBorder + PalTick + PalTick \ 2) AND (Ball.Angle > 180) AND ABS(CurPos - Ball.Y) - (PalTick \ 2) <= PalWidth \ 2 THEN
Ball.Angle = 360 - Ball.Angle
Ball.Angle -= (CurPos - Ball.Y) / (PalWidth / 2) * 40
IF Ball.Angle > 170 THEN Ball.Angle = 170
IF Ball.Angle < 10 THEN Ball.Angle = 10
PlaySound(2)
END IF
REM Player 2 handling...
IF (Ball.X > xRes - vBorder - PalTick - PalTick \ 2) AND (Ball.Angle < 180) AND ABS(CurPos2 - Ball.Y) - (PalTick \ 2) <= PalWidth \ 2 THEN
Ball.Angle = 360 - Ball.Angle
Ball.Angle += (CurPos2 - Ball.Y) / (PalWidth / 2) * 40
IF Ball.Angle > 350 THEN Ball.Angle = 350
IF Ball.Angle < 190 THEN Ball.Angle = 190
PlaySound(2)
END IF
REM Up & down bouncing
IF Ball.Y > (yRes - hBorder - PalTick \ 2) AND (Ball.Angle > 90) AND (Ball.Angle < 180) THEN Ball.Angle = 180 - Ball.Angle: PlaySound(1)
IF Ball.Y > (yRes - hBorder - PalTick \ 2) AND (Ball.Angle > 180) AND (Ball.Angle < 270) THEN Ball.Angle = 360 - (Ball.Angle MOD 180): PlaySound(1)
IF Ball.Y < (hBorder + PalTick \ 2) AND (Ball.Angle < 90) THEN Ball.Angle = 180 - (Ball.Angle MOD 180): PlaySound(1)
IF Ball.Y < (hBorder + PalTick \ 2) AND (Ball.Angle > 270) THEN Ball.Angle = 360 - (Ball.Angle MOD 180): PlaySound(1)
REM Checking if ball missed...
IF (Ball.X < vBorder + PalTick + PalTick \ 2) AND (Ball.Angle > 180) AND ABS(CurPos - Ball.Y) - (PalTick \ 2) > PalWidth \ 2 THEN BallMissed = 1
IF (Ball.X > xRes - vBorder - PalTick - PalTick \ 2) AND (Ball.Angle < 180) AND ABS(CurPos2 - Ball.Y) - (PalTick \ 2) > PalWidth \ 2 THEN BallMissed = 1
END SUB
SUB ShowScore()
DIM AS BYTE Score1a, Score1b, Score2a, Score2b, x, y
DIM AS INTEGER StartX, StartY
IF Score1 > 99 THEN Score1 = 99
IF Score2 > 99 THEN Score2 = 99
Score1a = Score1 \ 10
Score1b = Score1 MOD 10
Score2a = Score2 \ 10
Score2b = Score2 MOD 10
StartY = hBorder + PalTick
IF GameMode <> 3 THEN
IF Score1a > 0 THEN
StartX = xRes \ 2 - PalTick * 9
FOR y = 1 TO 5
FOR x = 1 TO 3
IF BigNumbers(Score1a, y, x) = 1 THEN
LINE (StartX + (x - 1) * PalTick, StartY + (y - 1) * PalTick) - (StartX + x * PalTick, StartY + y * PalTick), 7, BF
END IF
NEXT x
NEXT y
END IF
StartX = xRes \ 2 - PalTick * 5
FOR y = 1 TO 5
FOR x = 1 TO 3
IF BigNumbers(Score1b, y, x) = 1 THEN
LINE (StartX + (x - 1) * PalTick, StartY + (y - 1) * PalTick) - (StartX + x * PalTick, StartY + y * PalTick), 7, BF
END IF
NEXT x
NEXT y
END IF
IF Score2a > 0 OR GameMode = 4 THEN
StartX = xRes \ 2 + PalTick * 2
FOR y = 1 TO 5
FOR x = 1 TO 3
IF BigNumbers(Score2a, y, x) = 1 THEN
LINE (StartX + (x - 1) * PalTick, StartY + (y - 1) * PalTick) - (StartX + x * PalTick, StartY + y * PalTick), 7, BF
END IF
NEXT x
NEXT y
END IF
IF Score2a > 0 OR GameMode = 4 THEN StartX = xRes \ 2 + PalTick * 6 ELSE StartX = xRes \ 2 + PalTick * 2
FOR y = 1 TO 5
FOR x = 1 TO 3
IF BigNumbers(Score2b, y, x) = 1 THEN
LINE (StartX + (x - 1) * PalTick, StartY + (y - 1) * PalTick) - (StartX + x * PalTick, StartY + y * PalTick), 7, BF
END IF
NEXT x
NEXT y
END SUB
SUB InitBall()
Ball.Angle = 75 + INT(GenRnd() * 30)
IF GameMode <> 4 THEN Ball.Speed = InitSpeed ELSE Ball.Speed = 2
Ball.Y = (yRes / 2) - (yRes / 4) + GenRnd() * (yRes / 2)
IF Ball.X < xRes \ 2 THEN Ball.X = vBorder + PalTick + PalTick + 1 ELSE Ball.X = xRes - vBorder - PalTick - PalTick + 1: Ball.Angle = 360 - Ball.Angle
END SUB
SUB QuitGame()
#IFDEF __FB_DOS__
IF Snd = 1 THEN Sound(0, 0) ' Turns off the sound (if any)
#ELSE
' SDLNet_FreePacket(DataPacketRX)
' SDLNet_FreePacket(DataPacketTX)
' SDLNet_UDP_Close(UdpSrvSocket)
' SDLNet_Quit ' Unload the SDL module
IF Snd = 1 THEN
SLEEP 100, 1 ' Wait 1/10 of a second
Mix_FreeChunk(SndFilePong) ' Clearing allocated memory for sound files
Mix_FreeChunk(SndFileBounce) ' Clearing allocated memory for sound files
Mix_FreeChunk(SndFileMissed) ' Clearing allocated memory for sound files
Mix_CloseAudio() ' Close the SDL audio mixer
SDL_Quit() ' Unload the SDL library
END IF
#ENDIF
CLS
OPEN EXEPATH + Delimiter + "empong.cfg" FOR OUTPUT AS #1
PRINT #1, "# This is the configuration file of Emeritus Pong v"+ pVer + "."
PRINT #1, "xRes=" & xRes
PRINT #1, "yRes=" & yRes
PRINT #1, "Snd=" & Snd
PRINT #1, "ColorMode=" & ColorMode
PRINT #1, "FullScreen=" & FullScreen
PRINT #1, "InitSpeed=" & InitSpeed
CLOSE #1
SLEEP 100, 1
END
END SUB
SUB MainMenu()
DIM AS BYTE x, Choice = 1
DIM AS STRING TitleBar
DO
Player1Cmd = 0 ' Initializing all values...
Player2Cmd = 0
CurPos = INT(yRes / 2)
CurPos2 = INT(yRes / 2)
PalWidth = CINT(yRes * 14 / 100)
PalTick = CINT(xRes * 2 / 100)
hBorder = CINT(yRes * 6 / 100)
vBorder = CINT(xRes * 3 / 100)
BallMissed = 0
SyncCounter = 0
' *** GameMode: 1: vs CPU 2: vs Player 3: Solo 4: Pong Clock
InitBall()
Score1 = 0
Score2 = 0
#IFDEF __FB_DOS__
SCREEN 0 ' In DOS, just initialize 80x25.
WIDTH 80, 30 ' and turn off the cursor.
LOCATE ,,0
#ELSE ' Otherwise use video subsystem.
SCREEN 12,,,FullScreen
SETMOUSE ,,0 ' Disables the mouse cursor
#ENDIF
COLOR 7, 0
CLS
FOR x = 1 TO HIWORD(WIDTH())
LOCATE x, 1: PRINT STRING(80, CHR(176));
NEXT x
COLOR 0, 10
TitleBar = "Emeritus Pong v" + pVer + " Copyright (C) Mateusz Viste " + pDate
LOCATE 1, 1: PRINT SPACE(80);
LOCATE 1, 40 - LEN(TitleBar) \ 2: PRINT TitleBar;
COLOR 7, 0
LOCATE 9, 31: PRINT CHR(201); STRING(16, 205); CHR(187); ' Draws menu box
LOCATE 10, 31: PRINT CHR(186); STRING(16, 32); CHR(186);
LOCATE 11, 31: PRINT CHR(186); STRING(16, 32); CHR(186);
LOCATE 12, 31: PRINT CHR(186); STRING(16, 32); CHR(186);
LOCATE 13, 31: PRINT CHR(186); STRING(16, 32); CHR(186);
LOCATE 14, 31: PRINT CHR(186); STRING(16, 32); CHR(186);
LOCATE 15, 31: PRINT CHR(186); STRING(16, 32); CHR(186);
LOCATE 16, 31: PRINT CHR(200); STRING(16, 205); CHR(188);
LOCATE 20, 24: PRINT CHR(218); STRING(30, 196); CHR(191); ' Draws info box
LOCATE 21, 24: PRINT CHR(179); STRING(30, 32); CHR(179);
LOCATE 22, 24: PRINT CHR(179); STRING(30, 32); CHR(179);
LOCATE 23, 24: PRINT CHR(179); STRING(30, 32); CHR(179);
LOCATE 24, 24: PRINT CHR(192); STRING(30, 196); CHR(217);
DO
IF Choice = 1 THEN COLOR 15, 1 ELSE COLOR 7, 0
LOCATE 10, 32: PRINT " Play vs CPU ";
IF Choice = 2 THEN COLOR 15, 1 ELSE COLOR 7, 0
LOCATE 11, 32: PRINT " 2 Players game";
IF Choice = 3 THEN COLOR 15, 1 ELSE COLOR 7, 0
LOCATE 12, 32: PRINT " Play solo ";
IF Choice = 4 THEN COLOR 15, 1 ELSE COLOR 7, 0
LOCATE 13, 32: PRINT " Settings ";
IF Choice = 5 THEN COLOR 15, 1 ELSE COLOR 7, 0
LOCATE 14, 32: PRINT " Pong Clock ";
IF Choice = 6 THEN COLOR 15, 1 ELSE COLOR 7, 0
LOCATE 15, 32: PRINT " Quit game ";
COLOR 7, 0
SELECT CASE Choice
CASE 1
LOCATE 21, 26: PRINT "The player plays against the";
LOCATE 22, 26: PRINT "computer. Use up/down arrows"
LOCATE 23, 26: PRINT "to move and 'P' to pause. ";
CASE 2
LOCATE 21, 26: PRINT "Two-players mode. The second";
LOCATE 22, 26: PRINT "player uses +/- keys on the ";
LOCATE 23, 26: PRINT "numeric pad. ";
CASE 3
LOCATE 21, 26: PRINT "The 'solo' mode hasn't any ";
LOCATE 22, 26: PRINT "opponent. The player plays ";
LOCATE 23, 26: PRINT "alone against a wall. ";
CASE 4
LOCATE 21, 26: PRINT "Game settings (screen size, ";
LOCATE 22, 26: PRINT "sound...) ";
LOCATE 23, 26: PRINT " ";
CASE 5
LOCATE 21, 26: PRINT "Demo mode. The current time ";
LOCATE 22, 26: PRINT "is displayed in place of the";
LOCATE 23, 26: PRINT "players' scores. ";
CASE 6
LOCATE 21, 26: PRINT "Quits the game and returns ";
LOCATE 22, 26: PRINT "to the OS. Are you sure you ";
LOCATE 23, 26: PRINT "want that?? ";
END SELECT
SLEEP
LastKey = INKEY
IF LastKey = CHR(27) OR LastKey = CHR(255) + "k" THEN QuitGame()
IF LastKey = CHR(255) + "H" AND Choice > 1 THEN Choice -= 1
IF LastKey = CHR(255) + "P" AND Choice < 6 THEN Choice += 1
FlushKeyb()
LOOP UNTIL LastKey = CHR(13)
SELECT CASE Choice
CASE 1
GameMode = 1
PlayGame()
CASE 2
GameMode = 2
'NetGame.state = 1 ' Temporary hack for testing purpose - FIXME!
'CLS
'LINE INPUT "server or client? ", NetGame.LocalMode
'IF NetGame.LocalMode = "client" THEN
' LINE INPUT "What remote IP? ", NetGame.RemoteIP
'END IF
'InitNetworkSocket()
PlayGame()
CASE 3
GameMode = 3
PlayGame()
CASE 4
Setup()
CASE 5
GameMode = 4
PlayGame()
CASE 6
QuitGame()
END SELECT
LOOP
END SUB
SUB PlaySound(SndType AS BYTE)
REM SndTypes: 1: Bounce from up/down walls [Bounce]
REM 2: Bounce from palette [Pong]
REM 3: Ball Missed [Missed]
IF Snd = 1 THEN
#IFDEF __FB_DOS__
SELECT CASE SndType
CASE 1
SOUND(1600, 10)
CASE 2
SOUND(1000, 10)
CASE 3
DIM i AS INTEGER
FOR i = 500 TO 300 STEP -25
SOUND(i, 5)
SLEEP 1, 1
NEXT i
END SELECT
#ELSE
SELECT CASE SndType
CASE 1
Mix_PlayChannel(-1, SndFileBounce, 0)
CASE 2
Mix_PlayChannel(-1, SndFilePong, 0)
CASE 3
Mix_PlayChannel(-1, SndFileMissed, 0)
END SELECT
#ENDIF
END IF
END SUB
SUB Setup()
DIM AS BYTE x, Choice, Resolution
DIM ResList(1 TO 5) AS STRING => {"320x240 ", "640x480 ", "800x600 ", "1024x768 ", "1280x1024"}
DIM SoundList(0 TO 1) AS STRING => {"Off ", "On "}
DIM VideoList(0 TO 1) AS STRING => {"Mono ", "Color "}
DIM NoYes(0 TO 1) AS STRING => {"No ", "Yes "}
Choice = 1
SELECT CASE xRes
CASE 320
Resolution = 1
CASE 640
Resolution = 2
CASE 800
Resolution = 3
CASE 1024
Resolution = 4
CASE 1280
Resolution = 5
END SELECT
FOR x = 2 TO HIWORD(WIDTH())
LOCATE x, 1: PRINT STRING(80, CHR(176));
NEXT x
COLOR 7, 0
LOCATE 9, 28: PRINT CHR(201); STRING(22, 205); CHR(187); ' Draws menu box
LOCATE 10, 28: PRINT CHR(186); STRING(22, 32); CHR(186);
LOCATE 11, 28: PRINT CHR(186); STRING(22, 32); CHR(186);
LOCATE 12, 28: PRINT CHR(186); STRING(22, 32); CHR(186);
LOCATE 13, 28: PRINT CHR(186); STRING(22, 32); CHR(186);
LOCATE 14, 28: PRINT CHR(186); STRING(22, 32); CHR(186);
LOCATE 15, 28: PRINT CHR(186); STRING(22, 32); CHR(186);
LOCATE 16, 28: PRINT CHR(200); STRING(22, 205); CHR(188);
LOCATE 20, 24: PRINT CHR(218); STRING(30, 196); CHR(191); ' Draws info box
LOCATE 21, 24: PRINT CHR(179); STRING(30, 32); CHR(179);
LOCATE 22, 24: PRINT CHR(179); STRING(30, 32); CHR(179);
LOCATE 23, 24: PRINT CHR(179); STRING(30, 32); CHR(179);
LOCATE 24, 24: PRINT CHR(192); STRING(30, 196); CHR(217);
DO
IF Choice = 1 THEN COLOR 15, 1 ELSE COLOR 7, 0
LOCATE 10, 29: PRINT " Resolution: "; ResList(Resolution);
IF Choice = 2 THEN COLOR 15, 1 ELSE COLOR 7, 0
LOCATE 11, 29: PRINT " Fullscreen: "; NoYes(FullScreen)
IF Choice = 3 THEN COLOR 15, 1 ELSE COLOR 7, 0
LOCATE 12, 29: PRINT " Sound: "; SoundList(Snd);
IF Choice = 4 THEN COLOR 15, 1 ELSE COLOR 7, 0
LOCATE 13, 29: PRINT " Video Mode: "; VideoList(ColorMode);
IF Choice = 5 THEN COLOR 15, 1 ELSE COLOR 7, 0
LOCATE 14, 29: PRINT " Ball speed:"; InitSpeed; SPACE(8 - InitSpeed \ 10)
IF Choice = 6 THEN COLOR 15, 1 ELSE COLOR 7, 0
LOCATE 15, 29: PRINT " Go back to main menu ";
COLOR 7, 0
SELECT CASE Choice
CASE 1
LOCATE 21, 26: PRINT "Game's resolution. On Linux ";
LOCATE 22, 26: PRINT "and Windows it will be the "
LOCATE 23, 26: PRINT "window's dimensions. ";
CASE 2
LOCATE 21, 26: PRINT "Switches the full-screen ";
LOCATE 22, 26: PRINT "mode ON/OFF. This option ";
LOCATE 23, 26: PRINT "is ignored in DOS. ";
CASE 3
LOCATE 21, 26: PRINT "Enables / Disables sound. ";
LOCATE 22, 26: PRINT "DOS: Internal PC speaker ";
LOCATE 23, 26: PRINT "Linux/Windows: Sound card ";
CASE 4
LOCATE 21, 26: PRINT "There you may choose if you ";
LOCATE 22, 26: PRINT "wants a monochrome video ";
LOCATE 23, 26: PRINT "or a color one. ";
CASE 5
LOCATE 21, 26: PRINT "Sets the ball's speed. Note,";
LOCATE 22, 26: PRINT "that the speed is increasing";
LOCATE 23, 26: PRINT "during the game. ";
CASE 6
LOCATE 21, 26: PRINT "Go back to the main menu. ";
LOCATE 22, 26: PRINT " ";
LOCATE 23, 26: PRINT " ";
END SELECT
SLEEP
LastKey = INKEY
IF LastKey = CHR(255) + "H" AND Choice > 1 THEN Choice -= 1
IF LastKey = CHR(255) + "P" AND Choice < 6 THEN Choice += 1
IF LastKey = CHR(255) + "k" THEN QuitGame()
IF LastKey = CHR(13) THEN
SELECT CASE Choice
CASE 1
Resolution += 1
IF Resolution > 5 THEN Resolution = 1
SELECT CASE Resolution
CASE 1
xRes = 320
yRes = 240
CASE 2
xRes = 640
yRes = 480
CASE 3
xRes = 800
yRes = 600
CASE 4
xRes = 1024
yRes = 768
CASE 5
xRes = 1280
yRes = 1024
END SELECT
CASE 2
FullScreen = 1 - FullScreen
CASE 3
Snd = 1 - Snd
CASE 4
ColorMode = 1 - ColorMode
CASE 5
InitSpeed += 1
IF InitSpeed = 13 THEN InitSpeed = 1
END SELECT
END IF
FlushKeyb()
LOOP UNTIL (LastKey = CHR(13) AND Choice = 6) OR LastKey = CHR(27)
END SUB
SUB LoadConfig()
DIM DefaultConfig AS BYTE
DefaultConfig = 0
xRes = VAL(ReadCfg(EXEPATH + Delimiter + "empong.cfg", "xRes"))
yRes = VAL(ReadCfg(EXEPATH + Delimiter + "empong.cfg", "yRes"))
Snd = VAL(ReadCfg(EXEPATH + Delimiter + "empong.cfg", "Snd"))
ColorMode = VAL(ReadCfg(EXEPATH + Delimiter + "empong.cfg", "ColorMode"))
FullScreen = VAL(ReadCfg(EXEPATH + Delimiter + "empong.cfg", "FullScreen"))
InitSpeed = VAL(ReadCfg(EXEPATH + Delimiter + "empong.cfg", "InitSpeed"))
IF xRes < 20 THEN DefaultConfig = 1
IF yRes < 20 THEN DefaultConfig = 1
IF Snd < 0 OR Snd > 1 THEN DefaultConfig = 1
IF ColorMode < 0 OR ColorMode > 1 THEN DefaultConfig = 1
IF FullScreen < 0 OR FullScreen > 1 THEN DefaultConfig = 1
IF InitSpeed < 1 OR InitSpeed > 12 THEN DefaultConfig = 1
IF DefaultConfig = 1 THEN
xRes = 640
yRes = 480
Snd = 1
ColorMode = 1
FullScreen = 0
InitSpeed = 2
END IF
END SUB
FUNCTION ReadCFG(CFGfile AS STRING, CFGField AS STRING) AS STRING
STATIC CfgTable(1 TO 2, 0 TO 255) AS STRING
DIM AS STRING CfgReturnString, CfgTmpBuffer
DIM AS INTEGER Counter
IF CfgTable(1, 0) <> "init ok" THEN
DIM AS INTEGER CfgFileHandler, CfgColonPos, Counter
CfgFileHandler = FREEFILE
CfgReturnString = ""
Counter = 0
CfgTable(1, 0) = "init ok"
IF DIR(CFGfile) <> "" THEN
OPEN CFGfile FOR INPUT AS #CfgFileHandler
DO
Counter += 1
LINE INPUT #CfgFileHandler, CfgTmpBuffer
IF MID(TRIM(CfgTmpBuffer), 1, 1) <> "#" THEN
CfgColonPos = INSTR(CfgTmpBuffer, "=")
CfgTable(1, Counter) = TRIM(MID(CfgTmpBuffer, 1, CfgColonPos - 1))
CfgTable(2, Counter) = TRIM(MID(CfgTmpBuffer, CfgColonPos + 1))
END IF
LOOP UNTIL EOF(CfgFileHandler) OR Counter = 255
CLOSE #CfgFileHandler
END IF
CfgTable(2, 0) = STR(Counter)
END IF
Counter = 0
DO
Counter += 1
IF UCASE(CfgTable(1, Counter)) = UCASE(CFGField) THEN CfgReturnString = CfgTable(2, Counter)
LOOP UNTIL CfgReturnString <> "" OR Counter >= VAL(CfgTable(2, 0))
RETURN CfgReturnString
END FUNCTION
FUNCTION GenRnd(BYVAL NewGen AS BYTE = 0) AS DOUBLE
STATIC i AS INTEGER
STATIC RandomTable(1 TO 1024) AS DOUBLE
DIM AS DOUBLE Wynik
IF NewGen = 0 THEN
i += 1
IF i > 1025 THEN i = 1
Wynik = RandomTable(i)
RETURN Wynik
ELSE
RANDOMIZE TIMER, 3
FOR i = 1 TO 1024
RandomTable(i) = RND
NEXT i
i = 0
END IF
END FUNCTION
SUB Pause()
DIM AS INTEGER i
#IFDEF __FB_DOS__
IF Snd = 1 THEN Sound(0, 0) ' Turns off the sound (if any)
#ENDIF
FOR i = i TO yRes STEP 2
LINE (0, i)-(xRes, i), 0
NEXT i
PCOPY 1, 0
SLEEP 300, 1
FOR i = 0 TO xRes STEP 2
LINE (i, 0)-(i, yRes), 0
NEXT i
PCOPY 1, 0
SLEEP
SLEEP 100, 1
Player1LastMove = uTIMER ' Resync all game's variables
Player2LastMove = uTIMER '
LastUpdate = uTIMER '
FlushKeyb
END SUB
SUB Sound(BYVAL freq AS INTEGER, dur AS UINTEGER)
STATIC SoundDuration AS UINTEGER
STATIC SoundStarted AS DOUBLE
IF freq <= 0 THEN
IF (ABS(TIMER - SoundStarted) * 100 > SoundDuration) OR (freq = 0) THEN
OUT &H61, INP(&H61) AND &HFC
END IF
ELSE
DIM t AS DOUBLE, f1 AS USHORT
IF freq > 0 THEN
f1 = 1193181 \ freq
OUT &H61, INP(&H61) OR 3
OUT &H43, &HB6
OUT &H42, lobyte(f1)
OUT &H42, hibyte(f1)
END IF
SoundStarted = TIMER
SoundDuration = dur
END IF
END SUB
SUB CheckPlayersInputs()
Player1Cmd *= 2
Player2Cmd *= 2
IF GameMode <> 4 THEN
IF MultiKey(&h48) = -1 THEN Player1Cmd = 1 ' Up
IF MultiKey(&h50) = -1 THEN Player1Cmd = -1 ' Down
END IF
IF GameMode = 2 THEN
IF MultiKey(&h4A) = -1 THEN Player2Cmd = 1 ' Up (player 2)
IF MultiKey(&h4E) = -1 THEN Player2Cmd = -1 ' Down (player 2)
END IF
IF (GameMode = 1 OR GameMode = 4) THEN
IF Ball.Angle < 180 THEN
IF Ball.Angle > 90 THEN
IF CurPos2 - Ball.Y < (yres / 15) AND (Player2Cmd < 0 OR ABS(uTIMER - AiLastDecision) > AiSpeed * Mega) THEN Player2Cmd = -1 : AiLastDecision = uTIMER
IF CurPos2 - Ball.Y > (yres / 15) AND (Player2Cmd > 0 OR ABS(uTIMER - AiLastDecision) > AiSpeed * Mega) THEN Player2Cmd = 1 : AiLastDecision = uTIMER
ELSE
IF Ball.Y - CurPos2 > (yres / 15) AND (Player2Cmd < 0 OR ABS(uTIMER - AiLastDecision) > AiSpeed * Mega) THEN Player2Cmd = -1 : AiLastDecision = uTIMER
IF Ball.Y - CurPos2 < (yres / 15) AND (Player2Cmd > 0 OR ABS(uTIMER - AiLastDecision) > AiSpeed * Mega) THEN Player2Cmd = 1 : AiLastDecision = uTIMER
END IF
END IF
END IF
IF GameMode = 4 THEN
IF Ball.Angle > 180 THEN
IF Ball.Angle < 270 THEN
IF CurPos - Ball.Y < (yres / 15) AND (Player1Cmd < 0 OR ABS(uTIMER - AiLastDecision) > AiSpeed * Mega) THEN Player1Cmd = -1 : AiLastDecision = uTIMER
IF CurPos - Ball.Y > (yres / 15) AND (Player1Cmd > 0 OR ABS(uTIMER - AiLastDecision) > AiSpeed * Mega) THEN Player1Cmd = 1 : AiLastDecision = uTIMER
ELSE
IF Ball.Y - CurPos > (yres / 15) AND (Player1Cmd < 0 OR ABS(uTIMER - AiLastDecision) > AiSpeed * Mega) THEN Player1Cmd = -1 : AiLastDecision = uTIMER
IF Ball.Y - CurPos < (yres / 15) AND (Player1Cmd > 0 OR ABS(uTIMER - AiLastDecision) > AiSpeed * Mega) THEN Player1Cmd = 1 : AiLastDecision = uTIMER
END IF
END IF
END IF
IF ABS(Player1Cmd) > 1 THEN Player1Cmd = 0
IF ABS(Player2Cmd) > 1 THEN Player2Cmd = 0
REM Check general stuff (Pause, Escape)...
IF MultiKey(&h19) = -1 AND GameMode <> 4 THEN Pause() ' "P"
IF MultiKey(&h01) = -1 THEN EscPressed = 1 ' ESC
END SUB
SUB UpdatePlayersPositions()
REM Player 1...
IF Player1Cmd = 1 THEN CurPos -= ABS(uTIMER - Player1LastMove) / Mega * yRes / 1.5 ' Up
IF Player1Cmd = -1 THEN CurPos += ABS(uTIMER - Player1LastMove) / Mega * yRes / 1.5 ' Down
IF CurPos - PalWidth / 2 - hBorder < 1 THEN CurPos = hBorder + PalWidth / 2 + 1
IF CurPos + PalWidth / 2 + hBorder > yRes - 1 THEN CurPos = yRes - hBorder - PalWidth / 2 - 1
Player1LastMove = uTIMER
REM Player 2...
IF Player2Cmd = 1 THEN CurPos2 -= ABS(uTIMER - Player2LastMove) / Mega * yRes / 1.5 ' Up
IF Player2Cmd = -1 THEN CurPos2 += ABS(uTIMER - Player2LastMove) / Mega * yRes / 1.5 ' Down
IF CurPos2 - PalWidth / 2 - hBorder < 1 THEN CurPos2 = hBorder + PalWidth / 2 + 1
IF CurPos2 + PalWidth / 2 + hBorder > yRes - 1 THEN CurPos2 = yRes - hBorder - PalWidth / 2 - 1
Player2LastMove = uTIMER
END SUB
FUNCTION RDTSC() AS ULONGINT ' Requires Pentium or better
ASM
rdtsc
mov [Function], eax
mov [Function+4], edx
END ASM
END FUNCTION
FUNCTION uTIMER() AS ULONGINT
DIM AS ULONGINT Wynik
#IFDEF __FB_DOS__
STATIC CpuClock AS ULONGINT
IF CpuClock > 0 THEN
Wynik = RDTSC() \ CpuClock
ELSE
DIM AS DOUBLE TempDouble
SLEEP 100, 1
TempDouble = TIMER
CpuClock = RDTSC()
DO: LOOP UNTIL ABS(TIMER - TempDouble) >= 1
CpuClock = ABS(RDTSC() - CpuClock) \ 1000000
END IF
#ELSE
Wynik = TIMER * 1000000
#ENDIF
RETURN Wynik
END FUNCTION
'SUB NetGameSendSync()
' DIM AS STRING DataBuffer
' SELECT CASE Netgame.LocalMode
' CASE "server"
' DataBuffer = "SRV" & AiSpeed & ";" & CurPos & ";" & Score1 & ";" & Score2 & ";" & Ball.Speed & ";" & Ball.Angle & ";" & Ball.X & ";" & Ball.Y & ";"
' CASE "client"
' DataBuffer = "CLT" & CurPos2
' END SELECT
' REM Here I have to send the DataBuffer
'END SUB
'SUB NetGameGetSync()
' DIM AS STRING DataBuffer
' DIM AS INTEGER PacketRxFlag
' REM Here let's check if we got a packet.
' PacketRxFlag = SDLNet_UDP_Recv(UdpSrvSocket, DataPacketRX)
' SELECT CASE Netgame.LocalMode
' CASE "server"
'CurPos2 = ""
' CASE "client"
' AiSpeed = ""
' CurPos = ""
' Score1 = ""
' Score2 = ""
' BallParams.Speed = ""
' BallParams.Angle = ""
' BallParams.X = ""
' BallParams.Y = ""
' END SELECT
'END SUB
'SUB InitNetworkSocket()
' DIM AS INTEGER PacketRXflag
' DIM DataMsg AS ZSTRING*42 => "THIS IS AN EMPONG CLIENT. ANYBODY'S THERE?"
' IF SDLNet_Init <> 0 THEN
' PRINT "Error: Failed to initialize the SDLnet library."
' END(1)
' END IF
' ' Create emtpy structures for UDP RX/TX packets with a capacity of 256 bytes.
' DataPacketRX = SDLNet_AllocPacket(256)
' DataPacketTX = SDLNet_AllocPacket(256)
' SELECT CASE NetGame.LocalMode
' CASE "server"
' NetGame.LocalPort = 7495
' UdpSrvSocket = SDLNet_UDP_Open(7495)
' IF UdpSrvSocket = 0 THEN
' PRINT "Error: Failed to bind to the local UDP/7495 socket."
' END(1)
' END IF
' PRINT "Waiting for a client to connect..."
' DO
' PacketRxFlag = SDLNet_UDP_Recv(UdpSrvSocket, DataPacketRX)
' SLEEP 200, 1 ' Wait 0.2s before repolling
' LOOP UNTIL PacketRxFlag = 1
' NetGame.RemotePort = DataPacketRX->address.port
' PRINT "Got a connection from " & IPaddressToString(DataPacketRX->address.host) & ":" & NetGame.RemotePort & "."
' SLEEP 2000, 1
' CASE "client"
' UdpSrvSocket = SDLNet_UDP_Open(0)
' IF UdpSrvSocket = 0 THEN
' PRINT "Error: Failed to bind to a local UDP socket."
' END(1)
' END IF
' NetGame.LocalPort = UdpSrvSocket
' NetGame.RemotePort = 7495
' DataPacketTX->channel = -1
' DataPacketTX->len = 42
' DataPacketTX->data = @DataMsg
' 'SDLNet_ResolveHost(DataPacketTX->address, NetGame.RemoteIP, 7495)
' DataPacketTX->address.host = VAL("&b00000001000000000000000001111111") ' 127.0.0.1
' DataPacketTX->address.port = 7495
' PRINT "Waiting for the server to answer... "
' SDLNet_UDP_Send(UdpSrvSocket, DataPacketTX->channel, DataPacketTX)
' END SELECT
'END SUB
'FUNCTION IPaddressToString(sdlIP AS UINT32) AS STRING
' RETURN "" & (sdlIP AND &b11111111) & "." & ((sdlIP AND &b1111111100000000) SHR 8) & "." & ((sdlIP AND &b111111110000000000000000) SHR 16) & "." & ((sdlIP AND &b11111111000000000000000000000000) SHR 24)
'END FUNCTION