Added a new player "Turn Right Killer", and made a new tournament mode.
[troncode.git] / troncode.py
blob91330ded3f427986d047f7c8e6d19c6035dcba4e
1 #!/usr/bin/python -u
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,
20 # USA.
23 import gc
24 import math
25 import os
26 import pygame
27 from pygame.locals import *
28 import random
29 import sys
30 import time
32 from mopelib import mopelib
33 from troncode_values import *
35 from player_human import HumanPlayer
36 from player_null import NullPlayer
37 from player_turnright import TurnRightPlayer
38 from player_turnrightkiller import TurnRightKillerPlayer
40 # ----------------------
41 class TronCodeConfig( mopelib.Config ):
43 def default_config( self ):
44 self.num_players = 2
45 self.tick_time = 2
47 self.screen_size = ( 1010, 1010 )
48 self.colour_background = ( 15, 45, 15 )
50 self.volume = 50
52 self.music_on = 1
53 self.sound_effects_on = 1
55 self.keys_menu = mopelib.MyInputEvent( "Escape" )
56 self.keys_menu.add( pygame.KEYDOWN, pygame.K_ESCAPE )
57 self.keys_menu.add( pygame.JOYBUTTONDOWN, 8 ) # GP2X Start
58 self.keys_menu.add( pygame.JOYBUTTONDOWN, 9 ) # GP2X Select
60 self.keys_return = mopelib.MyInputEvent( "Return" )
61 self.keys_return.add( pygame.KEYDOWN, pygame.K_RETURN )
62 self.keys_return.add( pygame.JOYBUTTONDOWN, 13 ) # GP2X B button
64 self.keys_startgame = mopelib.MyInputEvent( "any key" )
65 self.keys_startgame.add_all( pygame.KEYDOWN )
66 self.keys_startgame.add_all( pygame.JOYBUTTONDOWN )
67 self.keys_startgame.add_all( pygame.MOUSEBUTTONDOWN )
69 self.keys_up = mopelib.MyInputEvent( "up" )
70 self.keys_up.add( pygame.KEYDOWN, ord( 'q' ) )
71 self.keys_up.add( pygame.KEYDOWN, pygame.K_UP )
72 self.keys_up.add( pygame.JOYBUTTONDOWN, 0 ) # GP2X Joy up
73 self.keys_up.add( pygame.JOYBUTTONDOWN, 15 ) # GP2X Y button
75 self.keys_right = mopelib.MyInputEvent( "right" )
76 self.keys_right.add( pygame.KEYDOWN, ord( 'p' ) )
77 self.keys_right.add( pygame.KEYDOWN, pygame.K_RIGHT )
78 self.keys_right.add( pygame.JOYBUTTONDOWN, 6 ) # GP2X Joy right
79 self.keys_right.add( pygame.JOYBUTTONDOWN, 13 ) # GP2X B button
81 self.keys_down = mopelib.MyInputEvent( "down" )
82 self.keys_down.add( pygame.KEYDOWN, ord( 'a' ) )
83 self.keys_down.add( pygame.KEYDOWN, pygame.K_DOWN )
84 self.keys_down.add( pygame.JOYBUTTONDOWN, 4 ) # GP2X Joy down
85 self.keys_down.add( pygame.JOYBUTTONDOWN, 14 ) # GP2X X button
87 self.keys_left = mopelib.MyInputEvent( "left" )
88 self.keys_left.add( pygame.KEYDOWN, ord( 'o' ) )
89 self.keys_left.add( pygame.KEYDOWN, pygame.K_LEFT )
90 self.keys_left.add( pygame.JOYBUTTONDOWN, 2 ) # GP2X Joy left
91 self.keys_left.add( pygame.JOYBUTTONDOWN, 12 ) # GP2X A button
93 self.keys_volup = mopelib.MyInputEvent( "+" )
94 self.keys_volup.add( pygame.KEYDOWN, ord( '+' ) )
95 self.keys_volup.add( pygame.KEYDOWN, ord( '=' ) )
96 self.keys_volup.add( pygame.JOYBUTTONDOWN, 16 ) # GP2X volume + button
98 self.keys_voldown = mopelib.MyInputEvent( "-" )
99 self.keys_voldown.add( pygame.KEYDOWN, ord( '-' ) )
100 self.keys_voldown.add( pygame.JOYBUTTONDOWN, 17 ) # GP2X volume - button
102 self.keys_p1_up = mopelib.MyInputEvent( "p1_up" )
103 self.keys_p1_up.add( pygame.KEYDOWN, pygame.K_UP )
104 self.keys_p1_up.add( pygame.JOYBUTTONDOWN, 0 ) # GP2X Joy up
106 self.keys_p1_right = mopelib.MyInputEvent( "p1_right" )
107 self.keys_p1_right.add( pygame.KEYDOWN, pygame.K_RIGHT )
108 self.keys_p1_right.add( pygame.JOYBUTTONDOWN, 6 ) # GP2X Joy right
110 self.keys_p1_down = mopelib.MyInputEvent( "p1_down" )
111 self.keys_p1_down.add( pygame.KEYDOWN, pygame.K_DOWN )
112 self.keys_p1_down.add( pygame.JOYBUTTONDOWN, 4 ) # GP2X Joy down
114 self.keys_p1_left = mopelib.MyInputEvent( "p1_left" )
115 self.keys_p1_left.add( pygame.KEYDOWN, pygame.K_LEFT )
116 self.keys_p1_left.add( pygame.JOYBUTTONDOWN, 4 ) # GP2X Joy left
118 self.keys_p2_up = mopelib.MyInputEvent( "p2_up" )
119 self.keys_p2_up.add( pygame.KEYDOWN, ord( '2' ) )
120 self.keys_p2_up.add( pygame.JOYBUTTONDOWN, 15 ) # GP2X Y button
122 self.keys_p2_right = mopelib.MyInputEvent( "p2_right" )
123 self.keys_p2_right.add( pygame.KEYDOWN, ord( 'e' ) )
124 self.keys_p2_right.add( pygame.JOYBUTTONDOWN, 13 ) # GP2X B button
126 self.keys_p2_down = mopelib.MyInputEvent( "p2_down" )
127 self.keys_p2_down.add( pygame.KEYDOWN, ord( 'w' ) )
128 self.keys_p2_down.add( pygame.JOYBUTTONDOWN, 14 ) # GP2X X button
130 self.keys_p2_left = mopelib.MyInputEvent( "p2_left" )
131 self.keys_p2_left.add( pygame.KEYDOWN, ord( 'q' ) )
132 self.keys_p2_left.add( pygame.JOYBUTTONDOWN, 12 ) # GP2X A button
134 self.keys_p3_up = mopelib.MyInputEvent( "p3_up" )
135 self.keys_p3_up.add( pygame.KEYDOWN, ord( 'i' ) )
137 self.keys_p3_right = mopelib.MyInputEvent( "p3_right" )
138 self.keys_p3_right.add( pygame.KEYDOWN, ord( 'l' ) )
140 self.keys_p3_down = mopelib.MyInputEvent( "p3_down" )
141 self.keys_p3_down.add( pygame.KEYDOWN, ord( 'k' ) )
143 self.keys_p3_left = mopelib.MyInputEvent( "p3_left" )
144 self.keys_p3_left.add( pygame.KEYDOWN, ord( 'j' ) )
146 self.keys_p4_up = mopelib.MyInputEvent( "p4_up" )
147 self.keys_p4_up.add( pygame.KEYDOWN, pygame.K_KP8 )
149 self.keys_p4_right = mopelib.MyInputEvent( "p4_right" )
150 self.keys_p4_right.add( pygame.KEYDOWN, pygame.K_KP6 )
152 self.keys_p4_down = mopelib.MyInputEvent( "p4_down" )
153 self.keys_p4_down.add( pygame.KEYDOWN, pygame.K_KP5 )
154 self.keys_p4_down.add( pygame.KEYDOWN, pygame.K_KP2 )
156 self.keys_p4_left = mopelib.MyInputEvent( "p4_left" )
157 self.keys_p4_left.add( pygame.KEYDOWN, pygame.K_KP4 )
160 # ----------------------
162 class TronCodeSoundManager( mopelib.SoundManager ):
164 def __init__( self, volume ):
165 mopelib.SoundManager.__init__( self, config )
167 #self.add_sample_group( "waddles", ["waddle1"] )
169 # ----------------------
171 def intro_draw_instructions():
172 write_text( "Press %s for menu, or %s to start" % (
173 config.keys_menu.name, config.keys_startgame.name ),
174 (0, 0, 0), 0.05, 0.99 )
176 # ----------------------
178 def general_menu_create_menu( menu, config, gamestate ):
179 menu.items = []
180 if gamestate == None: # We are on a title screen - Start Game option
181 menu.add_item( "Start game", MENU_START )
182 menu.add_item( "Number of players: %d" % (config.num_players),
183 MENU_NUM_PLAYERS )
184 for player_num in range( config.num_players ):
185 cls_name = "--unknown--"
186 if player_num < len( config.player_classes ):
187 cls_name = config.player_classes[player_num].GetName()
188 menu.add_item( "P%d: %s" % ( player_num, cls_name ),
189 MENU_CHANGE_PLAYER + player_num )
190 else:
191 menu.add_item( "Continue", MENU_START )
192 menu.add_item( "End game", MENU_END )
194 tmp_str = "Music: "
195 if config.music_on:
196 tmp_str += "on"
197 else:
198 tmp_str += "off"
199 menu.add_item( tmp_str, MENU_MUSIC )
201 tmp_str = "Effects: "
202 if config.sound_effects_on:
203 tmp_str += "on"
204 else:
205 tmp_str += "off"
206 menu.add_item( tmp_str, MENU_SOUND_EFFECTS )
208 menu.add_item( "Quit troncode", MENU_QUIT )
210 return menu
212 def general_menu_screen( config, gamestate ):
214 if gamestate == None:
215 menu_title = "troncode"
216 else:
217 menu_title = "troncode paused"
219 menu = mopelib.Menu()
220 general_menu_create_menu( menu, config, gamestate )
221 menurender.set_menu( menu, menu_title )
222 menurender.repaint_full()
224 game_start = False
226 waiting = True
227 while waiting:
228 event = pygame.event.wait()
229 if event.type == QUIT:
230 sys.exit(0)
231 elif config.keys_menu.matches( event ):
232 waiting = False
233 elif config.keys_down.matches( event ):
234 menurender.move_down()
235 elif config.keys_up.matches( event ):
236 menurender.move_up()
237 elif config.keys_return.matches( event ):
238 code = menu.get_selected_item().code
239 if code == MENU_START:
240 game_start = True
241 waiting = False
242 elif code == MENU_END:
243 gamestate.alive = INGAME_QUIT
244 waiting = False
245 elif code == MENU_MUSIC:
246 if config.music_on == 1:
247 config.music_on = 0
248 else:
249 config.music_on = 1
250 general_menu_create_menu( menu, config, gamestate )
251 menurender.repaint_full()
252 sound_mgr.setup( gamestate )
253 config.save()
254 elif code == MENU_SOUND_EFFECTS:
255 if config.sound_effects_on:
256 config.sound_effects_on = 0
257 else:
258 config.sound_effects_on = 1
259 general_menu_create_menu( menu, config, gamestate )
260 menurender.repaint_full()
261 sound_mgr.setup( gamestate )
262 config.save()
263 elif code == MENU_QUIT:
264 sys.exit(0)
266 return game_start
268 # ----------------------
270 def intro_draw_title():
271 screen.blit( intro_surface_title, (0,0) )
272 write_text( "Version " + troncode_version,
273 ( 0, 0, 0 ), 0.05, 0.88 )
274 write_text( "by Andy Balaam", ( 0, 0, 0 ), 0.06, 0.93 )
275 intro_draw_instructions()
277 def intro_draw_something( intro_mode ):
278 if intro_mode == INTRO_MODE_TITLE:
279 intro_draw_title()
280 elif intro_mode == INTRO_MODE_INSTR:
281 screen.blit( intro_surface_instr, (0,0) )
282 intro_draw_instructions()
283 elif intro_mode == INTRO_MODE_MUSIC:
284 screen.blit( intro_surface_music, (0,0) )
285 intro_draw_instructions()
286 pygame.display.update()
288 def intro_input( event, config, intro_mode ):
289 if event.type == QUIT:
290 sys.exit(0)
291 elif config.keys_volup.matches( event ):
292 config.volume = sound_mgr.increase_volume()
293 config.save()
294 elif config.keys_voldown.matches( event ):
295 config.volume = sound_mgr.decrease_volume()
296 config.save()
297 else:
298 if event.type == EVENTTYPE_TITLE_TICK:
299 intro_mode += 1
300 if intro_mode == INTRO_MODE_ENDED:
301 intro_mode = INTRO_MODE_TITLE
302 intro_draw_something( intro_mode )
303 elif config.keys_menu.matches( event ):
304 mopelib.clear_events( EVENTTYPE_TITLE_TICK )
305 start_game = general_menu_screen( config, None )
306 if start_game:
307 intro_mode = INTRO_MODE_ENDED
308 else:
309 intro_draw_something( intro_mode )
310 pygame.time.set_timer( EVENTTYPE_TITLE_TICK, TITLE_TICK_TIME )
311 elif config.keys_startgame.matches( event ):
312 intro_mode = INTRO_MODE_ENDED
313 return intro_mode
315 # ----------------------
317 def intro_mainloop( config ):
318 intro_draw_title()
320 pygame.display.update()
321 pygame.time.set_timer( EVENTTYPE_TITLE_TICK, TITLE_TICK_TIME )
323 intro_mode = INTRO_MODE_TITLE
324 while intro_mode < INTRO_MODE_ENDED:
325 intro_mode = intro_input( pygame.event.wait(), config, intro_mode )
327 mopelib.clear_events( EVENTTYPE_TITLE_TICK )
329 def draw_pixel( gamestate, surface, colour, x, y ):
330 col = mopelib.dim_colour( colour, gamestate.dim )
331 adj_x = screen_border[0] + scale * x
332 adj_y = screen_border[1] + scale * y
333 if scale < 1:
334 surface.set_at( ( int(adj_x), int(adj_y) ), col )
335 else:
336 pygame.draw.rect( surface, col,
337 (adj_x, adj_y, ceil_scale, ceil_scale ) )
339 def inlevel_screen_blit( gamestate ):
340 screen.blit( gamestate.offscreen_buff, (0,0) )
341 write_text_ingame( gamestate )
342 pygame.display.update()
344 def inlevel_redraw_screen( gamestate, arrows ):
345 gamestate.offscreen_buff = pygame.Surface( gamestate.config.screen_size )
346 gamestate.offscreen_buff.blit( ingame_surface_background, (0,0) )
347 for x, y, colour in gamestate.pixels_list:
348 draw_pixel( gamestate, gamestate.offscreen_buff, colour, x, y )
349 inlevel_draw_players( gamestate )
350 # TODO: draw arrows
351 inlevel_screen_blit( gamestate )
353 def inlevel_draw_players( gamestate ):
354 for player in gamestate.players:
355 x, y = gamestate.GetPosition( player )
356 draw_pixel( gamestate, gamestate.offscreen_buff, player.GetColour(),
357 x, y )
359 def inlevel_update_screen( gamestate ):
360 inlevel_draw_players( gamestate )
361 inlevel_screen_blit( gamestate )
365 # ----------------------
367 def inlevel_input( event, gamestate ):
368 if event.type == QUIT:
369 sys.exit(0)
370 elif( ( event.type == pygame.ACTIVEEVENT and event.state == 2 )
371 or config.keys_menu.matches( event ) ):
372 general_menu_screen( config, gamestate )
373 inlevel_redraw_screen( gamestate, False )
374 elif config.keys_volup.matches( event ):
375 config.volume = sound_mgr.increase_volume()
376 config.save()
377 elif config.keys_voldown.matches( event ):
378 config.volume = sound_mgr.decrease_volume()
379 config.save()
380 elif event.type == EVENTTYPE_INGAME_TICK:
381 gamestate.timer_tick()
382 gamestate.key_events = []
383 if gamestate.alive == INGAME_SOMEONE_DEAD:
384 finishedlevel_mainloop( gamestate )
385 else:
386 inlevel_update_screen( gamestate )
387 else:
388 gamestate.key_events.append( event )
391 # ----------------------
393 def finishedgame_input( event, waiting ):
394 if event.type == QUIT:
395 sys.exit(0)
396 elif config.keys_volup.matches( event ):
397 config.volume = sound_mgr.increase_volume()
398 config.save()
399 elif config.keys_voldown.matches( event ):
400 config.volume = sound_mgr.decrease_volume()
401 config.save()
402 elif config.keys_startgame.matches( event ):
403 waiting = False
404 return waiting
406 # ----------------------
408 class PlayerStatus:
409 def __init__( self, x, y, direction, player ):
410 self._x = x
411 self._y = y
412 self._dir = direction
413 self._colour = player.GetColour()
414 self._dead = False
416 def GetPosition( self ):
417 return ( self._x, self._y )
419 def GetDirection( self ):
420 return ( self._dir )
422 def SetDirection( self, direction ):
423 self._dir = direction
425 def IsDead( self ):
426 return self._dead
428 def Move( self, gamestate ):
429 """Moves the player one position depending on its direction, and
430 returns True if it hit anything, False otherwise."""
432 if self._dir == DIR_UP:
433 self._y -= 1
434 elif self._dir == DIR_RIGHT:
435 self._x += 1
436 elif self._dir == DIR_DOWN:
437 self._y += 1
438 elif self._dir == DIR_LEFT:
439 self._x -= 1
441 self._dead = gamestate.AddPixel( self._x, self._y, self._colour )
443 return self._dead
445 class GameBoard:
446 def __init__( self, gamestate ):
447 self._gamestate = gamestate
449 def GetRelativePixel( self, start_pos, facing_dir,
450 offset_fwd, offset_right ):
451 """Given a position to stand in the arena, and a direction to face,
452 return the status (0 for empty, >0 for non-empty) of a pixel that
453 is offset_fwd pixels in front, and offset_right pixels to the right
454 (negative values may be used to go backwards or left respectively).
455 Pixels outside the arena also return >0."""
457 if facing_dir == DIR_UP:
458 found_pos = ( start_pos[0] + offset_right,
459 start_pos[1] - offset_fwd )
460 elif facing_dir == DIR_RIGHT:
461 found_pos = ( start_pos[0] + offset_fwd,
462 start_pos[1] + offset_right )
463 elif facing_dir == DIR_DOWN:
464 found_pos = ( start_pos[0] - offset_right,
465 start_pos[1] + offset_fwd )
466 elif facing_dir == DIR_LEFT:
467 found_pos = ( start_pos[0] - offset_fwd,
468 start_pos[1] - offset_right )
470 if ( found_pos[0] < 0 or
471 found_pos[0] >= self._gamestate.config.arena_size[0] or
472 found_pos[1] < 0 or
473 found_pos[1] >= self._gamestate.config.arena_size[1] ):
474 retval = 100
475 elif found_pos in self._gamestate.pixels_set:
476 retval = 1
477 else:
478 retval = 0
480 return retval
483 def TurnRight( self, direction ):
484 """Return the direction found by turning 90 degrees right from
485 the supplied direction."""
486 direction += 1
487 if direction > DIR_LEFT:
488 direction = DIR_UP
489 return direction
491 def TurnLeft( self, direction ):
492 """Return the direction found by turning 90 degrees left from
493 the supplied direction."""
494 direction -= 1
495 if direction < DIR_UP:
496 direction = DIR_LEFT
497 return direction
499 class GameState:
500 def __init__( self, config ):
501 self.config = config
503 self.players = []
505 for cls in config.player_classes:
506 if "IsHuman" in cls.__dict__ and cls.IsHuman():
507 self.players.append( cls( config.keys_p1_up,
508 config.keys_p1_right, config.keys_p1_down,
509 config.keys_p1_left ) )
510 else:
511 self.players.append( cls() )
513 self.alive = INGAME_ALL_ALIVE
514 self.dim = 1
516 self.pixels_list = []
517 self.pixels_set = set()
518 self.create_initial_pixels()
520 self.statuses = {}
521 for player in self.players:
522 x = random.randint( config.starting_border, config.arena_size[0]
523 - config.starting_border )
524 y = random.randint( config.starting_border, config.arena_size[1]
525 - config.starting_border )
526 dr = random.randint( DIR_UP, DIR_LEFT )
527 self.statuses[player] = PlayerStatus( x, y, dr, player )
528 self.AddPixel( x, y, player.GetColour() )
530 self._gameboard = GameBoard( self )
532 def timer_tick( self ):
533 any_dead = False
534 for player in self.players:
535 status = self.statuses[player]
536 if( "IsHuman" in player.__class__.__dict__ and
537 player.__class__.IsHuman() ):
538 new_dir = player.GetDirWithInput( status.GetDirection(),
539 self.key_events )
540 else:
541 new_dir = player.GetDir( status.GetPosition(),
542 status.GetDirection(), self._gameboard )
543 status.SetDirection( new_dir )
544 # TODO: copy pixels list for "security"
546 dead = self.statuses[player].Move( self )
547 if dead:
548 any_dead = True
550 if any_dead:
551 self.alive = INGAME_SOMEONE_DEAD
553 def create_initial_pixels( self ):
554 colour = (100, 100, 100)
555 size_x, size_y = config.arena_size
556 for x in range( size_x ):
557 self.AddPixel( x, 0, colour )
558 self.AddPixel( x, size_y - 1, colour )
559 for y in range( 1, size_y - 1 ):
560 self.AddPixel( 0, y, colour )
561 self.AddPixel( size_x - 1, y, colour )
563 def GetPosition( self, player ):
564 return self.statuses[player].GetPosition()
566 def AddPixel( self, x, y, colour ):
567 if ( x, y ) in self.pixels_set:
568 dead = True
569 else:
570 dead = False
571 self.pixels_set.add( ( x, y ) )
573 self.pixels_list.append( ( x, y, colour ) )
575 return dead
578 # ----------------------
580 def ingame_mainloop( config ):
581 gamestate = GameState( config )
582 gamestate.key_events = []
583 while gamestate.alive == INGAME_ALL_ALIVE:
584 inlevel_mainloop( config, gamestate )
585 return gamestate
587 # ----------------------
589 def inlevel_mainloop( config, gamestate ):
591 inlevel_redraw_screen( gamestate, True )
592 #time.sleep( 1 )
593 inlevel_redraw_screen( gamestate, False )
595 gc.disable()
596 pygame.time.set_timer( EVENTTYPE_INGAME_TICK, config.tick_time )
597 while gamestate.alive == INGAME_ALL_ALIVE:
598 inlevel_input( pygame.event.wait(), gamestate )
599 gc.enable()
600 mopelib.clear_events( EVENTTYPE_INGAME_TICK )
602 ingame_surface_background.fill( config.colour_background )
605 # ----------------------
607 def write_text( txt, colour, size, y_pos ):
608 ft = pygame.font.Font( None, int( config.screen_size[1] * size ) )
609 sf = ft.render( txt, True, colour )
610 screen.blit( sf, ( (config.screen_size[0] - sf.get_width() )/2,
611 (config.screen_size[1] - sf.get_height() ) * y_pos ) )
613 # ----------------------
615 def write_text_ingame( gamestate ):
616 #global ingame_font
618 #bgcol = mopelib.dim_colour( gamestate.cur_lev.background_colour,
619 # gamestate.dim )
620 #fgcol = mopelib.dim_colour( gamestate.cur_lev.text_colour, gamestate.dim )
622 #write_text_blank_font_coords( "Level: %d" % ( gamestate.level_num + 1 ),
623 # fgcol, ingame_font, bgcol, 0.3, 0.99 )
625 #write_text_blank_font_coords( "Time: %s" % gamestate.cur_lev.current_time,
626 # fgcol, ingame_font, bgcol, 0.7, 0.99 )
627 pass
630 def write_text_blank_font_coords( txt, colour, font, bgcolour, x, y ):
631 sf = font.render( txt, True, colour )
632 sf_bg = pygame.Surface( ( int( sf.get_width() * 1.29 ), sf.get_height() ) )
633 sf_bg.fill( bgcolour )
635 tlx = ( config.screen_size[0] - sf_bg.get_width() ) * x
636 tly = ( config.screen_size[1] - sf_bg.get_height() ) * y
638 dirty_rect = Rect( tlx, tly, sf_bg.get_width(), sf_bg.get_height() )
639 screen.blit( sf_bg, dirty_rect )
640 screen.blit( sf, ( tlx * 1.01, tly ) )
642 # ----------------------
644 def finishedlevel_input( event, waiting ):
645 if event.type == QUIT:
646 sys.exit(0)
647 elif config.keys_volup.matches( event ):
648 config.volume = sound_mgr.increase_volume()
649 config.save()
650 elif config.keys_voldown.matches( event ):
651 config.volume = sound_mgr.decrease_volume()
652 config.save()
653 elif config.keys_startgame.matches( event ):
654 waiting = False
655 return waiting
657 def finishedlevel_mainloop( gamestate ):
658 gamestate.dim = 0.5
659 inlevel_redraw_screen( gamestate, False )
660 waiting = True
661 while waiting:
662 waiting = finishedlevel_input( pygame.event.wait(), waiting )
664 def finishedgame_mainloop( config, gamestate ):
665 pass
666 #config.start_level = 0
667 # config.save()
669 # inlevel_redraw_screen( gamestate )
670 # write_text( "Congratulations!", (255,255,255), 0.125, 0.38 )
671 # write_text( "You won!", (255,255,255), 0.125, 0.52 )
672 # waiting = True
673 # write_text( "Press %s" % config.keys_startgame.name, (255,255,255),
674 # 0.05, 0.8 )
675 # pygame.display.update()
676 # while waiting:
677 # waiting = finishedgame_input( pygame.event.wait(), waiting )
679 # ingame_surface_background.fill( config.colour_background )
681 def execute_tournament():
682 scores = {}
683 for cls in config.player_classes:
684 scores[cls] = 0
686 for i in range( config.num_games ):
687 gamestate = GameState( config )
689 #gamestate.key_events = []
690 while gamestate.alive == INGAME_ALL_ALIVE:
691 gamestate.timer_tick()
693 for player in gamestate.statuses.keys():
694 status = gamestate.statuses[player]
695 if not status.IsDead():
696 scores[player.__class__] += 1
698 for cls in config.player_classes:
699 print "%s: %d" % ( cls.GetName(), scores[cls] )
702 # ----------------------
703 # Execution starts here
704 # ----------------------
706 # Fixed constants
708 INTRO_MODE_TITLE = 0
709 INTRO_MODE_INSTR = 1
710 INTRO_MODE_MUSIC = 2
711 INTRO_MODE_ENDED = 3
713 MENU_START = 0
714 MENU_NUM_PLAYERS = 1
715 MENU_END = 2
716 MENU_MUSIC = 3
717 MENU_SOUND_EFFECTS = 4
718 MENU_QUIT = 6
719 MENU_CHANGE_PLAYER = 100 # Must go at end of this list
721 INGAME_ALL_ALIVE = 0
722 INGAME_SOMEONE_DEAD = 1
723 INGAME_QUIT = 2
725 TITLE_TICK_TIME = 4000
727 EVENTTYPE_INGAME_TICK = pygame.USEREVENT
728 EVENTTYPE_TITLE_TICK = pygame.USEREVENT + 1
730 num_args = len( sys.argv )
731 #if num_args > 1:
732 # if sys.argv[1] == "--help":
733 # print "Usage:"
734 # print "troncode.py [install_dir] [config_file] [resolution] "
735 # print " [num_players] [--tournament]"
736 # print
737 # print "e.g. ./troncode.py . troncoderc.txt '(640,480)' 2 --tournament"
738 # print
739 # sys.exit( 0 )
741 # install_dir = sys.argv[1]
742 #else:
743 # install_dir = "."
745 #if num_args > 2:
746 # config_filename = sys.argv[2]
747 #else:
748 # config_filename = os.path.expanduser( "~/.troncode/config" )
750 config_filename = os.path.expanduser( "~/.troncode/config" )
751 install_dir = "."
753 config = TronCodeConfig( config_filename )
755 if num_args > 1 and sys.argv[1] == "--tournament":
756 run_tournament = True
757 config.num_games = 100
758 else:
759 run_tournament = False
761 config.install_dir = install_dir
762 config.unsaved.append( "install_dir" )
764 config.images_dir = os.path.join( install_dir, "images" )
765 config.unsaved.append( "images_dir" )
767 config.music_dir = os.path.join( install_dir, "music" )
768 config.unsaved.append( "music_dir" )
770 if num_args > 3:
771 config.screen_size = config.parse_value( sys.argv[3] )
773 config.arena_size = ( 200, 200 )
774 config.unsaved.append( "arena_size" )
776 config.starting_border = 35
777 config.unsaved.append( "starting_border" )
779 config.player_classes = []
780 config.unsaved.append( "player_classes" )
781 config.player_classes.append( TurnRightKillerPlayer )
782 config.player_classes.append( TurnRightPlayer )
784 config.players = []
785 config.unsaved.append( "players" )
787 if run_tournament:
788 execute_tournament()
789 else:
790 pygame.init()
791 pygame.font.init()
792 pygame.mouse.set_visible( False )
794 window = pygame.display.set_mode( config.screen_size )
795 pygame.display.set_caption( 'troncode' )
796 screen = pygame.display.get_surface()
798 fixed_border = 5
799 scale = min(
800 ( float( config.screen_size[0] - fixed_border*2 )
801 / float( config.arena_size[0] ) ),
802 ( float( config.screen_size[1] - fixed_border*2 )
803 / float( config.arena_size[1] ) ) )
805 screen_border = ( float( config.screen_size[0] - config.arena_size[0]*scale )
806 / 2.0,
807 float( config.screen_size[1] - config.arena_size[1]*scale ) / 2.0, )
809 ceil_scale = math.ceil( scale )
811 # General initialisation
813 num_joysticks = pygame.joystick.get_count()
814 for j in range( num_joysticks ):
815 pygame.joystick.Joystick( j ).init()
817 intro_surface_title = mopelib.load_and_scale_image( "title.png", config )
818 intro_surface_instr = mopelib.load_and_scale_image( "instructions.png", config )
819 intro_surface_music = mopelib.load_and_scale_image( "music.png", config )
821 ingame_surface_background = pygame.Surface( screen.get_size() ).convert()
822 ingame_surface_background.fill( config.colour_background )
824 intro_mode = INTRO_MODE_TITLE
826 sound_mgr = TronCodeSoundManager( config.volume )
828 troncode_version = mopelib.read_version( config )
830 ingame_font = pygame.font.Font( None, int( config.screen_size[1] * 0.09 ) )
832 menurender = mopelib.MenuRenderer( screen, config, ingame_surface_background,
833 (128, 128, 128), (128, 255, 128), (128, 128, 128) )
835 while True:
836 sound_mgr.music_loud()
837 intro_mainloop( config )
838 sound_mgr.music_quiet()
839 gamestate = ingame_mainloop( config )
840 intro_mode = finishedgame_mainloop( config, gamestate )