class ParkPanel: def __init__(self,bgImage, pos): self.isDone = 0 self.boundRect = bgImage.get_rect() self.deviceGroup = RenderUpdates() self.rideGroup = RenderUpdates() self.carGroup = RenderUpdates() self.lineupGroup = RenderUpdates() self.highlightGroup = RenderUpdates() self.redVisitorGroup = RenderUpdates() self.greenVisitorGroup = RenderUpdates() self.parkGeography = parkGenerationFunction() allobjects.timeOfDay = simulation.startTime self.bgImage = bgImage for r in self.parkGeography.rides: self.rideGroup.add( r ) self.carGroup.add( r.idleCars ) for l in self.parkGeography.lineups: self.lineupGroup.add( l ) totalVisitors = simulation.InitialNumVisitors(0) numGreen = int(totalVisitors*simulation.deviceAcceptanceRate) numRed = totalVisitors - numGreen for i in xrange( numGreen ): device = allobjects.server.NewDevice() if device: self.deviceGroup.add( device ) newGuy = GuidedVisitor( device ) else: newGuy = UnGuidedVisitor() self.AddVisitor( newGuy ) for i in xrange( numRed ): newGuy = UnGuidedVisitor() self.AddVisitor( newGuy ) events.AddListener( self ) events.AddEvent( "SelectVisitor" ) events.AddEvent( "UnSelectVisitor" ) events.AddEvent( "HighlightRide" ) events.AddEvent( "UnHighlightRide" ) events.AddEvent( "HighlightLineup" ) events.AddEvent( "UnHighlightLineup" ) self.paused = False self.showRed = True self.justToggledShowRed = False self.showServer = True self.justToggledShowServer = False self.showGreen = True self.justToggledShowGreen = False self.redCircle = Surface( (17,17),SRCALPHA,32 ) pygame.draw.circle( self.redCircle, (255,0,0), (8,8), 8, 1 ) self.greenCircle = Surface( (17,17),SRCALPHA,32 ) pygame.draw.circle( self.greenCircle, (0,255,0), (8,8), 8, 1 ) self.highlight = Sprite() self.highlight.image = self.greenCircle self.highlight.rect = self.greenCircle.get_rect() self.selection = Sprite() self.selection.image = self.greenCircle self.selection.rect = self.greenCircle.get_rect() self.highlightVisitor = None self.selectedVisitor = None #--------------------------------------------------------------------- def On_Pause(self): log.info( 'parkpanel pause' ) self.paused = not self.paused #--------------------------------------------------------------------- def On_ShowRed(self): self.showRed = not self.showRed self.justToggledShowRed = True #--------------------------------------------------------------------- def On_ShowGreen(self): self.showGreen = not self.showGreen self.justToggledShowGreen = True #--------------------------------------------------------------------- def On_ShowServer(self): self.showServer = not self.showServer self.justToggledShowServer = True #--------------------------------------------------------------------- def On_MouseClick(self,pos): if not self.boundRect.collidepoint( pos ): return if self.selectedVisitor: events.Fire( "UnSelectVisitor", self.selectedVisitor ) self.selectedVisitor = None self.highlightGroup.remove( self.selection ) self.selectedVisitor = self.FindVisitorNear(pos) if not self.selectedVisitor: return self.highlightGroup.add( self.selection ) if hasattr( self.selectedVisitor, 'device' ): self.selection.image = self.greenCircle else: self.selection.image = self.redCircle events.Fire( "SelectVisitor", self.selectedVisitor ) #--------------------------------------------------------------------- def UpdateHighlightGroup(self): if self.selectedVisitor: self.selection.rect.center = self.selectedVisitor.rect.center self.highlightGroup.update() #--------------------------------------------------------------------- def On_MouseMove(self,event): pos = event.pos if not self.boundRect.collidepoint( pos ): return self.highlightVisitor = self.FindVisitorNear(pos) self.HighlightRideNear(pos) self.HighlightLineupNear(pos) if not self.highlightVisitor: self.highlightGroup.remove( self.highlight ) return if hasattr( self.highlightVisitor, 'device' ): self.highlight.image = self.greenCircle else: self.highlight.image = self.redCircle self.highlight.rect.center = self.highlightVisitor.rect.center self.highlightGroup.add( self.highlight ) #--------------------------------------------------------------------- def AddVisitor(self, visitor, pos=None): if hasattr( visitor, "device" ): self.greenVisitorGroup.add( visitor ) else: self.redVisitorGroup.add( visitor ) self.parkGeography.PlaceVisitor( visitor, pos ) allobjects.allVisitors.add( visitor ) #--------------------------------------------------------------------- def DoVisitorEntries( self ): if len( allobjects.allVisitors ) >= simulation.capacity: return totalVisitors = simulation.getEntryRate( allobjects.timeOfDay ) #print "entering ", totalVisitors numGreen = int(totalVisitors*simulation.deviceAcceptanceRate) if not allobjects.thousandCounter%2: numGreen += 1 numRed = totalVisitors - numGreen pos = self.parkGeography.GetEntryPoint() for i in xrange( numGreen ): device = allobjects.server.NewDevice() if device: self.deviceGroup.add( device ) newGuy = GuidedVisitor( device ) else: newGuy = UnGuidedVisitor() self.AddVisitor( newGuy, pos ) for i in xrange( numRed ): newGuy = UnGuidedVisitor() self.AddVisitor( newGuy, pos ) #--------------------------------------------------------------------- def DoVisitorExits( self ): if not allobjects.allVisitors: return totalVisitors = simulation.getExitRate( allobjects.timeOfDay ) for i in xrange( totalVisitors ): allobjects.allVisitors.sprites()[i].LeaveThePark() #--------------------------------------------------------------------- def RemoveVisitor(self, visitor): visitor.kill() #--------------------------------------------------------------------- def FindVisitorNear(self,pos,radius=4): for v in self.greenVisitorGroup.sprites(): if abs( v.rect.centerx - pos[0] ) < radius \ and abs( v.rect.centery - pos[1] ) < radius: return v for v in self.redVisitorGroup.sprites(): if abs( v.rect.centerx - pos[0] ) < radius \ and abs( v.rect.centery - pos[1] ) < radius: return v return None #--------------------------------------------------------------------- def HighlightRideNear(self,pos): events.Fire( "UnHighlightRide" ) for r in self.rideGroup.sprites(): if r.rect.collidepoint( pos ): events.Fire( "HighlightRide", r ) return #--------------------------------------------------------------------- def HighlightLineupNear(self,pos): events.Fire( "UnHighlightLineup" ) for l in self.lineupGroup.sprites(): if l.rect.collidepoint( pos ): events.Fire( "HighlightLineup", l ) return #--------------------------------------------------------------------- def SignalKey( self, event, remainingEvents ): pass #--------------------------------------------------------------------- def Click( self, pos ): pass #--------------------------------------------------------------------- def MouseOver( self, event ): pass #--------------------------------------------------------------------- def DoGraphics( self, screen, display, timeChange ): if self.justToggledShowRed \ or self.justToggledShowGreen \ or self.justToggledShowServer: screen.blit( self.bgImage, self.boundRect ) display.flip() self.justToggledShowRed = False self.justToggledShowGreen = False self.justToggledShowServer = False else: bg = self.bgImage self.rideGroup.clear(screen, self.bgImage) self.lineupGroup.clear(screen, self.bgImage) self.highlightGroup.clear( screen, self.bgImage ) if self.showRed: self.redVisitorGroup.clear( screen, bg ) if self.showGreen: self.greenVisitorGroup.clear( screen, bg ) if self.showServer: self.deviceGroup.clear(screen, self.bgImage ) self.UpdateHighlightGroup() if not allobjects.thousandCounter % 30: self.DoVisitorEntries() self.DoVisitorExits() if not self.paused: allobjects.timeOfDay += simulation.speed self.carGroup.update() self.rideGroup.update() self.lineupGroup.update() self.redVisitorGroup.update() self.greenVisitorGroup.update() if self.showServer: self.deviceGroup.update() changedRects = self.highlightGroup.draw(screen) changedRects += self.rideGroup.draw(screen) changedRects += self.carGroup.draw(screen) changedRects += self.lineupGroup.draw(screen) if self.showRed: changedRects += self.redVisitorGroup.draw(screen) if self.showGreen: changedRects += self.greenVisitorGroup.draw(screen) if self.showServer: changedRects += self.deviceGroup.draw(screen) display.update( changedRects )
class BaseEntity(Sprite): """Base Class Entity to use for games entity. Extends pygame.Sprite class Use to manage entities, for space collapsing and draw animation space. Instance variable: - rect : pygame.Rect class Use for collapse beetween entities - direction : direction state UP = 273 DOWN = 274 RIGHT = 275 LEFT = 276 - speed : list of speed mvt : [abs, ord] """ def __init__(self, name, rect_data, speed, max_frame, max_frame_delay, img): """ Init. - rect_data : list contains => - x : position x - y : position y - w : width of rect_collapse - h : height of collase - direction """ super(BaseEntity, self).__init__() self.name = name self.rect = None self.image = None self.childs = Group() self.rect_collapse = Rect(rect_data) self.speed = speed self.direction = UP # Create animation for the entity self.animation = self.init_animation(max_frame, max_frame_delay, img) def add_child(self, child): """Add a child entity.""" self.childs.add(child) def remove_child(self, child): """Remove a child entity.""" self.childs.remove(child) def direction_get(self): return self.direction def direction_set(self, direction): self.direction = direction def get_rect(self, value=0): """Return rect 0 = rect(actual rect to use) 1 = rect_animation 2 = rect_collapse """ if value == 1: return self.image.get_rect() elif value == 2: return self.rect_collapse else: return self.rect def init_animation(self, max_frame, max_frame_delay, img): """Function for animation initialisation. Need to be defined. """ pass def __str__(self): """Custom __str__.""" string = u"<Entity : %s -- Pos (%s,%s)>\n" % ( str(self.name), str(self.rect_collapse[0]), str(self.rect_collapse[1]), ) return string def move(self, move_direction): """Basic mouvement. Basic calcul tomove the entity, defined by direction parameter Reimplements if you need to change move's pattern """ x, y = self.rect_collapse.topleft direction_num = move_direction - UP if direction_num == 0: move = (0, -1) elif direction_num == 1: move = (0, 1) elif direction_num == 2: move = (1, 0) elif direction_num == 3: move = (-1, 0) x = x + (self.speed[0] * move[0]) y = y + (self.speed[1] * move[1]) self.rect_collapse.left = x self.rect_collapse.top = y def stop(self): """Basic stop. Stop the mouvement of the entity Reimplements if you need to change move's pattern """ pass def update(self, movement=None): """Update function. Basic update position of the entity (move or stop) Redefine it for your own purpose Action use by pygame.sprite.Group.update() function. """ if movement is None: self.stop() self.animation.stop() else: self.direction = movement self.move(movement) self.animation.update() self.setup_animation(self.direction) self.childs.update() def setup_collapse(self): """Setup variable. Set up rect attribute for collapse eval""" self.rect = self.rect_collapse def setup_animation(self, direction): """Setup variable. Set up rect attribute for animation draw Be careful :: is function move anim_sprite to center with rect_collapse Catch image.get_rect directly will give you the wrong coordinate """ self.image = self.animation.get_sprite(direction).convert_alpha() rect_anim_position = self.image.get_rect() rect_anim_position.center = self.rect_collapse.center self.rect = rect_anim_position
class Game: def __init__(self, title='Checkers', log_level=log.INFO, show_fps=False): log.basicConfig(level=log_level) self.show_fps = show_fps self.window_title = title self.game = Board(BOARD_DIM) # Initialize Game Groups self.brown_spaces = RenderUpdates() self.pieces = RenderUpdates() self.piece_selected = GroupSingle() self.space_selected = GroupSingle() self.current_piece_position = ORIGIN self.screen = None self.fps_clock = None self.font = None self.font_rect = None self.background = None self.background_rect = None self.fps_text = None self.fps_rect = None self.winner_text = None self.winner_rect = None def _board_setup(self, **kwargs): """ initialize board state """ brown_spaces = kwargs.get('brown_spaces') for col, row in self.game.usable_positions(): loc = TILE_WIDTH * col + (BORDER_WIDTH / 2), TILE_WIDTH * row + (BORDER_WIDTH / 2) brown_spaces.add(SquareSprite(loc, "brown", row, col)) def _screen_init(self): """ Initialise screen """ pygame.init() self.screen = pygame.display.set_mode(SCREEN_RES) pygame.display.set_caption(self.window_title) return self.screen def _get_background(self): result = pygame.Surface(self.screen.get_size()) (bg_img, bg_rect) = ImageLoader.load_img('marble-board.jpg') result.blit(bg_img, bg_rect) return result.convert(), bg_rect def _get_fps_text(self): fps_text = self.font.render("%4.1f fps" % self.fps_clock.get_fps(), True, WHITE) rect = fps_text.get_rect() rect.right, rect.bottom = self.background_rect.right, self.background_rect.bottom return fps_text, rect def _draw_fps(self): if self.show_fps: self.fps_text, self.fps_rect = self._get_fps_text() self.screen.blit(self.fps_text, self.fps_rect) def _clear_fps(self): if self.show_fps: self.screen.blit(self.background, self.fps_rect, area=self.fps_rect) def _clear_items(self): self._clear_winner() self._clear_fps() self.piece_selected.clear(self.screen, self.background) self.pieces.clear(self.screen, self.background) def _draw_winner(self): winner = self.game.winner() if winner: self.winner_text = self.font.render("%s wins!" % winner.title(), True, WHITE) winner_rect = self.winner_text.get_rect() winner_rect.centerx = self.background.get_rect().centerx winner_rect.top = 100 self.winner_rect = winner_rect self.screen.blit(self.winner_text, winner_rect) def _clear_winner(self): winner = self.game.winner() if winner: self.screen.blit(self.background, self.winner_rect, area=self.winner_rect) def _quit(self): log.debug('quitting') sys.exit() def _select_piece(self, event): # select the piece by seeing if the piece collides with cursor self.piece_selected.add(piece for piece in self.pieces if piece.rect.collidepoint(event.pos)) # Capture piece's original position (at center) to determine move on drop if len(self.piece_selected) > 0: # Assumed: starting a move pygame.event.set_grab(True) self.pieces.remove(self.piece_selected) self.current_piece_position = (self.piece_selected.sprite.rect.centerx, self.piece_selected.sprite.rect.centery) log.debug('grabbing input, picked up piece at %s', self.current_piece_position) def _drag_piece(self): # Until button is let go, move the piece with the mouse position self.piece_selected.update(pygame.mouse.get_pos()) log.debug('updated piece to %s', pygame.mouse.get_pos()) def _drop_piece(self, event): if pygame.event.get_grab(): pygame.event.set_grab(False) log.debug('releasing input') # center the piece on the valid space; if it is not touching a space, return it to its original position self.space_selected.add(space for space in self.brown_spaces if space.rect.collidepoint(event.pos)) if self.piece_selected and self.space_selected: log.debug('dropped a piece') piece, space = self.piece_selected.sprite, self.space_selected.sprite try: captured = self.game.move(piece.location, (space.col, space.row)) if captured: self.pieces.remove(captured) except InvalidMoveException as ce: log.debug(ce) log.debug("%s", str(self.game)) self.piece_selected.sprite.update_from_board() # Add piece back to stationary set self.pieces.add(self.piece_selected) # clean up for the next selected piece self.piece_selected.empty() self.space_selected.empty() def _draw_items(self): self.pieces.draw(self.screen) self.piece_selected.draw(self.screen) self._draw_winner() self._draw_fps() def run(self): log.debug('starting game') log.debug('initializing screen') self.screen = self._screen_init() log.debug('getting font') self.font = pygame.font.Font(None, 36) log.debug('loading background') self.background, self.background_rect= self._get_background() log.debug('building initial game board') self._board_setup(brown_spaces=self.brown_spaces) log.debug('initializing game pieces') for player, x, y in self.game.start_positions(): new_piece = PieceSprite(player) self.game.add_piece(new_piece, (x, y)) new_piece.update_from_board() self.pieces.add(new_piece) log.debug('drawing initial content to screen') self.screen.blit(self.background, ORIGIN) pygame.display.flip() self.piece_selected = GroupSingle() self.space_selected = GroupSingle() self.current_piece_position = ORIGIN self.fps_clock = Clock() self._draw_fps() # Event loop while True: self._clear_items() for event in pygame.event.get(): if event.type == QUIT: self._quit() if event.type == MOUSEBUTTONDOWN: # select a piece log.debug('mouse pressed') self._select_piece(event) if event.type == MOUSEBUTTONUP: # let go of a piece log.debug('mouse released') self._drop_piece(event) if pygame.event.get_grab(): # drag selected piece around log.debug('dragging') self._drag_piece() self._draw_items() self.fps_clock.tick(60) # Waits to maintain 60 fps # TODO: Use display.update instead pygame.display.flip()
class Game: def __init__(self, title='Checkers', log_level=log.INFO, show_fps=False): log.basicConfig(level=log_level) self.show_fps = show_fps self.window_title = title self.game = Board(BOARD_DIM) # Initialize Game Groups self.brown_spaces = RenderUpdates() self.pieces = RenderUpdates() self.piece_selected = GroupSingle() self.space_selected = GroupSingle() self.current_piece_position = ORIGIN self.screen = None self.fps_clock = None self.font = None self.font_rect = None self.background = None self.background_rect = None self.fps_text = None self.fps_rect = None self.winner_text = None self.winner_rect = None def _board_setup(self, **kwargs): """ initialize board state """ brown_spaces = kwargs.get('brown_spaces') for col, row in self.game.usable_positions(): loc = TILE_WIDTH * col + (BORDER_WIDTH / 2), TILE_WIDTH * row + (BORDER_WIDTH / 2) brown_spaces.add(SquareSprite(loc, "brown", row, col)) def _screen_init(self): """ Initialise screen """ pygame.init() self.screen = pygame.display.set_mode(SCREEN_RES) pygame.display.set_caption(self.window_title) return self.screen def _get_background(self): result = pygame.Surface(self.screen.get_size()) (bg_img, bg_rect) = ImageLoader.load_img('marble-board.jpg') result.blit(bg_img, bg_rect) return result.convert(), bg_rect def _get_fps_text(self): fps_text = self.font.render("%4.1f fps" % self.fps_clock.get_fps(), True, WHITE) rect = fps_text.get_rect() rect.right, rect.bottom = self.background_rect.right, self.background_rect.bottom return fps_text, rect def _draw_fps(self): if self.show_fps: self.fps_text, self.fps_rect = self._get_fps_text() self.screen.blit(self.fps_text, self.fps_rect) def _clear_fps(self): if self.show_fps: self.screen.blit(self.background, self.fps_rect, area=self.fps_rect) def _clear_items(self): self._clear_winner() self._clear_fps() self.piece_selected.clear(self.screen, self.background) self.pieces.clear(self.screen, self.background) def _draw_winner(self): winner = self.game.winner() if winner: self.winner_text = self.font.render("%s wins!" % winner.title(), True, WHITE) winner_rect = self.winner_text.get_rect() winner_rect.centerx = self.background.get_rect().centerx winner_rect.top = 100 self.winner_rect = winner_rect self.screen.blit(self.winner_text, winner_rect) def _clear_winner(self): winner = self.game.winner() if winner: self.screen.blit(self.background, self.winner_rect, area=self.winner_rect) def _quit(self): log.debug('quitting') sys.exit() def _select_piece(self, event): # select the piece by seeing if the piece collides with cursor self.piece_selected.add(piece for piece in self.pieces if piece.rect.collidepoint(event.pos)) # Capture piece's original position (at center) to determine move on drop if len(self.piece_selected) > 0: # Assumed: starting a move pygame.event.set_grab(True) self.pieces.remove(self.piece_selected) self.current_piece_position = ( self.piece_selected.sprite.rect.centerx, self.piece_selected.sprite.rect.centery) log.debug('grabbing input, picked up piece at %s', self.current_piece_position) def _drag_piece(self): # Until button is let go, move the piece with the mouse position self.piece_selected.update(pygame.mouse.get_pos()) log.debug('updated piece to %s', pygame.mouse.get_pos()) def _drop_piece(self, event): if pygame.event.get_grab(): pygame.event.set_grab(False) log.debug('releasing input') # center the piece on the valid space; if it is not touching a space, return it to its original position self.space_selected.add(space for space in self.brown_spaces if space.rect.collidepoint(event.pos)) if self.piece_selected and self.space_selected: log.debug('dropped a piece') piece, space = self.piece_selected.sprite, self.space_selected.sprite try: captured = self.game.move(piece.location, (space.col, space.row)) if captured: self.pieces.remove(captured) except InvalidMoveException as ce: log.debug(ce) log.debug("%s", str(self.game)) self.piece_selected.sprite.update_from_board() # Add piece back to stationary set self.pieces.add(self.piece_selected) # clean up for the next selected piece self.piece_selected.empty() self.space_selected.empty() def _draw_items(self): self.pieces.draw(self.screen) self.piece_selected.draw(self.screen) self._draw_winner() self._draw_fps() def run(self): log.debug('starting game') log.debug('initializing screen') self.screen = self._screen_init() log.debug('getting font') self.font = pygame.font.Font(None, 36) log.debug('loading background') self.background, self.background_rect = self._get_background() log.debug('building initial game board') self._board_setup(brown_spaces=self.brown_spaces) log.debug('initializing game pieces') for player, x, y in self.game.start_positions(): new_piece = PieceSprite(player) self.game.add_piece(new_piece, (x, y)) new_piece.update_from_board() self.pieces.add(new_piece) log.debug('drawing initial content to screen') self.screen.blit(self.background, ORIGIN) pygame.display.flip() self.piece_selected = GroupSingle() self.space_selected = GroupSingle() self.current_piece_position = ORIGIN self.fps_clock = Clock() self._draw_fps() # Event loop while True: self._clear_items() for event in pygame.event.get(): if event.type == QUIT: self._quit() if event.type == MOUSEBUTTONDOWN: # select a piece log.debug('mouse pressed') self._select_piece(event) if event.type == MOUSEBUTTONUP: # let go of a piece log.debug('mouse released') self._drop_piece(event) if pygame.event.get_grab(): # drag selected piece around log.debug('dragging') self._drag_piece() self._draw_items() self.fps_clock.tick(60) # Waits to maintain 60 fps # TODO: Use display.update instead pygame.display.flip()
class EventLoop(object): def __init__(self): self.objs = {} self.events = [] self.render = RenderUpdates() self.projectiles = Group() self.enemies = Group() self.you = None self.bg_sprite = BGSprite(pygame.display.get_surface()) # Since we don't care about MOST EVENTS pygame.event.set_allowed(None) pygame.event.set_allowed([ MOUSEBUTTONDOWN, KEYDOWN, QUIT, UPDATEEVENT, SPAWNEVENT ]) def add_object(self, obj, type=""): if isinstance(obj, HandlesEvents): if obj.events == ALL: try: self.objs[ALL].append(obj) except KeyError: self.objs[ALL] = [ obj ] else: for event in obj.events: try: self.objs[event].append(obj) except KeyError: self.objs[event] = [ obj ] if isinstance(obj, Sprite): self.render.add(obj) if type == "enemy": self.enemies.add(obj) if type == "projectile": self.projectiles.add(obj) if type == "you": self.you = obj def rm_object(self, obj): for key in self.objs.keys(): self.render.remove(obj) if obj in self.objs[key]: self.objs[key].remove(obj) if self.projectiles.has(obj): self.projectiles.remove(obj) if self.enemies.has(obj): self.enemies.remove(obj) print "Removed {0}".format(obj) def enqueue(self, event): if isinstance(event, Event): return self.events.append(event) elif isinstance(event, list): return [ self.enqueue(ev) for ev in event ] def tick(self): try: event = self.events.pop() except IndexError: event = None if event is not None: if isinstance(event, TargettedEvent): event.get_target().handle_event(event) for obj in self.objs[ALL]: obj.handle_event(event) else: for obj in self.objs[type(event)] + self.objs[ALL]: if obj.handles_event(event): obj.handle_event(event) if random.randint(0, 10000000) == 26: return win() if random.randint(0, 10000000) == 26: return lose() py_events = map(lambda event: PygameEvent(event), pygame.event.get()) for py_event in py_events: for obj in reduce(lambda obj_list, obj: obj_list + obj, map(lambda key: self.objs[key], filter(lambda handler_type: issubclass(handler_type, PygameEvent) if handler_type != ALL else False, self.objs.keys())), []): if obj.handles_event(py_event): obj.handle_event(py_event)
class Game(StatusHandler): def __init__(self, title='Checkers', log_drag=False, show_fps=False, ip='127.0.0.1', port=5000, spectate=False): self.game_running = True self.player = None self.log_drag = log_drag self.show_fps = show_fps self.window_title = title self.game = NetBoard(handler=self, ip=ip, port=port, spectate=spectate) # Initialize Game Groups self.board_spaces = set() self.pieces = RenderUpdates() self.piece_selected = GroupSingle() self.bg_text = RenderUpdates() self.fg_text = RenderUpdates() self.current_piece_position = ORIGIN self.screen = None self.fps_clock = None self.font = None self.background = None self.background_rect = None self.fps_text = None self.winner_text = None self.turn_text = None self.player_text = None self.game_id_text = None def handle_game_id(self, game_id): self.game_id_text.text = "Game: %s" % game_id def handle_list(self, game_list, list_type): if list_type == SPECTATE and game_list: game_id = game_list[0] self.game.client.spectate(game_id) self.player_text.text = 'You are a spectator' elif not list_type and game_list: game_id = game_list[0] self.game.client.join(game_id) elif not list_type and not game_list: self.game.client.new_game() def handle_board(self, board): for piece in board: new_piece = PieceSprite(piece.player) new_piece.king = piece.king self.game.add_piece(new_piece, piece.location) new_piece.update_from_board() self.pieces.add(new_piece) def handle_turn(self, player): self.game.turn = player def handle_you_are(self, player): self.player = player def handle_moved(self, src, dst): moved_pieces = [p for p in self.pieces if p.location == src] Board.move(self.game, src, dst) if moved_pieces: moved_pieces[0].update_from_board() Sounds.play('slap.ogg') log.debug("board after drop:\n%s", str(self.game)) def handle_captured(self, loc): captured_pieces = [p for p in self.pieces if p.location == loc] if captured_pieces: self.pieces.remove(captured_pieces[0]) def _board_space_setup(self): """ initialize board state """ for col, row in self.game.usable_positions(): self.board_spaces.add(Square(row, col)) def _screen_init(self): """ Initialise screen """ self.screen = pygame.display.set_mode(SCREEN_RES) pygame.display.set_caption(self.window_title) return self.screen def _get_background(self): result = pygame.Surface(self.screen.get_size()) (bg_img, bg_rect) = Images.load('marble-board.jpg') result.blit(bg_img, bg_rect) return result.convert(), bg_rect def _clear_items(self): self.fg_text.clear(self.screen, self.background) self.piece_selected.clear(self.screen, self.background) self.pieces.clear(self.screen, self.background) self.bg_text.clear(self.screen, self.background) def _quit(self): log.debug('quitting') self.game.client.quit() self.game_running = False def _select_piece(self, event): # select the piece by seeing if the piece collides with cursor self.piece_selected.add(piece for piece in self.pieces if piece.rect.collidepoint(event.pos) and piece.player == self.player and piece.player == self.game.turn) # Capture piece's original position (at center) to determine move on drop if len(self.piece_selected) > 0: # Assumed: starting a move pygame.event.set_grab(True) self.pieces.remove(self.piece_selected) self.current_piece_position = (self.piece_selected.sprite.rect.centerx, self.piece_selected.sprite.rect.centery) log.debug('grabbing input, picked up piece at %s', self.current_piece_position) Sounds.play('slide.ogg') def _drag_piece(self): # Until button is let go, move the piece with the mouse position if self.log_drag: log.debug('dragging') rect = self.piece_selected.sprite.rect rect.centerx, rect.centery = pygame.mouse.get_pos() if self.log_drag: log.debug('updated piece to %s', pygame.mouse.get_pos()) def _reset_selected_piece(self): self.piece_selected.sprite.update_from_board() Sounds.play('slap.ogg') log.debug("board after drop:\n%s", str(self.game)) def _drop_piece(self, event): if pygame.event.get_grab(): pygame.event.set_grab(False) log.debug('releasing input') # center the piece on the valid space; if it is not touching a space, return it to its original position space_selected = [space for space in self.board_spaces if space.collidepoint(event.pos)] if self.piece_selected and space_selected: log.debug('dropped a piece') piece, space = self.piece_selected.sprite, space_selected[0] try: self.game.move(piece.location, (space.col, space.row)) except InvalidMoveException as ce: log.debug(ce) self._reset_selected_piece() else: log.debug('dropped on unplayable game space') self._reset_selected_piece() # Add piece back to stationary set self.pieces.add(self.piece_selected) # clean up for the next selected piece self.piece_selected.empty() def _draw_items(self): self.bg_text.draw(self.screen) self.pieces.draw(self.screen) self.piece_selected.draw(self.screen) self.fg_text.draw(self.screen) def _update(self): self.game.update() self.fps_text.text = "%4.1f fps" % self.fps_clock.get_fps() if self.player: self.player_text.text = "Your pieces are %s" % self.player if self.game.turn not in players: self.turn_text.text = "Waiting for player" else: if self.player == self.game.turn: self.turn_text.text = "Your turn" else: self.turn_text.text = "%s's turn" % self.game.turn.title() if self.game.winner(): self.turn_text.text = '' self.winner_text.text = "%s wins!" % self.game.winner().title() else: self.winner_text.text = '' if not self.piece_selected and self.player == self.game.turn: highlight_player = self.game.turn else: highlight_player = None self.pieces.update(highlight_player) self.piece_selected.update(self.game.turn) self.bg_text.update() self.fg_text.update() def run(self): log.debug('pre-initializing sound') mixer.pre_init(buffer=32) log.debug('starting game') pygame.init() log.debug('initializing screen') self.screen = self._screen_init() log.debug('getting font') self.font = pygame.font.Font(None, 36) log.debug('loading background') self.background, self.background_rect = self._get_background() log.debug('setting up drop locations') self._board_space_setup() log.debug('building text') bg_rect = self.background_rect class FPSText(Text): def update(self, *args): Text.update(self, *args) self.rect.right, self.rect.bottom = bg_rect.right, bg_rect.bottom self.fps_text = FPSText('', self.font, WHITE) if self.show_fps: self.fg_text.add(self.fps_text) class TurnText(Text): def update(self, *args): Text.update(self, *args) self.rect.centerx, self.rect.centery = bg_rect.centerx, bg_rect.centery self.turn_text = TurnText('', self.font, WHITE) self.bg_text.add(self.turn_text) class WinnerText(Text): def update(self, *args): Text.update(self, *args) self.rect.centerx, self.rect.centery = bg_rect.centerx, bg_rect.centery self.winner_text = WinnerText('', self.font, WHITE) self.fg_text.add(self.winner_text) class PlayerText(Text): def update(self, *args): Text.update(self, *args) self.rect.centerx, self.rect.bottom = bg_rect.centerx, bg_rect.bottom self.player_text = PlayerText('', pygame.font.Font(None, 24), WHITE) self.bg_text.add(self.player_text) class GameIdText(Text): def update(self, *args): Text.update(self, *args) self.rect.centerx, self.rect.top = bg_rect.centerx, bg_rect.top + (0.25 * self.font.get_height()) self.game_id_text = GameIdText('', pygame.font.Font(None, 20), WHITE) self.bg_text.add(self.game_id_text) log.debug('drawing initial content to screen') self.screen.blit(self.background, ORIGIN) pygame.display.flip() self.piece_selected = GroupSingle() self.current_piece_position = ORIGIN self.fps_clock = Clock() # Event loop while self.game_running: self._clear_items() for event in pygame.event.get(): if event.type == QUIT: self._quit() if event.type == MOUSEBUTTONDOWN: # select a piece log.debug('mouse pressed') self._select_piece(event) if event.type == MOUSEBUTTONUP: # let go of a piece log.debug('mouse released') self._drop_piece(event) if pygame.event.get_grab(): # drag selected piece around self._drag_piece() self._update() self._draw_items() self.fps_clock.tick(60) # Waits to maintain 60 fps # TODO: Use display.update instead pygame.display.flip() log.debug('finishing game loop')
class GameWorld: """Hold Game World objects Initialize display >>> import pygame as pygame >>> import random >>> from widget import * >>> randomizer = Random() Create a gameworld >>> g = GameWorld(randomizer) >>> s = pygame.surface.Surface((30,30)) >>> w = Widget(s, (0,0,30,30), (0,0)) Add and test bomberman to world >>> g.appendBomber(w) >>> g.bomberGroup <RenderUpdates(1 sprites)> Add and test an immutable to world >>> w1 = Widget(s, (0,0,30,30), (0,0)) >>> g.appendImmutable(w1) >>> g.immutableGroup <RenderUpdates(1 sprites)> Add another bomberman to world >>> p2 = Widget(s, (100,100, 30,30), (0,0)) >>> g.appendBomber(p2) Add a bomb to the game world >>> bomb = Widget(s, (100,100,30,30), (0,0)) >>> g.appendBomb(bomb) Check the number of objects in game world >>> g.universalGroup <RenderUpdates(4 sprites)> Detonate bomb in game world >>> g.detonateBomb(bomb) >>> g.bombGroup <RenderUpdates(0 sprites)> >>> g.universalGroup <RenderUpdates(3 sprites)> Add bomb to populatedBombGroup >>> g.appendPopulatedBomb(bomb) >>> g.populatedBombGroup <RenderUpdates(1 sprites)> Add an explosion to game world >>> explosion = Widget(s, (0,0,30,30),(0,0)) >>> g.appendExplosion(explosion) >>> g.explosionGroup <RenderUpdates(1 sprites)> Add a power up to game world >>> powerup = Widget(s,(0,0,30,30),(0,0)) >>> g.appendPowerUp(powerup) >>> g.powerUpGroup <RenderUpdates(1 sprites)> Remove power up from game world >>> g.removePowerUp(powerup) >>> g.powerUpGroup <RenderUpdates(0 sprites)> Test bomberstrafe algorithm >>> g.bomberStrafe([98, 100]) (98, 100) >>> g.bomberStrafe([90, 100]) (80, 100) >>> g.bomberStrafe([98, 90]) (98, 80) >>> g.bomberStrafe([139, 140]) (144, 144) Test worldWrap >>> g.mapColumns = 19 >>> bomb = Bomb(s, (0,0,30,30),3,3,3,(0,0)) >>> bomb.beingPunched = 1 >>> g.appendBomb(bomb) >>> bomb.setPosition((10,100)) >>> g.worldWrap(bomb) >>> bomb.getPosition() (624, 100) >>> bomb.setPosition((630,100)) >>> g.worldWrap(bomb) >>> bomb.getPosition() (16, 100) >>> g.mapRows = 19 >>> bomb.setPosition((100, 10)) >>> g.worldWrap(bomb) >>> bomb.getPosition() (100, 656) >>> bomb.setPosition((100, 660)) >>> g.worldWrap(bomb) >>> bomb.getPosition() (100, 48) Test for new restart game state >>> g.cleanState() >>> g.populatedBombGroup <RenderUpdates(0 sprites)> >>> g.bombGroup <RenderUpdates(0 sprites)> >>> g.bomberGroup <RenderUpdates(0 sprites)> >>> g.explosionGroup <RenderUpdates(0 sprites)> >>> g.immutableGroup <RenderUpdates(0 sprites)> >>> g.powerUpGroup <RenderUpdates(0 sprites)> >>> g.mutableGroup <RenderUpdates(0 sprites)> >>> g.universalGroup <RenderUpdates(0 sprites)> >>> g.dirtyGroup <RenderUpdates(0 sprites)> >>> g.flyOverGroup <RenderUpdates(0 sprites)> >>> g.update() """ def __init__(self, randomizer): self.randomizer = randomizer # Set title pygame.display.set_caption("Pybomber") # Setup screen self.screen = pygame.display.get_surface() # Create sprite groups self.populatedBombGroup = SpriteGroup() self.bombGroup = SpriteGroup() self.bomberGroup = SpriteGroup() self.explosionGroup = SpriteGroup() self.immutableGroup = SpriteGroup() self.powerUpGroup = SpriteGroup() self.mutableGroup = SpriteGroup() self.universalGroup = SpriteGroup() # For drawing everything. self.dirtyGroup = SpriteGroup() self.flyOverGroup = SpriteGroup() # Load a background self.background = pygame.image.load(BACKGROUND).convert() self.map = None # Draw background on screen self.screen.blit(self.background, ((0, 0), RESOLUTION)) # Number of rows and colums in the current map. self.mapRows = 0 self.mapColumns = 0 def cleanState(self): pygame.display.set_caption("Pybomber") self.screen.blit(self.background, ((0, 0), RESOLUTION)) self.populatedBombGroup = SpriteGroup() self.bombGroup = SpriteGroup() self.bomberGroup = SpriteGroup() self.explosionGroup = SpriteGroup() self.immutableGroup = SpriteGroup() self.powerUpGroup = SpriteGroup() self.mutableGroup = SpriteGroup() self.universalGroup = SpriteGroup() # For drawing everything. self.dirtyGroup = SpriteGroup() self.groundGroup = SpriteGroup() self.flyOverGroup = SpriteGroup() self.mapRows = 0 self.mapColumns = 0 self.curWidgetID = 0 def appendBomb(self, sprite): self.bombGroup.add(sprite) self.universalGroup.add(sprite) self.groundGroup.add(sprite) def flyBomb(self, bombSprite): self.groundGroup.remove(bombSprite) def groundBomb(self, bomb): self.groundGroup.add(bomb) def appendPopulatedBomb(self, sprite): self.populatedBombGroup.add(sprite) def appendExplosion(self, sprite): self.explosionGroup.add(sprite) self.universalGroup.add(sprite) def appendImmutable(self, sprite): self.flyOverGroup.add(sprite) self.immutableGroup.add(sprite) self.universalGroup.add(sprite) self.groundGroup.add(sprite) def appendBomber(self, sprite): self.bomberGroup.add(sprite) self.universalGroup.add(sprite) self.groundGroup.add(sprite) def appendPowerUp(self, sprite): self.powerUpGroup.add(sprite) self.universalGroup.add(sprite) def appendMutable(self, sprite): self.flyOverGroup.add(sprite) self.mutableGroup.add(sprite) self.universalGroup.add(sprite) self.groundGroup.add(sprite) def detonateBomb(self, bomb): """Detonate bomb""" bomb.kill() def removeExplosion(self, explosion): """Remove explosion from all groups.""" self.explosionGroup.remove(explosion) self.universalGroup.remove(explosion) def removeBomber(self, bomber): """Remove explosion from all groups.""" self.bomberGroup.remove(bomber) self.groundGroup.remove(bomber) self.universalGroup.remove(bomber) def removePopulatedBomb(self, sprite): self.populatedBombGroup.remove(sprite) def removePowerUp(self, powerUp): """Remove powerUp from all groups.""" self.powerUpGroup.remove(powerUp) self.universalGroup.remove(powerUp) def bomberStrafe(self, bomberPos): """Take bomberman's position and find the nearest grid square.""" posX = 0 posY = 0 # Get the location between grid. posBetweenGridX = (bomberPos[X] - XOFFSET) % BLOCKSIZE[X] posBetweenGridY = (bomberPos[Y] - YOFFSET) % BLOCKSIZE[Y] # If location is less than 1/3 way, move back, # if over 2/3, move forward. inMiddleX = (BLOCKSIZE[X] / 3, 2 * BLOCKSIZE[X] / 3) inMiddleY = (BLOCKSIZE[Y] / 3, 2 * BLOCKSIZE[Y] / 3) # Fix x if posBetweenGridX <= inMiddleX[0]: posX = bomberPos[X] - posBetweenGridX # Please leave the 0 and 1, they don't mean X and Y. elif(posBetweenGridX > inMiddleX[0] and\ posBetweenGridX < inMiddleX[1]): posX = bomberPos[X] else: # > inMiddle[1] posX = bomberPos[X] + (BLOCKSIZE[X] - posBetweenGridX) # Fix y if posBetweenGridY <= inMiddleY[0]: posY = bomberPos[Y] - posBetweenGridY elif posBetweenGridY > inMiddleY[0] and\ posBetweenGridY < inMiddleY[1]: posY = bomberPos[Y] else: # > inMiddleY[1] posY = bomberPos[Y] + (BLOCKSIZE[Y] - posBetweenGridY) return posX, posY def snapToGrid(self, pos): """Take position and find the nearest grid square.""" posX = posY = 0 # Get the location between grid. posBetweenGridX = (pos[0] - XOFFSET) % BLOCKSIZE[0] posBetweenGridY = (pos[1] - YOFFSET) % BLOCKSIZE[1] # If location is less than 1/2 way, move back, else move forward. if posBetweenGridX <= BLOCKSIZE[X] / 2: posX = pos[X] - posBetweenGridX else: posX = pos[X] + (BLOCKSIZE[X] - posBetweenGridX) if posBetweenGridY <= BLOCKSIZE[Y] / 2: posY = pos[Y] - posBetweenGridY else: posY = pos[Y] + (BLOCKSIZE[Y] - posBetweenGridY) return posX, posY def worldWrap(self, bomb): """Send bomb to other side of world if it was punched out of bounds """ x, y = bomb.getPosition() if bomb.beingPunched: # Check top if y < YOFFSET: bomb.setPosition((x, self.mapRows * BLOCKSIZE[Y] + YOFFSET)) # Check bottom if y > self.mapRows * BLOCKSIZE[Y] + YOFFSET: bomb.setPosition((x, YOFFSET)) # Check left if x < XOFFSET: bomb.setPosition((self.mapColumns * BLOCKSIZE[X] + XOFFSET, y)) # Check right if x > self.mapColumns * BLOCKSIZE[X] + XOFFSET: bomb.setPosition((XOFFSET, y)) def update(self): """Update sprites in enviornment class Attempt to move each sprite, undoing movements that produce collisions. """ explosionsToSort = self.explosionGroup.sprites() explosionsToSort.sort(lambda x, y: cmp(str(x), str(y))) for explosion in explosionsToSort: debug(str(explosion.rect.topleft) + " ") explosion.update() # Update players bombersToSort = self.bomberGroup.sprites() bombersToSort.sort(lambda x, y: cmp(x.id, y.id)) for bomber in bombersToSort: bomber.update() # Update bombs bombsToSort = self.bombGroup.sprites() bombsToSort.sort(lambda x, y: cmp(x.id, y.id)) for bomb in bombsToSort: bomb.update() # Update power-ups powerupSorted = self.powerUpGroup.sprites() powerupSorted.sort(lambda x, y: cmp(x.id, y.id)) for powerup in powerupSorted: powerup.update()
class GameView(View): def __init__( self, screen, display ): self.screen = screen self.screenRect = screen.get_rect() self.display = display self.model = None self.currentTime = 0 self.bgImage = load_png( 'bg_game.png' ) self.hiGroup = RenderUpdates() self.lowGroup = RenderUpdates() self.viewOnlyGroup = RenderUpdates() self.bubbleGroup = RenderUpdates() self.ins_spin = None self.ins_press = None self.quitButton = None self.squeezePrompt = None self.groups = [self.lowGroup, self.bubbleGroup, self.hiGroup, self.viewOnlyGroup] self.locks = [] self.stripeOrder = ['violet','blue','green', 'yellow','orange','red'] self.stripeHeights = { 'violet': 233, 'blue': 189, 'green': 136, 'yellow': 85, 'orange': 44, 'red': 11, } self.heaterRects = { 'green': Rect ( 160, 470, 80, 100 ), 'blue': Rect ( 265, 470, 80, 100 ), 'violet': Rect ( 370, 470, 80, 100 ), 'red': Rect ( 475, 470, 80, 100 ), 'orange': Rect ( 580, 470, 80, 100 ), 'yellow': Rect ( 685, 470, 80, 100 ), } self.purseStatusbars = [] controller = mvcState.GetController() controller.gameEventListeners.append( self ) def ModelStarted( self, model ): self.quitButton = QuitButton() self.quitButton.rect.topleft = (10, 530) self.viewOnlyGroup.add( self.quitButton ) self.ins_spin = InstructionSpin() self.ins_spin.rect.topleft = (380, 440) self.viewOnlyGroup.add( self.ins_spin ) View.ModelStarted( self, model ) heater = self.model.manualHeater heater.rect.topleft = self.heaterRects['red'].topleft self.hiGroup.add( heater ) cloud = self.model.cloud cloud.rect.topleft = (10,40) self.hiGroup.add( cloud ) monWid = self.model.playerMoney monWid.rect.topleft = (10,490) self.hiGroup.add( monWid ) bladder = self.model.bladder bladder.rect.topleft = (410,378) bladder.FixBottom( bladder.rect.midbottom ) self.hiGroup.add( bladder ) self.squeezePrompt = SqueezePrompt( bladder ) self.squeezePrompt.rect.topleft = (190,340) m = self.model stripes = [m.violetstripe, m.bluestripe, m.greenstripe, m.yellowstripe, m.orangestripe, m.redstripe ] for i in range( len(stripes) ): name = stripes[i].name stripes[i].rect.topleft = (146,self.stripeHeights[name]) self.lowGroup.add( stripes[i] ) for i in range( len(m.colorPurses) ): cName = m.colorPurses[i].colorName dimensions = self.heaterRects[cName].move(0,0) dimensions.move_ip(0,100) dimensions.height = 10 sb = StatusBar( m.colorPurses[i], dimensions, outlineImg='sb_outline.png', attrName='amount', fullAmt=m.colorPurses[i].capacity, innerColor=COLORS[cName] ) self.purseStatusbars.append( sb ) self.lowGroup.add( sb ) def OnBubbleLaunch( self, centerx ): #log.debug( 'bubble birth' ) if self.ins_spin: self.ins_spin.kill() self.ins_spin = None bubble = Bubble( 438 ) minX = 140 maxX = 790 xpos = int(rng.normalvariate( 0,50 )) + centerx xpos = min( xpos, maxX ) xpos = max( xpos, minX ) bubble.rect.x = xpos bubble.rect.bottom = 470 self.bubbleGroup.add( bubble ) def Update( self, timeChange ): self.viewOnlyGroup.update( timeChange ) self.bubbleGroup.update( timeChange ) self.currentTime += timeChange #twoSecondsAgo = self.currentTime - 2000 #halfSecondAgo = self.currentTime - 500 heaterRange = [444,530] for sb in self.purseStatusbars: sb.update() def OnBladderShoot( self, power, height ): if self.ins_press: self.ins_press.kill() if height > 3: gey = Geyser( "tall" ) else: gey = Geyser( "short" ) colorName = self.stripeOrder[height] gey.rect.midtop = (450, self.stripeHeights[colorName]) self.viewOnlyGroup.add( gey ) self.viewOnlyGroup.add( self.squeezePrompt ) #print len(self.viewOnlyGroup) def OnBladderShootStop( self ): self.viewOnlyGroup.remove( self.squeezePrompt ) def OnBladderVent( self, power, height ): if height > 3: gey = Geyser( "tall" ) else: gey = Geyser( "short" ) colorName = self.stripeOrder[height] gey.rect.midtop = (450, self.stripeHeights[colorName]) self.viewOnlyGroup.add( gey ) def OnDropBirth( self, drop ): self.lowGroup.add( drop ) def OnStripeHitMaxOpacity(self, stripe): yPos = self.stripeHeights[stripe.name]+4 if yPos in [lock.rect.top for lock in self.locks]: #log.debug( 'already have that lock' ) return lock = Lock() lock.rect.topleft = (111, self.stripeHeights[stripe.name]+4 ) self.hiGroup.add( lock ) self.locks.append( lock ) def OnHeaterBirth( self, heater ): switch= { SolarHeater:'violet', WaterWheelHeater:'blue', WindHeater:'green', FireHeater:'yellow', NuclearHeater:'orange', } klass = heater.__class__ heater.rect.topleft = self.heaterRects[switch[klass]].topleft self.hiGroup.add( heater ) def OnBladderVentingImminent( self ): if self.ins_press == None: self.ins_press = InstructionPress() self.ins_press.rect.topleft = (560, 300) self.viewOnlyGroup.add( self.ins_press ) def Kill( self ): controller = mvcState.GetController() controller.gameEventListeners.remove( self ) for s in self.viewOnlyGroup.sprites(): s.kill() for s in self.bubbleGroup.sprites(): s.kill() def OnUserQuit( self ): self.Kill() def OnWin( self, time, money ): self.Kill()
class GameWorld: """Hold Game World objects Initialize display >>> import pygame as pygame >>> import random >>> from widget import * >>> randomizer = Random() Create a gameworld >>> g = GameWorld(randomizer) >>> s = pygame.surface.Surface((30,30)) >>> w = Widget(s, (0,0,30,30), (0,0)) Add and test bomberman to world >>> g.appendBomber(w) >>> g.bomberGroup <RenderUpdates(1 sprites)> Add and test an immutable to world >>> w1 = Widget(s, (0,0,30,30), (0,0)) >>> g.appendImmutable(w1) >>> g.immutableGroup <RenderUpdates(1 sprites)> Add another bomberman to world >>> p2 = Widget(s, (100,100, 30,30), (0,0)) >>> g.appendBomber(p2) Add a bomb to the game world >>> bomb = Widget(s, (100,100,30,30), (0,0)) >>> g.appendBomb(bomb) Check the number of objects in game world >>> g.universalGroup <RenderUpdates(4 sprites)> Detonate bomb in game world >>> g.detonateBomb(bomb) >>> g.bombGroup <RenderUpdates(0 sprites)> >>> g.universalGroup <RenderUpdates(3 sprites)> Add bomb to populatedBombGroup >>> g.appendPopulatedBomb(bomb) >>> g.populatedBombGroup <RenderUpdates(1 sprites)> Add an explosion to game world >>> explosion = Widget(s, (0,0,30,30),(0,0)) >>> g.appendExplosion(explosion) >>> g.explosionGroup <RenderUpdates(1 sprites)> Add a power up to game world >>> powerup = Widget(s,(0,0,30,30),(0,0)) >>> g.appendPowerUp(powerup) >>> g.powerUpGroup <RenderUpdates(1 sprites)> Remove power up from game world >>> g.removePowerUp(powerup) >>> g.powerUpGroup <RenderUpdates(0 sprites)> Test bomberstrafe algorithm >>> g.bomberStrafe([98, 100]) (98, 100) >>> g.bomberStrafe([90, 100]) (80, 100) >>> g.bomberStrafe([98, 90]) (98, 80) >>> g.bomberStrafe([139, 140]) (144, 144) Test worldWrap >>> g.mapColumns = 19 >>> bomb = Bomb(s, (0,0,30,30),3,3,3,(0,0)) >>> bomb.beingPunched = 1 >>> g.appendBomb(bomb) >>> bomb.setPosition((10,100)) >>> g.worldWrap(bomb) >>> bomb.getPosition() (624, 100) >>> bomb.setPosition((630,100)) >>> g.worldWrap(bomb) >>> bomb.getPosition() (16, 100) >>> g.mapRows = 19 >>> bomb.setPosition((100, 10)) >>> g.worldWrap(bomb) >>> bomb.getPosition() (100, 656) >>> bomb.setPosition((100, 660)) >>> g.worldWrap(bomb) >>> bomb.getPosition() (100, 48) Test for new restart game state >>> g.cleanState() >>> g.populatedBombGroup <RenderUpdates(0 sprites)> >>> g.bombGroup <RenderUpdates(0 sprites)> >>> g.bomberGroup <RenderUpdates(0 sprites)> >>> g.explosionGroup <RenderUpdates(0 sprites)> >>> g.immutableGroup <RenderUpdates(0 sprites)> >>> g.powerUpGroup <RenderUpdates(0 sprites)> >>> g.mutableGroup <RenderUpdates(0 sprites)> >>> g.universalGroup <RenderUpdates(0 sprites)> >>> g.dirtyGroup <RenderUpdates(0 sprites)> >>> g.flyOverGroup <RenderUpdates(0 sprites)> >>> g.update() """ def __init__(self, randomizer): self.randomizer = randomizer # Set title pygame.display.set_caption("Pybomber") # Setup screen self.screen = pygame.display.get_surface() # Create sprite groups self.populatedBombGroup = SpriteGroup() self.bombGroup = SpriteGroup() self.bomberGroup = SpriteGroup() self.explosionGroup = SpriteGroup() self.immutableGroup = SpriteGroup() self.powerUpGroup = SpriteGroup() self.mutableGroup = SpriteGroup() self.universalGroup = SpriteGroup() # For drawing everything. self.dirtyGroup = SpriteGroup() self.flyOverGroup = SpriteGroup() # Load a background self.background = pygame.image.load(BACKGROUND).convert() self.map = None # Draw background on screen self.screen.blit(self.background, ((0, 0), RESOLUTION)) # Number of rows and colums in the current map. self.mapRows = 0 self.mapColumns = 0 def cleanState(self): pygame.display.set_caption("Pybomber") self.screen.blit(self.background, ((0, 0), RESOLUTION)) self.populatedBombGroup = SpriteGroup() self.bombGroup = SpriteGroup() self.bomberGroup = SpriteGroup() self.explosionGroup = SpriteGroup() self.immutableGroup = SpriteGroup() self.powerUpGroup = SpriteGroup() self.mutableGroup = SpriteGroup() self.universalGroup = SpriteGroup() # For drawing everything. self.dirtyGroup = SpriteGroup() self.groundGroup = SpriteGroup() self.flyOverGroup = SpriteGroup() self.mapRows = 0 self.mapColumns = 0 self.curWidgetID = 0 def appendBomb(self, sprite): self.bombGroup.add(sprite) self.universalGroup.add(sprite) self.groundGroup.add(sprite) def flyBomb(self, bombSprite): self.groundGroup.remove(bombSprite) def groundBomb(self, bomb): self.groundGroup.add(bomb) def appendPopulatedBomb(self, sprite): self.populatedBombGroup.add(sprite) def appendExplosion(self, sprite): self.explosionGroup.add(sprite) self.universalGroup.add(sprite) def appendImmutable(self, sprite): self.flyOverGroup.add(sprite) self.immutableGroup.add(sprite) self.universalGroup.add(sprite) self.groundGroup.add(sprite) def appendBomber(self, sprite): self.bomberGroup.add(sprite) self.universalGroup.add(sprite) self.groundGroup.add(sprite) def appendPowerUp(self, sprite): self.powerUpGroup.add(sprite) self.universalGroup.add(sprite) def appendMutable(self, sprite): self.flyOverGroup.add(sprite) self.mutableGroup.add(sprite) self.universalGroup.add(sprite) self.groundGroup.add(sprite) def detonateBomb(self, bomb): """Detonate bomb""" bomb.kill() def removeExplosion(self, explosion): """Remove explosion from all groups.""" self.explosionGroup.remove(explosion) self.universalGroup.remove(explosion) def removeBomber(self, bomber): """Remove explosion from all groups.""" self.bomberGroup.remove(bomber) self.groundGroup.remove(bomber) self.universalGroup.remove(bomber) def removePopulatedBomb(self, sprite): self.populatedBombGroup.remove(sprite) def removePowerUp(self, powerUp): """Remove powerUp from all groups.""" self.powerUpGroup.remove(powerUp) self.universalGroup.remove(powerUp) def bomberStrafe(self, bomberPos): """Take bomberman's position and find the nearest grid square.""" posX = 0 posY = 0 # Get the location between grid. posBetweenGridX = (bomberPos[X]-XOFFSET) % BLOCKSIZE[X] posBetweenGridY = (bomberPos[Y]-YOFFSET) % BLOCKSIZE[Y] # If location is less than 1/3 way, move back, # if over 2/3, move forward. inMiddleX = (BLOCKSIZE[X]/3,2 * BLOCKSIZE[X]/3) inMiddleY = (BLOCKSIZE[Y]/3,2 * BLOCKSIZE[Y]/3) # Fix x if posBetweenGridX <= inMiddleX[0]: posX = bomberPos[X] - posBetweenGridX # Please leave the 0 and 1, they don't mean X and Y. elif(posBetweenGridX > inMiddleX[0] and\ posBetweenGridX < inMiddleX[1]): posX = bomberPos[X] else: # > inMiddle[1] posX = bomberPos[X] + (BLOCKSIZE[X] - posBetweenGridX) # Fix y if posBetweenGridY <= inMiddleY[0]: posY = bomberPos[Y] - posBetweenGridY elif posBetweenGridY > inMiddleY[0] and\ posBetweenGridY < inMiddleY[1]: posY = bomberPos[Y] else: # > inMiddleY[1] posY = bomberPos[Y] + (BLOCKSIZE[Y] - posBetweenGridY) return posX, posY def snapToGrid(self, pos): """Take position and find the nearest grid square.""" posX = posY = 0 # Get the location between grid. posBetweenGridX = (pos[0]-XOFFSET) % BLOCKSIZE[0] posBetweenGridY = (pos[1]-YOFFSET) % BLOCKSIZE[1] # If location is less than 1/2 way, move back, else move forward. if posBetweenGridX <= BLOCKSIZE[X]/2: posX = pos[X] - posBetweenGridX else: posX = pos[X] + (BLOCKSIZE[X] - posBetweenGridX) if posBetweenGridY <= BLOCKSIZE[Y]/2: posY = pos[Y] - posBetweenGridY else: posY = pos[Y] + (BLOCKSIZE[Y] - posBetweenGridY) return posX, posY def worldWrap(self, bomb): """Send bomb to other side of world if it was punched out of bounds """ x, y = bomb.getPosition() if bomb.beingPunched: # Check top if y < YOFFSET: bomb.setPosition((x, self.mapRows*BLOCKSIZE[Y] + YOFFSET)) # Check bottom if y > self.mapRows*BLOCKSIZE[Y] + YOFFSET: bomb.setPosition((x, YOFFSET)) # Check left if x < XOFFSET: bomb.setPosition((self.mapColumns*BLOCKSIZE[X] + XOFFSET, y)) # Check right if x > self.mapColumns*BLOCKSIZE[X] + XOFFSET: bomb.setPosition((XOFFSET, y)) def update(self): """Update sprites in enviornment class Attempt to move each sprite, undoing movements that produce collisions. """ explosionsToSort = self.explosionGroup.sprites() explosionsToSort.sort(lambda x, y: cmp(str(x), str(y))) for explosion in explosionsToSort: debug(str(explosion.rect.topleft) + " ") explosion.update() # Update players bombersToSort = self.bomberGroup.sprites() bombersToSort.sort(lambda x, y: cmp(x.id, y.id)) for bomber in bombersToSort: bomber.update() # Update bombs bombsToSort = self.bombGroup.sprites() bombsToSort.sort(lambda x, y: cmp(x.id, y.id)) for bomb in bombsToSort: bomb.update() # Update power-ups powerupSorted = self.powerUpGroup.sprites() powerupSorted.sort(lambda x, y: cmp(x.id, y.id)) for powerup in powerupSorted: powerup.update()
class EntityMaster: bee_ratio = 1 / 5 def __init__(self, initial_hives: int, default_bees_per_hive: int, play_area_dimensions: int(), flower_spawn_strategy: str, hive_spawn_strategy: str, flower_num: int): self.bees = RenderUpdates() self.hives = RenderUpdates() self.plants = RenderUpdates() self.flowers = RenderUpdates() self.ui_elements = RenderUpdates() self.flower_database = {} self.play_area = play_area_dimensions self.sim_paused = False self.grow_flora(play_area_dimensions) self.load_flower_data( get_flower_spawn_strategy(flower_spawn_strategy, play_area_dimensions, flower_num)) self.populate_hives( get_hive_spawn_strategy(hive_spawn_strategy, initial_hives, play_area_dimensions, self.flowers), default_bees_per_hive) self.clean_up_spawn() @property def bee_population(self): """ :return: Total number of bees in the simulation """ return len(self.bees) @property def flower_population(self): """ :return: Total number of flowers in the simulation """ return len(self.flowers) def get_entities(self): """ :return: Entities that need to be rendered next frame """ self.update_game_state() return self.plants.sprites() + self.flowers.sprites() + \ self.hives.sprites() + self.bees.sprites() + self.ui_elements.sprites() def update_game_state(self): """ :return: void """ for hive in self.hives: hive.last_tick = get_ticks() self.handle_hive_highlighting(hive) if hive.current_nectar == hive.max_nectar: self.hive_purchase_bee(hive) for flower in self.flowers: if flower.pollen == 0: flower.crosshair.kill() flower.kill() for hive in self.hives: if flower in hive.flowers: hive.flowers.remove(flower) else: if flower.inspecting_hives.__len__() == 0: self.ui_elements.remove(flower.crosshair) else: self.ui_elements.add(flower.crosshair) flower.inspecting_hives[flower.inspecting_hives.__len__() - 1].recolor_crosshair(flower) if not self.sim_paused: for bee in self.bees: # If the sim is not paused, we update the states of the bees bee.update() self.update_bee_crosshair(bee) bee.handle_collisions(self.flowers) else: for bee in self.bees: # If the sim is paused we only update the crosshairs self.update_bee_crosshair(bee) def update_bee_crosshair(self, bee): """ Updates the state of the bee crosshair, adding it to the list of rendered entities if necessary :param bee: :return: void """ if not bee.highlighted: bee.crosshair.kill() else: self.ui_elements.add(bee.crosshair) bee.crosshair.follow() def handle_hive_highlighting(self, hive): """ Takes care of everything that has to do with hive highlighting, adding the needed ui elements if necessary :param hive: :return: """ if not hive.highlighted: hive.honey_bar.kill() else: self.ui_elements.add(hive.honey_bar) hive.honey_bar.update() def add_bee(self, hive, caste): if caste == 'worker': new_bee = \ WorkerBee((hive.center.x + randint(-10, 10), hive.center.y + randint(-10, 10)), hive) elif caste == 'scout': new_bee = \ ScoutBee((hive.center.x + randint(-50, 50), hive.center.y + randint(-50, 50)), hive) self.ui_elements.add(new_bee.crosshair) hive.add_bee(new_bee, caste) self.bees.add(new_bee) def hive_purchase_bee(self, hive): """ Provided hive purchases a bee using some of its honey :param hive: :return: """ hive.buy_bee() bee_roll = random() self.add_bee(hive, 'scout') if bee_roll >= self.bee_ratio else self.add_bee( hive, 'worker') def populate_hives(self, hives, bees_per_hive): """ Populates the hives with bees :param hives: :param bees_per_hive: :return: """ for hive in hives: self.hives.add(hive) self.spawn_initial_bees(hive, bees_per_hive) def spawn_initial_bees(self, hive, bees_per_hive): """ Fills an individual hive with bees :param hive: :param bees_per_hive: :return: """ scouts = int(bees_per_hive * self.bee_ratio) workers = bees_per_hive - scouts for j in range(workers): self.add_bee(hive, 'worker') for j in range(scouts): self.add_bee(hive, 'scout') def clean_up_spawn(self): """ Removes flowers that are colliding with hives and adds UI elements to the hives :return: """ for hive in self.hives: flowers = self.flowers spritecollide(hive, flowers, True, collide_circle_ratio(1)) def load_flower_data(self, data): """ Loads the list of flowers :param data: :return: """ self.flower_database = data self.flowers = RenderUpdates(data.values()) def grow_flora(self, play_area): """ Grows the decorative plants :param play_area: :return: void """ plant_db = grow_plants(play_area, num=randint(90, 150), plant_type="grass", bias="center") plant_db = merge_plant_sets( plant_db, grow_plants(play_area, num=randint(50, 75), plant_type="grassy_plant", bias="edges")) plant_db = merge_plant_sets( plant_db, grow_plants(play_area, num=randint(1, 2), plant_type="pretty_log", bias="edges")) plant_db = merge_plant_sets( plant_db, grow_plants(play_area, num=randint(0, 3), plant_type="stump", bias="edges")) plant_db = merge_plant_sets( plant_db, grow_plants(play_area, num=randint(25, 55), plant_type="leaves", bias="edges")) plant_db = merge_plant_sets( plant_db, grow_plants(play_area, num=randint(15, 25), plant_type="bushy_grass", bias="edges")) self.plants = RenderUpdates(list(plant_db.values())) def get_hive_at(self, position): """ :param position: :return: Hive at given location, None if there are none such. """ for hive in self.hives: if hive.scaled_rect.collidepoint(position): return hive return None