3 # troncode - write programs to play the classic lines game
5 # Copyright (C) 2008 Andy Balaam
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License
9 # as published by the Free Software Foundation; either version 2
10 # of the License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
27 from pygame
.locals import *
33 from BasicGameBoard
import BasicGameBoard
34 from mopelib
import mopelib
35 from troncode_values
import *
37 # ----------------------
38 class TronCodeConfig( mopelib
.Config
):
40 def default_config( self
):
44 self
.screen_size
= ( 610, 635 )
45 self
.colour_background
= ( 0, 0, 0 )
50 self
.sound_effects_on
= 1
52 self
.keys_menu
= mopelib
.MyInputEvent( "Escape" )
53 self
.keys_menu
.add( pygame
.KEYDOWN
, pygame
.K_ESCAPE
)
54 self
.keys_menu
.add( pygame
.JOYBUTTONDOWN
, 8 ) # GP2X Start
55 self
.keys_menu
.add( pygame
.JOYBUTTONDOWN
, 9 ) # GP2X Select
57 self
.keys_return
= mopelib
.MyInputEvent( "Return" )
58 self
.keys_return
.add( pygame
.KEYDOWN
, pygame
.K_RETURN
)
59 self
.keys_return
.add( pygame
.JOYBUTTONDOWN
, 13 ) # GP2X B button
61 self
.keys_startgame
= mopelib
.MyInputEvent( "any key" )
62 self
.keys_startgame
.add_all( pygame
.KEYDOWN
)
63 self
.keys_startgame
.add_all( pygame
.JOYBUTTONDOWN
)
64 self
.keys_startgame
.add_all( pygame
.MOUSEBUTTONDOWN
)
66 self
.keys_up
= mopelib
.MyInputEvent( "up" )
67 self
.keys_up
.add( pygame
.KEYDOWN
, ord( 'q' ) )
68 self
.keys_up
.add( pygame
.KEYDOWN
, pygame
.K_UP
)
69 self
.keys_up
.add( pygame
.JOYBUTTONDOWN
, 0 ) # GP2X Joy up
70 self
.keys_up
.add( pygame
.JOYBUTTONDOWN
, 15 ) # GP2X Y button
72 self
.keys_right
= mopelib
.MyInputEvent( "right" )
73 self
.keys_right
.add( pygame
.KEYDOWN
, ord( 'p' ) )
74 self
.keys_right
.add( pygame
.KEYDOWN
, pygame
.K_RIGHT
)
75 self
.keys_right
.add( pygame
.JOYBUTTONDOWN
, 6 ) # GP2X Joy right
76 self
.keys_right
.add( pygame
.JOYBUTTONDOWN
, 13 ) # GP2X B button
78 self
.keys_down
= mopelib
.MyInputEvent( "down" )
79 self
.keys_down
.add( pygame
.KEYDOWN
, ord( 'a' ) )
80 self
.keys_down
.add( pygame
.KEYDOWN
, pygame
.K_DOWN
)
81 self
.keys_down
.add( pygame
.JOYBUTTONDOWN
, 4 ) # GP2X Joy down
82 self
.keys_down
.add( pygame
.JOYBUTTONDOWN
, 14 ) # GP2X X button
84 self
.keys_left
= mopelib
.MyInputEvent( "left" )
85 self
.keys_left
.add( pygame
.KEYDOWN
, ord( 'o' ) )
86 self
.keys_left
.add( pygame
.KEYDOWN
, pygame
.K_LEFT
)
87 self
.keys_left
.add( pygame
.JOYBUTTONDOWN
, 2 ) # GP2X Joy left
88 self
.keys_left
.add( pygame
.JOYBUTTONDOWN
, 12 ) # GP2X A button
90 self
.keys_volup
= mopelib
.MyInputEvent( "+" )
91 self
.keys_volup
.add( pygame
.KEYDOWN
, ord( '+' ) )
92 self
.keys_volup
.add( pygame
.KEYDOWN
, ord( '=' ) )
93 self
.keys_volup
.add( pygame
.JOYBUTTONDOWN
, 16 ) # GP2X volume + button
95 self
.keys_slow
= mopelib
.MyInputEvent( "s" )
96 self
.keys_slow
.add( pygame
.KEYDOWN
, ord( 's' ) )
98 self
.keys_fast
= mopelib
.MyInputEvent( "f" )
99 self
.keys_fast
.add( pygame
.KEYDOWN
, ord( 'f' ) )
101 self
.keys_skip_to_end
= mopelib
.MyInputEvent( "k" )
102 self
.keys_skip_to_end
.add( pygame
.KEYDOWN
, ord( 'k' ) )
104 self
.keys_voldown
= mopelib
.MyInputEvent( "-" )
105 self
.keys_voldown
.add( pygame
.KEYDOWN
, ord( '-' ) )
106 self
.keys_voldown
.add( pygame
.JOYBUTTONDOWN
, 17 ) # GP2X volume - button
108 self
.keys_p1_up
= mopelib
.MyInputEvent( "p1_up" )
109 self
.keys_p1_up
.add( pygame
.KEYDOWN
, pygame
.K_UP
)
110 self
.keys_p1_up
.add( pygame
.JOYBUTTONDOWN
, 0 ) # GP2X Joy up
112 self
.keys_p1_right
= mopelib
.MyInputEvent( "p1_right" )
113 self
.keys_p1_right
.add( pygame
.KEYDOWN
, pygame
.K_RIGHT
)
114 self
.keys_p1_right
.add( pygame
.JOYBUTTONDOWN
, 6 ) # GP2X Joy right
116 self
.keys_p1_down
= mopelib
.MyInputEvent( "p1_down" )
117 self
.keys_p1_down
.add( pygame
.KEYDOWN
, pygame
.K_DOWN
)
118 self
.keys_p1_down
.add( pygame
.JOYBUTTONDOWN
, 4 ) # GP2X Joy down
120 self
.keys_p1_left
= mopelib
.MyInputEvent( "p1_left" )
121 self
.keys_p1_left
.add( pygame
.KEYDOWN
, pygame
.K_LEFT
)
122 self
.keys_p1_left
.add( pygame
.JOYBUTTONDOWN
, 4 ) # GP2X Joy left
124 self
.keys_p2_up
= mopelib
.MyInputEvent( "p2_up" )
125 self
.keys_p2_up
.add( pygame
.KEYDOWN
, ord( '2' ) )
126 self
.keys_p2_up
.add( pygame
.JOYBUTTONDOWN
, 15 ) # GP2X Y button
128 self
.keys_p2_right
= mopelib
.MyInputEvent( "p2_right" )
129 self
.keys_p2_right
.add( pygame
.KEYDOWN
, ord( 'e' ) )
130 self
.keys_p2_right
.add( pygame
.JOYBUTTONDOWN
, 13 ) # GP2X B button
132 self
.keys_p2_down
= mopelib
.MyInputEvent( "p2_down" )
133 self
.keys_p2_down
.add( pygame
.KEYDOWN
, ord( 'w' ) )
134 self
.keys_p2_down
.add( pygame
.JOYBUTTONDOWN
, 14 ) # GP2X X button
136 self
.keys_p2_left
= mopelib
.MyInputEvent( "p2_left" )
137 self
.keys_p2_left
.add( pygame
.KEYDOWN
, ord( 'q' ) )
138 self
.keys_p2_left
.add( pygame
.JOYBUTTONDOWN
, 12 ) # GP2X A button
140 self
.keys_p3_up
= mopelib
.MyInputEvent( "p3_up" )
141 self
.keys_p3_up
.add( pygame
.KEYDOWN
, ord( 'i' ) )
143 self
.keys_p3_right
= mopelib
.MyInputEvent( "p3_right" )
144 self
.keys_p3_right
.add( pygame
.KEYDOWN
, ord( 'l' ) )
146 self
.keys_p3_down
= mopelib
.MyInputEvent( "p3_down" )
147 self
.keys_p3_down
.add( pygame
.KEYDOWN
, ord( 'k' ) )
149 self
.keys_p3_left
= mopelib
.MyInputEvent( "p3_left" )
150 self
.keys_p3_left
.add( pygame
.KEYDOWN
, ord( 'j' ) )
152 self
.keys_p4_up
= mopelib
.MyInputEvent( "p4_up" )
153 self
.keys_p4_up
.add( pygame
.KEYDOWN
, pygame
.K_KP8
)
155 self
.keys_p4_right
= mopelib
.MyInputEvent( "p4_right" )
156 self
.keys_p4_right
.add( pygame
.KEYDOWN
, pygame
.K_KP6
)
158 self
.keys_p4_down
= mopelib
.MyInputEvent( "p4_down" )
159 self
.keys_p4_down
.add( pygame
.KEYDOWN
, pygame
.K_KP5
)
160 self
.keys_p4_down
.add( pygame
.KEYDOWN
, pygame
.K_KP2
)
162 self
.keys_p4_left
= mopelib
.MyInputEvent( "p4_left" )
163 self
.keys_p4_left
.add( pygame
.KEYDOWN
, pygame
.K_KP4
)
166 # ----------------------
168 class TronCodeSoundManager( mopelib
.SoundManager
):
170 def __init__( self
, volume
):
171 mopelib
.SoundManager
.__init
__( self
, config
)
173 #self.add_sample_group( "waddles", ["waddle1"] )
175 # ----------------------
177 def intro_draw_keys():
178 keys_colour
= (0, 0, 0)
179 write_text( "Keys", keys_colour
, 0.2, 0.05 )
180 write_text( "Slow down: %s" % config
.keys_slow
.name
, keys_colour
, 0.1, 0.25 )
181 write_text( "Speed up: %s" % config
.keys_fast
.name
, keys_colour
, 0.1, 0.35 )
182 write_text( "Skip round: %s" % config
.keys_skip_to_end
.name
, keys_colour
, 0.1, 0.45 )
184 # ----------------------
186 def intro_draw_instructions():
187 write_text( "Press %s for menu, or %s to start" % (
188 config
.keys_menu
.name
, config
.keys_startgame
.name
),
189 (0, 0, 0), 0.05, 0.99 )
191 # ----------------------
193 def general_menu_create_menu( menu
, config
, gamestate
):
195 if gamestate
== None: # We are on a title screen - Start Game option
196 menu
.add_item( "Start game", MENU_START
)
197 menu
.add_item( "Number of players: %d" % (config
.num_players
),
199 for player_num
in range( config
.num_players
):
200 cls_name
= "--unknown--"
201 if player_num
< len( config
.player_classes
):
202 cls_name
= config
.player_classes
[player_num
].GetName()
203 menu
.add_item( "P%d: %s" % ( player_num
, cls_name
),
204 MENU_CHANGE_PLAYER
+ player_num
)
206 menu
.add_item( "Continue", MENU_START
)
207 menu
.add_item( "End game", MENU_END
)
214 menu
.add_item( tmp_str
, MENU_MUSIC
)
216 tmp_str
= "Effects: "
217 if config
.sound_effects_on
:
221 menu
.add_item( tmp_str
, MENU_SOUND_EFFECTS
)
223 menu
.add_item( "Quit troncode", MENU_QUIT
)
227 def general_menu_screen( config
, gamestate
):
229 if gamestate
== None:
230 menu_title
= "troncode"
232 menu_title
= "troncode paused"
234 menu
= mopelib
.Menu()
235 general_menu_create_menu( menu
, config
, gamestate
)
236 menurender
.set_menu( menu
, menu_title
)
237 menurender
.repaint_full()
243 event
= pygame
.event
.wait()
244 if event
.type == QUIT
:
246 elif config
.keys_menu
.matches( event
):
248 elif config
.keys_down
.matches( event
):
249 menurender
.move_down()
250 elif config
.keys_up
.matches( event
):
252 elif config
.keys_return
.matches( event
):
253 code
= menu
.get_selected_item().code
254 if code
== MENU_START
:
257 elif code
== MENU_END
:
258 gamestate
.alive
= INGAME_QUIT
260 elif code
== MENU_MUSIC
:
261 if config
.music_on
== 1:
265 general_menu_create_menu( menu
, config
, gamestate
)
266 menurender
.repaint_full()
267 sound_mgr
.setup( gamestate
)
269 elif code
== MENU_SOUND_EFFECTS
:
270 if config
.sound_effects_on
:
271 config
.sound_effects_on
= 0
273 config
.sound_effects_on
= 1
274 general_menu_create_menu( menu
, config
, gamestate
)
275 menurender
.repaint_full()
276 sound_mgr
.setup( gamestate
)
278 elif code
== MENU_QUIT
:
283 # ----------------------
285 def intro_draw_title():
286 screen
.blit( intro_surface_title
, (0,0) )
287 write_text( "Version " + troncode_version
,
288 ( 0, 0, 0 ), 0.05, 0.88 )
289 write_text( "by Andy Balaam", ( 0, 0, 0 ), 0.06, 0.93 )
290 intro_draw_instructions()
292 def intro_draw_something( intro_mode
):
293 if intro_mode
== INTRO_MODE_TITLE
:
295 elif intro_mode
== INTRO_MODE_INSTR
:
296 screen
.blit( intro_surface_instr
, (0,0) )
297 intro_draw_instructions()
298 elif intro_mode
== INTRO_MODE_MUSIC
:
299 screen
.blit( intro_surface_music
, (0,0) )
301 intro_draw_instructions()
302 pygame
.display
.update()
304 def intro_input( event
, config
, intro_mode
):
305 if event
.type == QUIT
:
307 elif config
.keys_volup
.matches( event
):
308 config
.volume
= sound_mgr
.increase_volume()
310 elif config
.keys_voldown
.matches( event
):
311 config
.volume
= sound_mgr
.decrease_volume()
314 if event
.type == EVENTTYPE_TITLE_TICK
:
316 if intro_mode
== INTRO_MODE_ENDED
:
317 intro_mode
= INTRO_MODE_TITLE
318 intro_draw_something( intro_mode
)
319 elif config
.keys_menu
.matches( event
):
320 mopelib
.clear_events( EVENTTYPE_TITLE_TICK
)
321 start_game
= general_menu_screen( config
, None )
323 intro_mode
= INTRO_MODE_ENDED
325 intro_draw_something( intro_mode
)
326 pygame
.time
.set_timer( EVENTTYPE_TITLE_TICK
, TITLE_TICK_TIME
)
327 elif config
.keys_startgame
.matches( event
):
328 intro_mode
= INTRO_MODE_ENDED
331 # ----------------------
333 def intro_mainloop( config
):
336 pygame
.display
.update()
337 pygame
.time
.set_timer( EVENTTYPE_TITLE_TICK
, TITLE_TICK_TIME
)
339 intro_mode
= INTRO_MODE_TITLE
340 while intro_mode
< INTRO_MODE_ENDED
:
341 intro_mode
= intro_input( pygame
.event
.wait(), config
, intro_mode
)
343 mopelib
.clear_events( EVENTTYPE_TITLE_TICK
)
345 def draw_pixel( gamestate
, surface
, colour
, x
, y
):
346 if colour
in gamestate
.alive_colours
:
349 col
= mopelib
.dim_colour( colour
, gamestate
.dim
)
350 adj_x
= screen_border
[0] + scale
* x
351 adj_y
= screen_border
[1] + scale
* y
353 surface
.set_at( ( int(adj_x
), int(adj_y
) ), col
)
355 pygame
.draw
.rect( surface
, col
,
356 (adj_x
, adj_y
, ceil_scale
, ceil_scale
) )
358 def inlevel_screen_blit( gamestate
):
359 screen
.blit( gamestate
.offscreen_buff
, (0,0) )
360 pygame
.display
.update()
362 def inlevel_redraw_screen( gamestate
, arrows
, scores
):
363 gamestate
.offscreen_buff
= pygame
.Surface( gamestate
.config
.screen_size
)
364 gamestate
.offscreen_buff
.blit( ingame_surface_background
, (0,0) )
365 for x
, y
, colour
in gamestate
.pixels_list
:
366 draw_pixel( gamestate
, gamestate
.offscreen_buff
, colour
, x
, y
)
367 inlevel_draw_players( gamestate
)
368 write_text_ingame( gamestate
, scores
)
370 inlevel_screen_blit( gamestate
)
372 def inlevel_draw_players( gamestate
):
373 for player
in gamestate
.players
:
374 x
, y
= gamestate
.GetPosition( player
)
375 draw_pixel( gamestate
, gamestate
.offscreen_buff
, player
.GetColour(),
378 def inlevel_update_screen( gamestate
, scores
):
379 inlevel_draw_players( gamestate
)
380 inlevel_screen_blit( gamestate
)
384 # ----------------------
386 def inlevel_input( event
, gamestate
, scores
):
387 if event
.type == QUIT
:
389 elif config
.keys_menu
.matches( event
):
390 general_menu_screen( config
, gamestate
)
391 inlevel_redraw_screen( gamestate
, False, scores
)
392 elif config
.keys_volup
.matches( event
):
393 config
.volume
= sound_mgr
.increase_volume()
395 elif config
.keys_voldown
.matches( event
):
396 config
.volume
= sound_mgr
.decrease_volume()
398 elif config
.keys_slow
.matches( event
):
399 gamestate
.framerate
= 0
400 inlevel_redraw_screen( gamestate
, False, scores
)
401 elif config
.keys_fast
.matches( event
):
402 if gamestate
.framerate
== 0:
403 gamestate
.framerate
= 1
404 elif gamestate
.framerate
== 1:
405 gamestate
.framerate
= 250
407 gamestate
.framerate
*= 2
408 elif config
.keys_skip_to_end
.matches( event
):
409 gamestate
.framerate
= 1000000
411 gamestate
.key_events
.append( event
)
414 # ----------------------
416 def finishedgame_input( event
, waiting
):
417 if event
.type == QUIT
:
419 elif config
.keys_volup
.matches( event
):
420 config
.volume
= sound_mgr
.increase_volume()
422 elif config
.keys_voldown
.matches( event
):
423 config
.volume
= sound_mgr
.decrease_volume()
425 elif config
.keys_startgame
.matches( event
):
429 # ----------------------
432 def __init__( self
, x
, y
, direction
, player
):
435 self
._dir
= direction
436 self
._colour
= player
.GetColour()
439 def GetPosition( self
):
440 return ( self
._x
, self
._y
)
442 def GetDirection( self
):
445 def SetDirection( self
, direction
):
446 self
._dir
= direction
448 def SetDead( self
, dead
):
454 def Move( self
, gamestate
):
455 """Moves the player one position depending on its direction, and
456 returns True if it hit anything, False otherwise."""
461 if self
._dir
== DIR_UP
:
463 elif self
._dir
== DIR_RIGHT
:
465 elif self
._dir
== DIR_DOWN
:
467 elif self
._dir
== DIR_LEFT
:
470 self
._dead
= gamestate
.AddPixel( self
._x
, self
._y
, self
._colour
)
472 gamestate
.alive_colours
.remove( self
._colour
)
476 class GameBoard( BasicGameBoard
):
478 def __init__( self
, gamestate
):
479 self
._gamestate
= gamestate
481 def GetArenaSize( self
):
482 return self
._gamestate
.config
.arena_size
484 def GetAbsolutePixel( self
, pos
):
485 if pos
in self
._gamestate
.pixels_set
:
491 def GetPlayerPositions( self
, pos_to_exclude
= None ):
494 for player
in self
._gamestate
.statuses
.keys():
495 status
= self
._gamestate
.statuses
[player
]
496 if not status
.IsDead():
497 pos
= status
.GetPosition()
498 if pos_to_exclude
!= pos
:
499 ret
.append( ( pos
, status
.GetDirection() ) )
502 def class_is_human( cls
):
503 return "IsHuman" in cls
.__dict
__ and cls
.IsHuman()
506 def __init__( self
, config
, classes
):
511 self
.any_humans
= False
514 if class_is_human( cls
):
515 self
.players
.append( cls( config
.keys_p1_up
,
516 config
.keys_p1_right
, config
.keys_p1_down
,
517 config
.keys_p1_left
) )
518 self
.any_humans
= True
520 self
.players
.append( cls() )
522 self
.alive
= INGAME_TWO_ALIVE
525 self
.pixels_list
= []
526 self
.pixels_set
= set()
527 self
.create_initial_pixels()
529 self
.alive_colours
= []
531 for player
in self
.players
:
532 x
= random
.randint( config
.starting_border
, config
.arena_size
[0]
533 - config
.starting_border
)
534 y
= random
.randint( config
.starting_border
, config
.arena_size
[1]
535 - config
.starting_border
)
536 dr
= random
.randint( DIR_UP
, DIR_LEFT
)
537 self
.statuses
[player
] = PlayerStatus( x
, y
, dr
, player
)
538 self
.AddPixel( x
, y
, player
.GetColour() )
539 self
.alive_colours
.append( player
.GetColour() )
541 self
._gameboard
= GameBoard( self
)
543 def timer_tick( self
):
545 for player
in self
.players
:
546 status
= self
.statuses
[player
]
547 if not status
.IsDead():
548 if class_is_human( player
.__class
__ ):
549 new_dir
= player
.GetDirWithInput( status
.GetDirection(),
553 new_dir
= player
.GetDir( status
.GetPosition(),
554 status
.GetDirection(), self
._gameboard
)
556 sys
.stderr
.write( ( "Player '%s' threw "
557 + "an exception from GetDir(). "
558 + "Killing it.\nException: '%s'\n" ) %
559 ( player
.GetName(), e
) )
561 status
.SetDead( True )
563 if new_dir
not in (DIR_UP
, DIR_RIGHT
, DIR_DOWN
, DIR_LEFT
):
564 sys
.stderr
.write( "Player '" + player
.GetName()
565 + "' returned an invalid value from GetDir(). Killing it.\n" )
567 status
.SetDead( True )
569 status
.SetDirection( new_dir
)
570 # TODO: copy pixels list for "security"?
572 dead
= self
.statuses
[player
].Move( self
)
577 self
.alive
= INGAME_MOST_DEAD
579 def create_initial_pixels( self
):
580 colour
= (100, 100, 100)
581 size_x
, size_y
= config
.arena_size
582 for x
in range( size_x
):
583 self
.AddPixel( x
, 0, colour
)
584 self
.AddPixel( x
, size_y
- 1, colour
)
585 for y
in range( 1, size_y
- 1 ):
586 self
.AddPixel( 0, y
, colour
)
587 self
.AddPixel( size_x
- 1, y
, colour
)
589 def GetPosition( self
, player
):
590 return self
.statuses
[player
].GetPosition()
592 def AddPixel( self
, x
, y
, colour
):
593 if ( x
, y
) in self
.pixels_set
:
597 self
.pixels_set
.add( ( x
, y
) )
599 self
.pixels_list
.append( ( x
, y
, colour
) )
604 # ----------------------
606 def ingame_mainloop( config
):
609 for cls
in config
.player_classes
:
613 gamestate
= GameState( config
, config
.player_classes
)
614 inlevel_mainloop( config
, gamestate
, scores
)
615 if gamestate
.alive
== INGAME_QUIT
:
620 def increment_scores( gamestate
, scores
):
621 for player
in gamestate
.players
:
622 if not gamestate
.statuses
[player
]._dead
:
623 scores
[player
.__class
__] += 1
625 # ----------------------
627 def inlevel_mainloop( config
, gamestate
, scores
):
629 gamestate
.alive
= INGAME_TWO_ALIVE
631 inlevel_redraw_screen( gamestate
, True, scores
)
632 if gamestate
.any_humans
:
634 inlevel_redraw_screen( gamestate
, False, scores
)
639 gamestate
.framerate
= 0
640 num_alive
= len(gamestate
.alive_colours
)
642 while gamestate
.alive
== INGAME_TWO_ALIVE
:
643 for evt
in pygame
.event
.get():
644 inlevel_input( evt
, gamestate
, scores
)
645 gamestate
.timer_tick()
646 gamestate
.key_events
= []
648 if gamestate
.alive
!= INGAME_MOST_DEAD
:
649 if gamestate
.framerate
<= 1:
650 inlevel_update_screen( gamestate
, scores
)
651 if gamestate
.framerate
== 0:
652 pygame
.time
.wait( config
.tick_time
)
653 elif tick_counter
>= gamestate
.framerate
:
655 inlevel_redraw_screen( gamestate
, False, scores
)
656 if num_alive
!= len(gamestate
.alive_colours
):
657 num_alive
= len(gamestate
.alive_colours
)
658 # redraw with dead players dimmed
659 inlevel_redraw_screen( gamestate
, False, scores
)
663 mopelib
.clear_events( EVENTTYPE_INGAME_TICK
)
664 increment_scores( gamestate
, scores
)
666 finishedlevel_mainloop( gamestate
, scores
)
668 ingame_surface_background
.fill( config
.colour_background
)
671 # ----------------------
673 def write_text( txt
, colour
, size
, y_pos
):
674 ft
= pygame
.font
.Font( None, int( config
.screen_size
[1] * size
) )
675 sf
= ft
.render( txt
, True, colour
)
676 screen
.blit( sf
, ( (config
.screen_size
[0] - sf
.get_width() )/2,
677 (config
.screen_size
[1] - sf
.get_height() ) * y_pos
) )
679 # ----------------------
681 def write_text_ingame( gamestate
, scores
):
685 for cls
in scores
.keys():
686 txt
+= "%s: %d " % ( cls
.GetName(), scores
[cls
] )
688 colour
= (128, 128, 128)
689 bgcolour
= config
.colour_background
691 sf
= ingame_font
.render( txt
, True, colour
)
692 sf_bg
= pygame
.Surface( ( int( sf
.get_width() ), sf
.get_height() ) )
693 sf_bg
.fill( bgcolour
)
695 tlx
= ( config
.screen_size
[0] - sf_bg
.get_width() ) / 2
696 tly
= ( config
.screen_size
[1] - sf_bg
.get_height() ) * y_pos
698 dirty_rect
= Rect( tlx
, tly
, sf_bg
.get_width(), sf_bg
.get_height() )
699 gamestate
.offscreen_buff
.blit( sf_bg
, dirty_rect
)
700 gamestate
.offscreen_buff
.blit( sf
, ( tlx
* 1.01, tly
) )
702 # ----------------------
704 def finishedlevel_input( event
, waiting
, gamestate
, scores
):
705 if event
.type == QUIT
:
707 elif( ( event
.type == pygame
.ACTIVEEVENT
and event
.state
== 2 )
708 or config
.keys_menu
.matches( event
) ):
709 general_menu_screen( config
, gamestate
)
710 inlevel_redraw_screen( gamestate
, False, scores
)
711 if gamestate
.alive
== INGAME_QUIT
:
713 elif config
.keys_volup
.matches( event
):
714 config
.volume
= sound_mgr
.increase_volume()
716 elif config
.keys_voldown
.matches( event
):
717 config
.volume
= sound_mgr
.decrease_volume()
719 elif event
.type == EVENTTYPE_TITLE_TICK
:
721 elif config
.keys_startgame
.matches( event
):
725 def finishedlevel_mainloop( gamestate
, scores
):
727 if not gamestate
.any_humans
:
728 pygame
.time
.set_timer( EVENTTYPE_TITLE_TICK
, TITLE_TICK_TIME
)
730 inlevel_redraw_screen( gamestate
, False, scores
)
733 waiting
= finishedlevel_input( pygame
.event
.wait(), waiting
, gamestate
, scores
)
735 if not gamestate
.any_humans
:
736 mopelib
.clear_events( EVENTTYPE_TITLE_TICK
)
738 def finishedgame_mainloop( config
, gamestate
):
740 #config.start_level = 0
743 # inlevel_redraw_screen( gamestate )
744 # write_text( "Congratulations!", (255,255,255), 0.125, 0.38 )
745 # write_text( "You won!", (255,255,255), 0.125, 0.52 )
747 # write_text( "Press %s" % config.keys_startgame.name, (255,255,255),
749 # pygame.display.update()
751 # waiting = finishedgame_input( pygame.event.wait(), waiting )
753 # ingame_surface_background.fill( config.colour_background )
755 def sort_by_score( class2score
):
756 scorename_sorted
= []
758 for cls
in class2score
.keys():
759 scorename_sorted
.append( ( class2score
[cls
], cls
.GetName() ) )
761 scorename_sorted
.sort( reverse
= True )
763 return scorename_sorted
765 def execute_tests( config
):
766 module_name
= config
.test_player_name
767 module
= __import__( "players." + module_name
,
768 globals(), locals() ).__dict
__[module_name
]
772 def execute_tournament( config
):
776 for cls
in config
.player_classes
:
777 if not class_is_human( cls
):
778 classes
.append( cls
)
779 total_scores
[cls
] = 0
782 print " " * 30 + "=== Pairings ==="
786 num_classes
= len( classes
)
787 for i
in range( num_classes
):
788 for j
in range( i
+1, num_classes
):
790 ( classes
[i
], classes
[j
] ),
791 total_scores
, False, config
.num_games
)
794 print " " * 30 + u
"=== Melee ==="
797 execute_match( classes
, total_scores
, True,
798 config
.num_games
* (len( classes
)-1) )
801 print " " * 30 + "=== Total Scores ==="
804 scorename_sorted
= sort_by_score( total_scores
)
806 for score
, name
in scorename_sorted
:
807 print "%30s % 4d" % ( name
, score
)
811 def execute_match( classes
, total_scores
, newlines
, num_games
):
814 match_scores
[cls
] = 0
818 for i
in range( num_games
):
820 sys
.stdout
.write( "." )
821 gamestate
= GameState( config
, classes
)
823 while gamestate
.alive
== INGAME_TWO_ALIVE
:
824 gamestate
.timer_tick()
826 for player
in gamestate
.statuses
.keys():
827 status
= gamestate
.statuses
[player
]
828 if not status
.IsDead():
829 match_scores
[player
.__class
__] += 1
834 scorename_sorted
= sort_by_score( match_scores
)
837 for score
, name
in scorename_sorted
:
838 if oldscore
!= -1 and not newlines
:
839 if oldscore
== score
:
843 print "%- 4d %-30s" % ( score
, name
),
845 print "%30s% 4d" % ( name
, score
),
856 total_scores
[cls
] += match_scores
[cls
]
858 def get_players( players_dir
):
861 player_re
= re
.compile( """^(\w*Player).py$""" )
862 for filename
in os
.listdir( players_dir
):
863 m
= player_re
.match( filename
)
865 class_name
= m
.group( 1 )
866 cls
= __import__( "players." + class_name
,
867 globals(), locals() ).__dict
__[class_name
].__dict
__[class_name
]
868 ret_classes
.append( cls
)
870 #return ret_classes[:2]
873 # ----------------------
874 # Execution starts here
875 # ----------------------
888 MENU_SOUND_EFFECTS
= 4
890 MENU_CHANGE_PLAYER
= 100 # Must go at end of this list
896 TITLE_TICK_TIME
= 4000
898 EVENTTYPE_INGAME_TICK
= pygame
.USEREVENT
899 EVENTTYPE_TITLE_TICK
= pygame
.USEREVENT
+ 1
901 num_args
= len( sys
.argv
)
903 config_filename
= os
.path
.expanduser( "~/.troncode/config" )
907 run_tournament
= False
909 config
= TronCodeConfig( config_filename
)
912 if sys
.argv
[1] == "--tournament":
913 run_tournament
= True
914 config
.num_games
= 100
915 elif sys
.argv
[1] == "--test":
918 config
.test_player_name
= sys
.argv
[2]
919 config
.unsaved
.append( "test_player_name" )
921 sys
.stderr
.write( "Please supply the name of the player to test.\n" )
924 config
.install_dir
= install_dir
925 config
.unsaved
.append( "install_dir" )
927 config
.images_dir
= os
.path
.join( install_dir
, "images" )
928 config
.unsaved
.append( "images_dir" )
930 config
.music_dir
= os
.path
.join( install_dir
, "music" )
931 config
.unsaved
.append( "music_dir" )
934 config
.screen_size
= config
.parse_value( sys
.argv
[3] )
936 config
.arena_size
= ( 200, 200 )
937 config
.unsaved
.append( "arena_size" )
939 config
.starting_border
= 35
940 config
.unsaved
.append( "starting_border" )
942 config
.players_dir
= "players"
943 config
.unsaved
.append( "players_dir" )
945 config
.player_classes
= get_players( config
.players_dir
)
946 config
.unsaved
.append( "player_classes" )
949 config
.unsaved
.append( "players" )
952 execute_tests( config
)
954 execute_tournament( config
)
959 window
= pygame
.display
.set_mode( config
.screen_size
)
960 pygame
.display
.set_caption( 'troncode' )
961 screen
= pygame
.display
.get_surface()
966 ( float( config
.screen_size
[0] - fixed_border
*2 )
967 / float( config
.arena_size
[0] ) ),
968 ( float( config
.screen_size
[1] - ( fixed_border
*2 + bottom_border
) )
969 / float( config
.arena_size
[1] ) ) )
971 screen_border
= ( float( config
.screen_size
[0] - config
.arena_size
[0]*scale
)
973 float( ( config
.screen_size
[1] - config
.arena_size
[1]*scale
) - ( bottom_border
- fixed_border
) ) / 2.0 )
975 ceil_scale
= math
.ceil( scale
)
977 # General initialisation
979 num_joysticks
= pygame
.joystick
.get_count()
980 for j
in range( num_joysticks
):
981 pygame
.joystick
.Joystick( j
).init()
983 intro_surface_title
= mopelib
.load_and_scale_image( "title.png", config
)
984 intro_surface_instr
= mopelib
.load_and_scale_image( "instructions.png", config
)
985 intro_surface_music
= mopelib
.load_and_scale_image( "music.png", config
)
987 ingame_surface_background
= pygame
.Surface( screen
.get_size() ).convert()
988 ingame_surface_background
.fill( config
.colour_background
)
990 intro_mode
= INTRO_MODE_TITLE
992 sound_mgr
= TronCodeSoundManager( config
.volume
)
994 troncode_version
= mopelib
.read_version( config
)
996 ingame_font
= pygame
.font
.Font( None, int( config
.screen_size
[1] * 0.019 ) )
998 menurender
= mopelib
.MenuRenderer( screen
, config
, ingame_surface_background
,
999 (128, 128, 128), (128, 255, 128), (128, 128, 128) )
1002 sound_mgr
.music_loud()
1003 intro_mainloop( config
)
1004 sound_mgr
.music_quiet()
1005 gamestate
= ingame_mainloop( config
)
1006 intro_mode
= finishedgame_mainloop( config
, gamestate
)