class Application(BaseApplication.BaseApplication): """The application class represents a group of browser windows.""" def __init__(self, prefs=None, display=None): self.root = Tk(className='Grail', screenName=display) self.root.withdraw() resources = os.path.join(script_dir, "data", "Grail.ad") if os.path.isfile(resources): self.root.option_readfile(resources, "startupFile") BaseApplication.BaseApplication.__init__(self, prefs) # The stylesheet must be initted before any Viewers, so it # registers its' prefs callbacks first, hence reloads before the # viewers reconfigure w.r.t. the new styles. self.stylesheet = Stylesheet.Stylesheet(self.prefs) self.load_images = 1 # Overridden by cmd line or pref. # socket management sockets = self.prefs.GetInt('sockets', 'number') self.sq = SocketQueue(sockets) self.prefs.AddGroupCallback('sockets', lambda self=self: \ self.sq.change_max( self.prefs.GetInt('sockets', 'number'))) # initialize on_exit_methods before global_history self.on_exit_methods = [] self.global_history = GlobalHistory.GlobalHistory(self) self.login_cache = {} self.rexec_cache = {} self.url_cache = CacheManager(self) self.image_cache = ImageCache(self.url_cache) self.auth = AuthenticationManager(self) self.root.report_callback_exception = self.report_callback_exception if sys.stdin.isatty(): # only useful if stdin might generate KeyboardInterrupt self.keep_alive() self.browsers = [] self.iostatuspanel = None self.in_exception_dialog = None from ancillary import Greek for k, v in Greek.entitydefs.items(): Application.dingbatimages[k] = (v, '_sym') self.root.bind_class("Text", "<Alt-Left>", self.dummy_event) self.root.bind_class("Text", "<Alt-Right>", self.dummy_event) def dummy_event(self, event): pass def register_on_exit(self, method): self.on_exit_methods.append(method) def unregister_on_exit(self, method): try: self.on_exit_methods.remove(method) except ValueError: pass def exit_notification(self): for m in self.on_exit_methods[:]: try: m() except: pass def add_browser(self, browser): self.browsers.append(browser) def del_browser(self, browser): try: self.browsers.remove(browser) except ValueError: pass def quit(self): self.root.quit() def open_io_status_panel(self): if not self.iostatuspanel: from ancillary import IOStatusPanel self.iostatuspanel = IOStatusPanel.IOStatusPanel(self) else: self.iostatuspanel.reopen() def maybe_quit(self): if not (self.embedded or self.browsers): self.quit() def go(self): try: try: if ilu_tk: ilu_tk.RunMainLoop() else: self.root.mainloop() except KeyboardInterrupt: pass finally: self.exit_notification() def keep_alive(self): # Exercise the Python interpreter regularly so keyboard # interrupts get through self.root.tk.createtimerhandler(KEEPALIVE_TIMER, self.keep_alive) def get_cached_image(self, url): return self.image_cache.get_image(url) def set_cached_image(self, url, image, owner=None): self.image_cache.set_image(url, image, owner) def open_url(self, url, method, params, reload=0, data=None): api = self.url_cache.open(url, method, params, reload, data=data) api._url_ = url return api def open_url_simple(self, url): api = self.open_url(url, 'GET', {}) errcode, errmsg, meta = api.getmeta() if errcode != 200: raise IOError(('url open error', errcode, errmsg, meta)) return URLReadWrapper(api, meta) def get_cache_keys(self): """For applets.""" return self.url_cache.items.keys() def decode_pipeline(self, fp, content_encoding, error=1): if self.decode_prog.has_key(content_encoding): prog = self.decode_prog[content_encoding] if not prog: return fp tfn = tempfile.mktemp() ok = 0 try: temp = open(tfn, 'w') BUFSIZE = 8192 while 1: buf = fp.read(BUFSIZE) if not buf: break temp.write(buf) temp.close() ok = 1 finally: if not ok: try: os.unlink(tfn) except os.error: pass pipeline = '%s <%s; rm -f %s' % (prog, tfn, tfn) # XXX What if prog fails? return os.popen(pipeline, 'r') if error: self.error_dialog( IOError, "Can't decode content-encoding: %s" % content_encoding) return None decode_prog = { 'gzip': 'gzip -d', 'x-gzip': 'gzip -d', 'compress': 'compress -d', 'x-compress': 'compress -d', } def exception_dialog(self, message="", root=None): exc, val, tb = sys.exc_type, sys.exc_value, sys.exc_traceback self.exc_dialog(message, exc, val, tb, root) def report_callback_exception(self, exc, val, tb, root=None): self.exc_dialog("in a callback function", exc, val, tb, root) def exc_dialog(self, message, exc, val, tb, root=None): if self.in_exception_dialog: print() print("*** Recursive exception", message) import traceback traceback.print_exception(exc, val, tb) return self.in_exception_dialog = 1 def f(s=self, m=message, e=exc, v=val, t=tb, root=root): s._exc_dialog(m, e, v, t, root) if TkVersion >= 4.1: self.root.after_idle(f) else: self.root.after(0, f) def _exc_dialog(self, message, exc, val, tb, root=None): # XXX This needn't be a modal dialog -- # XXX should SafeDialog be changed to support callbacks? from utils import SafeDialog msg = "An exception occurred " + str(message) + " :\n" msg = msg + str(exc) + " : " + str(val) dlg = SafeDialog.Dialog( root or self.root, text=msg, title="Python Exception: " + str(exc), bitmap='error', default=0, strings=("OK", "Show traceback"), ) self.in_exception_dialog = 0 if dlg.num == 1: self.traceback_dialog(exc, val, tb) def traceback_dialog(self, exc, val, tb): # XXX This could actually just create a new Browser window... from utils import TbDialog TbDialog.TracebackDialog(self.root, exc, val, tb) def error_dialog(self, exc, msg, root=None): # Display an error dialog. # Return when the user clicks OK # XXX This needn't be a modal dialog from utils import SafeDialog if type(msg) in (List, Tuple): s = '' for item in msg: s = s + ':\n' + str(item) msg = s[2:] else: msg = str(msg) SafeDialog.Dialog( root or self.root, text=msg, title="Error: " + str(exc), bitmap='error', default=0, strings=('OK', ), ) dingbatimages = { 'ldots': ('...', None), # math stuff 'sp': (' ', None), 'hairsp': ('\240', None), 'thinsp': ('\240', None), 'emdash': ('--', None), 'endash': ('-', None), 'mdash': ('--', None), 'ndash': ('-', None), 'ensp': (' ', None) } def clear_dingbat(self, entname): if self.dingbatimages.has_key(entname): del self.dingbatimages[entname] def set_dingbat(self, entname, entity): self.dingbatimages[entname] = entity def load_dingbat(self, entname): if self.dingbatimages.has_key(entname): return self.dingbatimages[entname] gifname = grailutil.which(entname + '.gif', self.iconpath) if gifname: img = PhotoImage(file=gifname, master=self.root) self.dingbatimages[entname] = img return img self.dingbatimages[entname] = None return None
class Game: tile_dir = "data/images/tiles/" ui_dir = "data/images/ui/" def __init__(self): # Define window properties self.window_size = self.window_width, self.window_height = 640, 640 self.tile_size = 32 self.depth = 0 self.flags = 0 self.fps = 60 # Initialize pygame pygame.init() # Define basic pygame properties self.screen = pygame.display.set_mode(self.window_size, self.flags, self.depth) self.clock = pygame.time.Clock() # Load the image cache self.tile_cache = ImageCache(Game.tile_dir) self.ui_cache = ImageCache(Game.ui_dir) # Define the key entities and default to None until we start the main game loop self.player = None def main_menu_loop(self): start_game = False # Create buttons with the static callback functions new_game_img = self.ui_cache.get_image("new_game_but") new_game_img_hov = self.ui_cache.get_image("new_game_but_hover") new_game_button = Button(self.get_half_width(), self.get_half_height() - 50, new_game_img, new_game_img_hov, 200, 50, start_game_cb) quit_img = self.ui_cache.get_image("quit_but") quit_img_hover = self.ui_cache.get_image("quit_but_hover") quit_button = Button(self.get_half_width(), self.get_half_height() + 10, quit_img, quit_img_hover, 200, 50, quit_game) # Define sprite group for buttons to easily draw button_entities = pygame.sprite.Group() button_entities.add((new_game_button, quit_button)) # Retrieve other UI images from cache bg_img = self.ui_cache.get_image("menu_bg") credits_img = self.ui_cache.get_image("credits") # Only draw background and other static images once to screen self.screen.blit(bg_img, (0, 0)) self.screen.blit(credits_img, (20, self.window_height - 50)) # Set the screen title pygame.display.set_caption("The Forming") while not start_game: self.clock.tick(self.fps) mouse_clicked = False # Loop through all events for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() if event.type == pygame.MOUSEBUTTONDOWN: mouse_clicked = True if event.type == pygame.KEYDOWN: pass # Update the buttons. Start game will return True if clicked, exiting the menu loop start_game = new_game_button.update(pygame.mouse.get_pos(), mouse_clicked) quit_button.update(pygame.mouse.get_pos(), mouse_clicked) button_entities.draw(self.screen) pygame.display.flip() self.main_game_loop() def main_game_loop(self): # Define the entity groups character_entities = pygame.sprite.Group() # All character entities (including Player.py) built_entities = pygame.sprite.Group() # Anything the player has built # Build the level level = Level() level.generate(self.tile_cache) # Create the player player_sprites = "data/images/player/" camera = Camera(main_camera, level.width * self.tile_size, level.height * self.tile_size, self.window_width, self.window_height) self.player = Player(32, 32, player_sprites, self.tile_size, self.tile_size, 2, camera) character_entities.add(self.player) # Create cursor entity for better collisions cursor = Cursor() game_running = True up, down, left, right = 0, 0, 0, 0 while game_running: # Reset game variables mouse_clicked = False for event in pygame.event.get(): if event.type == pygame.QUIT: quit_game() # Key down events if event.type == pygame.KEYDOWN: if event.key in (pygame.K_UP, pygame.K_w): up = 1 if event.key in (pygame.K_DOWN, pygame.K_s): down = 1 if event.key in (pygame.K_LEFT, pygame.K_a): left = 1 if event.key in (pygame.K_RIGHT, pygame.K_d): right = 1 # Key up events if event.type == pygame.KEYUP: if event.key in (pygame.K_UP, pygame.K_w): up = 0 if event.key in (pygame.K_DOWN, pygame.K_s): down = 0 if event.key in (pygame.K_LEFT, pygame.K_a): left = 0 if event.key in (pygame.K_RIGHT, pygame.K_d): right = 0 cursor.update() for tile in level.terrain: self.screen.blit(tile.image, self.player.camera.apply(tile)) for tile in level.obstacles: self.screen.blit(tile.image, self.player.camera.apply(tile)) for character in character_entities: self.screen.blit(character.image, self.player.camera.apply(character)) self.player.update(up, down, left, right, level.obstacles) pygame.display.flip() self.main_menu_loop() def get_half_width(self): return self.window_width / 2 def get_half_height(self): return self.window_height / 2
class Application(BaseApplication.BaseApplication): """The application class represents a group of browser windows.""" def __init__(self, prefs=None, display=None): self.root = Tk(className='Grail', screenName=display) self.root.withdraw() resources = os.path.join(script_dir, "data", "Grail.ad") if os.path.isfile(resources): self.root.option_readfile(resources, "startupFile") BaseApplication.BaseApplication.__init__(self, prefs) # The stylesheet must be initted before any Viewers, so it # registers its' prefs callbacks first, hence reloads before the # viewers reconfigure w.r.t. the new styles. self.stylesheet = Stylesheet.Stylesheet(self.prefs) self.load_images = 1 # Overridden by cmd line or pref. # socket management sockets = self.prefs.GetInt('sockets', 'number') self.sq = SocketQueue(sockets) self.prefs.AddGroupCallback('sockets', lambda self=self: \ self.sq.change_max( self.prefs.GetInt('sockets', 'number'))) # initialize on_exit_methods before global_history self.on_exit_methods = [] self.global_history = GlobalHistory.GlobalHistory(self) self.login_cache = {} self.rexec_cache = {} self.url_cache = CacheManager(self) self.image_cache = ImageCache(self.url_cache) self.auth = AuthenticationManager(self) self.root.report_callback_exception = self.report_callback_exception if sys.stdin.isatty(): # only useful if stdin might generate KeyboardInterrupt self.keep_alive() self.browsers = [] self.iostatuspanel = None self.in_exception_dialog = None import Greek for k, v in Greek.entitydefs.items(): Application.dingbatimages[k] = (v, '_sym') self.root.bind_class("Text", "<Alt-Left>", self.dummy_event) self.root.bind_class("Text", "<Alt-Right>", self.dummy_event) def dummy_event(self, event): pass def register_on_exit(self, method): self.on_exit_methods.append(method) def unregister_on_exit(self, method): try: self.on_exit_methods.remove(method) except ValueError: pass def exit_notification(self): for m in self.on_exit_methods[:]: try: m() except: pass def add_browser(self, browser): self.browsers.append(browser) def del_browser(self, browser): try: self.browsers.remove(browser) except ValueError: pass def quit(self): self.root.quit() def open_io_status_panel(self): if not self.iostatuspanel: import IOStatusPanel self.iostatuspanel = IOStatusPanel.IOStatusPanel(self) else: self.iostatuspanel.reopen() def maybe_quit(self): if not (self.embedded or self.browsers): self.quit() def go(self): try: try: if ilu_tk: ilu_tk.RunMainLoop() else: self.root.mainloop() except KeyboardInterrupt: pass finally: self.exit_notification() def keep_alive(self): # Exercise the Python interpreter regularly so keyboard # interrupts get through self.root.tk.createtimerhandler(KEEPALIVE_TIMER, self.keep_alive) def get_cached_image(self, url): return self.image_cache.get_image(url) def set_cached_image(self, url, image, owner=None): self.image_cache.set_image(url, image, owner) def open_url(self, url, method, params, reload=0, data=None): api = self.url_cache.open(url, method, params, reload, data=data) api._url_ = url return api def open_url_simple(self, url): api = self.open_url(url, 'GET', {}) errcode, errmsg, meta = api.getmeta() if errcode != 200: raise IOError, ('url open error', errcode, errmsg, meta) return URLReadWrapper(api, meta) def get_cache_keys(self): """For applets.""" return self.url_cache.items.keys() def decode_pipeline(self, fp, content_encoding, error=1): if self.decode_prog.has_key(content_encoding): prog = self.decode_prog[content_encoding] if not prog: return fp tfn = tempfile.mktemp() ok = 0 try: temp = open(tfn, 'w') BUFSIZE = 8192 while 1: buf = fp.read(BUFSIZE) if not buf: break temp.write(buf) temp.close() ok = 1 finally: if not ok: try: os.unlink(tfn) except os.error: pass pipeline = '%s <%s; rm -f %s' % (prog, tfn, tfn) # XXX What if prog fails? return os.popen(pipeline, 'r') if error: self.error_dialog(IOError, "Can't decode content-encoding: %s" % content_encoding) return None decode_prog = { 'gzip': 'gzip -d', 'x-gzip': 'gzip -d', 'compress': 'compress -d', 'x-compress': 'compress -d', } def exception_dialog(self, message="", root=None): exc, val, tb = sys.exc_type, sys.exc_value, sys.exc_traceback self.exc_dialog(message, exc, val, tb, root) def report_callback_exception(self, exc, val, tb, root=None): self.exc_dialog("in a callback function", exc, val, tb, root) def exc_dialog(self, message, exc, val, tb, root=None): if self.in_exception_dialog: print print "*** Recursive exception", message import traceback traceback.print_exception(exc, val, tb) return self.in_exception_dialog = 1 def f(s=self, m=message, e=exc, v=val, t=tb, root=root): s._exc_dialog(m, e, v, t, root) if TkVersion >= 4.1: self.root.after_idle(f) else: self.root.after(0, f) def _exc_dialog(self, message, exc, val, tb, root=None): # XXX This needn't be a modal dialog -- # XXX should SafeDialog be changed to support callbacks? import SafeDialog msg = "An exception occurred " + str(message) + " :\n" msg = msg + str(exc) + " : " + str(val) dlg = SafeDialog.Dialog(root or self.root, text=msg, title="Python Exception: " + str(exc), bitmap='error', default=0, strings=("OK", "Show traceback"), ) self.in_exception_dialog = 0 if dlg.num == 1: self.traceback_dialog(exc, val, tb) def traceback_dialog(self, exc, val, tb): # XXX This could actually just create a new Browser window... import TbDialog TbDialog.TracebackDialog(self.root, exc, val, tb) def error_dialog(self, exc, msg, root=None): # Display an error dialog. # Return when the user clicks OK # XXX This needn't be a modal dialog import SafeDialog if type(msg) in (ListType, TupleType): s = '' for item in msg: s = s + ':\n' + str(item) msg = s[2:] else: msg = str(msg) SafeDialog.Dialog(root or self.root, text=msg, title="Error: " + str(exc), bitmap='error', default=0, strings=('OK',), ) dingbatimages = {'ldots': ('...', None), # math stuff 'sp': (' ', None), 'hairsp': ('\240', None), 'thinsp': ('\240', None), 'emdash': ('--', None), 'endash': ('-', None), 'mdash': ('--', None), 'ndash': ('-', None), 'ensp': (' ', None) } def clear_dingbat(self, entname): if self.dingbatimages.has_key(entname): del self.dingbatimages[entname] def set_dingbat(self, entname, entity): self.dingbatimages[entname] = entity def load_dingbat(self, entname): if self.dingbatimages.has_key(entname): return self.dingbatimages[entname] gifname = grailutil.which(entname + '.gif', self.iconpath) if gifname: img = PhotoImage(file=gifname, master=self.root) self.dingbatimages[entname] = img return img self.dingbatimages[entname] = None return None
class Character(Entity): def __init__(self, start_x, start_y, sprite_dir, width, height, frames, cache=None): Entity.__init__(self) # Directory where all the sprites will be held # This must include up, down, left, right in both idle and walking positions # TODO: Implement other movement types self.sprite_dir = sprite_dir # We can preload the cache in for duplicated characters or just create a new one if cache is None: self.cache = ImageCache(sprite_dir) else: self.cache = cache # Default the sprite to face down self.image = self.cache.get_image("down_1") # Where to spawn the character self.x_speed = 0 self.y_speed = 0 self.width = width self.height = height # Define movement properties self.speed = 8 self.sneak_mult = 0.4 self.sprint_mult = 1.5 # Animation properties self.anim_frames = frames # How many animation frames self.current_anim_frame = 0 self.frame_wait = 4 # How long to wait between frames self.current_frame_wait = 0 self.rect = Rect(start_x, start_y, self.width, self.height) def update(self, up, down, left, right, obstacles): x_dir = -left + right y_dir = -up + down self.x_speed = x_dir * self.speed self.y_speed = y_dir * self.speed # Update the characters rect to apply movement self.rect.left += self.x_speed self.rect.top += self.y_speed self.animate() def animate(self): # Just determine which frame of which direction to use as current image if self.x_speed < 0: self.image = self.cache.get_image("left_" + str(self.current_anim_frame)) elif self.x_speed > 0: self.image = self.cache.get_image("right_" + str(self.current_anim_frame)) elif self.y_speed < 0: self.image = self.cache.get_image("up_" + str(self.current_anim_frame)) elif self.y_speed > 0: self.image = self.cache.get_image("down_" + str(self.current_anim_frame)) # Because there is such a low frame rate in the animations, it is handy to have a system inplace # to create a pause between frames self.current_frame_wait += 1 if self.current_frame_wait >= self.frame_wait: # Frame switch self.current_anim_frame += 1 if self.current_anim_frame >= self.anim_frames: self.current_anim_frame = 0 self.current_frame_wait = 0 def get_pos(self): return self.rect.left, self.rect.top