class Bomb(Sprite): width = 10 height = 10 color = 0,200,255 fuse = 3000 def __init__(self, pos): self.image = Surface((self.width, self.height)) self.image.fill(self.color) self.rect = Rect(0,0,self.width, self.height) self.rect.center = pos self.time = fuse def update(self, dt): self.time -= dt step = self.time / 500 if step % 2 == 0: color = 255,255,0 else: color = 255,0,0 self.image.fill(color, self.rect.inflate(-self.width/2, self.height/2))
def bar(self, surface, text, pos, w=300): rect = Rect(pos, (w, 60)) draw.rect(surface, (0, 200, 200), rect) sf = font.SysFont('blah', 60, bold=False, italic=False) txt = sf.render(text, True, (0, 0, 0)) surface.blit(txt, rect.inflate(-10, -10))
def init (self): n_pads = pg.joystick.get_count() if n_pads == 0: img = 'nopads' elif n_pads == 1: img = '1pad' else: img = '2pads' gm_r = Rect((0, 0), self.graphics.size) frame = gm_r.inflate(*(-2 * w for w in conf.INTRO_FRAME_WIDTH)) viewport = GraphicsManager(self.scheduler, frame.size) setup_bg(viewport, 1, self.scheduler) text = Graphic('intro-{0}.png'.format(img)) viewport.add(text) text.align() self.graphics.add( util.tile_graphic(Graphic('solid.png'), gm_r, 1), viewport ) viewport.align() for i in xrange(n_pads): pg.joystick.Joystick(i).init() self.evthandler.add((pg.KEYDOWN, pg.JOYBUTTONDOWN, lambda: conf.GAME.switch_world(Level)))
def test_inflate__larger( self ): "The inflate method inflates around the center of the rectangle" r = Rect( 2, 4, 6, 8 ) r2 = r.inflate( 4, 6 ) self.assertEqual( r.center, r2.center ) self.assertEqual( r.left-2, r2.left ) self.assertEqual( r.top-3, r2.top ) self.assertEqual( r.right+2, r2.right ) self.assertEqual( r.bottom+3, r2.bottom ) self.assertEqual( r.width+4, r2.width ) self.assertEqual( r.height+6, r2.height )
def test_inflate__smaller( self ): "The inflate method inflates around the center of the rectangle" r = Rect( 2, 4, 6, 8 ) r2 = r.inflate( -4, -6 ) self.assertEqual( r.center, r2.center ) self.assertEqual( r.left+2, r2.left ) self.assertEqual( r.top+3, r2.top ) self.assertEqual( r.right-2, r2.right ) self.assertEqual( r.bottom-3, r2.bottom ) self.assertEqual( r.width-4, r2.width ) self.assertEqual( r.height-6, r2.height )
def blit_borders_on(self, surface): rect = Rect((0, 0), self.size) for x in range(0, self.thick): r = rect.inflate(-x, -x) tc = get_top_coords(r) bc = get_bottom_coords(r) if self.pressed: lines(surface, self.dark, False, tc, 1) lines(surface, self.light, False, bc, 1) else: lines(surface, self.light, False, tc, 1) lines(surface, self.dark, False, bc, 1)
def test_inflate__smaller(self): """The inflate method inflates around the center of the rectangle """ r = Rect(2, 4, 6, 8) r2 = r.inflate(-4, -6) self.assertEqual(r.center, r2.center) self.assertEqual(r.left + 2, r2.left) self.assertEqual(r.top + 3, r2.top) self.assertEqual(r.right - 2, r2.right) self.assertEqual(r.bottom - 3, r2.bottom) self.assertEqual(r.width - 4, r2.width) self.assertEqual(r.height - 6, r2.height)
def test_inflate__larger(self): """The inflate method inflates around the center of the rectangle """ r = Rect(2, 4, 6, 8) r2 = r.inflate(4, 6) self.assertEqual(r.center, r2.center) self.assertEqual(r.left - 2, r2.left) self.assertEqual(r.top - 3, r2.top) self.assertEqual(r.right + 2, r2.right) self.assertEqual(r.bottom + 3, r2.bottom) self.assertEqual(r.width + 4, r2.width) self.assertEqual(r.height + 6, r2.height)
def _ball_routine_hit_rect(self, rect: pygame.Rect, routines) -> bool: """ Check if the ball hits the `rect` by checking if any of ball routine collide with the `rect`. """ rect_expand = rect.inflate(1, 1) for routine in routines: # Exclude the case of the ball goes from the surface if not rect_expand.collidepoint(routine[0]) and \ physics.rect_collideline(rect, routine): return True return False
class Level(object): initial_coins = 5 song = "maintheme" def __init__(self, size): self.bounds = Rect((0, 0), size) self.bg_tile = load_image("grass") self.score_font = pygame.font.Font(None, 48) def draw_background(self, surf): tw, th = self.bg_tile.get_size() sw, sh = surf.get_size() for y in range(0, sh, th): for x in range(0, sw, tw): surf.blit(self.bg_tile, (x, y)) def restart(self): self.score = 0 self.player = Player() self.player.rect.center = self.bounds.center self.coins = CoinGroup(self.bounds) # create initial coins for i in range(self.initial_coins): self.coins.spawn() # start the background music play_song(self.song) def update(self, dt): self.player.update(dt) self.coins.update(dt) # lock player in bounds self.player.rect.clamp_ip(self.bounds) # collide player with coins if spritecollide(self.player, self.coins, True): self.score += 1 def draw(self, surf): self.draw_background(surf) self.coins.draw(surf) surf.blit(self.player.image, self.player.rect) score = self.score_font.render(str(self.score), True, (0, 0, 0)) rect = score.get_rect() rect.topright = self.bounds.inflate(-5, -5).topright surf.blit(score, rect)
def draw_onto_base(self, surf: pg.Surface, rect: pg.Rect, edit_mode: bool, step_progress: float = 0.0, neighborhood=(([], ) * 5, ) * 5): s = rect.width pg.draw.rect(surf, self.color.rgb(), rect) padding = s * 0.35 radius = round(s * 0.2) pg.draw.rect(surf, (255, 255, 255), rect.inflate(-padding, -padding), border_radius=radius) render_text_centered_xy( str(self.count), (0, 0, 0), surf, rect.center, s - padding * 1.75, )
def align_rect (rect, within, alignment = 0, pad = 0, offset = 0): """Align a rect within another rect. align_rect(rect, within, alignment = 0, pad = 0, offset = 0) -> pos :arg rect: the Pygame-style rect to align. :arg within: the rect to align ``rect`` within. :arg alignment: ``(x, y)`` alignment; each is ``< 0`` for left-/top-aligned, ``0`` for centred, ``> 0`` for right-/bottom-aligned. Can be just one number to use on both axes. :arg pad: ``(x, y)`` padding to leave around the inner edge of ``within``. Can be negative to allow positioning outside of ``within``, and can be just one number to use on both axes. :arg offset: ``(x, y)`` amounts to offset by after all other positioning; can be just one number to use on both axes. :return: the position the top-left corner of the rect should be moved to for the wanted alignment. """ pos = alignment pos = [pos, pos] if isinstance(pos, (int, float)) else list(pos) if isinstance(pad, (int, float)): pad = (pad, pad) if isinstance(offset, (int, float)): offset = (offset, offset) rect = Rect(rect) sz = rect.size within = Rect(within) within = list(within.inflate(-2 * pad[0], -2 * pad[1])) for axis in (0, 1): align = pos[axis] if align < 0: x = 0 elif align == 0: x = (within[2 + axis] - sz[axis]) / 2. else: # align > 0 x = within[2 + axis] - sz[axis] pos[axis] = ir(within[axis] + x + offset[axis]) return pos
class HerdDemo(Game, Neighborhood): cells_x = 12 cells_y = 8 cell_width = 60 cell_height = 60 bg_color = (240, 230, 140) num_foragers = 60 num_predators = 7 def __init__(self): global game, neighborhood self.set_resource_dir_from_file(__file__) self.quiet = True Game.__init__(self) self.rect = Rect(0, 0, self.cell_width * self.cells_x, self.cell_width * self.cells_y) neighborhood = Neighborhood(self.rect.inflate(-30, -30), self.cells_x, self.cells_y) game = self def initial_background(self, display_size): bg = Surface(self.rect.size) bg.fill(self.bg_color) return bg def initial_state(self): r = neighborhood.rect midleft, midtop = neighborhood.rect.center for i in range(self.num_foragers): Forager(randrange(r.left, midleft), randrange(r.top, midtop), randrange(0, 360)) for i in range(self.num_predators): Predator(randrange(midleft, r.right), randrange(midtop, r.bottom), randrange(0, 360)) return GSWatch() def _end_loop(self): neighborhood.notify_sprites() self._state.loop_end()
def align (self, pos = 0, pad = 0, offset = 0, rect = None): """Position this graphic within a rect. align(pos = 0, pad = 0, offset = 0, rect = self.manager.surface.get_rect()) -> self :arg pos: ``(x, y)`` alignment; each is ``< 0`` for left-aligned, ``0`` for centred, ``> 0`` for right-aligned. Can be just one number to use on both axes. :arg pad: ``(x, y)`` padding to leave around the inner edge of ``rect``. Can be negative to allow positioning outside of ``rect``, and can be just one number to use on both axes. :arg offset: ``(x, y)`` amounts to offset by after all other positioning; can be just one number to use on both axes. :arg rect: Pygame-style rect to align in. """ pos = [pos, pos] if isinstance(pos, (int, float)) else list(pos) if isinstance(pad, (int, float)): pad = (pad, pad) if isinstance(offset, (int, float)): offset = (offset, offset) if rect is None: rect = self._manager.surface.get_rect() else: rect = Rect(rect) rect = rect.inflate(-2 * pad[0], -2 * pad[1]) sz = self._rect[2:] for axis in (0, 1): align = pos[axis] if align < 0: x = 0 elif align == 0: x = (rect[2 + axis] - sz[axis]) / 2. else: # align > 0 x = rect[2 + axis] - sz[axis] pos[axis] = ir(rect[axis] + x + offset[axis]) self.rect = (pos, sz) return self
def rect_collideline(rect: Rect, line) -> bool: """ Check if line segment intersects with a rect @param rect The Rect of the target rectangle @param line A tuple (Vector2, Vector2) representing both end points of line segment """ # Either of line ends is in the target rect. rect_expanded = rect.inflate(1, 1) # Take the bottom and right line into account if rect_expanded.collidepoint(line[0]) or rect_expanded.collidepoint(line[1]): return True line_top = (Vector2(rect.left, rect.top), Vector2(rect.right, rect.top)) line_bottom = (Vector2(rect.left, rect.bottom), Vector2(rect.right, rect.bottom)) line_left = (Vector2(rect.left, rect.top), Vector2(rect.left, rect.bottom)) line_right = (Vector2(rect.right, rect.top), Vector2(rect.right, rect.bottom)) return line_intersect(line_top, line) or \ line_intersect(line_bottom, line) or \ line_intersect(line_left, line) or \ line_intersect(line_right, line)
def AAfilledRoundedRect(surface, rect, color, radius=0.4): """ AAfilledRoundedRect(surface, rect, color, radius=0.4) surface : destination rect : rectangle color : rgb or rgba radius : 0 <= radius <= 1 """ rect = Rect(rect) color = pygame.Color(*color) alpha = color.a color.a = 0 pos = rect.topleft rect.topleft = 0, 0 rectangle = pygame.Surface(rect.size, SRCALPHA) circle = pygame.Surface([min(rect.size) * 3] * 2, SRCALPHA) pygame.draw.ellipse(circle, (0, 0, 0), circle.get_rect(), 0) circle = pygame.transform.smoothscale(circle, [int(min(rect.size) * radius)] * 2) radius = rectangle.blit(circle, (0, 0)) radius.bottomright = rect.bottomright rectangle.blit(circle, radius) radius.topright = rect.topright rectangle.blit(circle, radius) radius.bottomleft = rect.bottomleft rectangle.blit(circle, radius) rectangle.fill((0, 0, 0), rect.inflate(-radius.w, 0)) rectangle.fill((0, 0, 0), rect.inflate(0, -radius.h)) rectangle.fill(color, special_flags=BLEND_RGBA_MAX) rectangle.fill((255, 255, 255, alpha), special_flags=BLEND_RGBA_MIN) return surface.blit(rectangle, pos)
def draw(self): surface = Surface(self.size, flags=SRCALPHA).convert_alpha() rect = Rect((0, 0), self.size) color = Color(*self.color) alpha = color.a color.a = 0 rectangle = Surface(rect.size, SRCALPHA) #ex: [h*3, h*3] circle = Surface([min(rect.size) * 5] * 2, SRCALPHA) draw.ellipse(circle, (0, 0, 0), circle.get_rect(), 0) #ex: [h*0.5, h*.05] circle = transform.smoothscale(circle, [int(self.radius_value)] * 2) #now circle is just a small circle of radius self.radius*h (for example) #blit topleft circle: radius = rectangle.blit(circle, (0, 0)) #now radius = Rect((0, 0), circle.size), rect=Rect((0, 0), self.size) #blit bottomright circle: radius.bottomright = rect.bottomright #radius is growing rectangle.blit(circle, radius) #blit topright circle: radius.topright = rect.topright rectangle.blit(circle, radius) #blit bottomleft circle: radius.bottomleft = rect.bottomleft rectangle.blit(circle, radius) #black-fill of the internal rect rectangle.fill((0, 0, 0), rect.inflate(-radius.w, 0)) rectangle.fill((0, 0, 0), rect.inflate(0, -radius.h)) #fill with color using blend_rgba_max rectangle.fill(color, special_flags=BLEND_RGBA_MAX) #fill with alpha-withe using blend_rgba_min in order to make transparent #the rectangle.fill((255, 255, 255, alpha), special_flags=BLEND_RGBA_MIN) surface.blit(rectangle, rect.topleft) return surface
def make_house(self, x, y, direction, width, depth): if random.random() > self.house_chance: return False # Get corners of house rect = Rect(x+2, y - width/2, depth, width) self.rotate_rect(rect, (x, y), -direction) door_pos = self.rotate((x+2, y), (x, y), -direction) path_pos = self.rotate((x+1, y), (x, y), -direction) # Make sure not out of bounds or overlapping something if rect.left < 0 or rect.right >= self.size or rect.top < 0 or rect.bottom >= self.size: return False points = self.get_rect(rect) if set(points) & self.occupied: return False # Place the terrain self.fill_rect(rect, road) self.outline(rect, wall) # Region name will get reset later region = Region('unoccupied house', self.level, self.get_rect(rect.inflate(-2,-2))) door = Door(door_pos, level=self.level, blocks=region) region.door = door region.path = path_pos self.houses.append(region) self.level.set_terrain(door_pos, floor) self.level.set_terrain(path_pos, road) self.occupied |= set(self.get_rect(rect)) return True
class BasicMapRenderer(object): DEFAULT_LIFESPAN = 30 * 2 # ticks * calls to get_tiles() _allowed_rect = ( 'x', 'y', 'left', 'right', 'top', 'bottom', 'center', 'midleft', 'midright', 'midtop', 'midbottom', 'topleft', 'topright', 'bottomleft', 'bottomright', ) def __init__(self, basic_map, tile_size=0, max_scroll_speed=10): self._basic_map = basic_map self._rect = Rect(State.camera.view.rect) self._max_scroll_speed = max_scroll_speed self._tiles = {} self._visible_tiles = [] self._view_count = 0 self._lifespan = self.DEFAULT_LIFESPAN self._examine_queue = [] self._dead = [] self.dirty_rects = [] self.tile_size = tile_size self.get_tiles() def get_rect(self): return Rect(self._rect) def set_rect(self, **kwargs): """set the world location of the renderer's view rect""" # may be an opportunity here for a memoization performance gain to # keep tiles if the move does not select a new region del self._visible_tiles[:] for k in kwargs: if k not in self._allowed_rect: raise pygame.error('rect attribute not permitted: %s' % (k,)) setattr(self._rect, k, kwargs[k]) self.get_tiles() del self.dirty_rects[:] @property def max_scroll_speed(self): return self._max_scroll_speed @max_scroll_speed.setter def max_scroll_speed(self, val): assert isinstance(val, int) self._max_scroll_speed = val @property def lifespan(self): return self._lifespan @lifespan.setter def lifespan(self, val): """set a new lifespan on tiles""" self._lifespan = val @property def tile_size(self): return self._tile_size @tile_size.setter def tile_size(self, val): """set a new tile size""" assert isinstance(val, int) if val <= 0: # change from 4 to 6 for better FPS while scrolling, which activity # adds newly rendered tiles on the fly val = self._rect.width // 6 self._tile_size = val self.clear() self.get_tiles() @property def basic_map(self): return self._basic_map @basic_map.setter def basic_map(self, val): """register a new BasicMap""" assert isinstance(val, BasicMap) self._basic_map = val self.clear() self.get_tiles() def set_dirty(self, *areas): """specify areas to re-render The areas argument must be one or more pygame.Rect. Marking areas dirty is necessary only if the underlying BasicMap tiles are procedurally modified during runtime. Though it involves some management, it is potentially much more efficient than triggering the entire view be recreated. """ tiles = self._tiles for rect in areas: for tile in tuple(tiles.values()): if rect.colliderect(tile.rect): self.dirty_rects.append(tile.rect) del tiles[tile.idx] self.get_tiles() def get_tiles(self): """call once per tick to calculate visible tiles The constructor does this automatically, and it is done each time set_rect() is called. It may be necessary to call get_tiles() manually depending on the implementation; for example, if the renderer object is created before the map is loaded. """ visible_tiles = self._visible_tiles if visible_tiles: return self._view_count += 1 self._age_tiles() stamp = self._view_count speed = self._max_scroll_speed * 2 X, Y, W, H = self._rect.inflate(speed, speed) SIZE = self._tile_size X = X // SIZE * SIZE Y = Y // SIZE * SIZE TILES = self._tiles get_tile = TILES.get for x in xrange(X, X + W + SIZE, SIZE): for y in xrange(Y, Y + H + SIZE, SIZE): idx = x, y if idx not in TILES: tile = BasicMapRendererTile(idx, SIZE, self) TILES[idx] = tile else: tile = get_tile(idx) tile.stamp = stamp visible_tiles.append(tile) def draw_tiles(self): """draw the visible tiles on the screen""" cam_rect = State.camera.rect self._rect.center = cam_rect.center blit = State.camera.surface.blit view_rect = self._rect colliderect = view_rect.colliderect x, y = cam_rect.topleft for tile in self._visible_tiles: r = tile.rect im = tile.image if im and colliderect(r): blit(im, r.move(-x, -y)) def _age_tiles(self): """weed out aged tiles nicely""" queue = self._examine_queue tiles = self._tiles if not queue: queue[:] = tiles.values() stamp = self._view_count dead = self._dead lifespan = self._lifespan i = 0 while queue: i += 1 if i > 30: # performance throttle: only do 30 per check break tile = queue.pop(0) if stamp - tile.stamp > lifespan: dead.append(tile) for tile in dead: try: del tiles[tile.idx] except KeyError: pass del dead[:] def clear(self): """clear all tile caches""" self._tiles.clear() del self._visible_tiles[:] del self._examine_queue[:] del self._dead[:] del self.dirty_rects[:]
class Radio(Widget): selected = False value = None # (optional) value assigned to this Radio diameter = 30 # Outside diameter of the radio circle dot_color = (160, 160, 160 ) # Color of the inner circle rendered when selected dot_radius = 8 # Radius of the inner circle rendered when selected _groups = {} # Class variable; lists of radio widgets def __init__(self, group, text, **kwargs): """ Initialize a Radio Widget. . "group" identifies the group that this Radio is a member of. Only one Radio in a group may be selected at a time. "text" is the text to show on the associated label. """ self.group = group if group not in Radio._groups: Radio._groups[group] = [] Radio._groups[group].append(self) Widget.__init__(self, **kwargs) self._label = Label(text, **kwargs) @classmethod def selected_element(cls, group): """ Returns the selected Radio instance having the given "group" id. Returns none if no Radio in the given group is selected. """ if group in Radio._groups: for radio in Radio._groups[group]: if radio.selected: return radio return None @classmethod def selected_value(cls, group): """ Returns the "value" attribute of the selected Radio instance having the given "group" id. If the Radio widget's value is "None", returns the "text" attribute of the Radio's label. Returns none if no Radio in the given group is selected. """ radio = Radio.selected_element(group) if radio is None: return None return radio.text if radio.value is None else radio.value def __setattr__(self, varname, value): """ Delegate the "text" attribute to the label contained in this Radio widget. """ if varname == "text": self._label.text = value else: super(Radio, self).__setattr__(varname, value) def __getattr__(self, varname): """ Delegate the "text" attribute to the label contained in this Radio widget. """ if varname == "text": return self._label.text if varname == "value" and self.__dict__["value"] is None: return self._label.text return super(Radio, self).__getattr__(varname) def get_initial_rect(self): """ Determine the minimum space necessary for this element and its' contained elements. Returns Rect """ self._circle_rect = Rect(0, 0, self.diameter, self.diameter) self._circle_boundary = self._circle_rect.inflate( self.padding_left, self.padding_top + self.padding_bottom) self._circle_boundary.topleft = 0, 0 self._label_rect = self._label.get_initial_rect() self._label_rect.left = self._circle_boundary.width self.rect = self._circle_boundary.union(self._label_rect) self._label_rect.centery = self.rect.centery self._circle_boundary.centery = self.rect.centery self._circle_rect.center = self._circle_boundary.center return self.rect def get_surface(self): """ Returns a Surface to use as this element's background, decorated with whichever effect function is set on this widget having been applied. """ surface = Surface(self.rect.size) surface.fill(self.current_background_color()) color = Color(*self.current_background_color()) highlight = color.correct_gamma(2) shadow = color.correct_gamma(0.5) arc(surface, shadow, self._circle_rect, RADIANS_TOPRIGHT, RADIANS_BOTTOM_LEFT, self.bevel_depth) arc(surface, highlight, self._circle_rect, RADIANS_BOTTOM_LEFT, RADIANS_TOPRIGHT, self.bevel_depth) if self.selected: circle(surface, self.dot_color, self._circle_rect.center, self.dot_radius) for att in ["disabled", "focused", "hovering"]: setattr(self._label, att, getattr(self, att)) surface.blit(self._label.get_surface(), self._label_rect.topleft) return surface def click(self, pos): """ Click pseudo-event. """ for radio in Radio._groups[self.group]: radio.selected = False self.selected = True if callable(self.click_handler): self.click_handler(self) def __str__(self): return 'Radio ["%s" group] "%s" (%d x %d) @ (%d, %d)' % ( self.group, self.text, self.rect.width, self.rect.height, self.rect.left, self.rect.top)
def __init__(self, parent_rect: pygame.Rect, x_inflation: int = 0, y_inflation: int = 0): self.rect = parent_rect.inflate(x_inflation, y_inflation) self.offset = Vector2() self.image = None self._set_offset_with_rect(parent_rect) self._set_image(RED_T)
def __init__(self, board: Rect): """ Construct all the carrom board parameters required to draw the carrom board layout """ """ Board must be square in shape """ assert board.width == board.height self.board = board self.frame_width = int(board.width * Board.FRAME_LENGTH / Board.TOTAL_LENGTH) """ Coins are restricted to the container """ self.container = board.inflate(-2 * self.frame_width, -2 * self.frame_width) """ Calculate the multiplier by which to scale parameters to obtain values wrt board""" m = self.board.width / Board.TOTAL_LENGTH self.m = m """ Calculate the pocket radius and the center positions """ self.pocket_radius = m * Board.POCKET_RADIUS self.coin_radius = m * Board.COIN_RADIUS self.striker_radius = m * Board.STRIKER_RADIUS self.pocket_centers = [ Vector2(self.container.left + self.pocket_radius, self.container.top + self.pocket_radius), Vector2(self.container.right - self.pocket_radius, self.container.top + self.pocket_radius), Vector2(self.container.right - self.pocket_radius, self.container.bottom - self.pocket_radius), Vector2(self.container.left + self.pocket_radius, self.container.bottom - self.pocket_radius), ] """ These are solely defined for the purpose of rebounds """ self.diagonal_pocket_opposite = [ (self.container.right - self.striker_radius, self.container.bottom - self.striker_radius), (self.container.left + self.striker_radius, self.container.bottom - self.striker_radius), (self.container.left + self.striker_radius, self.container.top + self.striker_radius), (self.container.right - self.striker_radius, self.container.top + self.striker_radius), ] self.normal_vectors = [ (Vector2(-1, 0), Vector2(0, -1)), (Vector2(1, 0), Vector2(0, -1)), (Vector2(1, 0), Vector2(0, 1)), (Vector2(-1, 0), Vector2(0, 1)), ] """ Calculate the base line positions, the lines defining the region where player places the striker """ base_offset = m * Board.BASE_OFFSET base_distance = m * Board.BASE_DISTANCE base_height = m * Board.BASE_HEIGHT base_length = m * Board.BASE_LENGTH base_radius = m * Board.BASE_RADIUS self.base_lines = [ ((self.container.left + base_offset + base_radius, self.container.top + base_distance), (self.container.left + base_offset + base_length - base_radius, self.container.top + base_distance)), ((self.container.left + base_offset + base_radius, self.container.top + base_distance + base_height), (self.container.left + base_offset + base_length - base_radius, self.container.top + base_distance + base_height)), ((self.container.left + base_offset + base_radius, self.container.bottom - base_distance), (self.container.left + base_offset + base_length - base_radius, self.container.bottom - base_distance)), ((self.container.left + base_offset + base_radius, self.container.bottom - base_distance - base_height), (self.container.left + base_offset + base_length - base_radius, self.container.bottom - base_distance - base_height)), ((self.container.left + base_distance, self.container.top + base_offset + base_radius), (self.container.left + base_distance, self.container.top + base_offset + base_length - base_radius)), ((self.container.left + base_distance + base_height, self.container.top + base_offset + base_radius), (self.container.left + base_distance + base_height, self.container.bottom - base_offset - base_radius)), ((self.container.right - base_distance, self.container.top + base_offset + base_radius), (self.container.right - base_distance, self.container.top + base_offset + base_length - base_radius)), ((self.container.right - base_distance - base_height, self.container.top + base_offset + base_radius), (self.container.right - base_distance - base_height, self.container.bottom - base_offset - base_radius)) ] """ Base circle centers, the circles at the end of base lines """ self.base_circle_centers = [ (int(self.container.left + base_offset + base_radius), int(self.container.top + base_distance + base_radius)), (int(self.container.left + base_offset + base_length - base_radius), int(self.container.top + base_distance + base_radius)), (int(self.container.left + base_offset + base_radius), int(self.container.bottom - base_distance - base_radius)), (int(self.container.left + base_offset + base_length - base_radius), int(self.container.bottom - base_distance - base_radius)), (int(self.container.left + base_distance + base_radius), int(self.container.top + base_offset + base_radius)), (int(self.container.left + base_distance + base_radius), int(self.container.bottom - base_offset - base_radius)), (int(self.container.right - base_distance - base_radius), int(self.container.top + base_offset + base_radius)), (int(self.container.right - base_distance - base_radius), int(self.container.bottom - base_offset - base_radius)) ] """ Arrow lines """ arrow_start = self.pocket_radius * 2 + m * Board.ARROW_OFFSET / sqrt(2) arrow_end = arrow_start + m * Board.ARROW_LENGTH / sqrt(2) self.arrow_lines = [((int(self.container.left + arrow_start), int(self.container.top + arrow_start)), (int(self.container.left + arrow_end), int(self.container.top + arrow_end))), ((int(self.container.left + arrow_start), int(self.container.bottom - arrow_start)), (int(self.container.left + arrow_end), int(self.container.bottom - arrow_end))), ((int(self.container.right - arrow_start), int(self.container.bottom - arrow_start)), (int(self.container.right - arrow_end), int(self.container.bottom - arrow_end))), ((int(self.container.right - arrow_start), int(self.container.top + arrow_start)), (int(self.container.right - arrow_end), int(self.container.top + arrow_end)))] arc_offset = int(arrow_end - m * Board.ARROW_RADIUS * (1 + 1 / sqrt(2))) arc_width = int(m * Board.ARROW_DIAMETER) """ Arrow Arcs""" self.arrow_arcs = [(Rect(self.container.left + arc_offset, self.container.top + arc_offset, arc_width, arc_width), radians(180), radians(90)), (Rect(self.container.left + arc_offset, self.container.bottom - arc_offset, arc_width, -arc_width), radians(-90), radians(180)), (Rect(self.container.right - arc_offset, self.container.bottom - arc_offset, -arc_width, -arc_width), radians(0), radians(-90)), (Rect(self.container.right - arc_offset, self.container.top + arc_offset, -arc_width, arc_width), radians(90), radians(0))] self.base_radius = m * Board.BASE_RADIUS self.base_distance = m * Board.BASE_DISTANCE self.base_offset = m * Board.BASE_OFFSET self.base_inner_radius = m * Board.BASE_INNER_RADIUS self.center_outer_radius = m * Board.CENTER_OUTER_RADIUS self.center_inner_radius = m * Board.CENTER_INNER_RADIUS
def draw(self, surface, camera): rect = Rect(camera.transform(self.pos), (0, 0)).inflate(20, 20) pygame.draw.rect(surface, Color('darkgray'), rect) pygame.draw.rect(surface, Color('black'), rect.inflate(-4, -4))
def __init__(self, board: Rect): assert board.width == board.height self.board = board self.frame_width = int(board.width * Board.FRAME_LENGTH / Board.TOTAL_LENGTH) self.container = board.inflate(-2 * self.frame_width, -2 * self.frame_width) m = self.board.width / Board.TOTAL_LENGTH self.m = m self.pocket_radius = m * Board.POCKET_RADIUS self.coin_radius = m * Board.COIN_RADIUS self.striker_radius = m * Board.STRIKER_RADIUS self.pocket_centers = [ Vector2(self.container.left + self.pocket_radius, self.container.top + self.pocket_radius), Vector2(self.container.right - self.pocket_radius, self.container.top + self.pocket_radius), Vector2(self.container.right - self.pocket_radius, self.container.bottom - self.pocket_radius), Vector2(self.container.left + self.pocket_radius, self.container.bottom - self.pocket_radius), ] self.diagonal_pocket_opposite = [ (self.container.right - self.striker_radius, self.container.bottom - self.striker_radius), (self.container.left + self.striker_radius, self.container.bottom - self.striker_radius), (self.container.left + self.striker_radius, self.container.top + self.striker_radius), (self.container.right - self.striker_radius, self.container.top + self.striker_radius), ] self.normal_vectors = [ (Vector2(-1, 0), Vector2(0, -1)), (Vector2(1, 0), Vector2(0, -1)), (Vector2(1, 0), Vector2(0, 1)), (Vector2(-1, 0), Vector2(0, 1)), ] base_offset = m * Board.BASE_OFFSET base_distance = m * Board.BASE_DISTANCE base_height = m * Board.BASE_HEIGHT base_length = m * Board.BASE_LENGTH base_radius = m * Board.BASE_RADIUS self.base_lines = [ ((self.container.left + base_offset + base_radius, self.container.top + base_distance), (self.container.left + base_offset + base_length - base_radius, self.container.top + base_distance)), ((self.container.left + base_offset + base_radius, self.container.top + base_distance + base_height), (self.container.left + base_offset + base_length - base_radius, self.container.top + base_distance + base_height)), ((self.container.left + base_offset + base_radius, self.container.bottom - base_distance), (self.container.left + base_offset + base_length - base_radius, self.container.bottom - base_distance)), ((self.container.left + base_offset + base_radius, self.container.bottom - base_distance - base_height), (self.container.left + base_offset + base_length - base_radius, self.container.bottom - base_distance - base_height)), ((self.container.left + base_distance, self.container.top + base_offset + base_radius), (self.container.left + base_distance, self.container.top + base_offset + base_length - base_radius)), ((self.container.left + base_distance + base_height, self.container.top + base_offset + base_radius), (self.container.left + base_distance + base_height, self.container.bottom - base_offset - base_radius)), ((self.container.right - base_distance, self.container.top + base_offset + base_radius), (self.container.right - base_distance, self.container.top + base_offset + base_length - base_radius)), ((self.container.right - base_distance - base_height, self.container.top + base_offset + base_radius), (self.container.right - base_distance - base_height, self.container.bottom - base_offset - base_radius)) ] self.base_circle_centers = [ (int(self.container.left + base_offset + base_radius), int(self.container.top + base_distance + base_radius)), (int(self.container.left + base_offset + base_length - base_radius), int(self.container.top + base_distance + base_radius)), (int(self.container.left + base_offset + base_radius), int(self.container.bottom - base_distance - base_radius)), (int(self.container.left + base_offset + base_length - base_radius), int(self.container.bottom - base_distance - base_radius)), (int(self.container.left + base_distance + base_radius), int(self.container.top + base_offset + base_radius)), (int(self.container.left + base_distance + base_radius), int(self.container.bottom - base_offset - base_radius)), (int(self.container.right - base_distance - base_radius), int(self.container.top + base_offset + base_radius)), (int(self.container.right - base_distance - base_radius), int(self.container.bottom - base_offset - base_radius)) ] arrow_start = self.pocket_radius * 2 + m * Board.ARROW_OFFSET / sqrt(2) arrow_end = arrow_start + m * Board.ARROW_LENGTH / sqrt(2) self.arrow_lines = [((int(self.container.left + arrow_start), int(self.container.top + arrow_start)), (int(self.container.left + arrow_end), int(self.container.top + arrow_end))), ((int(self.container.left + arrow_start), int(self.container.bottom - arrow_start)), (int(self.container.left + arrow_end), int(self.container.bottom - arrow_end))), ((int(self.container.right - arrow_start), int(self.container.bottom - arrow_start)), (int(self.container.right - arrow_end), int(self.container.bottom - arrow_end))), ((int(self.container.right - arrow_start), int(self.container.top + arrow_start)), (int(self.container.right - arrow_end), int(self.container.top + arrow_end)))] arc_offset = int(arrow_end - m * Board.ARROW_RADIUS * (1 + 1 / sqrt(2))) arc_width = int(m * Board.ARROW_DIAMETER) self.arrow_arcs = [(Rect(self.container.left + arc_offset, self.container.top + arc_offset, arc_width, arc_width), radians(180), radians(90)), (Rect(self.container.left + arc_offset, self.container.bottom - arc_offset, arc_width, -arc_width), radians(-90), radians(180)), (Rect(self.container.right - arc_offset, self.container.bottom - arc_offset, -arc_width, -arc_width), radians(0), radians(-90)), (Rect(self.container.right - arc_offset, self.container.top + arc_offset, -arc_width, arc_width), radians(90), radians(0))] self.base_radius = m * Board.BASE_RADIUS self.base_distance = m * Board.BASE_DISTANCE self.base_offset = m * Board.BASE_OFFSET self.base_inner_radius = m * Board.BASE_INNER_RADIUS self.center_outer_radius = m * Board.CENTER_OUTER_RADIUS self.center_inner_radius = m * Board.CENTER_INNER_RADIUS
class Game(Application): title = "Splodey Fun Times" max_ships = 400 def __init__(self): pygame.init() #self.screen_size = pygame.display.list_modes()[0] Application.__init__(self) #pygame.display.toggle_fullscreen() self.ships = ShipGroup(self.max_ships) self.xplos = ExplosionGroup() self.screen_bounds = bounds = self.screen.get_rect() dc_height = 40 self.dc_font = pygame.font.Font(None, 30) self.dc_bounds = Rect(0, bounds.height - dc_height, bounds.width, dc_height) self.game_bounds = Rect(0, 0, bounds.width, bounds.height - dc_height) spawn_area = self.game_bounds.inflate(-self.game_bounds.width/4, -self.game_bounds.height/4) self.spawners = [ TieSpawner(1000, self.ships, self.game_bounds, spawn_area), YWingSpawner(2000, self.ships, self.game_bounds) ] self.detonator = Detonator(self.game_bounds, self.xplos) for spawner in self.spawners: spawner.spawn() Ship.death_count = DeathCount() self.current_weapon = MiniExplosionsWeapon(self) def on_quit(self): Ship.death_count.write() def handle_event(self, event): pos = pygame.mouse.get_pos() if event.type == MOUSEBUTTONDOWN and event.button == 1: self.current_weapon.primarydown(pos) elif event.type == MOUSEBUTTONDOWN and event.button == 3: self.current_weapon.secondarydown(pos) elif event.type == MOUSEBUTTONUP and event.button == 1: self.current_weapon.primaryup(pos) elif event.type == MOUSEBUTTONUP and event.button == 3: self.current_weapon.secondaryup(pos) def update(self): dt = min(200, self.clock.get_time()) # cap dt # update all the spawners for spawner in self.spawners: spawner.update(dt) # update ships self.ships.update(dt) self.xplos.update(dt) self.xplos.explode_ships(self.ships) self.detonator.update(dt) if len(self.xplos) == 0: Ship.death_count.write() def draw(self, screen): screen.fill((0,0,0)) self.ships.draw(screen) self.xplos.draw(screen) # hud screen.fill((40,40,40), self.dc_bounds) # death count msg = format_int(Ship.death_count.val()) + " dead" text = self.dc_font.render(msg, True, (255,255,255), (40,40,40)) loc = text.get_rect() loc.centery = self.dc_bounds.centery loc.right = self.dc_bounds.right - 10 screen.blit(text, loc)
def generate(self): # Fill with grass self.fill_rect(-self.width/2-self.turret_project-10, -self.turret_project-10, self.width+2*(self.turret_project+10), self.height+2*(self.turret_project+10), grass) # Calculate valid space for buildings quad = Rect(-self.width/2+self.wallwidth+1, self.wallwidth, self.width-self.wallwidth*2-1, self.height-self.wallwidth*2) self.interior_space = set(self.get_rect(quad.inflate(2, 2))) self.fixed_rooms = [] # Draw outer walls self.fourwalls(self.width, self.height, self.turretsize, self.wallwidth, self.turret_project, self.gatesize, self.gatehouse_project, 4) # Keep dimensions k_gs = (self.gatesize + 2) % 4 if self.gatesize != 2 else 4 if self.height < 75: keep_style = KEEP_SIDE else: keep_style = random.choice(KEEP_STYLES) if keep_style == KEEP_CENTRE: k_w = (random.randint(int(self.width*0.3), int(self.width*0.5))/2)*2 + k_gs k_h = random.randint(int(self.height*0.3), int(self.height*0.5)) k_x, k_y = 0, (self.height-k_h)/2 side = 0 elif keep_style == KEEP_SIDE: k_w = (random.randint(int(self.width*0.4), int(self.width*0.6))/2)*2 + k_gs k_h = random.randint(int(self.height*0.4), int(self.height*0.6)) side = random.choice((-1, 1)) k_x, k_y = (self.width-k_w)/2*side, (self.height-k_h) # Draw keep self.translate(k_x, k_y) self.fourwalls(k_w, k_h, self.turretsize, self.wallwidth+1, self.turret_project, k_gs, 0, 4) self.translate(-k_x, -k_y) path_to_keep = self.get_path(0, 1, k_x, k_y, self.gatesize, k_gs) for p in path_to_keep: self.draw(p, path) self.expand(path_to_keep, 2) self.interior_space -= set(path_to_keep) margin = 6 if keep_style == KEEP_CENTRE: around_keep = Rect(-k_w/2-(self.wallwidth+margin), self.height/2-k_h/2-self.wallwidth-margin, k_w+(self.wallwidth+margin)*2, k_h+(self.wallwidth+margin)*2) self.interior_space -= set(self.get_rect(around_keep)) elif keep_style == KEEP_SIDE: if side == -1: left = -self.width/2+self.wallwidth width = k_w + margin else: left = self.width/2-self.wallwidth-k_w-margin width = k_w + margin around_keep = Rect(left, self.height/2-self.wallwidth-margin, width, self.height) self.interior_space -= set(self.get_rect(around_keep)) # Pick alternative entrance if keep_style == KEEP_CENTRE: alt_entrance_side = random.choice((LEFT, DOWN, RIGHT)) if alt_entrance_side == LEFT: exit_room = (-k_w/2, self.height/2) exit_room_space = Rect(-k_w/2-9, self.height/2-5, 10, 10) elif alt_entrance_side == RIGHT: exit_room = (k_w/2+1, self.height/2) exit_room_space = Rect(k_w/2, self.height/2-5, 10, 10) elif alt_entrance_side == DOWN: exit_room = (0, self.height/2 + k_h/2+1) exit_room_space = Rect(-5, self.height/2 + k_h/2, 10, 10) elif keep_style == KEEP_SIDE: if side == -1: exit_room = (-self.width/2+k_w+1, self.height - k_h/2) exit_room_space = Rect(-self.width/2+k_w+1, self.height - k_h/2 - 5, 10, 10) else: exit_room = (self.width/2-k_w, self.height - k_h/2) exit_room_space = Rect(self.width/2-k_w-9, self.height - k_h/2 - 5, 10, 10) self.interior_space |= set(self.get_rect(exit_room_space)) # Creaee buildings entrance_room = (self.gatesize/2+3, 10) graph = BuildingGraph([entrance_room, exit_room], self.fixed_rooms, self.interior_space, 10, 1) # Draw building walls for building in graph.buildings: if building[0] not in self.fixed_rooms: self.draw_points(self.get_outlines(building), wall) if building[0].width == 1 or building[0].height == 1: self.draw_points(self.get_rect(building[0]), grass) for door in graph.doors: self.draw(door, floor) Door(door, level=self.level) self.draw((self.gatesize/2+2, 10), floor) Door((self.gatesize/2+2, 10), level=self.level) self.lock_doors([x[0] for x in graph.buildings], graph.path)
class Game(Application): title = "Splodey Fun Times" max_ships = 400 def __init__(self): pygame.init() #self.screen_size = pygame.display.list_modes()[0] Application.__init__(self) #pygame.display.toggle_fullscreen() self.ships = ShipGroup(self.max_ships) self.xplos = ExplosionGroup() self.screen_bounds = bounds = self.screen.get_rect() dc_height = 40 self.dc_font = pygame.font.Font(None, 30) self.dc_bounds = Rect(0, bounds.height - dc_height, bounds.width, dc_height) self.game_bounds = Rect(0, 0, bounds.width, bounds.height - dc_height) spawn_area = self.game_bounds.inflate(-self.game_bounds.width / 4, -self.game_bounds.height / 4) self.spawners = [ TieSpawner(1000, self.ships, self.game_bounds, spawn_area), YWingSpawner(2000, self.ships, self.game_bounds) ] self.detonator = Detonator(self.game_bounds, self.xplos) for spawner in self.spawners: spawner.spawn() Ship.death_count = DeathCount() self.current_weapon = MiniExplosionsWeapon(self) def on_quit(self): Ship.death_count.write() def handle_event(self, event): pos = pygame.mouse.get_pos() if event.type == MOUSEBUTTONDOWN and event.button == 1: self.current_weapon.primarydown(pos) elif event.type == MOUSEBUTTONDOWN and event.button == 3: self.current_weapon.secondarydown(pos) elif event.type == MOUSEBUTTONUP and event.button == 1: self.current_weapon.primaryup(pos) elif event.type == MOUSEBUTTONUP and event.button == 3: self.current_weapon.secondaryup(pos) def update(self): dt = min(200, self.clock.get_time()) # cap dt # update all the spawners for spawner in self.spawners: spawner.update(dt) # update ships self.ships.update(dt) self.xplos.update(dt) self.xplos.explode_ships(self.ships) self.detonator.update(dt) if len(self.xplos) == 0: Ship.death_count.write() def draw(self, screen): screen.fill((0, 0, 0)) self.ships.draw(screen) self.xplos.draw(screen) # hud screen.fill((40, 40, 40), self.dc_bounds) # death count msg = format_int(Ship.death_count.val()) + " dead" text = self.dc_font.render(msg, True, (255, 255, 255), (40, 40, 40)) loc = text.get_rect() loc.centery = self.dc_bounds.centery loc.right = self.dc_bounds.right - 10 screen.blit(text, loc)
class InGame: """ A class that creates the buttons for the gui ======Public Attributes====== rect: The button object, with width, height and position colour: The colour of the button. text: The text on the button. on_click: The function that will exectute when the button has been pressed . font: The font of the text on the button gui: The gui for the game rows: The rows for the game cols: The columns for the game current_col: The current column padding: Padding to be used as spacing square_size: Baseline for token size mode: The mode of the game board: The board for the game grid_rect: A grid version of the board used for visuals opponent: The opponent for the game ======Private Attributes====== """ def __init__(self, gui, mode: Mode) -> None: """ Creates a button with attributes Note: If colour is none then the button is invisible on_click is a function that will exectute when the button has been pressed """ self.gui = gui self.rows = 7 self.cols = 6 self.padding = 10 self.square_size = 50 self.current_col = -1 self.mode = mode self.board = Board(self.rows, self.cols) self.grid_rect = Rect( 0, 0, self.cols * (self.square_size + 2 * self.padding), self.rows * (self.square_size + 2 * self.padding)) self.grid_rect.center = (400, 325) if (self.mode == Mode.Easy): self.opponent = AIEasy('R', self.board) elif (self.mode == Mode.Hard): self.opponent = AIHard('R', self.board) else: self.opponent = PlayerHuman("R", self.board) def display(self, screen: pygame.Surface) -> None: """ Displays the button with the text centered """ screen.fill(BACKGROUND_COLOR) self.draw_grid(screen) if self.current_col != -1: # Ghost self.draw_piece(screen, self.board.get_drop_y(self.current_col), self.current_col, self.get_ghost_color(self.board.get_whos_turn())) # Fill self.draw_piece(screen, -1, self.current_col, self.get_piece_color(self.board.get_whos_turn())) def draw_grid(self, screen) -> None: """ Draws the game board """ pygame.draw.rect(screen, OUTLINE_COLOR, self.grid_rect.inflate(2, 2)) pygame.draw.rect(screen, BOARD_COLOR, self.grid_rect) for col in range(self.cols): for row in range(self.rows): self.draw_piece( screen, row, col, self.get_piece_color(self.board.get_grid()[row][col])) def get_piece_color(self, player: Player) -> None: """ Returns piece colour of the given player """ if player == "R": return PLAYER1_COLOR if player == "Y": return PLAYER2_COLOR return EMPTY_COLOR def get_ghost_color(self, player) -> None: """ Returns the colour of the given ghost """ if player == "R": return PLAYER1_GHOST_COLOR if player == "Y": return PLAYER2_GHOST_COLOR return EMPTY_COLOR def draw_piece(self, screen, row, col, fill_color) -> None: """ Draws the coloured piece on the board """ pos = (col * (self.square_size + 2 * self.padding) + self.square_size // 2 + self.padding + self.grid_rect.x, row * (self.square_size + 2 * self.padding) + self.square_size // 2 + self.padding + self.grid_rect.y) # Outline gfxdraw.aacircle(screen, pos[0], pos[1], self.square_size // 2 + 1, OUTLINE_COLOR) # Fill gfxdraw.aacircle(screen, pos[0], pos[1], self.square_size // 2, fill_color) gfxdraw.filled_circle(screen, pos[0], pos[1], self.square_size // 2, fill_color) def update(self, event) -> None: if event.type == pygame.MOUSEBUTTONDOWN \ and (event.button == 1 or event.button == 3): if self.current_col != -1 and self.board.drop_piece( self.board.get_whos_turn(), self.current_col): result = self.board.is_connected() if result != " " or self.board.is_board_full(): print("WINNER", result) self.gui.goto_main_menu() if self.mode != Mode.Two_Player: opp_move = self.opponent.decision_function()[1] if self.current_col != -1 and self.board.drop_piece( \ self.board.get_whos_turn(), opp_move): result = self.board.is_connected() if result != " " or self.board.is_board_full(): print("WINNER", result) self.gui.goto_main_menu() elif event.type == pygame.MOUSEMOTION: if self.grid_rect.collidepoint(event.pos): self.current_col = int(( (event.pos[0] - self.grid_rect.x) / self.grid_rect.width) * self.cols) else: self.current_col = -1
class Dialog: def __init__(self, text, options=None): self.options = options if options is not None: self.selection = 0 else: self.selection = None self.fg = pygame.color.Color('white') self.bg = pygame.color.Color('blue') self.tr = pygame.color.Color('black') half = core.screen.get_width() * 4 / 5 self.rect = Rect(0,0,half,0) self.text = text self.font = pygame.font.Font(None, 20) self.split_text() self.render_text() self.window = core.wm.window(half,self.rect.height,'center','center') self.window.update = self.update self.window.handle_event = self.handle_event self.screen = self.window.image self.screen.set_colorkey(self.tr, RLEACCEL) self.rect = Rect(self.window.rect) self.rect.center = self.screen.get_rect().center r = self.rect.inflate(-6,-6) self.bgwin = core.wm.window(r.width,r.height,'center','center',z=3) self.bgwin.image.fill(self.bg) self.bgwin.image.set_alpha(128) self.borderwin = core.wm.window(self.rect.width,self.rect.height, \ 'center','center', z=2) self.borderwin.image.fill(self.tr) dialog.draw_round_border(self.borderwin.image,color=self.fg) self.borderwin.image.set_colorkey(self.tr, RLEACCEL) def hide(self): self.window.hide() self.bgwin.hide() self.borderwin.hide() def nop(self): pass def update(self): self.window.update = self.nop self.screen.fill(self.tr) draw_round_border(self.screen,color=self.fg) current_y = 10 for line in self.rendered: self.screen.blit(line, self.rect.move(10,current_y)) current_y = current_y + self.font.get_linesize() current_y = current_y + self.font.get_linesize() first_option_y = current_y for option in self.rendered_opts: self.screen.blit(option, self.rect.move(20,current_y)) current_y = current_y + self.font.get_linesize() if self.selection is not None: y_pos = first_option_y + self.selection * self.font.get_linesize() y_pos = y_pos + self.font.get_linesize() / 2 # Center it in line rect = self.rect.move(10, y_pos) center = (rect.left, rect.top) draw.circle(self.screen, self.fg, center, 5) def render_one_line(self, line): return self.font.render(line, True, self.fg).convert_alpha() def render_text(self): self.rendered = map(self.render_one_line, self.lines) if self.options is not None: self.rendered_opts = map(self.render_one_line, self.options) line_count = len(self.rendered) + len(self.rendered_opts) + 1 else: self.rendered_opts = [] line_count = len(self.rendered) self.rect.height = self.font.get_linesize() * line_count + 20 def split_text(self): self.lines = [] current = '' for chunk in re.split(' ', self.text): width, height = self.font.size(current + chunk + ' ') if width < self.rect.width - 10: current = current + chunk + ' ' else: self.lines.append(current) current = chunk + ' ' if len(current) > 1: self.lines.append(current) def selection_up(self): if self.selection is not None: if self.selection == 0: self.selection = len(self.options)-1 else: self.selection = self.selection - 1 self.window.update = self.update def selection_down(self): if self.selection is not None: if self.selection < len(self.options)-1: self.selection = self.selection + 1 else: self.selection = 0 self.window.update = self.update def run(self): self.window.show() self.window.focus() core.wm.run() core.wm.running = True self.dispose() core.game.save_data.map.focus() if self.selection is not None: return self.selection else: return None def handle_event(self, event): if event.type == PUSH_ARROW_EVENT or \ event.type == REPEAT_ARROW_EVENT: if core.event_bag.is_up(): self.selection_up() elif core.event_bag.is_down(): self.selection_down() elif event.type == PUSH_ACTION_EVENT: core.wm.running = False def dispose(self): self.rendered = None self.window.destroy() self.bgwin.destroy() self.borderwin.destroy() self.window.update = None
class Window(pygame.sprite.Sprite): def __init__(self, x, y, width, height): pygame.sprite.Sprite.__init__(self) self.image = Surface((width, height)).convert(32, pygame.RLEACCEL) self._windowskin = None self.rect = Rect(x, y, width, height) self.selected_rect = None self.widgets = [] @property def offset(self): return 16 @property def screen_pos(self): pos = self.rect.topleft return (pos[0] + self.offset, pos[1] + self.offset) @property def font(self): if not hasattr(self, '_font'): self._font = pygame.font.Font(None, 24) return self._font @font.setter def font(self, value): self._font = value def create_contents(self): w, h = self.rect.inflate(-2*self.offset, -2*self.offset).size self.contents = Surface((w, h)).convert_alpha() self.font_color = (255,255,255) def draw_text(self, x, y, text): bmp = self.font.render(text, 1, self.font_color) self.contents.blit(bmp, (x,y)) def draw_selected(self, destsurf): if self.selected_rect: rect = self.selected_rect.move(self.rect.topleft) selected_surf = destsurf.subsurface(rect) pygame.transform.scale(self._select_src, self.selected_rect.size, selected_surf) @property def windowskin(self): return self._windowskin @windowskin.setter def windowskin(self, value): self._windowskin = value self._background = self._windowskin.subsurface(0,0,64,64) self._cadre = self._windowskin.subsurface(64,0,64,64) self._select_src = self._windowskin.subsurface(64,64,32,32) w, h = self.rect.size pygame.transform.scale(self._background, (w, h), self.image) #draw the four corners on the window for src, dest in [[(0, 0), (0,0)], [(48, 0), (w-16, 0)], [(0, 48), (0, h-16)], [(48, 48), (w-16, h-16)]]: src_rect = Rect(src, (16, 16)) dest_rect = Rect(dest, (16, 16)) self.image.blit(self._cadre.subsurface(src_rect), dest) def draw(self, destsurf): destsurf.blit(self.image, self.rect) self.draw_selected(destsurf) destsurf.blit(self.contents, self.rect.move(self.offset, self.offset), self.contents_src) def add_widget(self, widget): self.widgets.append(widget) widget.parent = self widget.parent_offset = self.offset @property def contents_size(self): width, height = self.rect.inflate(-2*self.offset, -2*self.offset).size return width, height @property def contents_rect(self): "return the visible rect of contents" return self.rect.inflate(-2*self.offset, -2*self.offset) @property def contents_src(self): return self.contents.get_rect()
def update(self, mousePos: tuple, mouseButtons: tuple, availableSpace: Rect, focused: bool, interactable=True): resizeReady = [False, False, False, False] canvasMousePos = (mousePos[0] - self.position[0], mousePos[1] - self.position[1]) if mouseButtons[0]: if self.beingDragged: self.position = [mousePos[0] - self.drag_offset_x, mousePos[1] - self.drag_offset_y] elif not self.minimized and interactable: # Dragging logic and snapping logic if self.get_title_bar_rect().collidepoint(mousePos) and not self.beingDragged and focused and not any(self.beingResized): #the window can be dragged if the mouse is touching the title bar, if it's not already being dragged, if it's focused, and if it's not being resized self.set_being_dragged(mouseButtons[0], mousePos) if self.resizable: if self.maximized: self.unMaximize() elif mousePos[1] < availableSpace.top + 5: self.snap[Window.WINDOW_TOP] = True elif mousePos[0] < availableSpace.left + 5: self.snap[Window.WINDOW_LEFT] = True elif mousePos[0] > availableSpace.width - 5: self.snap[Window.WINDOW_RIGHT] = True else: self.snap = [False, False, False] else: # Snap only occurs after dragging has stopped self.beingDragged = False if self.snap[Window.WINDOW_TOP] and self.lastButtonState[0] and not mouseButtons[0]: self.maximize(availableSpace) self.snap[Window.WINDOW_TOP] = False elif self.snap[Window.WINDOW_LEFT] and self.lastButtonState[0] and not mouseButtons[0]: self.maximize(availableSpace.inflate(availableSpace.width // -2, 0)) self.position = [0, 0] self.snap[Window.WINDOW_LEFT] = False elif self.snap[Window.WINDOW_RIGHT] and self.lastButtonState[0] and not mouseButtons[0]: self.maximize(availableSpace.inflate(availableSpace.width // -2, 0)) self.position = [availableSpace.width // 2, 0] self.snap[Window.WINDOW_RIGHT] = False if focused and self.resizable: if not any(self.beingResized): for index, side in enumerate(self.get_hot_edges()): if side.collidepoint(mousePos): self.set_being_resized(True, index) break else: for index, being_resized in enumerate(self.beingResized): if being_resized == True: self.resize_to_mouse(mousePos, index) # Window control buttons if self.minimizeButtonRect.collidepoint(canvasMousePos) and mouseButtons[0] and not self.lastButtonState[0]: self.minimize() elif self.growButtonRect.collidepoint(canvasMousePos) and mouseButtons[0] and not self.lastButtonState[0] and self.resizable: if self.maximized: self.unMaximize() else: self.maximize(availableSpace) elif self.closeButtonRect.collidepoint(canvasMousePos) and mouseButtons[0] and not self.lastButtonState[0]: self.close() else: self.set_being_dragged(False) self.set_being_resized(False) if focused and not self.minimized: try: self.program.update(self.programMousePos(mousePos), mouseButtons) except: self.crash("Program %s crashed during update call..." % self.program.name) self.lastButtonState = mouseButtons return self.beingResized, self.beingDragged, resizeReady
screen.fill(WHITE) BLUE_BOX = Rect(50, 30, 220, 220) BLACK_BOX = Rect(290, 30, 220, 220) RED_BOX = Rect(530, 30, 220, 220) YELLOW_BOX = Rect(170, 140, 220, 220) GREEN_BOX = Rect(410, 140, 220, 220) pygame.draw.ellipse(screen, BLUE, BLUE_BOX, THICKNESS) pygame.draw.ellipse(screen, BLACK, BLACK_BOX, THICKNESS) pygame.draw.ellipse(screen, RED, RED_BOX, THICKNESS) pygame.draw.ellipse(screen, YELLOW, YELLOW_BOX, THICKNESS) pygame.draw.ellipse(screen, GREEN, GREEN_BOX, THICKNESS) c = 255,0,255 pygame.draw.arc(screen, WHITE, BLUE_BOX.inflate(12,12), radians(-20), radians(10), THICKNESS + 12) pygame.draw.arc(screen, BLUE, BLUE_BOX, radians(-20), radians(10), THICKNESS) pygame.draw.arc(screen, WHITE, BLACK_BOX.inflate(12,12), radians(240), radians(270), THICKNESS + 12) pygame.draw.arc(screen, WHITE, BLACK_BOX.inflate(12,12), radians(-20), radians(10), THICKNESS + 12) pygame.draw.arc(screen, BLACK, BLACK_BOX, radians(240), radians(270), THICKNESS) pygame.draw.arc(screen, BLACK, BLACK_BOX, radians(-20), radians(10), THICKNESS) pygame.draw.arc(screen, WHITE, RED_BOX.inflate(12,12), radians(240), radians(270), THICKNESS + 12) pygame.draw.arc(screen, RED, RED_BOX, radians(240), radians(270), THICKNESS) pygame.draw.arc(screen, WHITE, YELLOW_BOX.inflate(12,12), radians(60), radians(90), THICKNESS + 12) pygame.draw.arc(screen, WHITE, YELLOW_BOX.inflate(12,12), radians(150), radians(190), THICKNESS + 12) pygame.draw.arc(screen, YELLOW, YELLOW_BOX, radians(60), radians(90), THICKNESS) pygame.draw.arc(screen, YELLOW, YELLOW_BOX, radians(150), radians(190), THICKNESS)
screen.fill(WHITE) BLUE_BOX = Rect(50, 30, 220, 220) BLACK_BOX = Rect(290, 30, 220, 220) RED_BOX = Rect(530, 30, 220, 220) YELLOW_BOX = Rect(170, 140, 220, 220) GREEN_BOX = Rect(410, 140, 220, 220) pygame.draw.ellipse(screen, BLUE, BLUE_BOX, THICKNESS) pygame.draw.ellipse(screen, BLACK, BLACK_BOX, THICKNESS) pygame.draw.ellipse(screen, RED, RED_BOX, THICKNESS) pygame.draw.ellipse(screen, YELLOW, YELLOW_BOX, THICKNESS) pygame.draw.ellipse(screen, GREEN, GREEN_BOX, THICKNESS) c = 255, 0, 255 pygame.draw.arc(screen, WHITE, BLUE_BOX.inflate(12, 12), radians(-20), radians(10), THICKNESS + 12) pygame.draw.arc(screen, BLUE, BLUE_BOX, radians(-20), radians(10), THICKNESS) pygame.draw.arc(screen, WHITE, BLACK_BOX.inflate(12, 12), radians(240), radians(270), THICKNESS + 12) pygame.draw.arc(screen, WHITE, BLACK_BOX.inflate(12, 12), radians(-20), radians(10), THICKNESS + 12) pygame.draw.arc(screen, BLACK, BLACK_BOX, radians(240), radians(270), THICKNESS) pygame.draw.arc(screen, BLACK, BLACK_BOX, radians(-20), radians(10), THICKNESS) pygame.draw.arc(screen, WHITE, RED_BOX.inflate(12, 12), radians(240), radians(270), THICKNESS + 12) pygame.draw.arc(screen, RED, RED_BOX, radians(240), radians(270), THICKNESS)
class MapBase(Window): def __init__(self, width, height, default_tile_name='floor'): Window.__init__(self,None,10) self.save_data = SaveObject() self.tile_manager = TileManager() default_tile = self.tile_manager.get_tile(default_tile_name) self.tiles = [] for x in range(width): col = [] self.tiles.append(col) for y in range(height): location = MapLocation(self, (x,y), default_tile) col.append(location) self.width = width self.height = height tiles_x = core.screen.get_width() / 32 tiles_y = core.screen.get_height() / 32 self.dimentions = Rect(0,0,width,height) self.character = None self.entities = RenderEntity() self.non_passable_entities = RenderEntity() self.viewport = Rect(0,0,tiles_x,tiles_y) self.offset = Rect(0,0,0,0) self.map_tile_coverage = Rect(0,0,tiles_x+5,tiles_y+5) if self.map_tile_coverage.width > width: self.map_tile_coverage.width = width if self.map_tile_coverage.height > height: self.map_tile_coverage.height = height self.map_non_scroll_region = \ self.viewport.inflate(SCROLL_EDGE*-2,SCROLL_EDGE*-2) self.action_listeners = {} self.entry_listeners = {} self.movement_listeners = [] self.scrolling = False self.frame = 0 self.map_frames_dirty = [True,True,True,True] self.map_frames = [] self.heal_points = 0 self.regen_rate = 2000000000 self.sound = core.mixer.Sound('%s/sounds/beep.wav' % DATA_DIR) for f in range(4): #TODO Add non hardcoded values for buffer #TODO Make sure we don't make a larger surface than we need #TODO Ex: 5x5 map self.map_frames.append(Surface(((1+width) * TILE_SIZE, \ (1+height) * TILE_SIZE))) def __getstate__(self): dict = {} dict['width'] = self.width dict['height'] = self.height dict['offset.width'] = self.offset.width dict['offset.height'] = self.offset.height dict['save_data'] = self.save_data return dict def __setstate__(self, dict): if self.__class__.__name__ == 'MapBase': self.__init__(dict['width'],dict['height']) else: self.__init__() self.save_data = dict['save_data'] self.offset.width = dict['offset.width'] self.offset.height = dict['offset.height'] self.blur_events() def dispose(self): self.destroy() del self.tiles self.tile_manager.clear() del self.action_listeners del self.entry_listeners del self.movement_listeners self.entities.empty() self.non_passable_entities.empty() self.character.map = None def set_regen_rate(self, rate): self.regen_rate = rate def get(self, x, y): if x<0 or y<0: return None try: return self.tiles[x][y] except: return None def calculate_tile_coverage(self, viewable): if self.character is None: return coverage = self.map_tile_coverage coverage.center = self.character.pos view_scroll = viewable.inflate(8,8) coverage.clamp_ip(view_scroll) coverage.clamp_ip(self.dimentions) self.offset.left = (viewable.left - coverage.left) * TILE_SIZE self.offset.top = (viewable.top - coverage.top ) * TILE_SIZE if not self.map_non_scroll_region.collidepoint(self.character.pos): self.map_non_scroll_region = \ self.viewport.inflate(SCROLL_EDGE*-2,SCROLL_EDGE*-2) def set_location(self, loc, tile_name, walkable=True, tile_pos=None): x, y = loc location = self.get(x, y) tile = self.tile_manager.get_tile(tile_name, None, tile_pos) location.set_tile(tile) location.set_walkable(walkable) def place_character(self, character, pos, passable=False, direction=NORTH): self.character = character character.map = self character.can_trigger_actions = 1 if not self.viewport.collidepoint(pos): self.viewport.center = pos self.viewport.clamp_ip(self.dimentions) self.place_entity(character, pos, passable, direction) self.calculate_tile_coverage(self.viewport) def place_entity(self, entity, entity_pos, passable=False, direction=NORTH): entity.face(direction) entity.map = self entity.move_to(entity_pos) self.entities.add(entity) if not passable: self.non_passable_entities.add(entity) def add_entry_listener(self, x, y, listener): self.entry_listeners[ (x,y) ] = listener def add_movement_listener(self, listener): self.movement_listeners.append(listener) def update(self): """Invoked once per cycle of the event loop, to allow animation to update""" if self.character.entered_tile: self.character.entered_tile = False self.check_heal() if self.entry_listeners.has_key( self.character.pos ): self.entry_listeners[self.character.pos]() for listener in self.movement_listeners: listener() if self.scrolling: axis = self.scroll_axis diff = [0,0] diff[axis] = self.scroll_anchor - self.character.rect[axis] self.entities.scroll(diff) diff[axis] = diff[axis] * -1 self.offset[axis] = self.offset[axis] + diff[axis] if not self.character.moving: self.scrolling = False if self.is_left(): self.move_character(WEST) if self.is_right(): self.move_character(EAST) if self.is_up(): self.move_character(NORTH) if self.is_down(): self.move_character(SOUTH) if self.map_frames_dirty[self.frame]: self.build_current_frame() self.map_frames_dirty[self.frame] = False self.entities.update() def draw(self, blit): blit(self.map_frames[self.frame], (0,0), self.offset) self.entities.draw(blit) def build_current_frame(self): #TODO Decide if map_tile_coverage is the right name for this blit = self.map_frames[self.frame].blit rect = (self.frame * TILE_SIZE, 0, TILE_SIZE, TILE_SIZE) x = 0 y = 0 for col in range(self.map_tile_coverage.left, \ self.map_tile_coverage.right): column = self.tiles[col] for row in range(self.map_tile_coverage.top, \ self.map_tile_coverage.bottom): blit(column[row].tile, (x,y), rect) y = y + TILE_SIZE x = x + TILE_SIZE y = 0 def init(self): self.offset.width = core.screen.get_rect().width self.offset.height = core.screen.get_rect().height self.entities.run_command('enter_map') def handle_event(self,event): if event.type == PUSH_ACTION_EVENT: self.character_activate() if event.type == PUSH_ACTION2_EVENT: menu.run_main_menu() if event.type == QUIT_EVENT: core.wm.running = False def check_heal(self): self.heal_points = self.heal_points + 1 if self.heal_points >= self.regen_rate: core.game.save_data.hero.regenerate() self.heal_points = 0 def character_activate(self): if not self.character.moving: target = add(self.character.pos,MOVE_VECTORS[self.character.facing]) entities = self.non_passable_entities.entity_collisions(target) for e in entities: e.activate() def move_character(self, dir): self.character.move(dir) if not self.scrolling: if self.character.moving: nsr = self.map_non_scroll_region x,y = self.character.pos if dir % 2 == 0: # North or south if y < nsr.bottom and \ y >= nsr.top: return if y < SCROLL_EDGE or \ y >= self.height - SCROLL_EDGE: return self.scroll_axis = 1 else: # East or west if x < nsr.right and \ x >= nsr.left: return if x < SCROLL_EDGE or \ x >= self.width - SCROLL_EDGE: return self.scroll_axis = 0 self.scrolling = True vector = MOVE_VECTORS[dir] self.map_non_scroll_region.move_ip(vector) old_viewport = self.viewport self.viewport = old_viewport.move(vector) self.scroll_anchor = self.character.rect[self.scroll_axis] if not self.map_tile_coverage.contains(self.viewport): self.calculate_tile_coverage(old_viewport) self.dirty() def dirty(self): for f in range(4): self.map_frames_dirty[f] = True def move_ok(self, target_pos, character): x, y = target_pos target = self.get(x,y) if target is not None and target.is_walkable(): entities = self.non_passable_entities.entity_collisions(target_pos) if len(entities) > 0: if character.can_trigger_actions: for e in entities: e.touch() else: for e in entities: if e.can_trigger_actions: character.touch() return 0 else: self.sound.play() return 1 else: return 0 def get_tiles_from_ascii(self,ascii,tile_map): for y in range(self.height): line = ascii[y] for x in range(self.width): c = line[x] args = tile_map[c] pos = None if len(args) > 1: pos = args[1] self.set_location( (x,y), args[0], tile_map['walkable'].find(c)!=-1, pos )
class Element: def __init__(self, rect = None, layer=0, visible=True, active=True): if rect is not None: self.rect = rect else: self.rect = Rect((0, 0), (0, 0)) self.old_active = None self.old_visible = None self.old_rect = None self.layer = layer self.visible = visible self.active = active self.dirty_list = [] return def draw(self, screen): return def update(self): def rects_are_synchronized(): r = self.rect o = self.old_rect return (r.x, r.y, r.size) == (o.x, o.y, o.size) if self.old_rect is None: self.dirty_list.append(self.rect.inflate(5, 5)) self.old_rect = Rect((self.rect.x, self.rect.y), (self.rect.w, self.rect.h)) elif not rects_are_synchronized(): self.refresh() self.old_rect = Rect((self.rect.x, self.rect.y), (self.rect.w, self.rect.h)) self.update_active_and_visible() self.remove_duplicate_dirty_rects() return def remove_duplicate_dirty_rects(self): unique_list = [] for dirty_rect in self.dirty_list: if not dirty_rect in unique_list: unique_list.append(dirty_rect) self.dirty_list = unique_list return def update_active_and_visible(self): if self.old_visible is None or self.old_visible != self.visible \ or self.old_active is None or self.old_active != self.active: self.refresh() self.old_active = self.active self.old_visible = self.visible return def refresh(self): if self.old_rect is not None: self.dirty_list.append(self.old_rect.inflate(5, 5)) self.dirty_list.append(self.rect.inflate(5, 5)) return def suspend(self): self.active = False self.visible = False return def restore(self): self.active = True self.visible = True return def decorated_element(self): return self def is_mouse_over(self): return self.visible and self.rect.collidepoint(mouse_position()) def is_mouse_clicking(self): return self.visible and is_left_mouse_clicked() and self.is_mouse_over()
def get_aa_round_rect(size, radius, color): surface = Surface(size, flags=SRCALPHA).convert_alpha() rect = Rect((0, 0), size) color = Color(*color) alpha = color.a color.a = 0 rectangle = Surface(size, SRCALPHA) #here 5 is an arbitrary multiplier, we will just rescale later circle = Surface([min(size) * 5, min(size) * 5], SRCALPHA) draw.ellipse(circle, (0,0,0), circle.get_rect(), 0) circle = transform.smoothscale(circle, (2*radius, 2*radius)) #now circle is just a small circle of radius #blit topleft circle: radius_rect = rectangle.blit(circle, (0, 0)) #now radius_rect = Rect((0, 0), circle.size), rect=Rect((0, 0), size) #blit bottomright circle: radius_rect.bottomright = rect.bottomright #radius_rect is growing rectangle.blit(circle, radius_rect) #blit topright circle: radius_rect.topright = rect.topright rectangle.blit(circle, radius_rect) #blit bottomleft circle: radius_rect.bottomleft = rect.bottomleft rectangle.blit(circle, radius_rect) #black-fill of the internal rect rectangle.fill((0, 0, 0), rect.inflate(-radius_rect.w, 0)) rectangle.fill((0, 0, 0), rect.inflate(0, -radius_rect.h)) #fill with color using blend_rgba_max rectangle.fill(color, special_flags=BLEND_RGBA_MAX) #fill with alpha-withe using blend_rgba_min in order to make transparent #the rectangle.fill((255, 255, 255, alpha), special_flags=BLEND_RGBA_MIN) surface.blit(rectangle, rect.topleft) return surface