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 mopelib
import mopelib
34 from troncode_values
import *
36 # ----------------------
37 class TronCodeConfig( mopelib
.Config
):
39 def default_config( self
):
43 self
.screen_size
= ( 810, 810 )
44 self
.colour_background
= ( 15, 45, 15 )
49 self
.sound_effects_on
= 1
51 self
.keys_menu
= mopelib
.MyInputEvent( "Escape" )
52 self
.keys_menu
.add( pygame
.KEYDOWN
, pygame
.K_ESCAPE
)
53 self
.keys_menu
.add( pygame
.JOYBUTTONDOWN
, 8 ) # GP2X Start
54 self
.keys_menu
.add( pygame
.JOYBUTTONDOWN
, 9 ) # GP2X Select
56 self
.keys_return
= mopelib
.MyInputEvent( "Return" )
57 self
.keys_return
.add( pygame
.KEYDOWN
, pygame
.K_RETURN
)
58 self
.keys_return
.add( pygame
.JOYBUTTONDOWN
, 13 ) # GP2X B button
60 self
.keys_startgame
= mopelib
.MyInputEvent( "any key" )
61 self
.keys_startgame
.add_all( pygame
.KEYDOWN
)
62 self
.keys_startgame
.add_all( pygame
.JOYBUTTONDOWN
)
63 self
.keys_startgame
.add_all( pygame
.MOUSEBUTTONDOWN
)
65 self
.keys_up
= mopelib
.MyInputEvent( "up" )
66 self
.keys_up
.add( pygame
.KEYDOWN
, ord( 'q' ) )
67 self
.keys_up
.add( pygame
.KEYDOWN
, pygame
.K_UP
)
68 self
.keys_up
.add( pygame
.JOYBUTTONDOWN
, 0 ) # GP2X Joy up
69 self
.keys_up
.add( pygame
.JOYBUTTONDOWN
, 15 ) # GP2X Y button
71 self
.keys_right
= mopelib
.MyInputEvent( "right" )
72 self
.keys_right
.add( pygame
.KEYDOWN
, ord( 'p' ) )
73 self
.keys_right
.add( pygame
.KEYDOWN
, pygame
.K_RIGHT
)
74 self
.keys_right
.add( pygame
.JOYBUTTONDOWN
, 6 ) # GP2X Joy right
75 self
.keys_right
.add( pygame
.JOYBUTTONDOWN
, 13 ) # GP2X B button
77 self
.keys_down
= mopelib
.MyInputEvent( "down" )
78 self
.keys_down
.add( pygame
.KEYDOWN
, ord( 'a' ) )
79 self
.keys_down
.add( pygame
.KEYDOWN
, pygame
.K_DOWN
)
80 self
.keys_down
.add( pygame
.JOYBUTTONDOWN
, 4 ) # GP2X Joy down
81 self
.keys_down
.add( pygame
.JOYBUTTONDOWN
, 14 ) # GP2X X button
83 self
.keys_left
= mopelib
.MyInputEvent( "left" )
84 self
.keys_left
.add( pygame
.KEYDOWN
, ord( 'o' ) )
85 self
.keys_left
.add( pygame
.KEYDOWN
, pygame
.K_LEFT
)
86 self
.keys_left
.add( pygame
.JOYBUTTONDOWN
, 2 ) # GP2X Joy left
87 self
.keys_left
.add( pygame
.JOYBUTTONDOWN
, 12 ) # GP2X A button
89 self
.keys_volup
= mopelib
.MyInputEvent( "+" )
90 self
.keys_volup
.add( pygame
.KEYDOWN
, ord( '+' ) )
91 self
.keys_volup
.add( pygame
.KEYDOWN
, ord( '=' ) )
92 self
.keys_volup
.add( pygame
.JOYBUTTONDOWN
, 16 ) # GP2X volume + button
94 self
.keys_voldown
= mopelib
.MyInputEvent( "-" )
95 self
.keys_voldown
.add( pygame
.KEYDOWN
, ord( '-' ) )
96 self
.keys_voldown
.add( pygame
.JOYBUTTONDOWN
, 17 ) # GP2X volume - button
98 self
.keys_p1_up
= mopelib
.MyInputEvent( "p1_up" )
99 self
.keys_p1_up
.add( pygame
.KEYDOWN
, pygame
.K_UP
)
100 self
.keys_p1_up
.add( pygame
.JOYBUTTONDOWN
, 0 ) # GP2X Joy up
102 self
.keys_p1_right
= mopelib
.MyInputEvent( "p1_right" )
103 self
.keys_p1_right
.add( pygame
.KEYDOWN
, pygame
.K_RIGHT
)
104 self
.keys_p1_right
.add( pygame
.JOYBUTTONDOWN
, 6 ) # GP2X Joy right
106 self
.keys_p1_down
= mopelib
.MyInputEvent( "p1_down" )
107 self
.keys_p1_down
.add( pygame
.KEYDOWN
, pygame
.K_DOWN
)
108 self
.keys_p1_down
.add( pygame
.JOYBUTTONDOWN
, 4 ) # GP2X Joy down
110 self
.keys_p1_left
= mopelib
.MyInputEvent( "p1_left" )
111 self
.keys_p1_left
.add( pygame
.KEYDOWN
, pygame
.K_LEFT
)
112 self
.keys_p1_left
.add( pygame
.JOYBUTTONDOWN
, 4 ) # GP2X Joy left
114 self
.keys_p2_up
= mopelib
.MyInputEvent( "p2_up" )
115 self
.keys_p2_up
.add( pygame
.KEYDOWN
, ord( '2' ) )
116 self
.keys_p2_up
.add( pygame
.JOYBUTTONDOWN
, 15 ) # GP2X Y button
118 self
.keys_p2_right
= mopelib
.MyInputEvent( "p2_right" )
119 self
.keys_p2_right
.add( pygame
.KEYDOWN
, ord( 'e' ) )
120 self
.keys_p2_right
.add( pygame
.JOYBUTTONDOWN
, 13 ) # GP2X B button
122 self
.keys_p2_down
= mopelib
.MyInputEvent( "p2_down" )
123 self
.keys_p2_down
.add( pygame
.KEYDOWN
, ord( 'w' ) )
124 self
.keys_p2_down
.add( pygame
.JOYBUTTONDOWN
, 14 ) # GP2X X button
126 self
.keys_p2_left
= mopelib
.MyInputEvent( "p2_left" )
127 self
.keys_p2_left
.add( pygame
.KEYDOWN
, ord( 'q' ) )
128 self
.keys_p2_left
.add( pygame
.JOYBUTTONDOWN
, 12 ) # GP2X A button
130 self
.keys_p3_up
= mopelib
.MyInputEvent( "p3_up" )
131 self
.keys_p3_up
.add( pygame
.KEYDOWN
, ord( 'i' ) )
133 self
.keys_p3_right
= mopelib
.MyInputEvent( "p3_right" )
134 self
.keys_p3_right
.add( pygame
.KEYDOWN
, ord( 'l' ) )
136 self
.keys_p3_down
= mopelib
.MyInputEvent( "p3_down" )
137 self
.keys_p3_down
.add( pygame
.KEYDOWN
, ord( 'k' ) )
139 self
.keys_p3_left
= mopelib
.MyInputEvent( "p3_left" )
140 self
.keys_p3_left
.add( pygame
.KEYDOWN
, ord( 'j' ) )
142 self
.keys_p4_up
= mopelib
.MyInputEvent( "p4_up" )
143 self
.keys_p4_up
.add( pygame
.KEYDOWN
, pygame
.K_KP8
)
145 self
.keys_p4_right
= mopelib
.MyInputEvent( "p4_right" )
146 self
.keys_p4_right
.add( pygame
.KEYDOWN
, pygame
.K_KP6
)
148 self
.keys_p4_down
= mopelib
.MyInputEvent( "p4_down" )
149 self
.keys_p4_down
.add( pygame
.KEYDOWN
, pygame
.K_KP5
)
150 self
.keys_p4_down
.add( pygame
.KEYDOWN
, pygame
.K_KP2
)
152 self
.keys_p4_left
= mopelib
.MyInputEvent( "p4_left" )
153 self
.keys_p4_left
.add( pygame
.KEYDOWN
, pygame
.K_KP4
)
156 # ----------------------
158 class TronCodeSoundManager( mopelib
.SoundManager
):
160 def __init__( self
, volume
):
161 mopelib
.SoundManager
.__init
__( self
, config
)
163 #self.add_sample_group( "waddles", ["waddle1"] )
165 # ----------------------
167 def intro_draw_instructions():
168 write_text( "Press %s for menu, or %s to start" % (
169 config
.keys_menu
.name
, config
.keys_startgame
.name
),
170 (0, 0, 0), 0.05, 0.99 )
172 # ----------------------
174 def general_menu_create_menu( menu
, config
, gamestate
):
176 if gamestate
== None: # We are on a title screen - Start Game option
177 menu
.add_item( "Start game", MENU_START
)
178 menu
.add_item( "Number of players: %d" % (config
.num_players
),
180 for player_num
in range( config
.num_players
):
181 cls_name
= "--unknown--"
182 if player_num
< len( config
.player_classes
):
183 cls_name
= config
.player_classes
[player_num
].GetName()
184 menu
.add_item( "P%d: %s" % ( player_num
, cls_name
),
185 MENU_CHANGE_PLAYER
+ player_num
)
187 menu
.add_item( "Continue", MENU_START
)
188 menu
.add_item( "End game", MENU_END
)
195 menu
.add_item( tmp_str
, MENU_MUSIC
)
197 tmp_str
= "Effects: "
198 if config
.sound_effects_on
:
202 menu
.add_item( tmp_str
, MENU_SOUND_EFFECTS
)
204 menu
.add_item( "Quit troncode", MENU_QUIT
)
208 def general_menu_screen( config
, gamestate
):
210 if gamestate
== None:
211 menu_title
= "troncode"
213 menu_title
= "troncode paused"
215 menu
= mopelib
.Menu()
216 general_menu_create_menu( menu
, config
, gamestate
)
217 menurender
.set_menu( menu
, menu_title
)
218 menurender
.repaint_full()
224 event
= pygame
.event
.wait()
225 if event
.type == QUIT
:
227 elif config
.keys_menu
.matches( event
):
229 elif config
.keys_down
.matches( event
):
230 menurender
.move_down()
231 elif config
.keys_up
.matches( event
):
233 elif config
.keys_return
.matches( event
):
234 code
= menu
.get_selected_item().code
235 if code
== MENU_START
:
238 elif code
== MENU_END
:
239 gamestate
.alive
= INGAME_QUIT
241 elif code
== MENU_MUSIC
:
242 if config
.music_on
== 1:
246 general_menu_create_menu( menu
, config
, gamestate
)
247 menurender
.repaint_full()
248 sound_mgr
.setup( gamestate
)
250 elif code
== MENU_SOUND_EFFECTS
:
251 if config
.sound_effects_on
:
252 config
.sound_effects_on
= 0
254 config
.sound_effects_on
= 1
255 general_menu_create_menu( menu
, config
, gamestate
)
256 menurender
.repaint_full()
257 sound_mgr
.setup( gamestate
)
259 elif code
== MENU_QUIT
:
264 # ----------------------
266 def intro_draw_title():
267 screen
.blit( intro_surface_title
, (0,0) )
268 write_text( "Version " + troncode_version
,
269 ( 0, 0, 0 ), 0.05, 0.88 )
270 write_text( "by Andy Balaam", ( 0, 0, 0 ), 0.06, 0.93 )
271 intro_draw_instructions()
273 def intro_draw_something( intro_mode
):
274 if intro_mode
== INTRO_MODE_TITLE
:
276 elif intro_mode
== INTRO_MODE_INSTR
:
277 screen
.blit( intro_surface_instr
, (0,0) )
278 intro_draw_instructions()
279 elif intro_mode
== INTRO_MODE_MUSIC
:
280 screen
.blit( intro_surface_music
, (0,0) )
281 intro_draw_instructions()
282 pygame
.display
.update()
284 def intro_input( event
, config
, intro_mode
):
285 if event
.type == QUIT
:
287 elif config
.keys_volup
.matches( event
):
288 config
.volume
= sound_mgr
.increase_volume()
290 elif config
.keys_voldown
.matches( event
):
291 config
.volume
= sound_mgr
.decrease_volume()
294 if event
.type == EVENTTYPE_TITLE_TICK
:
296 if intro_mode
== INTRO_MODE_ENDED
:
297 intro_mode
= INTRO_MODE_TITLE
298 intro_draw_something( intro_mode
)
299 elif config
.keys_menu
.matches( event
):
300 mopelib
.clear_events( EVENTTYPE_TITLE_TICK
)
301 start_game
= general_menu_screen( config
, None )
303 intro_mode
= INTRO_MODE_ENDED
305 intro_draw_something( intro_mode
)
306 pygame
.time
.set_timer( EVENTTYPE_TITLE_TICK
, TITLE_TICK_TIME
)
307 elif config
.keys_startgame
.matches( event
):
308 intro_mode
= INTRO_MODE_ENDED
311 # ----------------------
313 def intro_mainloop( config
):
316 pygame
.display
.update()
317 pygame
.time
.set_timer( EVENTTYPE_TITLE_TICK
, TITLE_TICK_TIME
)
319 intro_mode
= INTRO_MODE_TITLE
320 while intro_mode
< INTRO_MODE_ENDED
:
321 intro_mode
= intro_input( pygame
.event
.wait(), config
, intro_mode
)
323 mopelib
.clear_events( EVENTTYPE_TITLE_TICK
)
325 def draw_pixel( gamestate
, surface
, colour
, x
, y
):
326 col
= mopelib
.dim_colour( colour
, gamestate
.dim
)
327 adj_x
= screen_border
[0] + scale
* x
328 adj_y
= screen_border
[1] + scale
* y
330 surface
.set_at( ( int(adj_x
), int(adj_y
) ), col
)
332 pygame
.draw
.rect( surface
, col
,
333 (adj_x
, adj_y
, ceil_scale
, ceil_scale
) )
335 def inlevel_screen_blit( gamestate
):
336 screen
.blit( gamestate
.offscreen_buff
, (0,0) )
337 write_text_ingame( gamestate
)
338 pygame
.display
.update()
340 def inlevel_redraw_screen( gamestate
, arrows
):
341 gamestate
.offscreen_buff
= pygame
.Surface( gamestate
.config
.screen_size
)
342 gamestate
.offscreen_buff
.blit( ingame_surface_background
, (0,0) )
343 for x
, y
, colour
in gamestate
.pixels_list
:
344 draw_pixel( gamestate
, gamestate
.offscreen_buff
, colour
, x
, y
)
345 inlevel_draw_players( gamestate
)
347 inlevel_screen_blit( gamestate
)
349 def inlevel_draw_players( gamestate
):
350 for player
in gamestate
.players
:
351 x
, y
= gamestate
.GetPosition( player
)
352 draw_pixel( gamestate
, gamestate
.offscreen_buff
, player
.GetColour(),
355 def inlevel_update_screen( gamestate
):
356 inlevel_draw_players( gamestate
)
357 inlevel_screen_blit( gamestate
)
361 # ----------------------
363 def inlevel_input( event
, gamestate
):
364 if event
.type == QUIT
:
366 elif( ( event
.type == pygame
.ACTIVEEVENT
and event
.state
== 2 )
367 or config
.keys_menu
.matches( event
) ):
368 general_menu_screen( config
, gamestate
)
369 inlevel_redraw_screen( gamestate
, False )
370 elif config
.keys_volup
.matches( event
):
371 config
.volume
= sound_mgr
.increase_volume()
373 elif config
.keys_voldown
.matches( event
):
374 config
.volume
= sound_mgr
.decrease_volume()
376 elif event
.type == EVENTTYPE_INGAME_TICK
:
377 gamestate
.timer_tick()
378 gamestate
.key_events
= []
379 if gamestate
.alive
== INGAME_MOST_DEAD
:
380 finishedlevel_mainloop( gamestate
)
382 inlevel_update_screen( gamestate
)
384 gamestate
.key_events
.append( event
)
387 # ----------------------
389 def finishedgame_input( event
, waiting
):
390 if event
.type == QUIT
:
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_startgame
.matches( event
):
402 # ----------------------
405 def __init__( self
, x
, y
, direction
, player
):
408 self
._dir
= direction
409 self
._colour
= player
.GetColour()
412 def GetPosition( self
):
413 return ( self
._x
, self
._y
)
415 def GetDirection( self
):
418 def SetDirection( self
, direction
):
419 self
._dir
= direction
424 def Move( self
, gamestate
):
425 """Moves the player one position depending on its direction, and
426 returns True if it hit anything, False otherwise."""
431 if self
._dir
== DIR_UP
:
433 elif self
._dir
== DIR_RIGHT
:
435 elif self
._dir
== DIR_DOWN
:
437 elif self
._dir
== DIR_LEFT
:
440 self
._dead
= gamestate
.AddPixel( self
._x
, self
._y
, self
._colour
)
445 def __init__( self
, gamestate
):
446 self
._gamestate
= gamestate
448 def GetRelativePixel( self
, start_pos
, facing_dir
,
449 offset_fwd
, offset_right
):
450 """Given a position to stand in the arena, and a direction to face,
451 return the status (0 for empty, >0 for non-empty) of a pixel that
452 is offset_fwd pixels in front, and offset_right pixels to the right
453 (negative values may be used to go backwards or left respectively).
454 Pixels outside the arena also return >0."""
456 if facing_dir
== DIR_UP
:
457 found_pos
= ( start_pos
[0] + offset_right
,
458 start_pos
[1] - offset_fwd
)
459 elif facing_dir
== DIR_RIGHT
:
460 found_pos
= ( start_pos
[0] + offset_fwd
,
461 start_pos
[1] + offset_right
)
462 elif facing_dir
== DIR_DOWN
:
463 found_pos
= ( start_pos
[0] - offset_right
,
464 start_pos
[1] + offset_fwd
)
465 elif facing_dir
== DIR_LEFT
:
466 found_pos
= ( start_pos
[0] - offset_fwd
,
467 start_pos
[1] - offset_right
)
469 if ( found_pos
[0] < 0 or
470 found_pos
[0] >= self
._gamestate
.config
.arena_size
[0] or
472 found_pos
[1] >= self
._gamestate
.config
.arena_size
[1] ):
474 elif found_pos
in self
._gamestate
.pixels_set
:
481 def GetPlayerPositions( self
, pos_to_exclude
= None ):
482 """Returns a list of pairs (pos, dir) for each player on screen.
483 Excludes the player at the position specified if pos_to_exclude
488 for player
in self
._gamestate
.statuses
.keys():
489 status
= self
._gamestate
.statuses
[player
]
490 pos
= status
.GetPosition()
491 if pos_to_exclude
!= pos
:
492 ret
.append( ( pos
, status
.GetDirection() ) )
496 def TurnRight( self
, direction
):
497 """Return the direction found by turning 90 degrees right from
498 the supplied direction."""
500 if direction
> DIR_LEFT
:
504 def TurnLeft( self
, direction
):
505 """Return the direction found by turning 90 degrees left from
506 the supplied direction."""
508 if direction
< DIR_UP
:
512 def class_is_human( cls
):
513 return "IsHuman" in cls
.__dict
__ and cls
.IsHuman()
516 def __init__( self
, config
, classes
):
523 if class_is_human( cls
):
524 self
.players
.append( cls( config
.keys_p1_up
,
525 config
.keys_p1_right
, config
.keys_p1_down
,
526 config
.keys_p1_left
) )
528 self
.players
.append( cls() )
530 self
.alive
= INGAME_ALL_ALIVE
533 self
.pixels_list
= []
534 self
.pixels_set
= set()
535 self
.create_initial_pixels()
538 for player
in self
.players
:
539 x
= random
.randint( config
.starting_border
, config
.arena_size
[0]
540 - config
.starting_border
)
541 y
= random
.randint( config
.starting_border
, config
.arena_size
[1]
542 - config
.starting_border
)
543 dr
= random
.randint( DIR_UP
, DIR_LEFT
)
544 self
.statuses
[player
] = PlayerStatus( x
, y
, dr
, player
)
545 self
.AddPixel( x
, y
, player
.GetColour() )
547 self
._gameboard
= GameBoard( self
)
549 def timer_tick( self
):
551 for player
in self
.players
:
552 status
= self
.statuses
[player
]
553 if class_is_human( player
.__class
__ ):
554 new_dir
= player
.GetDirWithInput( status
.GetDirection(),
557 new_dir
= player
.GetDir( status
.GetPosition(),
558 status
.GetDirection(), self
._gameboard
)
559 status
.SetDirection( new_dir
)
560 # TODO: copy pixels list for "security"
562 dead
= self
.statuses
[player
].Move( self
)
567 self
.alive
= INGAME_MOST_DEAD
569 def create_initial_pixels( self
):
570 colour
= (100, 100, 100)
571 size_x
, size_y
= config
.arena_size
572 for x
in range( size_x
):
573 self
.AddPixel( x
, 0, colour
)
574 self
.AddPixel( x
, size_y
- 1, colour
)
575 for y
in range( 1, size_y
- 1 ):
576 self
.AddPixel( 0, y
, colour
)
577 self
.AddPixel( size_x
- 1, y
, colour
)
579 def GetPosition( self
, player
):
580 return self
.statuses
[player
].GetPosition()
582 def AddPixel( self
, x
, y
, colour
):
583 if ( x
, y
) in self
.pixels_set
:
587 self
.pixels_set
.add( ( x
, y
) )
589 self
.pixels_list
.append( ( x
, y
, colour
) )
594 # ----------------------
596 def ingame_mainloop( config
):
597 gamestate
= GameState( config
, config
.player_classes
)
598 while gamestate
.alive
== INGAME_ALL_ALIVE
:
599 inlevel_mainloop( config
, gamestate
)
602 # ----------------------
604 def inlevel_mainloop( config
, gamestate
):
606 inlevel_redraw_screen( gamestate
, True )
608 inlevel_redraw_screen( gamestate
, False )
611 pygame
.time
.set_timer( EVENTTYPE_INGAME_TICK
, config
.tick_time
)
612 while gamestate
.alive
== INGAME_ALL_ALIVE
:
613 inlevel_input( pygame
.event
.wait(), gamestate
)
615 mopelib
.clear_events( EVENTTYPE_INGAME_TICK
)
617 ingame_surface_background
.fill( config
.colour_background
)
620 # ----------------------
622 def write_text( txt
, colour
, size
, y_pos
):
623 ft
= pygame
.font
.Font( None, int( config
.screen_size
[1] * size
) )
624 sf
= ft
.render( txt
, True, colour
)
625 screen
.blit( sf
, ( (config
.screen_size
[0] - sf
.get_width() )/2,
626 (config
.screen_size
[1] - sf
.get_height() ) * y_pos
) )
628 # ----------------------
630 def write_text_ingame( gamestate
):
633 #bgcol = mopelib.dim_colour( gamestate.cur_lev.background_colour,
635 #fgcol = mopelib.dim_colour( gamestate.cur_lev.text_colour, gamestate.dim )
637 #write_text_blank_font_coords( "Level: %d" % ( gamestate.level_num + 1 ),
638 # fgcol, ingame_font, bgcol, 0.3, 0.99 )
640 #write_text_blank_font_coords( "Time: %s" % gamestate.cur_lev.current_time,
641 # fgcol, ingame_font, bgcol, 0.7, 0.99 )
645 def write_text_blank_font_coords( txt
, colour
, font
, bgcolour
, x
, y
):
646 sf
= font
.render( txt
, True, colour
)
647 sf_bg
= pygame
.Surface( ( int( sf
.get_width() * 1.29 ), sf
.get_height() ) )
648 sf_bg
.fill( bgcolour
)
650 tlx
= ( config
.screen_size
[0] - sf_bg
.get_width() ) * x
651 tly
= ( config
.screen_size
[1] - sf_bg
.get_height() ) * y
653 dirty_rect
= Rect( tlx
, tly
, sf_bg
.get_width(), sf_bg
.get_height() )
654 screen
.blit( sf_bg
, dirty_rect
)
655 screen
.blit( sf
, ( tlx
* 1.01, tly
) )
657 # ----------------------
659 def finishedlevel_input( event
, waiting
):
660 if event
.type == QUIT
:
662 elif config
.keys_volup
.matches( event
):
663 config
.volume
= sound_mgr
.increase_volume()
665 elif config
.keys_voldown
.matches( event
):
666 config
.volume
= sound_mgr
.decrease_volume()
668 elif config
.keys_startgame
.matches( event
):
672 def finishedlevel_mainloop( gamestate
):
674 inlevel_redraw_screen( gamestate
, False )
677 waiting
= finishedlevel_input( pygame
.event
.wait(), waiting
)
679 def finishedgame_mainloop( config
, gamestate
):
681 #config.start_level = 0
684 # inlevel_redraw_screen( gamestate )
685 # write_text( "Congratulations!", (255,255,255), 0.125, 0.38 )
686 # write_text( "You won!", (255,255,255), 0.125, 0.52 )
688 # write_text( "Press %s" % config.keys_startgame.name, (255,255,255),
690 # pygame.display.update()
692 # waiting = finishedgame_input( pygame.event.wait(), waiting )
694 # ingame_surface_background.fill( config.colour_background )
696 def sort_by_score( class2score
):
697 scorename_sorted
= []
699 for cls
in class2score
.keys():
700 scorename_sorted
.append( ( class2score
[cls
], cls
.GetName() ) )
702 scorename_sorted
.sort( reverse
= True )
704 return scorename_sorted
706 def execute_tournament( config
):
710 for cls
in config
.player_classes
:
711 if not class_is_human( cls
):
712 classes
.append( cls
)
713 total_scores
[cls
] = 0
716 print " " * 30 + "=== Pairings ==="
720 num_classes
= len( classes
)
721 for i
in range( num_classes
):
722 for j
in range( i
+1, num_classes
):
724 ( classes
[i
], classes
[j
] ),
725 total_scores
, False )
728 print " " * 30 + "=== Medley ==="
731 execute_match( classes
, total_scores
, True )
734 print " " * 30 + "=== Total Scores ==="
737 scorename_sorted
= sort_by_score( total_scores
)
739 for score
, name
in scorename_sorted
:
740 print "%30s % 4d" % ( name
, score
)
744 def execute_match( classes
, total_scores
, newlines
):
747 match_scores
[cls
] = 0
749 for i
in range( config
.num_games
):
750 gamestate
= GameState( config
, classes
)
752 while gamestate
.alive
== INGAME_ALL_ALIVE
:
753 gamestate
.timer_tick()
755 for player
in gamestate
.statuses
.keys():
756 status
= gamestate
.statuses
[player
]
757 if not status
.IsDead():
758 match_scores
[player
.__class
__] += 1
760 scorename_sorted
= sort_by_score( match_scores
)
763 for score
, name
in scorename_sorted
:
764 if oldscore
!= -1 and not newlines
:
765 if oldscore
== score
:
769 print "%- 4d %-30s" % ( score
, name
),
771 print "%30s% 4d" % ( name
, score
),
782 total_scores
[cls
] += match_scores
[cls
]
784 def get_players( players_dir
):
787 player_re
= re
.compile( """^(\w*Player).py$""" )
788 for filename
in os
.listdir( players_dir
):
789 m
= player_re
.match( filename
)
791 class_name
= m
.group( 1 )
792 cls
= __import__( "players/" + class_name
,
793 globals(), locals() ).__dict
__[class_name
]
794 ret_classes
.append( cls
)
798 # ----------------------
799 # Execution starts here
800 # ----------------------
813 MENU_SOUND_EFFECTS
= 4
815 MENU_CHANGE_PLAYER
= 100 # Must go at end of this list
821 TITLE_TICK_TIME
= 4000
823 EVENTTYPE_INGAME_TICK
= pygame
.USEREVENT
824 EVENTTYPE_TITLE_TICK
= pygame
.USEREVENT
+ 1
826 num_args
= len( sys
.argv
)
828 # if sys.argv[1] == "--help":
830 # print "troncode.py [install_dir] [config_file] [resolution] "
831 # print " [num_players] [--tournament]"
833 # print "e.g. ./troncode.py . troncoderc.txt '(640,480)' 2 --tournament"
837 # install_dir = sys.argv[1]
842 # config_filename = sys.argv[2]
844 # config_filename = os.path.expanduser( "~/.troncode/config" )
846 config_filename
= os
.path
.expanduser( "~/.troncode/config" )
849 config
= TronCodeConfig( config_filename
)
851 if num_args
> 1 and sys
.argv
[1] == "--tournament":
852 run_tournament
= True
853 config
.num_games
= 100
855 run_tournament
= False
857 config
.install_dir
= install_dir
858 config
.unsaved
.append( "install_dir" )
860 config
.images_dir
= os
.path
.join( install_dir
, "images" )
861 config
.unsaved
.append( "images_dir" )
863 config
.music_dir
= os
.path
.join( install_dir
, "music" )
864 config
.unsaved
.append( "music_dir" )
867 config
.screen_size
= config
.parse_value( sys
.argv
[3] )
869 config
.arena_size
= ( 200, 200 )
870 config
.unsaved
.append( "arena_size" )
872 config
.starting_border
= 35
873 config
.unsaved
.append( "starting_border" )
875 config
.players_dir
= "players"
876 config
.unsaved
.append( "players_dir" )
878 config
.player_classes
= get_players( config
.players_dir
)
879 config
.unsaved
.append( "player_classes" )
882 config
.unsaved
.append( "players" )
885 execute_tournament( config
)
889 pygame
.mouse
.set_visible( False )
891 window
= pygame
.display
.set_mode( config
.screen_size
)
892 pygame
.display
.set_caption( 'troncode' )
893 screen
= pygame
.display
.get_surface()
897 ( float( config
.screen_size
[0] - fixed_border
*2 )
898 / float( config
.arena_size
[0] ) ),
899 ( float( config
.screen_size
[1] - fixed_border
*2 )
900 / float( config
.arena_size
[1] ) ) )
902 screen_border
= ( float( config
.screen_size
[0] - config
.arena_size
[0]*scale
)
904 float( config
.screen_size
[1] - config
.arena_size
[1]*scale
) / 2.0, )
906 ceil_scale
= math
.ceil( scale
)
908 # General initialisation
910 num_joysticks
= pygame
.joystick
.get_count()
911 for j
in range( num_joysticks
):
912 pygame
.joystick
.Joystick( j
).init()
914 intro_surface_title
= mopelib
.load_and_scale_image( "title.png", config
)
915 intro_surface_instr
= mopelib
.load_and_scale_image( "instructions.png", config
)
916 intro_surface_music
= mopelib
.load_and_scale_image( "music.png", config
)
918 ingame_surface_background
= pygame
.Surface( screen
.get_size() ).convert()
919 ingame_surface_background
.fill( config
.colour_background
)
921 intro_mode
= INTRO_MODE_TITLE
923 sound_mgr
= TronCodeSoundManager( config
.volume
)
925 troncode_version
= mopelib
.read_version( config
)
927 ingame_font
= pygame
.font
.Font( None, int( config
.screen_size
[1] * 0.09 ) )
929 menurender
= mopelib
.MenuRenderer( screen
, config
, ingame_surface_background
,
930 (128, 128, 128), (128, 255, 128), (128, 128, 128) )
933 sound_mgr
.music_loud()
934 intro_mainloop( config
)
935 sound_mgr
.music_quiet()
936 gamestate
= ingame_mainloop( config
)
937 intro_mode
= finishedgame_mainloop( config
, gamestate
)