2005-06-14 23:15:01 +00:00
#@+leo-ver=4-thin
#@+node:jpenner.20050305105206:@thin TennisForTwo.py
#@@language python
2005-06-15 18:09:46 +00:00
import pygame , math , cPickle , sys , StringIO , socket , re , urllib , random
2005-06-14 23:15:01 +00:00
from pygame . locals import *
from twisted . internet import task , reactor , protocol , udp
from widgets import *
#@+others
#@+node:jpenner.20050601180947:Helper / Misc
#@+others
#@+node:jpenner.20050305105934:Constants
#constants
SCREENRECT = Rect ( 0 , 0 , 640 , 480 )
GRAVITY = 0.25
FRICTION = 4
BALL_STARTX = 100
BALL_STARTY = 350
SINGLE_PLAYER = 10
#@nonl
#@-node:jpenner.20050305105934:Constants
#@+node:jpenner.20050310195953:Globals
#globals
class Game :
currentplayer = 1
2005-06-15 18:09:46 +00:00
AIPlayer = 0
2005-06-14 23:15:01 +00:00
SeverPort = 7554
2005-11-14 14:08:54 +00:00
port = None
2005-06-16 15:25:45 +00:00
logfile = None
2005-06-14 23:15:01 +00:00
#@nonl
#@-node:jpenner.20050310195953:Globals
#@+node:jpenner.20050424164431:Logging
def startLogging ( fn ) :
Game . logfile = open ( fn , ' wt ' )
def log ( text ) :
2005-06-16 15:25:45 +00:00
if Game . logfile < > None :
Game . logfile . write ( str ( text ) + ' \n ' )
2005-06-14 23:15:01 +00:00
def stopLogging ( ) :
Game . logfile . close ( )
#@nonl
#@-node:jpenner.20050424164431:Logging
#@+node:jpenner.20050427175747:Angles
def makeAngle ( pos ) :
xdist = pos [ 0 ] - Game . ball . rect . centerx
ydist = pos [ 1 ] - Game . ball . rect . centery
return math . atan2 ( ydist , xdist )
def anglePos ( rect , angle , radius ) :
return ( ( rect . centerx + ( math . cos ( angle ) * radius ) ) , ( rect . centery + ( math . sin ( angle ) * radius ) ) )
2005-06-15 18:09:46 +00:00
def velocityFromAngle ( angle ) :
return ( math . cos ( angle ) * 12 , math . sin ( angle ) * 9 )
2005-06-14 23:15:01 +00:00
#@-node:jpenner.20050427175747:Angles
#@+node:jpenner.20050605110605:IP address
def getMyIP ( ) :
try :
f = urllib . urlopen ( ' http://checkip.dyndns.org ' )
s = f . read ( )
2005-06-16 15:25:45 +00:00
log ( " checkip: " + s )
m = re . search ( ' Current IP Address: ([ \ d]* \ .[ \ d]* \ .[ \ d]* \ .[ \ d]*) ' , s )
2005-06-14 23:15:01 +00:00
outsideip = m . group ( 0 )
2005-06-16 15:25:45 +00:00
log ( " outside ip: " + outsideip )
2005-06-14 23:15:01 +00:00
except :
outsideip = None
try :
insideip = socket . gethostbyname ( socket . gethostname ( ) )
2005-06-16 15:25:45 +00:00
log ( " inside ip: " + insideip )
2005-06-14 23:15:01 +00:00
except :
insideip = None
if ( outsideip == None ) and ( insideip == None ) :
ip = " Unknown "
else :
if ( outsideip == None ) :
2005-06-16 15:25:45 +00:00
ip = insideip + " (Firewalled / Proxied?) "
2005-06-14 23:15:01 +00:00
else :
ip = outsideip
if ( insideip < > None ) and ( insideip < > outsideip ) :
ip = ip + " (Firewalled?) "
return ip
#@-node:jpenner.20050605110605:IP address
#@-others
#@nonl
#@-node:jpenner.20050601180947:Helper / Misc
#@+node:jpenner.20050305124252:Events
#@+others
#@+node:jpenner.20050305130011.1:Event Manager
class EventManager :
def __init__ ( self ) :
self . queue = [ ]
self . handlers = [ [ ] for i in range ( NUM_EVENTS ) ]
def postEvent ( self , event ) :
self . queue . append ( event ) ;
def tick ( self ) :
for event in self . queue :
for handler in self . handlers [ event . type ] :
handler ( event )
self . queue = [ ]
def registerHandler ( self , evtype , func ) :
if ( evtype == NUM_EVENTS ) :
for i in range ( NUM_EVENTS ) :
self . registerHandler ( i , func )
else :
self . handlers [ evtype ] . append ( func )
#@nonl
#@-node:jpenner.20050305130011.1:Event Manager
#@+node:jpenner.20050305130011:Types
EV_HIT = 0
EV_SCORE = 1
EV_BOUNCE = 2
EV_PLAYER_UPDATE = 3
EV_SERVE = 4
EV_CLICK = 5
EV_BALLPOS = 6
EV_CONNECT = 7
EV_PLAYER_SWITCH = 8
EV_HELLO = 9
NUM_EVENTS = 10
class Event :
def __init__ ( self , type ) :
self . type = type
self . fromplayer = Game . myplayer
class ClickEvent ( Event ) :
2005-06-15 18:09:46 +00:00
def __init__ ( self , ( xvel , yvel ) , player ) :
2005-06-14 23:15:01 +00:00
Event . __init__ ( self , EV_CLICK )
self . xvel = xvel
self . yvel = yvel
self . player = player
class HitEvent ( Event ) :
def __init__ ( self , xvel , yvel ) :
Event . __init__ ( self , EV_HIT )
self . xvel = xvel
self . yvel = yvel
class ScoreEvent ( Event ) :
def __init__ ( self , player ) :
Event . __init__ ( self , EV_SCORE )
self . player = player
class BounceEvent ( Event ) :
def __init__ ( self ) :
Event . __init__ ( self , EV_BOUNCE )
class PlayerUpdateEvent ( Event ) :
def __init__ ( self ) :
Event . __init__ ( self , EV_PLAYER_UPDATE )
class ServeEvent ( Event ) :
def __init__ ( self ) :
Event . __init__ ( self , EV_SERVE )
class BallPosEvent ( Event ) :
def __init__ ( self , x , y ) :
Event . __init__ ( self , EV_BALLPOS )
self . x = x
self . y = y
class ConnectEvent ( Event ) :
def __init__ ( self ) :
Event . __init__ ( self , EV_CONNECT )
class PlayerSwitchEvent ( Event ) :
lastseq = 0
def __init__ ( self , xvel , yvel , xpos , ypos , stopped ) :
Event . __init__ ( self , EV_PLAYER_SWITCH )
self . xvel = xvel
self . yvel = yvel
self . xpos = xpos
self . ypos = ypos
self . stopped = stopped
self . seq = PlayerSwitchEvent . lastseq
PlayerSwitchEvent . lastseq = PlayerSwitchEvent . lastseq + 1
class HelloEvent ( Event ) :
def __init__ ( self ) :
Event . __init__ ( self , EV_HELLO )
#@-node:jpenner.20050305130011:Types
#@-others
#@nonl
#@-node:jpenner.20050305124252:Events
#@+node:jpenner.20050319143816:Networking
#@+others
#@+node:jpenner.20050320120319:Message
class TFTMessage :
def __init__ ( self , sequence , event ) :
self . sequence = sequence
self . event = event
#@nonl
#@-node:jpenner.20050320120319:Message
#@+node:jpenner.20050424135832:Protocol
class TFTProtocol ( protocol . DatagramProtocol ) :
def __init__ ( self ) :
self . mysequence = 0
self . hissequence = - 1
self . address = None
Game . evMgr . registerHandler ( EV_BALLPOS , self . sendEvent )
Game . evMgr . registerHandler ( EV_SERVE , self . sendEvent )
Game . evMgr . registerHandler ( EV_PLAYER_SWITCH , self . sendEvent )
Game . evMgr . registerHandler ( EV_CONNECT , self . connectTransport )
def startProtocol ( self ) :
if self . address < > None :
log ( self . address )
def datagramReceived ( self , data , address ) :
if self . address == None :
self . address = address
log ( address )
msg = cPickle . Unpickler ( StringIO . StringIO ( data ) ) . load ( )
if ( msg . sequence > self . hissequence ) :
self . hissequence = msg . sequence
if msg . event < > None :
log ( " get " + str ( msg . event . type ) + " : " + str ( msg . event . fromplayer ) )
Game . evMgr . postEvent ( msg . event )
def sendEvent ( self , event ) :
if ( self . address < > None ) and ( self . transport < > None ) and ( event . fromplayer == Game . myplayer ) :
log ( " send " + str ( event . type ) )
self . mysequence = self . mysequence + 1
s = StringIO . StringIO ( )
cPickle . Pickler ( s ) . dump ( TFTMessage ( self . mysequence , event ) )
self . transport . write ( s . getvalue ( ) , self . address )
def connectTransport ( self , event ) :
Game . evMgr . postEvent ( HelloEvent ( ) )
#@-node:jpenner.20050424135832:Protocol
#@-others
#@-node:jpenner.20050319143816:Networking
#@+node:jpenner.20050604112932:Menu
#@+others
#@+node:jpenner.20050605142055:Base Menu
class BaseMenu ( WidgetWindow ) :
def __init__ ( self ) :
WidgetWindow . __init__ ( self , Game . screen )
self . menuLayout ( )
def onEnter ( self ) :
self . invalidaterect ( )
self . eventproc ( pygame . event . Event ( NOEVENT , { } ) )
def tick ( self ) :
for event in pygame . event . get ( ) :
if event . type == QUIT :
reactor . stop ( )
try :
self . eventproc ( event )
except :
pass
#@-node:jpenner.20050605142055:Base Menu
#@+node:jpenner.20050605102056:Main Menu
class MainMenu ( BaseMenu ) :
def menuLayout ( self ) :
#@ << Menu layout >>
#@+node:jpenner.20050605102056.1:<< Menu layout >>
self . addwidget ( TextClass ( self , ( 120 , 40 , 400 , 30 ) , " Tennis For Two " , 36 ) )
self . addwidget ( ButtonClass ( self , self . startSingle , ( 120 , 120 , 400 , 30 ) , " Start Single-Player Game " ) )
2005-06-16 15:25:45 +00:00
self . addwidget ( ButtonClass ( self , self . startAI , ( 120 , 155 , 400 , 30 ) , " Start Game Vs. Tennis-O-Tron " ) )
2005-06-14 23:15:01 +00:00
2005-06-16 15:25:45 +00:00
self . addwidget ( TextClass ( self , ( 120 , 220 , 450 , 20 ) , " Your IP Address: " + getMyIP ( ) ) )
self . addwidget ( TextClass ( self , ( 120 , 245 , 60 , 20 ) , " Port: " ) )
self . serverPort = EditClass ( self , None , ( 180 , 245 , 60 , 20 ) , " 7554 " )
2005-06-14 23:15:01 +00:00
self . addwidget ( self . serverPort )
2005-06-16 15:25:45 +00:00
self . addwidget ( ButtonClass ( self , self . startServer , ( 120 , 270 , 400 , 30 ) , " Start Server " ) )
2005-06-14 23:15:01 +00:00
self . addwidget ( TextClass ( self , ( 120 , 310 , 160 , 20 ) , " Server Address: " ) )
self . clientAddr = EditClass ( self , None , ( 280 , 310 , 240 , 20 ) )
self . addwidget ( self . clientAddr )
self . addwidget ( TextClass ( self , ( 120 , 335 , 60 , 20 ) , " Port: " ) )
self . clientPort = EditClass ( self , None , ( 180 , 335 , 60 , 20 ) , " 7554 " )
self . addwidget ( self . clientPort )
self . addwidget ( ButtonClass ( self , self . startClient , ( 120 , 360 , 400 , 30 ) , " Start Client " ) )
#@-node:jpenner.20050605102056.1:<< Menu layout >>
#@nl
def startSingle ( self ) :
2005-06-16 15:25:45 +00:00
Game . myplayer = SINGLE_PLAYER
Game . isClient = False
Game . rules . infinitehits = True
Game . gameMgr . changeState ( Game . gameMgr . STATE_SINGLE_PLAYER )
def startAI ( self ) :
2005-06-15 18:09:46 +00:00
Game . myplayer = 1
2005-06-14 23:15:01 +00:00
Game . isClient = False
2005-06-15 18:09:46 +00:00
Game . AIPlayer = 2
2005-06-14 23:15:01 +00:00
Game . gameMgr . changeState ( Game . gameMgr . STATE_SINGLE_PLAYER )
2005-06-16 15:25:45 +00:00
2005-06-14 23:15:01 +00:00
def startServer ( self ) :
Game . myplayer = 1
Game . isClient = False
Game . ServerPort = int ( self . serverPort . text )
Game . gameMgr . changeState ( Game . gameMgr . STATE_WAIT_FOR_CLIENT )
def startClient ( self ) :
Game . myplayer = 2
Game . isClient = True
Game . ServerPort = int ( self . clientPort . text )
Game . ServerIP = self . clientAddr . text
Game . gameMgr . changeState ( Game . gameMgr . STATE_CONNECT )
#@-node:jpenner.20050605102056:Main Menu
#@+node:jpenner.20050605173506:Server Wait Screen
class ServerWait ( BaseMenu ) :
def menuLayout ( self ) :
self . addwidget ( TextClass ( self , ( 120 , 180 , 400 , 40 ) , " Waiting For Client... " , 48 ) )
self . addwidget ( ButtonClass ( self , self . cancel , ( 120 , 360 , 400 , 30 ) , " Cancel " ) )
self . connected = False
Game . evMgr . registerHandler ( EV_HELLO , self . connect )
def cancel ( self ) :
Game . gameMgr . changeState ( Game . gameMgr . STATE_MENU )
def connect ( self , ev ) :
self . connected = True
def tick ( self ) :
BaseMenu . tick ( self )
if self . connected :
self . connected = False
Game . gameMgr . changeState ( Game . gameMgr . STATE_NETWORK_GAME )
#@nonl
#@-node:jpenner.20050605173506:Server Wait Screen
#@+node:jpenner.20050605180040:Client Wait Screen
class ClientWait ( BaseMenu ) :
def menuLayout ( self ) :
self . addwidget ( TextClass ( self , ( 120 , 180 , 400 , 40 ) , " Contacting Server... " , 48 ) )
self . addwidget ( ButtonClass ( self , self . cancel , ( 120 , 360 , 400 , 30 ) , " Cancel " ) )
self . connected = False
Game . evMgr . registerHandler ( EV_BALLPOS , self . connect )
def cancel ( self ) :
Game . gameMgr . changeState ( Game . gameMgr . STATE_MENU )
def connect ( self , ev ) :
self . connected = True
def tick ( self ) :
BaseMenu . tick ( self )
if self . connected :
self . connected = False
Game . gameMgr . changeState ( Game . gameMgr . STATE_NETWORK_GAME )
else :
Game . network . sendEvent ( ConnectEvent ( ) )
#@-node:jpenner.20050605180040:Client Wait Screen
2005-06-16 15:25:45 +00:00
#@+node:jpenner.20050616085506:Failure Screen
class FailScreen ( BaseMenu ) :
def menuLayout ( self ) :
self . errText = MultiLineTextClass ( self , ( 120 , 60 , 400 , 270 ) , " " , 24 )
self . addwidget ( self . errText )
self . addwidget ( ButtonClass ( self , self . ok , ( 120 , 360 , 400 , 30 ) , " OK " ) )
def ok ( self ) :
Game . gameMgr . changeState ( Game . gameMgr . STATE_MENU )
def onEnter ( self ) :
BaseMenu . onEnter ( self )
self . errText . settext ( Game . failure )
#@nonl
#@-node:jpenner.20050616085506:Failure Screen
2005-06-14 23:15:01 +00:00
#@-others
#@-node:jpenner.20050604112932:Menu
#@+node:jpenner.20050305121157:Game
#@+others
#@+node:jpenner.20050604175241:Graphics
#@+others
#@+node:jpenner.20050604192205:Generic Graphics Manager
class GameGraphicsMgr :
def __init__ ( self ) :
self . all = pygame . sprite . RenderUpdates ( )
self . bkg = pygame . Surface ( ( SCREENRECT . width , SCREENRECT . height ) )
self . bkg . fill ( pygame . color . Color ( " black " ) )
self . trackedSprites = [ ]
def trackSprite ( self , sprite , spriteToTrack ) :
self . trackedSprites . append ( { ' sprite ' : sprite , ' spriteToTrack ' : spriteToTrack } )
def clearScreen ( self ) :
Game . screen . set_clip ( )
Game . screen . fill ( pygame . color . Color ( " black " ) )
pygame . display . update ( )
def tick ( self ) :
for item in self . trackedSprites :
item [ ' sprite ' ] . rect . centerx = item [ ' spriteToTrack ' ] . rect . centerx
item [ ' sprite ' ] . rect . centery = item [ ' spriteToTrack ' ] . rect . centery
self . all . clear ( Game . screen , self . bkg )
self . all . update ( )
dirty = self . all . draw ( Game . screen )
pygame . display . update ( dirty )
#@-node:jpenner.20050604192205:Generic Graphics Manager
#@+node:jpenner.20050604175241.1:TFT Graphics Manager
class TFTGraphicsMgr ( GameGraphicsMgr ) :
def __init__ ( self ) :
GameGraphicsMgr . __init__ ( self )
Floor . containers = self . all
Net . containers = self . all
Ball . containers = self . all
ShotLine . containers = self . all
Game . ball = Ball ( BALL_STARTX , BALL_STARTY )
Game . floor = Floor ( )
Game . net = Net ( )
self . shotLine = ShotLine ( )
self . trackSprite ( self . shotLine , Game . ball )
def tick ( self ) :
self . shotLine . updateAngle ( )
GameGraphicsMgr . tick ( self )
#@-node:jpenner.20050604175241.1:TFT Graphics Manager
#@+node:jpenner.20050305120654:Sprites
#@+others
#@+node:jpenner.20050305112032:Floor
class Floor ( pygame . sprite . Sprite ) :
def __init__ ( self ) :
pygame . sprite . Sprite . __init__ ( self , self . containers )
self . image = pygame . Surface ( ( SCREENRECT . width - ( SCREENRECT . width / 8 ) , SCREENRECT . height / 96 ) )
self . image . fill ( pygame . color . Color ( " white " ) )
self . rect = self . image . get_rect ( )
self . reloading = 0
self . rect . centerx = SCREENRECT . centerx
self . rect . bottom = SCREENRECT . bottom * 0.875
self . origtop = self . rect . top
self . facing = - 1
#@nonl
#@-node:jpenner.20050305112032:Floor
#@+node:jpenner.20050305120626:Net
class Net ( pygame . sprite . Sprite ) :
def __init__ ( self ) :
pygame . sprite . Sprite . __init__ ( self , self . containers )
self . image = pygame . Surface ( ( SCREENRECT . width / 128 , SCREENRECT . height / 12 ) )
self . image . fill ( pygame . color . Color ( " white " ) )
self . rect = self . image . get_rect ( )
self . reloading = 0
self . rect . centerx = SCREENRECT . centerx
self . rect . bottom = SCREENRECT . bottom * 0.875
self . origtop = self . rect . top
self . facing = - 1
#@-node:jpenner.20050305120626:Net
#@+node:jpenner.20050427175728:Ball
class Ball ( pygame . sprite . Sprite ) :
def __init__ ( self , x , y ) :
pygame . sprite . Sprite . __init__ ( self , self . containers )
self . image = pygame . Surface ( ( SCREENRECT . width / 128 , SCREENRECT . height / 96 ) )
self . image . fill ( pygame . color . Color ( " white " ) )
self . rect = self . image . get_rect ( )
self . reloading = 0
self . rect . centerx = x
self . rect . centery = y
self . origtop = self . rect . top
self . facing = - 1
def currentPlayer ( self ) :
if self . rect . centerx < Game . net . rect . centerx :
return 1
else :
return 2
#@-node:jpenner.20050427175728:Ball
#@+node:jpenner.20050305123236:Shot Line
class ShotLine ( pygame . sprite . Sprite ) :
def __init__ ( self ) :
pygame . sprite . Sprite . __init__ ( self , self . containers )
self . image = pygame . Surface ( ( SCREENRECT . width / 32 , SCREENRECT . height / 24 ) , SRCALPHA )
self . image . set_colorkey ( pygame . color . Color ( " purple " ) )
self . rect = self . image . get_rect ( )
self . updateAngle ( )
self . reloading = 0
self . origtop = self . rect . top
self . facing = - 1
def updateAngle ( self ) :
angle = makeAngle ( pygame . mouse . get_pos ( ) )
self . image . fill ( pygame . color . Color ( " purple " ) ) # clear
pygame . draw . line ( self . image , pygame . color . Color ( " white " ) ,
anglePos ( self . image . get_rect ( ) , angle , 6 ) , anglePos ( self . image . get_rect ( ) , angle , 12 ) )
#@-node:jpenner.20050305123236:Shot Line
#@-others
#@nonl
#@-node:jpenner.20050305120654:Sprites
#@-others
#@nonl
#@-node:jpenner.20050604175241:Graphics
#@+node:jpenner.20050320112219:Physics
#@+others
#@+node:jpenner.20050424152556:Physics Manager
class PhysicsMgr :
def __init__ ( self ) :
self . gamePhysics = PhysicsEngine ( )
self . netPhysics = NetworkPhysicsEngine ( )
Game . evMgr . registerHandler ( EV_PLAYER_UPDATE , self . switchEngines )
Game . evMgr . registerHandler ( EV_PLAYER_SWITCH , self . switchToGame )
def onEnter ( self ) :
if Game . currentplayer == Game . myplayer :
self . current = self . gamePhysics
else :
self . current = self . netPhysics
self . lastSeq = - 1
def switchEngines ( self , ev ) :
if Game . ball . currentPlayer ( ) < > Game . myplayer :
self . current = self . netPhysics
self . current . switchStarted = True
else :
self . current = self . gamePhysics
def switchToGame ( self , ev ) :
if ev . fromplayer < > Game . myplayer :
if ev . seq > self . lastSeq :
self . lastSeq = ev . seq
self . gamePhysics . resync ( ev )
Game . evMgr . postEvent ( PlayerUpdateEvent ( ) )
def tick ( self ) :
self . current . tick ( )
#@nonl
#@-node:jpenner.20050424152556:Physics Manager
#@+node:jpenner.20050306103246:Game Physics
class PhysicsEngine :
def __init__ ( self ) :
Game . evMgr . registerHandler ( EV_HIT , self . hitHandler )
Game . evMgr . registerHandler ( EV_SERVE , self . serveHandler )
self . xvel = 0
self . yvel = 0
self . stopped = True
def tick ( self ) :
# only do gravity / collision detection if ball is moving
if not self . stopped :
# Gravity
self . yvel + = GRAVITY
#@ << Floor collision >>
#@+node:jpenner.20050310185348:<< Floor collision >>
if ( Game . ball . rect . bottom + self . yvel ) > Game . floor . rect . top :
self . yvel = - abs ( self . yvel ) + FRICTION
if self . yvel > 0 :
self . yvel = 0
self . xvel = 0
self . stopped = True
Game . evMgr . postEvent ( BounceEvent ( ) )
#@nonl
#@-node:jpenner.20050310185348:<< Floor collision >>
#@nl
#@ << Net collision >>
#@+node:jpenner.20050310185348.1:<< Net collision >>
if ( ( ( ( Game . ball . rect . right < Game . net . rect . left ) and ( ( Game . ball . rect . right + self . xvel ) > = Game . net . rect . left ) ) or
( ( Game . ball . rect . left > Game . net . rect . right ) and ( ( Game . ball . rect . left + self . xvel ) < = Game . net . rect . right ) ) ) and
( ( Game . ball . rect . bottom + self . yvel ) > Game . net . rect . top ) ) :
self . xvel = - self . xvel
#@-node:jpenner.20050310185348.1:<< Net collision >>
#@nl
#@ << Switch players >>
#@+node:jpenner.20050310185414:<< Switch players >>
if ( ( Game . ball . rect . centerx < Game . net . rect . centerx ) and ( ( Game . ball . rect . centerx + self . xvel ) > = Game . net . rect . centerx ) or
( Game . ball . rect . centerx > = Game . net . rect . centerx ) and ( ( Game . ball . rect . centerx + self . xvel ) < Game . net . rect . centerx ) ) :
Game . evMgr . postEvent ( PlayerUpdateEvent ( ) )
#@-node:jpenner.20050310185414:<< Switch players >>
#@nl
Game . ball . rect . centerx + = self . xvel
Game . ball . rect . centery + = self . yvel
Game . evMgr . postEvent ( BallPosEvent ( Game . ball . rect . centerx , Game . ball . rect . centery ) )
Game . switchEv = PlayerSwitchEvent ( self . xvel , self . yvel , Game . ball . rect . centerx , Game . ball . rect . centery , self . stopped )
def hitHandler ( self , hitEvent ) :
self . xvel = hitEvent . xvel
self . yvel = hitEvent . yvel
self . stopped = False
def serveHandler ( self , ev ) :
self . stopped = True
def resync ( self , ev ) :
self . xvel = ev . xvel
self . yvel = ev . yvel
Game . ball . rect . centerx = ev . xpos
Game . ball . rect . centery = ev . ypos
self . stopped = ev . stopped
#@nonl
#@-node:jpenner.20050306103246:Game Physics
#@+node:jpenner.20050320112219.1:Network Physics
class NetworkPhysicsEngine :
def __init__ ( self ) :
Game . evMgr . registerHandler ( EV_BALLPOS , self . updatePos )
self . switchStarted = False
def updatePos ( self , ev ) :
if ( ev . fromplayer < > Game . myplayer ) :
self . switchStarted = False
Game . ball . rect . centerx = ev . x
Game . ball . rect . centery = ev . y
def tick ( self ) :
log ( " netphysics " )
if self . switchStarted :
Game . evMgr . postEvent ( Game . switchEv )
#@-node:jpenner.20050320112219.1:Network Physics
#@-others
#@nonl
#@-node:jpenner.20050320112219:Physics
2005-06-15 18:09:46 +00:00
#@+node:jpenner.20050615085910:AI
class AIPlayer :
def tick ( self ) :
if random . randint ( 0 , 13 ) == 1 :
angle = math . radians ( random . randint ( 180 + 10 , 270 - 10 ) )
Game . evMgr . postEvent ( ClickEvent ( velocityFromAngle ( angle ) , Game . AIPlayer ) )
#@-node:jpenner.20050615085910:AI
2005-06-14 23:15:01 +00:00
#@+node:jpenner.20050307180329:Sound
class SoundEngine :
def __init__ ( self ) :
f = { EV_HIT : ' hit.wav ' ,
EV_BOUNCE : ' bounce.wav ' ,
EV_SCORE : ' score.wav ' ,
EV_HELLO : ' connect.wav ' }
self . sound = { }
for evtype , filename in f . iteritems ( ) :
try :
self . sound [ evtype ] = pygame . mixer . Sound ( ' data/ ' + filename )
Game . evMgr . registerHandler ( evtype , self . noise )
except :
pass
def noise ( self , ev ) :
self . sound [ ev . type ] . play ( )
#@-node:jpenner.20050307180329:Sound
#@+node:jpenner.20050312135503:Rules
class RuleManager :
def __init__ ( self ) :
Game . evMgr . registerHandler ( EV_CLICK , self . clickHandler )
Game . evMgr . registerHandler ( EV_SERVE , self . serveHandler )
Game . evMgr . registerHandler ( EV_PLAYER_UPDATE , self . updateHandler )
self . playerhit = False
self . infinitehits = False
Game . currentplayer = 1
def clickHandler ( self , ev ) :
if not self . playerhit and ( ev . player == Game . currentplayer or ev . player == SINGLE_PLAYER ) :
Game . evMgr . postEvent ( HitEvent ( ev . xvel , ev . yvel ) )
if not self . infinitehits :
self . playerhit = True
def updateHandler ( self , ev ) :
self . playerhit = False
Game . currentplayer = Game . ball . currentPlayer ( )
def serveHandler ( self , ev ) :
Game . ball . rect . centerx = BALL_STARTX
Game . ball . rect . centery = BALL_STARTY
Game . evMgr . postEvent ( PlayerUpdateEvent ( ) )
#@-node:jpenner.20050312135503:Rules
#@+node:jpenner.20050310200258:Input
class InputManager :
def tick ( self ) :
for event in pygame . event . get ( ) :
if event . type == QUIT :
reactor . stop ( )
self . sdl_event ( event )
def sdl_event ( self , event ) :
if event . type == MOUSEBUTTONUP :
angle = makeAngle ( event . pos )
2005-06-15 18:09:46 +00:00
Game . evMgr . postEvent ( ClickEvent ( velocityFromAngle ( angle ) , Game . myplayer ) )
2005-06-14 23:15:01 +00:00
if event . type == KEYUP :
Game . evMgr . postEvent ( ServeEvent ( ) )
#@nonl
#@-node:jpenner.20050310200258:Input
#@+node:jpenner.20050305122430:Game Loop
#@+others
#@+node:jpenner.20050606072251:State Machine
class GameState :
def __init__ ( self , manager , onenter = None , onexit = None ) :
def doNothing ( ) : pass
if manager < > None :
self . tick = manager . tick
else :
self . tick = doNothing
if onenter < > None :
self . enter = onenter
else :
self . enter = doNothing
if onexit < > None :
self . exit = onexit
else :
self . exit = doNothing
class StateMachine :
def __init__ ( self , states , initialState = 0 ) :
self . state = initialState
self . states = states
self . states [ self . state ] . enter ( )
self . newstate = - 1
def changeState ( self , newstate ) :
# Don't change states in the middle of a tick!
self . newstate = newstate
def tick ( self ) :
if self . newstate > = 0 :
self . states [ self . state ] . exit ( )
self . state = self . newstate
self . states [ self . state ] . enter ( )
self . newstate = - 1
self . states [ self . state ] . tick ( )
class GameLoop :
def __init__ ( self , managers ) :
self . managers = managers
def tick ( self ) :
for manager in self . managers :
manager . tick ( )
#@-node:jpenner.20050606072251:State Machine
#@+node:jpenner.20050606072251.1:Game Manager
class GameMgr ( StateMachine ) :
def __init__ ( self ) :
self . STATE_MENU = 0
self . STATE_NETWORK_GAME = 1
self . STATE_SINGLE_PLAYER = 2
self . STATE_WAIT_FOR_CLIENT = 3
self . STATE_CONNECT = 4
2005-06-16 15:25:45 +00:00
self . STATE_FAILURE = 5
2005-06-14 23:15:01 +00:00
inputMgr = InputManager ( )
self . graphicsMgr = TFTGraphicsMgr ( )
self . physicsMgr = PhysicsMgr ( )
mainMenu = MainMenu ( )
2005-06-16 15:25:45 +00:00
failScreen = FailScreen ( )
2005-06-14 23:15:01 +00:00
self . serverWait = ServerWait ( )
self . clientWait = ClientWait ( )
StateMachine . __init__ ( self ,
2005-06-16 15:25:45 +00:00
[ GameState ( mainMenu , mainMenu . onEnter ) ,
2005-06-14 23:15:01 +00:00
GameState ( GameLoop ( [ inputMgr , Game . evMgr , self . physicsMgr , self . graphicsMgr ] ) , self . gameEnter ) ,
2005-06-15 18:09:46 +00:00
GameState ( GameLoop ( [ inputMgr , AIPlayer ( ) , Game . evMgr , PhysicsEngine ( ) , self . graphicsMgr ] ) , self . gameEnter ) ,
2005-06-14 23:15:01 +00:00
GameState ( GameLoop ( [ Game . evMgr , self . serverWait ] ) , self . serverEnter , self . networkExit ) ,
2005-06-16 15:25:45 +00:00
GameState ( GameLoop ( [ Game . evMgr , self . clientWait ] ) , self . clientEnter , self . networkExit ) ,
2005-11-14 14:08:54 +00:00
GameState ( failScreen , failScreen . onEnter )
2005-06-14 23:15:01 +00:00
] )
def gameEnter ( self ) :
self . graphicsMgr . clearScreen ( )
self . physicsMgr . onEnter ( )
def clientEnter ( self ) :
2005-06-16 15:25:45 +00:00
self . clientWait . onEnter ( )
2005-06-14 23:15:01 +00:00
Game . network = TFTProtocol ( )
2005-11-14 14:08:54 +00:00
def onResolve ( ip , self = self ) :
if self . state == self . STATE_CONNECT :
Game . network . address = ( ip , Game . ServerPort )
Game . port = reactor . listenUDP ( 0 , Game . network )
def onFail ( err , self = self ) :
if self . state == self . STATE_CONNECT :
Game . failure = err . getErrorMessage ( )
self . changeState ( self . STATE_FAILURE )
2005-06-16 15:25:45 +00:00
reactor . resolve ( Game . ServerIP ) . addCallback ( onResolve ) . addErrback ( onFail )
2005-06-14 23:15:01 +00:00
def serverEnter ( self ) :
2005-06-16 15:25:45 +00:00
self . serverWait . onEnter ( )
2005-06-14 23:15:01 +00:00
Game . network = TFTProtocol ( )
2005-06-16 15:25:45 +00:00
try :
Game . port = reactor . listenUDP ( Game . ServerPort , Game . network )
log ( " Listening on " + str ( Game . ServerPort ) )
except :
Game . failure = " Unable to listen on UDP port " + str ( Game . ServerPort ) + " . Make sure nothing else is using that port, and you have the proper permissions to create a server on it. "
self . changeState ( self . STATE_FAILURE )
2005-06-14 23:15:01 +00:00
def networkExit ( self ) :
2005-06-16 15:25:45 +00:00
if ( ( self . newstate == self . STATE_MENU ) or ( self . newstate == self . STATE_FAILURE ) ) and ( Game . port < > None ) : # cancelled
2005-06-14 23:15:01 +00:00
Game . port . stopListening ( )
#@-node:jpenner.20050606072251.1:Game Manager
#@-others
#@-node:jpenner.20050305122430:Game Loop
#@+node:jpenner.20050319125841:Setup
def main ( ) :
2005-06-15 18:09:46 +00:00
random . seed ( )
2005-06-14 23:15:01 +00:00
pygame . init ( )
2005-06-16 15:25:45 +00:00
# startLogging('tennis.log')
2005-06-14 23:15:01 +00:00
2005-06-16 17:08:43 +00:00
pygame . display . set_caption ( ' Tennis For Two ' )
pygame . display . set_icon ( pygame . image . load ( ' tennis.bmp ' ) )
2005-06-14 23:15:01 +00:00
Game . screen = pygame . display . set_mode ( SCREENRECT . size , DOUBLEBUF , 16 )
Game . evMgr = EventManager ( )
Game . sound = SoundEngine ( )
Game . rules = RuleManager ( )
Game . gameMgr = GameMgr ( )
task . LoopingCall ( Game . gameMgr . tick ) . start ( 0.03 )
reactor . run ( )
#cleanup
2005-06-16 15:25:45 +00:00
# stopLogging()
2005-06-14 23:15:01 +00:00
2005-06-16 17:08:43 +00:00
2005-06-14 23:15:01 +00:00
#@-node:jpenner.20050319125841:Setup
#@-others
#@nonl
#@-node:jpenner.20050305121157:Game
#@-others
if __name__ == ' __main__ ' : main ( )
#@nonl
#@-node:jpenner.20050305105206:@thin TennisForTwo.py
#@-leo