class Projectile(sprite.Sprite): def __init__(self, pos, tower, target, image, speed, damage): sprite.Sprite.__init__(self) self.pos = pos self.tower = tower self.target = target self.image = image self.speed = speed self.damage = damage self.rect = Rect(self.pos, self.image.get_size()) self.angle = atan2((self.target.rect.centery - self.rect.centery), (self.target.rect.centerx - self.rect.centerx)) self.x_speed = cos(self.angle) * self.speed self.y_speed = sin(self.angle) * self.speed def update(self, monsters, screen): # Kills the projectile if it doesn't get there before the target dies if self.target is None: self.kill() return # Calculates where the projectile needs to go self.angle = atan2((self.target.rect.centery - self.rect.centery), (self.target.rect.centerx - self.rect.centerx)) distance = hypot(self.target.rect.centerx - self.rect.centerx, self.target.rect.centery - self.rect.centery) mod = self.target.speed + 1 # Calculates the X and Y speed xspeed, yspeed = cos(self.angle) * mod, sin(self.angle) * mod self.x_speed = xspeed + xspeed / abs(xspeed) if xspeed != 0 else 0 self.y_speed = yspeed + yspeed / abs(yspeed) if yspeed != 0 else 0 # If the projectile is within range, it hit the target if abs(self.rect.centerx - self.target.rect.centerx) <= 20: if abs(self.rect.centery - self.target.rect.centery) <= 20: self.do_damage(monsters) self.kill() else: self.rect.move_ip((self.x_speed, self.y_speed)) else: self.rect.move_ip((self.x_speed, self.y_speed)) # Destroys the projectile if it goes off screen if not screen.contains(self.rect): self.kill() def do_damage(self, monsters): for monster in monsters: # Does damage to the target, and adds kills and money rewards if it dies, also adds damage_done to tower if monster == self.target: # dmg_result returns (None/Value of monster, Damage done by projectile) dmg_result = monster.damage(self.damage) if dmg_result[0] is not None: self.tower.kills += 1 self.tower.turn_yield += dmg_result[0] self.tower.damage_done += dmg_result[1] break
class Projectile(sprite.Sprite): def __init__(self, pos, tower, target, image, speed, damage): sprite.Sprite.__init__(self) self.pos = pos self.tower = tower self.target = target self.image = image self.speed = speed self.damage = damage self.rect = Rect(self.pos, self.image.get_size()) self.angle = atan2((self.target.rect.centery-self.rect.centery), (self.target.rect.centerx-self.rect.centerx)) self.x_speed = cos(self.angle)*self.speed self.y_speed = sin(self.angle)*self.speed def update(self, monsters, screen): # Kills the projectile if it doesn't get there before the target dies if self.target is None: self.kill() return # Calculates where the projectile needs to go self.angle = atan2((self.target.rect.centery-self.rect.centery), (self.target.rect.centerx-self.rect.centerx)) distance = hypot(self.target.rect.centerx - self.rect.centerx, self.target.rect.centery - self.rect.centery) mod = self.target.speed+1 # Calculates the X and Y speed xspeed, yspeed = cos(self.angle)*mod, sin(self.angle)*mod self.x_speed = xspeed + xspeed/abs(xspeed) if xspeed != 0 else 0 self.y_speed = yspeed + yspeed/abs(yspeed) if yspeed != 0 else 0 # If the projectile is within range, it hit the target if abs(self.rect.centerx - self.target.rect.centerx) <= 20: if abs(self.rect.centery - self.target.rect.centery) <= 20: self.do_damage(monsters) self.kill() else: self.rect.move_ip((self.x_speed, self.y_speed)) else: self.rect.move_ip((self.x_speed, self.y_speed)) # Destroys the projectile if it goes off screen if not screen.contains(self.rect): self.kill() def do_damage(self, monsters): for monster in monsters: # Does damage to the target, and adds kills and money rewards if it dies, also adds damage_done to tower if monster == self.target: # dmg_result returns (None/Value of monster, Damage done by projectile) dmg_result = monster.damage(self.damage) if dmg_result[0] is not None: self.tower.kills += 1 self.tower.turn_yield += dmg_result[0] self.tower.damage_done += dmg_result[1] break
class Thumbnail(): """Thumbnail that has a surface, with a background color set""" def __init__(self, width=64): self.surface_thumb = Rect(0,0,width,width) self.rect_thumb = Rect(0,0,width,width) # ex [1] the standard pygame.Color("white") , same as Color("#ffffff") self.color_bg = Color("white") # ex [2] random color with a filter: self.color_bg = random_color('light') self.color_border = random_color('dark') # create empty surface; fill it self.surface_thumb = pygame.Surface([width, width]) self.surface_thumb.fill(self.color_bg) def fill(self): """clear thumb.""" self.surface_thumb.fill(self.color_bg) def move(self, x, y): """move all rects/images together""" self.rect_thumb.move_ip(x,y) def draw(self, screen): screen.blit(self.surface_thumb, self.rect_thumb) def __str__(self): return "<Thumbnail( bg={bg}, rect_thumb={rect_thumb} >".format(bg=self.color_bg, rect_thumb=self.rect_thumb)
class Platform(Sprite): def __init__(self, init_pos, play_area_rect: Rect, *groups): super().__init__(*groups) self._play_area_rect = play_area_rect self._shift_speed = 5 self._speed = [0, 0] self._init_pos = init_pos self.rect = Rect(init_pos[0], init_pos[1], 40, 5) self.image = self._create_surface() def _create_surface(self): surface = Surface((self.rect.width, self.rect.height)) surface.fill((66, 226, 126)) # Green return surface @property def pos(self): return self.rect.topleft def reset(self): self.rect.topleft = self._init_pos def move(self, move_action: PlatformAction): if (move_action == PlatformAction.MOVE_LEFT and self.rect.left > self._play_area_rect.left): self._speed[0] = -self._shift_speed elif (move_action == PlatformAction.MOVE_RIGHT and self.rect.right < self._play_area_rect.right): self._speed[0] = self._shift_speed else: self._speed[0] = 0 self.rect.move_ip(*self._speed)
def drawStacks(stacks): __screen.fill(Color(50, 50, 150, 255)) stackWidth = 15 stack = Rect(0 - stackWidth / 2, 90, stackWidth, 310) disk = None for l in stacks: stack.move_ip(640 / 4, 0) draw.rect(__screen, BROWN, stack) for i, disk in enumerate(l, start=1): diskWidth = 640 / 5 * (float(disk) / float(__maxSize)) + stackWidth * 2 disk = Rect((stack.left + stackWidth / 2) - diskWidth / 2, 400 - __diskHeight * (i), diskWidth, __diskHeight) draw.rect(__screen, RED, disk) draw.rect(__screen, BLACK, disk, 2) disk.width /= 10 disk.height -= 6 disk.move_ip(diskWidth / 6, 3) draw.rect(__screen, WHITE, disk) draw.rect(__screen, GREEN, Rect(0, 400, 640, 80)) display.flip() handleEvents()
class Ball(): ball_diameter = 20 ball_move_x = 4 ball_move_y = 4 def __init__(self, screen, color): self.screen = screen self.color = color self.pos_x = self.screen.get_height() / 2 self.pos_y = self.screen.get_width() / 2 self.rect = Rect(self.pos_x, self.pos_y, Ball.ball_diameter, Ball.ball_diameter) self.move_x = Ball.ball_move_x self.move_y = Ball.ball_move_y self.ball = draw_ellipse(self.screen, self.color, self.rect) def draw_ball(self): self.ball = draw_ellipse(self.screen, self.color, self.rect) def move_ball(self): if self.rect.top > (self.screen.get_height() - self.rect.height) or self.rect.top < 0: self.move_y *= -1 if self.rect.left > (self.screen.get_width() - self.rect.width) or self.rect.left < 0: self.move_x = 0 self.move_y = 0 print("Game Over") self.rect.move_ip(0, self.move_y) self.rect.move_ip(self.move_x, 0) def racket_shoot(self): self.move_x *= -1 self.rect.move_ip(self.move_x * 2, 0)
def __initPosition(self, windowSize: tuple, playerId: int) -> math.Vector2: rect = Rect(0, 0, windowSize[0] / 2, windowSize[1] / 2) if (0 == playerId): return math.Vector2(rect.center) else: rect.move_ip(windowSize[0] / 2, windowSize[1] / 2) return math.Vector2(rect.center)
def __updateTankSurface(self, surface: Surface, color: Color, direction: math.Vector2): surface.fill((0, 0, 0, 0)) # rect inside surface surfaceSize = surface.get_size() surfaceRect = Rect(0, 0, surfaceSize[0], surfaceSize[1]) tankRect = Rect(0, 0, self.size[0], self.size[1]) diff = math.Vector2(surfaceRect.center) - math.Vector2(tankRect.center) tankRect.move_ip(diff) temp = surface.copy() draw.rect(temp, color, tankRect) # apply tank direction to surface degree = -math.Vector2(0, 1).angle_to(direction) temp = transform.rotate(temp, degree) # temp was enlarged by rotate (wtf): # calculate diff so that temp surface is positioned outside # of the destination surface below tempRectSize = temp.get_size() diff = math.Vector2(tempRectSize) - math.Vector2(surfaceSize) # copy back wanted portion from rotation surface.blit(temp, -diff / 2)
class Thumbnail(): """Thumbnail that has a surface, with a background color set""" def __init__(self, width=64): self.surface_thumb = Rect(0, 0, width, width) self.rect_thumb = Rect(0, 0, width, width) # ex [1] the standard pygame.Color("white") , same as Color("#ffffff") self.color_bg = Color("white") # ex [2] random color with a filter: self.color_bg = random_color('light') self.color_border = random_color('dark') # create empty surface; fill it self.surface_thumb = pygame.Surface([width, width]) self.surface_thumb.fill(self.color_bg) def fill(self): """clear thumb.""" self.surface_thumb.fill(self.color_bg) def move(self, x, y): """move all rects/images together""" self.rect_thumb.move_ip(x, y) def draw(self, screen): screen.blit(self.surface_thumb, self.rect_thumb) def __str__(self): return "<Thumbnail( bg={bg}, rect_thumb={rect_thumb} >".format( bg=self.color_bg, rect_thumb=self.rect_thumb)
class Ball(BeBarBallSprite): def __init__(self, screen, position=None): # 继承__init__ BeBarBallSprite.__init__(self, screen) # 属性 self.screen = screen self.init_speed = BALL_INIT_SPEED # 最大ball速度 self.max_speed = BALL_MAX_SPEED # ball每次x速度增长 self.speed_increase = BALL_INCREASE_SPEED self.init_position = BALL_INIT_POSITION self.rad = BALL_RAD # 当前方向 self.direction = [random.choice((-1, 1)), random.choice((-1, 1))] # 当前速度 self.speed = [self.init_speed[0], random.randrange(0, self.init_speed[1])] # 自身矩形 self.rect = Rect(0, 0, self.rad, self.rad) if position is not None: self.rect.center = position self.init_position = position else: self.rect.center = self.init_position # 更新函数,每次主循环中更新 def update(self): # 检测与上下边界碰撞,碰撞则y速度取反 if self.rect.top < 0 and self.direction[1] < 0: self.direction[1] = -self.direction[1] elif self.rect.bottom > self.screen.get_height() and self.direction[1] > 0: self.direction[1] = -self.direction[1] # 保持移动 self.rect.move_ip((self.speed[0] * self.direction[0], self.speed[1] * self.direction[1])) if self.speed[1] > self.max_speed[1]: self.speed[1] = self.max_speed[1] if self.speed[1] < 0: self.speed[1] = - self.speed[1] self.direction[1] = - self.direction[1] # 碰撞检测函数 def hit(self, target): # 取自身的矩形大小 return self.rect.colliderect(target.rect) # 重置速度与位置 def reset(self): # 回到初始位置与速度 self.speed = [self.init_speed[0], random.randrange(0, self.init_speed[1])] self.direction = [random.choice((-1, 1)), random.choice((-1, 1))] self.rect.center = self.init_position def blit(self): circle(self.screen, COLOR_BALL, self.rect.center, self.rad, 0)
def test_move_ip( self ): r = Rect( 1, 2, 3, 4 ) r2 = Rect( r ) move_x = 10 move_y = 20 r2.move_ip( move_x, move_y ) expected_r2 = Rect(r.left+move_x,r.top+move_y,r.width,r.height) self.assertEqual( expected_r2, r2 )
def test_move_ip(self): r = Rect(1, 2, 3, 4) r2 = Rect(r) move_x = 10 move_y = 20 r2.move_ip(move_x, move_y) expected_r2 = Rect(r.left + move_x, r.top + move_y, r.width, r.height) self.assertEqual(expected_r2, r2)
class Camera(object): """Manages current view rectangle and provides methods to convert coordinates from/to screen/map coordinate systems.""" MOVE_SPEED = 0.25 def __init__(self, view_size): self.view_rect = Rect((0, 0), view_size) self.move_event = EventManager.new_event_code() self._moving = False self._move_vector = [0, 0] self._last_update = 0 def update(self, time_passed): """Update camera. Execute movement and such""" if self._moving: delta = util.vector_mul(util.vector_normalize(self._move_vector), Camera.MOVE_SPEED * time_passed) self.view_rect.move_ip(delta) self._post_move_event() def set_move(self, x=None, y=None): if x is not None: self._move_vector[0] = x if y is not None: self._move_vector[1] = y self._moving = True def stop_moving(self): self._move_vector = [0, 0] self._moving = False def move_to(self, x, y): self.view_rect.x = x self.view_rect.y = y self._post_move_event() def rect_to_screen(self, rect): return rect.move(-self.view_rect.x, -self.view_rect.y) def rect_to_map(self, rect): return rect.move(self.view_rect.x, self.view_rect.y) def point_to_screen(self, p): return p[0] - self.view_rect.x, p[1] - self.view_rect.y def point_to_map(self, p): return p[0] + self.view_rect.x, p[1] + self.view_rect.y def _post_move_event(self): EventManager.post(self.move_event, cam=self)
def _update (self, col, row, tile_type_id, tile_rect=None): if self._cache_graphic: if tile_type_id in self._cache: g = self._cache[tile_type_id] else: g = self._type_to_graphic(tile_type_id) self._cache[tile_type_id] = g else: g = self._type_to_graphic(tile_type_id) dest = self._orig_sfc if tile_rect is None: tile_rect = self.grid.tile_rect(col, row) if isinstance(g, (Graphic, pg.Surface, basestring)): g = (g,) if (g is not None and isinstance(g[0], (Graphic, pg.Surface, basestring))): sfc = g[0] if isinstance(sfc, basestring): sfc = self._load_img(sfc) elif isinstance(sfc, Graphic): sfc = sfc.surface if len(g) == 1: alignment = rect = None else: if isinstance(g[1], int) or len(g[1]) == 2: alignment = g[1] rect = None else: alignment = None rect = g[1] if len(g) == 3: if rect is None: rect = g[2] else: alignment = g[2] if alignment is None: alignment = 0 if rect is None: rect = sfc.get_rect() # clip rect to fit in tile_rect dest_rect = Rect(rect) dest_rect.center = tile_rect.center fit = dest_rect.clip(tile_rect) rect = Rect(rect) rect.move_ip(fit.x - dest_rect.x, fit.y - dest_rect.y) rect.size = dest_rect.size # copy rect to tile_rect with alignment pos = gameutil.align_rect(rect, tile_rect, alignment) dest.blit(sfc, pos, rect) else: if g is None: g = (0, 0, 0, 0) # now we have a colour dest.fill(gameutil.normalise_colour(g), tile_rect) return tile_rect
class Marquee(BaseAnimation): """ This class animates a surface like a marquee. Nothing fancy, just scrolls it back and forth, takes a pause when returning to ground zero. This is just intended as a demo for testing the rendering. """ smoothfactor = 0.1 image = None def __init__(self, rectstyle, surface, step_x=1, fps=25, init_sleep=-1, bg_update=True, bg_wait=False, bg_redraw=False): BaseAnimation.__init__(self, rectstyle, fps, bg_update, bg_wait, bg_redraw) self.image = surface self.step_x = step_x self.w, self.h = surface.get_size() self.animrect = Rect((0, 0, self.rect.width, self.rect.height)) def draw(self): if self.w < self.rect.width: self.surface.blit(self.image, (0, 0)) return ### move and blit self.animrect.move_ip(int(self.step_x * self.smoothfactor), 0) self.surface.blit(self.image, (0, 0), self.animrect) ### recalculate for next pass if self.smoothfactor < float(1): self.smoothfactor *= 1.1 if self.smoothfactor > float(1): self.smoothfactor = 1 # at bounds, change direction if self.animrect.right > self.w or self.animrect.left < 0: self.smoothfactor = 0.08 if self.step_x < 0: self.animrect.left = 0 self.sleep = True else: self.animrect.right = self.w self.step_x = -self.step_x
def cache_inventory(self): '''Making the assumption that we won't really have that many items''' self.inventory_text = [] add = self.inventory_text.append text = self.render_text loc = Rect(375,50,0,0) for i in self.inventory: add(text(i.get_name(),loc)) loc.move_ip(0,25) if len(self.inventory_text) == 0: add(text("Your inventory is empty",loc.move(30,0)))
def cache_stats(self): self.stats_text = [] add = self.stats_text.append text = self.render_text hero = core.game.save_data.hero.get label = Rect( 30,50,120,0) value = Rect(160,50, 0,0) for s in self.character_fields: add(text(s.capitalize(),label,align='right')) add(text(str(hero(s)),value)) label.move_ip(0,25) value.move_ip(0,25)
class Marquee(BaseAnimation): """ This class animates a surface like a marquee. Nothing fancy, just scrolls it back and forth, takes a pause when returning to ground zero. This is just intended as a demo for testing the rendering. """ smoothfactor = 0.1 image = None def __init__( self, rectstyle, surface, step_x=1, fps=25, init_sleep=-1, bg_update=True, bg_wait=False, bg_redraw=False ): BaseAnimation.__init__(self, rectstyle, fps, bg_update, bg_wait, bg_redraw) self.image = surface self.step_x = step_x self.w, self.h = surface.get_size() self.animrect = Rect((0, 0, self.rect.width, self.rect.height)) def draw(self): if self.w < self.rect.width: self.surface.blit(self.image, (0, 0)) return ### move and blit self.animrect.move_ip(int(self.step_x * self.smoothfactor), 0) self.surface.blit(self.image, (0, 0), self.animrect) ### recalculate for next pass if self.smoothfactor < float(1): self.smoothfactor *= 1.1 if self.smoothfactor > float(1): self.smoothfactor = 1 # at bounds, change direction if self.animrect.right > self.w or self.animrect.left < 0: self.smoothfactor = 0.08 if self.step_x < 0: self.animrect.left = 0 self.sleep = True else: self.animrect.right = self.w self.step_x = -self.step_x
class Ball(Sprite): def __init__(self, init_pos, play_area_rect: Rect, *groups): super().__init__(*groups) self._play_area_rect = play_area_rect self._speed = [7, 7] # (x, y) self._init_pos = init_pos self.rect = Rect(init_pos[0], init_pos[1], 5, 5) self.image = self._create_surface() def _create_surface(self): surface = pygame.Surface((self.rect.width, self.rect.height)) surface.fill((44, 185, 214)) # Blue return surface @property def pos(self): return self.rect.topleft def reset(self): self.rect.topleft = self._init_pos self._speed = [7, 7] def move(self): self.rect.move_ip(self._speed) def check_bouncing(self, platform: Platform): if physics.collide_or_tangent(self, platform): physics.bounce_off_ip(self.rect, self._speed, platform.rect, platform._speed) physics.bounce_in_box(self.rect, self._speed, self._play_area_rect) def check_hit_brick(self, group_brick: pygame.sprite.RenderPlain) -> int: hit_bricks = pygame.sprite.spritecollide(self, group_brick, 1, \ physics.collide_or_tangent) if len(hit_bricks) > 0: # XXX: Bad multiple collision bouncing handling if len(hit_bricks) == 2 and \ hit_bricks[0].rect.y == hit_bricks[1].rect.y: combined_rect = hit_bricks[0].rect.union(hit_bricks[1].rect) physics.bounce_off_ip(self.rect, self._speed, combined_rect, (0, 0)) else: physics.bounce_off_ip(self.rect, self._speed, hit_bricks[0].rect, (0, 0)) return len(hit_bricks)
class RoadPiece(object): def __init__(self, _img, _x, _y): self.img = _img self.bounds = Rect(_x, _y, self.img.get_width(), self.img.get_height()) self.alive = True self.road_speed = DIFFICULTY def render(self, _screen): _screen.blit(self.img, self.bounds) def update(self, _dt): self.bounds.move_ip(0, self.road_speed) if self.bounds.top > HEIGHT: self.alive = False
class Enemy(object): def __init__(self, _x, _img, _type): self.img = _img self.bounds = Rect(_x, -_img.get_height(), self.img.get_width(), self.img.get_height()) self.alive = True self.speed = DIFFICULTY - 2 self.vx = 0 self.type = _type def update(self, _dt): self.bounds.move_ip(self.vx, self.speed) def render(self, _screen): _screen.blit(self.img, self.bounds)
class Player0(BaseSprite): def __init__(self, coords, image): super(Player, self).__init__(coords, image) self.isfalling = False self.bottom_rect = None self.place(self.rect.left, self.rect.top) self.vector = np.zeros(2, dtype=np.int) def place(self, left, top): self.rect = Rect(left, top, self.rect.width, self.rect.height) t = self.rect.width * 8 // 25 self.bottom_rect = Rect(left + t, self.rect.bottom, self.rect.width - 2 * t, 5) def update(self, dx, dy): super(Player, self).update(dx, dy) self.bottom_rect.move_ip(dx, dy)
class Paddle: background_color = (0, 0, 0) foreground_color = (255, 255, 255) def __init__(self, x, y, width, height, surface): self.rect = Rect(x, y, width, height) self.surface = surface def move(self, dx): self.undraw() self.rect.move_ip(dx, 0) self.draw() def draw(self): pygame.draw.rect(self.surface, Paddle.foreground_color, self.rect, 0) def undraw(self): pygame.draw.rect(self.surface, Paddle.background_color, self.rect, 0)
class Player: def __init__(self, x, y, speed=5): self.x = x self.y = y self.color = PLAYER_COLOR self.speed = 5 self.dir = 0 self.score = 0 self.size = {'width': 3, 'height': 80} self.rect = Rect((x, y, self.size['width'], self.size['height'])) def update(self): self.rect.move_ip(0, self.speed * self.dir) if (self.rect.y < 0): self.rect.y = 0 if (self.rect.y > H - self.size['height']): self.rect.y = H - self.size['height'] def stop(self): self.dir = 0 def changeDir(self, up=False): if (up): self.dir = -1 else: self.dir = 1 def goal(self): self.score += 1 def move(self, x, y): self.rect.x = x self.rect.y = y def draw(self, sc): draw.rect(sc, self.color, self.rect)
class Ball(): def __init__(self, screen, x, y, speed = 4, angle = 45): self.screen = screen self.rect = Rect(x, y, 20, 20) self.moving = False self.moving_right = True self.moving_up = angle > 0 self.speed = speed self.angle = math.pi * abs(angle)/180 def is_offscreen(self): return not self.screen.get_rect().colliderect(self.rect) def step(self): if not self.moving: return dx = int(round(math.cos(self.angle) * self.speed)) dy = int(round(math.sin(self.angle) * self.speed)) if not self.moving_right: dx *= -1 if self.moving_up: dy *= -1 self.rect.move_ip(dx, dy) if self.rect.y < 0 or self.rect.y > 460: self.bounce_y() return True return False def bounce_x(self): self.moving_right = not self.moving_right def bounce_y(self): self.moving_up = not self.moving_up def display(self): pygame.draw.rect(self.screen, WHITE, self.rect)
def move(self, step, carrect: pygame.Rect, PictureArr, sc): """Изменение координат машинки""" if self.isLive: dx = self.v * math.cos(self.angle)*step dy = -self.v * math.sin(self.angle)*step self.dx_error += dx - int(dx) self.dy_error += dy - int(dy) self.x += dx self.y += dy if abs(self.dx_error)>=1: dx+=int(self.dx_error) self.dx_error-=int(self.dx_error) if abs(self.dy_error)>=1: dy+=int(self.dy_error) self.dy_error-=int(self.dy_error) carrect.move_ip(dx, dy) distance = [] for laser in self.arr_laser: distance.append(laser.move(PictureArr, self, sc)) return distance
class Zombie: def __init__(self, size, position, _range): self.size = size self.position = position self.xposition = (position[0] + size[0] // 2), (position[1] + size[1] // 2) self._range = _range self.rect = Rect(self.position, self.size) def findAngle(self, oposition, _range): angles = [tan(2 * pi / 12 * i) for i in range(12)] tangle = aprox(self.xposition[1] - oposition[1]) / aprox( self.xposition[0] - oposition[0]) if self.xposition[0] != oposition[0] else False if tangle == False and type(tangle) == bool: return False return (min(angles, key=lambda i: (tangle - i)**2)) def move(self, oposition): ang = self.findAngle(oposition, self._range) #if ang == 0: print("ta indo") if type(ang) == bool: if oposition[1] - self.xposition[1] > 0: self.position[1] += self._range self.rect.move_ip(0, self._range) elif oposition[1] - self.xposition[1] < 0: self.position[1] -= self._range self.rect.move_ip(0, -self._range) self.xposition = (self.position[0] + self.size[0] // 2), (self.position[1] + self.size[1] // 2) else: #print(ang) if oposition[0] - self.xposition[0] > 0: self.position = [ self.position[0] + self._range, (round(ang * (self._range)) + self.position[1]) ] self.rect.move_ip(self._range, round(ang * (self._range))) elif oposition[0] - self.xposition[0] < 0: self.position = [ self.position[0] - self._range, (round(ang * (-self._range)) + self.position[1]) ] self.rect.move_ip(-self._range, round(ang * (-self._range))) self.xposition = (self.position[0] + self.size[0] // 2), (self.position[1] + self.size[1] // 2)
class PowerUp(object): def __init__(self, _x, _y, _nombre): self.bounds = Rect(_x, _y, 0, 0) self.animaciones = GestorAnimaciones() self.bounds.width, self.bounds.height = self.animaciones.añadir_animacion( _nombre, self.animaciones.cargar_frames("powerups/" + _nombre, 6), 200, 0) self.temporizador_movimiento = Timer(10) self.tipo = _nombre def dibujar(self, _pantalla): self.animaciones.render(_pantalla, self.bounds) def actualizar(self, _dt): self.animaciones.update(_dt) self.temporizador_movimiento.update(_dt) self.mover() def mover(self): if self.temporizador_movimiento.tick: self.bounds.move_ip(0, 1)
class Ball: def __init__(self, x, y, speed=3): self.color = BALL_COLOR self.isMove = True self.speed = speed self.size = 10 self.rect = Rect((x, y, self.size, self.size)) self.dx = 1 if random.random() < 0.5 else -1 self.dy = 1 if random.random() < 0.5 else -1 def update(self): self.rect.move_ip(self.speed * self.dx, self.speed * self.dy) if (self.rect.y < 0): self.rect.y = 0 self.dy = 1 if (self.rect.y + self.size > H): self.rect.y = H - self.size self.dy = -1 self.speed += 0.001 def stop(self): self.isMove = False def move(self): self.isMove = True def changeDir(self, dx=None, dy=None): self.dx = dx if dx is not None else self.dx self.dy = dy if dy is not None else self.dy def draw(self, sc): draw.circle(sc, self.color, self.rect.center, self.size)
def callback(event_object): rotation_center = grid_centroid(well_offset, grid_spacing, tile.sprites()) for brick in iter(tile): rotated_vertices = [] for vertexname in [ 'topleft', 'topright', 'bottomleft', 'bottomright' ]: vertex = getattr(brick.rect, vertexname) rvertex = rotator(rotation_center, vertex) rotated_vertices.append(rvertex) coord1, coord2 = zip(*rotated_vertices) left, top = min(coord1), min(coord2) rotated_rect = Rect((left, top), (grid_spacing, grid_spacing)) # prevent tile from creeping upwards with repeated rotations rotated_rect.move_ip((0, grid_spacing)) brick.rect = rotated_rect if heap.did_collide( (0, 0))(tile): # ensure that tiles don't rotate into a heap for brick in iter(tile): brick.rect.move_ip((0, -grid_spacing))
class Player(Rect): size_x = 20 size_y = 100 move_step = 6 def __init__(self, screen, color, pos_x, pos_y): self.screen = screen self.color = color self.rect = Rect(pos_x, pos_y, self.size_x, self.size_y) self.move_step = self.move_step self.racket = draw_rect(self.screen, self.color, self.rect) def draw_racket(self): self.racket = draw_rect(self.screen, self.color, self.rect) def move_down(self): ic() if (self.rect.top < self.screen.get_height() - self.rect.height): self.rect.move_ip(0, self.move_step) def move_up(self): ic() if (self.rect.top > 0): self.rect.move_ip(0, -self.move_step)
class Map: TILE_SIZE = (30, 30) def __init__(self, view_size): self.view_rect = Rect((0,0), view_size) self.update_rects = [] self.previous_rects = [] self.actual_rects = [] self.group = MapGroup(self) self.special_block_group = Group() self.bullet_group = Group() self.enemies_group = Group() self.items_group = Group() self.enemy_bullet_group = Group() self.special_items_group = Group() self.main_character = TMan(self, self.group) self.got_key = False self.won = False self.finish = False def try_won(self): if self.got_key: self.won = True def load(self, map_name): tile1 = data.load_image('tile-solid-1.png') tile2 = data.load_image('tile-solid-2.png') tilenull = data.load_image('tile-null.png') mapfile = open(data.filepath('maps', map_name)) self.name = mapfile.readline().strip() self.time = float(mapfile.readline().strip()) bg = mapfile.readline().strip() self.background = data.load_image('background-%s.png' % bg) def append_tile(code, tilex, tiley): def add(c, tile,*args): obj = c(self, *args) x, y = self.tile_center(tilex, tiley) obj.put(x, y) self.tiles.append(tile) if code == '1': self.tiles.append(tile1) elif code == '2': self.tiles.append(tile2) elif code == 'S': add(SpecialTile, 0, len(self.tiles)) elif code == 'a': add(Fly, None, EllipsePath(20, 20, 10), random.randint(25, 75)) elif code == 'b': add(SpikeSpawn, None, 4, 50) elif code == 'c': add(Defender, None, HorizontalLinePath(100, 'bounce'), random.randint(50, 100)) elif code == 't': add(TwisterUpItem, None) elif code == 'L': add(LifeButterflyItem, None) elif code == 'l': add(LifeTankItem, None) elif code == 'z': add(KeyItem, None) elif code == 'Z': add(LockItem, None) elif code == 'X': self.tiles.append(None) x, y = self.tile_center(tilex, tiley) self.main_character.put(x, y) else: self.tiles.append(None) height = 0 width = 0 self.tiles = [] for line in mapfile: line = line.strip() width = len(line) for char, i in zip(line, range(len(line))): append_tile(char, i, height) height += 1 self.width = width self.height = height tw, th = self.TILE_SIZE right, bottom = tw*width, th*height self.rect = Rect((0, 0), (right, bottom)) def get_time(self): return '%d:%d' % (int(self.time/60), int(self.time%60)) def get_tile(self, x, y): if x<0 or y<0 or x>=self.width or y>=self.height: return None return self.tiles[y*self.width+x] def pixel_to_tile(self, x, y): tw, th = self.TILE_SIZE tile_x = x / tw tile_y = y / th return (tile_x, tile_y) def tile_to_pixel(self, tilex, tiley): tw, th = self.TILE_SIZE return tilex * tw, tiley * th def tile_center(self, tilex, tiley): tw, th = self.TILE_SIZE x, y = self.tile_to_pixel(tilex, tiley) return (x+tw/2, y+th/2) def tile_rect(self, tilex, tiley): x, y = self.tile_to_pixel(tilex, tiley) return Rect((x, y), self.TILE_SIZE) def tile_at(self, x, y): tile_x, tile_y = self.pixel_to_tile(x, y) return self.get_tile(tile_x, tile_y), self.tile_rect(tile_x, tile_y) def scroll(self): spritex = self.main_character.rect.centerx spritey = self.main_character.rect.centery width_space = self.view_rect.width * 1 / 3 height_space = self.view_rect.height * 1 / 3 scroll_right_point = self.view_rect.right - width_space if spritex > scroll_right_point: self.move_view(spritex - scroll_right_point, 0) scroll_left_point = self.view_rect.left + width_space if spritex < scroll_left_point: self.move_view(spritex - scroll_left_point, 0) scroll_top_point = self.view_rect.top + height_space if spritey < scroll_top_point: self.move_view(0, spritey - scroll_top_point) scroll_bottom_point = self.view_rect.bottom - height_space if spritey > scroll_bottom_point: self.move_view(0, spritey - scroll_bottom_point) def update(self, *args): self.group.update(*args) self.scroll() self.check_collisions() self.time = self.time - (1.0/FPS) self.check_status() def check_status(self): self.finish = self.main_character.death() or self.won or self.time <= 0 def check_collisions(self): tman = self.main_character pygame.sprite.groupcollide(self.bullet_group, self.enemies_group, True, True) pygame.sprite.groupcollide(self.special_block_group, self.bullet_group, True, True) pygame.sprite.groupcollide(self.special_block_group, self.enemy_bullet_group, False, True) pygame.sprite.groupcollide(self.enemy_bullet_group, self.bullet_group, True, True) enemies = pygame.sprite.spritecollide(tman, self.enemies_group, not tman.blinking) bullets = pygame.sprite.spritecollide(tman, self.enemy_bullet_group, not tman.blinking) items = pygame.sprite.spritecollide(tman, self.items_group, True) special_items = pygame.sprite.spritecollide(tman, self.special_items_group, True) for enemy in enemies: tman.enemy_touched(enemy) for bullet in bullets: tman.bullet_touched() for item in items: tman.item_touched(item) for item in special_items: item.use() def draw(self, surface, draw_position): tw, th = self.TILE_SIZE tile_x1, tile_y1 = self.pixel_to_tile(self.view_rect.left, self.view_rect.top) tile_x2, tile_y2 = self.pixel_to_tile(self.view_rect.right, self.view_rect.bottom) dx, dy = draw_position clip_rect = Rect(draw_position, (self.view_rect.width, self.view_rect.height)) surface.set_clip(clip_rect) self.actual_rects = [] self.update_rects = self.previous_rects if len(self.previous_rects) == 0: self.previous_rects.append(clip_rect) for rect in self.previous_rects: area = rect.move(-dx, -dy) surface.blit(self.background, rect, area) for x in range(tile_x1, tile_x2 + 1): for y in range(tile_y1, tile_y2 + 1): pos_x = dx + x * tw - self.view_rect.left pos_y = dy + y * th - self.view_rect.top image = self.get_tile(x, y) if image is not None and image != 0: surface.blit(image, (pos_x, pos_y)) self.actual_rects.append(image.get_rect().move(pos_x, pos_y)) self.actual_rects += self.group.draw(surface, draw_position) surface.set_clip(None) self.update_rects += self.actual_rects self.previous_rects = self.actual_rects def move_view(self, x, y): self.view_rect.move_ip(x, y) self.view_rect.top = max(self.rect.top, self.view_rect.top) self.view_rect.left = max(self.rect.left, self.view_rect.left) self.view_rect.right = min(self.rect.right, self.view_rect.right) self.view_rect.bottom = min(self.rect.bottom, self.view_rect.bottom) def goto_bottom(self): self.view_rect.bottom = self.rect.bottom def goto_left(self): self.view_rect.left = self.rect.left def goto_right(self): self.view_rect.right = self.rect.right def goto_top(self): self.view_rect.right = self.rect.right def set_main_character(self, sprite): self.main_character = sprite
class IsometricBufferedRenderer(BufferedRenderer): """ TEST ISOMETRIC here be dragons. lots of odd, untested, and unoptimised stuff. - coalescing of surfaces is not supported - drawing may have depth sorting issues """ def _draw_surfaces(self, surface, rect, surfaces): if surfaces is not None: [(surface.blit(i[0], i[1]), i[2]) for i in surfaces] def _initialize_buffers(self, view_size): """ Create the buffers to cache tile drawing :param view_size: (int, int): size of the draw area :return: None """ import math from pygame import Rect tw, th = self.data.tile_size mw, mh = self.data.map_size buffer_tile_width = int(math.ceil(view_size[0] / tw) + 2) * 2 buffer_tile_height = int(math.ceil(view_size[1] / th) + 2) * 2 buffer_pixel_size = buffer_tile_width * tw, buffer_tile_height * th self.map_rect = Rect(0, 0, mw * tw, mh * th) self.view_rect.size = view_size self._tile_view = Rect(0, 0, buffer_tile_width, buffer_tile_height) self._redraw_cutoff = 1 # TODO: optimize this value self._create_buffers(view_size, buffer_pixel_size) self._half_width = view_size[0] // 2 self._half_height = view_size[1] // 2 self._x_offset = 0 self._y_offset = 0 self.redraw_tiles() def _flush_tile_queue(self): """ Blits (x, y, layer) tuples to buffer from iterator """ iterator = self._tile_queue surface_blit = self._buffer.blit map_get = self._animation_map.get bw, bh = self._buffer.get_size() bw /= 2 tw, th = self.data.tile_size twh = tw // 2 thh = th // 2 for x, y, l, tile, gid in iterator: tile = map_get(gid, tile) x -= self._tile_view.left y -= self._tile_view.top # iso => cart iso_x = ((x - y) * twh) + bw iso_y = ((x + y) * thh) surface_blit(tile, (iso_x, iso_y)) def center(self, coords): """ center the map on a "map pixel" """ x, y = [round(i, 0) for i in coords] self.view_rect.center = x, y tw, th = self.data.tile_size left, ox = divmod(x, tw) top, oy = divmod(y, th) vec = int(ox / 2), int(oy) iso = vector2_to_iso(vec) self._x_offset = iso[0] self._y_offset = iso[1] print(self._tile_view.size) print(self._buffer.get_size()) # center the buffer on the screen self._x_offset += (self._buffer.get_width() - self.view_rect.width) // 2 self._y_offset += (self._buffer.get_height() - self.view_rect.height) // 4 # adjust the view if the view has changed without a redraw dx = int(left - self._tile_view.left) dy = int(top - self._tile_view.top) view_change = max(abs(dx), abs(dy)) # force redraw every time: edge queuing not supported yet self._redraw_cutoff = 0 if view_change and (view_change <= self._redraw_cutoff): self._buffer.scroll(-dx * tw, -dy * th) self._tile_view.move_ip(dx, dy) self._queue_edge_tiles(dx, dy) self._flush_tile_queue() elif view_change > self._redraw_cutoff: # logger.info('scrolling too quickly. redraw forced') self._tile_view.move_ip(dx, dy) self.redraw_tiles()
class Ball(Sprite): def __init__(self, init_pos, play_area_rect: Rect, enable_slide_ball: bool, *groups): super().__init__(*groups) self._play_area_rect = play_area_rect self._do_slide_ball = enable_slide_ball self._init_pos = init_pos self._speed = [0, 0] self.hit_platform_times = 0 self.rect = Rect(*self._init_pos, 5, 5) self.image = self._create_surface() # For additional collision checking self._last_pos = self.rect.copy() def _create_surface(self): surface = pygame.Surface((self.rect.width, self.rect.height)) surface.fill((44, 185, 214)) # Blue return surface @property def pos(self): return self.rect.topleft def reset(self): self.hit_platform_times = 0 self.rect.topleft = self._init_pos self._speed = [0, 0] def stick_on_platform(self, platform_centerx): self.rect.centerx = platform_centerx def serve(self, platform_action: PlatformAction): if platform_action == PlatformAction.SERVE_TO_LEFT: self._speed = [-7, -7] elif platform_action == PlatformAction.SERVE_TO_RIGHT: self._speed = [7, -7] def move(self): self._last_pos.topleft = self.rect.topleft self.rect.move_ip(self._speed) def check_bouncing(self, platform: Platform): if (physics.collide_or_contact(self, platform) or self._platform_additional_check(platform)): self.hit_platform_times += 1 rect_after_bounce, speed_after_bounce = physics.bounce_off( self.rect, self._speed, platform.rect, platform._speed) # Check slicing ball when the ball goes up after bouncing (not game over) if self._do_slide_ball and speed_after_bounce[1] < 0: speed_after_bounce[0] = self._slice_ball(self._speed[0], platform._speed[0]) self.rect = rect_after_bounce self._speed = speed_after_bounce if physics.rect_break_or_contact_box(self.rect, self._play_area_rect): physics.bounce_in_box_ip(self.rect, self._speed, self._play_area_rect) def _platform_additional_check(self, platform: Platform): """ The additional checking for the condition that the ball passes the corner of the platform """ if self.rect.bottom > platform.rect.top: routine_a = (Vector2(self._last_pos.bottomleft), Vector2(self.rect.bottomleft)) routine_b = (Vector2(self._last_pos.bottomright), Vector2(self.rect.bottomright)) return (physics.rect_collideline(platform.rect, routine_a) or physics.rect_collideline(platform.rect, routine_b)) return False def _slice_ball(self, ball_speed_x, platform_speed_x): """ Check if the platform slices the ball, and modify the ball speed. @return The new x speed of the ball after slicing """ # If the platform doesn't move, bounce normally. if platform_speed_x == 0: return 7 if ball_speed_x > 0 else -7 # If the platform moves at the same direction as the ball moving, # speed up the ball. elif ball_speed_x * platform_speed_x > 0: return 10 if ball_speed_x > 0 else -10 # If the platform moves at the opposite direction against the ball moving, # hit the ball back. else: return -7 if ball_speed_x > 0 else 7 def check_hit_brick(self, group_brick: pygame.sprite.RenderPlain) -> int: """ Check if the ball hits bricks in the `group_brick`. The hit bricks will be removed from `group_brick`, but the alive hard brick will not. However, if the ball speed is high, the hard brick will be removed with only one hit. @param group_brick The sprite group containing bricks @return The number of destroyed bricks """ hit_bricks = pygame.sprite.spritecollide(self, group_brick, 1, physics.collide_or_contact) num_of_destroyed_brick = len(hit_bricks) if num_of_destroyed_brick > 0: # XXX: Bad multiple collision bouncing handling if (num_of_destroyed_brick == 2 and (hit_bricks[0].rect.y == hit_bricks[1].rect.y or hit_bricks[0].rect.x == hit_bricks[1].rect.x)): combined_rect = hit_bricks[0].rect.union(hit_bricks[1].rect) physics.bounce_off_ip(self.rect, self._speed, combined_rect, (0, 0)) else: physics.bounce_off_ip(self.rect, self._speed, hit_bricks[0].rect, (0, 0)) if abs(self._speed[0]) == 7: for brick in hit_bricks: if isinstance(brick, HardBrick) and brick.hit(): group_brick.add((brick,)) num_of_destroyed_brick -= 1 return num_of_destroyed_brick
class cMsg(): HOLD_TIME = 2 def __init__(self,string,window): font.init() self.text = string self.myfont = font.SysFont("Arial", 20) self.render_font = self.myfont.render(string, 1, C.black) w = self.render_font.get_width() h = self.render_font.get_height() self.winh = window.get_height() self.rect = Rect(0,self.winh,w,h) self.movein = False self.moveout = False self.hold = False self.hold_start = 0 def draw(self,window): """ Draw the current message """ draw.rect(window, C.white, self.rect) window.blit(self.render_font, (self.rect.x, self.rect.y)) def update_logic(self,window): """ Movein and Moveout logic movement """ if self.movein: fh = self.render_font.get_height() self.rect.move_ip(0,-1) if self.rect.y <= window.get_height()-fh: self.move_hold() elif self.moveout: self.rect.move_ip(0,1) if self.rect.y > window.get_height(): self.move_stop() elif self.hold: self.update_hold() def update_hold(self): """ Controls how much time the message must stay between movein state and moveout state """ ctime = time.time() if (ctime - self.hold_start) > self.HOLD_TIME: self.move_out() # # Flag control for movement # def move_in(self): self.movein = True self.moveout = False self.hold = False def move_out(self): self.movein = False self.moveout = True self.hold = False def move_hold(self): self.movein = False self.moveout = False self.hold = True self.hold_start = time.time() def move_stop(self): self.movein = False self.moveout = False self.hold = False def set_text(self,string,movein=True): """ Set a new text for the message. Additionally it shows the message. the flag movein controls that True=>Move in, False=> simply set text """ self.text = string self.render_font = self.myfont.render(string, 1, C.black) w = self.render_font.get_width() h = self.render_font.get_height() self.rect = Rect(0,self.winh,w,h) if movein: self.move_in()
class cPlayer(): mx = 0 my = 0 speed = 4 sounds = True def __init__(self,left,top,width,height): self.startx = left self.starty = top self.width = width self.height = height self.rect = Rect(left, top, width, height) self.path = [self.rect.center] self.pathlist = [] def update_logic(self): self.rect.move_ip(self.mx,self.my) def move_down(self): self.my = self.speed self.mx = 0 self.add_point_to_path(self.rect.center) if self.sounds: SOUNDS.play_fx(11) def move_up(self): self.my = -self.speed self.mx = 0 self.add_point_to_path(self.rect.center) if self.sounds: SOUNDS.play_fx(11) def move_left(self): self.my = 0 self.mx = -self.speed self.add_point_to_path(self.rect.center) if self.sounds: SOUNDS.play_fx(11) def move_right(self): self.my = 0 self.mx = self.speed self.add_point_to_path(self.rect.center) if self.sounds: SOUNDS.play_fx(11) def stop_moving(self): self.my = 0 self.mx = 0 def set_to_start(self): self.stop_moving() self.path.append(self.rect.center) self.rect = Rect(self.startx,self.starty,self.width,self.height) self.move_path_to_pathlist() def add_point_to_path(self,point): self.path.append(point) def move_path_to_pathlist(self): self.pathlist.append(self.path) self.path = [self.rect.center] def clear_pathlist(self): self.pathlist = [] def random_speed(self): self.speed = random.uniform(2,5) def set_speed(self,speed): self.speed = speed def toggle_sounds(self): if self.sounds: self.sounds = False else: self.sounds = True return self.sounds
class Player(): def __init__(self, screen, x, y): self.screen = screen self.rect = Rect(x, y, 20, 80) def move_up(self): if self.rect.y > 0: self.rect.move_ip(0, -4) def move_down(self): if self.rect.y < 400: self.rect.move_ip(0, 4) def display(self): pygame.draw.rect(self.screen, WHITE, self.rect) def check_contact(self, ball): """ Checks whether this player has contact with the given ball. If so, will bounce the ball in the appropriate direction. Uses PyGame's overlap functionality which is convenient but somewhat imprecise. @return bool whether there's a contact between player and ball """ if not self.overlaps(ball): return False if ball.moving_right: x_diff = abs(ball.rect.right - self.rect.left) else: x_diff = abs(ball.rect.left - self.rect.right) if ball.moving_up: y_diff = abs(ball.rect.top - self.rect.bottom) else: y_diff = abs(ball.rect.bottom - self.rect.top) if x_diff < y_diff and ball.rect.x: ball.bounce_x() elif x_diff > y_diff: ball.bounce_y() else: # corner bounce ball.bounce_x() ball.bounce_y() return True def overlaps(self, other): return self.rect.colliderect(other.rect) def ai_move(self, ball): if not ball.moving: return if ball.moving_right: # go back to center target_y = 200 else: # follow ball target_y = ball.rect.y - 30 if self.rect.y < target_y: self.move_down() elif self.rect.y > target_y: self.move_up()
class InputBox(Rect): PADDING = 3 HANDLED_KEYS = set( [ pygame.K_DELETE, pygame.K_BACKSPACE, pygame.K_LEFT, pygame.K_RIGHT]).union( range( pygame.K_a, pygame.K_z + 1)).union( range( pygame.K_0, pygame.K_9 + 1)) def __init__(self, dimensions, pos, text="", text_colour=(0, 0, 0), text_font=(None, 50), bg_colour=(255, 255, 255), frame_colour=(0, 0, 0), frame_size=1): Rect.__init__(self, (pos[0] - dimensions[0] / 2, pos[1] - dimensions[1] / 2), dimensions) self.text = text self.text_colour = text_colour self.text_font = text_font self.create_char_avatars() self.bg_colour = bg_colour self.frame_colour = frame_colour self.frame_size = frame_size self.input_cursor = Rect((self.left, self.top + InputBox.PADDING), (2, (self.height - 2 * InputBox.PADDING))) self.put_cursor(self.right) self.clicked = False self.state = "normal" self.char_limit = None self.handled_chars = InputBox.HANDLED_KEYS def draw(self, surface): if self.bg_colour is not None: pygame.draw.rect(surface, self.bg_colour, self, 0) if self.frame_colour is not None: pygame.draw.rect(surface, self.frame_colour, self, self.frame_size) shift = 0 for char in self.char_avatars: surface.blit(char, ( self.left + InputBox.PADDING + shift, self.centery - char.get_height() / 2)) shift += char.get_width() if self.state == "active": pygame.draw.rect(surface, self.text_colour, self.input_cursor) def create_char_avatars(self): self.char_avatars = [] for char in self.text: char_avatar = pygame.font.Font(*self.text_font).render( char, 20, self.text_colour) if char_avatar.get_height() > self.y: char_avatar = pygame.transform.scale( char_avatar, (char_avatar.get_width(), self.height)) self.char_avatars.append(char_avatar) def put_cursor(self, cursor_x): error = cursor_x - (self.left + InputBox.PADDING) self.cursor_index = len(self.char_avatars) for index, char in enumerate(self.char_avatars): if error < char.get_width() / 2: self.cursor_index = index break error -= char.get_width() cursor_x -= error self.input_cursor.move_ip(cursor_x - self.input_cursor.left, 0) def update_state(self, cursor_pos, event): cursor_on_inputbox = self.collidepoint(cursor_pos) if self.state is "active": if not cursor_on_inputbox: if event: self.state = "normal" CURSORS["arrow"]() if cursor_on_inputbox: if event: self.put_cursor(cursor_pos[0]) CURSORS["textmarker"]() elif self.state is "hover": if event and cursor_on_inputbox: self.state = "active" self.put_cursor(cursor_pos[0]) elif not cursor_on_inputbox: self.state = "normal" CURSORS["arrow"]() else: if event and cursor_on_inputbox: self.state = "active" CURSORS["textmarker"]() self.put_cursor(cursor_pos[0]) elif cursor_on_inputbox: self.state = "hover" CURSORS["textmarker"]() def shift_cursor(self, positions): direction = int(abs(positions) / positions) side = 1 if direction == 1: side = 0 for index in range(0, abs(positions)): self.input_cursor.move_ip((direction * self.char_avatars[ self.cursor_index - side].get_width()), 0) self.cursor_index += direction def handle_key_event(self, key): if self.state != "active": pass elif key == pygame.K_BACKSPACE and self.cursor_index: self.shift_cursor(-1) self.text = self.text[:self.cursor_index] + \ self.text[1 + self.cursor_index:] self.create_char_avatars() elif key == pygame.K_DELETE and self.cursor_index < len( self.char_avatars): self.text = self.text[:self.cursor_index] + \ self.text[self.cursor_index + 1:] self.create_char_avatars() elif key in self.handled_chars: if self.char_limit is None or self.char_limit > len(self.text): self.text = self.text[:self.cursor_index] + chr(key) +\ self.text[self.cursor_index:] self.create_char_avatars() self.shift_cursor(1) elif key == pygame.K_LEFT and self.cursor_index: self.shift_cursor(-1) elif key == pygame.K_RIGHT and self.cursor_index < len( self.char_avatars): self.shift_cursor(1)
class SpeciesInfoBox(object): def __init__(self, ui, surface, font, x, y, species): self.ui = ui self.surface = surface self.species = species self.font = font self.x = x self.y = y self.padding = (10, 10, 10, 10) self.line_height = 20 self.width = info_panel_config.width - 1 self.height = 300 self.color = (255, 255, 255) self.mutated_param_color = (100, 255, 100) self.labels = None self.border_rect = Rect(self.x, self.y, self.width, self.height) self.border_width = 1 self.border_color = (255, 255, 255) self.bg_rect = Rect(self.x + self.border_width, self.y + self.border_width, self.width - self.border_width * 2, self.height - self.border_width * 2) self.bg_color = (30, 30, 30) self.y_offset = 0 self._generate_labels() def _generate_labels(self): self.labels = [] self.y_offset = self.y + self.padding[0] self._add_label('Type: %s' % str(self.species.life_obj.type)) self._add_label('Size: %.2f' % float(self.species.life_obj.inheritable_size), 'size' in self.species.life_obj.mutated_params) self._add_label('Nutritional value: %.2f' % float(self.species.life_obj.inheritable_nutritional_value), 'nutritional_value' in self.species.life_obj.mutated_params) self._add_label('Defence: %.2f' % float(self.species.life_obj.inheritable_defence), 'defence' in self.species.life_obj.mutated_params) self._add_label('Attack: %.2f' % float(self.species.life_obj.inheritable_attack), 'attack' in self.species.life_obj.mutated_params) self._add_label('Speed: %.2f' % float(self.species.life_obj.inheritable_speed * self.species.life_obj.environment.calculate_speed_factor( self.species.life_obj.humidity_likeness, self.species.life_obj.temperature_likeness)), 'speed' in self.species.life_obj.mutated_params) self._add_label('Reproducing interval: %.2f' % float(self.species.life_obj.reproducing_interval), 'reproducing_interval' in self.species.life_obj.mutated_params) self._add_label('Reproducing probability: %.2f' % float(self.species.life_obj.reproducing_probability), 'reproducing_probability' in self.species.life_obj.mutated_params) self._add_label('Energy needed to reproduce: %.2f' % float(self.species.life_obj.energy_needed_to_reproduce), 'energy_needed_to_reproduce' in self.species.life_obj.mutated_params) self._add_label('Energy move cost: %.2f' % float(self.species.life_obj.energy_move_cost), 'energy_move_cost' in self.species.life_obj.mutated_params) self._add_label('Energy live cost: %.2f' % float(self.species.life_obj.energy_live_cost), 'energy_live_cost' in self.species.life_obj.mutated_params) def _add_label(self, text, param_has_mutated=False): self.labels.append(Label(self.ui, self.surface, text, self.x + self.padding[3], self.y_offset, self.font, self.color if not param_has_mutated else self.mutated_param_color)) self.y_offset += self.line_height def draw(self, y): if y is not None and y + self.line_height != self.y: y += self.line_height self.border_rect.move_ip(0, y - self.y) self.bg_rect.move_ip(0, y - self.y) self.y = y self._generate_labels() pygame.draw.rect(self.surface, self.border_color, self.border_rect, self.border_width) pygame.draw.rect(self.surface, self.bg_color, self.bg_rect, 0) for label in self.labels: label.draw()
def draw(self, surface, rect): onScreen = [] if self.blank: self.blank = False self.maprender.blank = True visible_shapes = self.area.space.bb_query(toBB(self.extent)) for child in [ child for child in self.area if child.avatar]: shape = child.shapes[0] if shape not in visible_shapes: continue bb = child.bb x = bb.left - self.extent.left - child.avatar.axis.x y = bb.top - self.extent.top if hasattr(shape, "radius"): w, h = child.avatar.image.get_size() angle = -(math.degrees(shape.body.angle)) % 360 image = rotozoom(child.avatar.image.convert_alpha(), angle, 1.0) ww, hh = image.get_size() rrect = Rect(x, y-h, ww, hh) rrect.move_ip((w-ww)/2, (h-hh)/2) else: w, h = child.avatar.image.get_size() rrect = Rect(x, y - h, w, h) image = child.avatar.image onScreen.append((image, rrect, 1)) if parallax: self.parallaxrender.draw(surface, rect, []) dirty = self.maprender.draw(surface, rect, onScreen) if DEBUG: def translate((x, y)): return x - self.extent.left, y - self.extent.top for shape in self.area.space.shapes: try: points = [ translate(i) for i in shape.get_points() ] except AttributeError: pass else: draw.aalines(surface, (255,100,100), 1, points) continue try: radius = shape.radius pos = shape.body.position pos = translate(pos) pos += shape.offset except AttributeError: pass else: pos = map(int, pos) draw.circle(surface, (255,100,100), pos, int(radius), 1) continue return dirty
class Monster(sprite.Sprite): def __init__(self, move_time, nodes): sprite.Sprite.__init__(self) self.nodes = nodes self.orig_nodes = nodes self.move_time = move_time self.spawn_time = time.time() self.image = Surface((40, 40)).convert() self.image_inside = Surface((38, 38)).convert() self.image_inside.fill((0, 255, 0)) self.image.blit(self.image_inside, (1, 1)) self.pos = (80, 40) self.real_pos = (80, 40) self.rect = Rect(self.pos, self.image.get_size()) self.speed = 2 self.speed_mod = 1 self.diag_speed = 2 self.target_pos = (880, 560) self.value = 1 self.cost = 0 self.health = 100 self.damage_mod = 1 self.counter = 0 self.cur_node = self.nodes[0] self.the_dir = (0, 0) self.can_move = False self.name = "Monster" self.description = "A basic monster with slow movement speed and moderate health." def update(self, window): if time.time() - self.spawn_time >= self.move_time: self.can_move = True # If it's hit the last block if len(self.nodes) < 1: self.kill() return self.value else: # Figuring direction if self.nodes[0].rect.x > self.cur_node.rect.x: self.the_dir = (1, 0) elif self.nodes[0].rect.x < self.cur_node.rect.x: self.the_dir = (-1, 0) elif self.nodes[0].rect.y > self.cur_node.rect.y: self.the_dir = (0, 1) elif self.nodes[0].rect.y < self.cur_node.rect.y: self.the_dir = (0, -1) # Check to see the most the monster can move for speed in range(0, self.speed+1): t_dir = tuple([x * speed * self.speed_mod for x in self.the_dir]) # Monster can only move this much if self.rect.move(t_dir) == self.nodes[0].rect: self.rect.move_ip(t_dir) self.real_pos = tuple(map(sum, zip(self.real_pos, t_dir))) self.cur_node = self.nodes.pop(0) break else: # The monster can move by self.speed a = tuple([x * self.speed * self.speed_mod for x in self.the_dir]) self.real_pos = tuple(map(sum, zip(self.real_pos, a))) self.pos = tuple(map(round, self.real_pos)) self.rect.x, self.rect.y = self.pos # Conditions for the monster to die die_conditions = [self.rect.top >= window.get_height(), self.rect.left >= window.get_width(), self.rect.bottom <= 0] if any(die_conditions): self.kill() return self.value # Resetting the modifiers, they'll be changed if the monster is under an effect self.speed_mod = 1 self.damage_mod = 1 return 0 # Does damage to the monster and checks if it dies def damage(self, damage): self.health -= damage*self.damage_mod # Returns the amount of money to grant the player if the monster dies and also how much damage was done if self.health <= 0: self.kill() return self.value, damage*self.damage_mod else: return None, damage*self.damage_mod
class MovingSprite(Entity): """ Sprite base class that implements movement on a gameboard. """ # Sprite state speed = 5 def __init__(self, x, y, width, height): super(MovingSprite, self).__init__(x, y, width, height) # Center the sprite to initial coords center_coords = (self.rect.centerx - self.rect.width, self.rect.centery - self.rect.height) self.rect = Rect(center_coords, self.rect.size) # Set current movement target equal to initial position self.target = vec2(self.rect.centerx, self.rect.centery) self.targets = deque() # Target queue self.facing_direction = Direction.DOWN self.state = State.STATIONARY def move_to_target(self, x, y): """ Add new target for the sprite to move towards. Starts movement towards target if stationary, otherwise appends the target to a queue. """ # Set new direction new_target = vec2(x, y) if self.state == State.STATIONARY and new_target != self.target and not self.need_to_move(): self.facing_direction = self.get_facing(x, y) self.state = State.MOVING self.target = vec2(x, y) else: self.targets.append(vec2(x, y)) def get_facing(self, x, y): # TODO: If target diagonally away, do something if x > self.target.x: return Direction.RIGHT elif x < self.target.x: return Direction.LEFT elif y > self.target.y: return Direction.DOWN elif y < self.target.y: return Direction.UP else: return Direction.STATIONARY def need_to_move(self): current_position = self.rect.center return current_position[0] != self.target.x or current_position[1] != self.target.y def calculate_movement(self): if self.state != State.STATIONARY: current_position = vec2(self.rect.centerx, self.rect.centery) velocity = DIRECTIONS[self.facing_direction] * self.speed new_position = current_position + velocity target_position = self.target # Calculate velocity to just reach the target, not go over if (current_position.x < target_position.x < new_position.x or new_position.x < target_position.x < current_position.x): velocity.x = target_position.x - current_position.x elif (current_position.y < target_position.y < new_position.y or new_position.y < target_position.y < current_position.y): velocity.y = target_position.y - current_position.y return velocity else: return vec2(0, 0) def move(self): """ Move the sprite towards next target. Switches to a new target if targets queue is not empty, otherwise stays still. """ # Check if already at current target if not self.need_to_move(): # Get new target if len(self.targets) != 0: next_target = self.targets.popleft() facing = self.get_facing(*next_target) # If new target is equal to previous if facing == Direction.STATIONARY: self.state = State.STATIONARY return self.facing_direction = facing self.target = next_target # No target, stay still else: if self.state != State.STATIONARY: self.state = State.STATIONARY return else: self.state = State.MOVING velocity = self.calculate_movement() self.rect.move_ip(*velocity)
def get_cell_rect(self, i, j): """Get rectangle from index (i, j).""" rect = Rect(*self.pos, self.case_length, self.case_length) rect.move_ip(j * self.case_length, i * self.case_length) return rect
class BufferedRenderer(object): """ Renderer that support scrolling, zooming, layers, and animated tiles The buffered renderer must be used with a data class to get tile, shape, and animation information. See the data class api in pyscroll.data, or use the built-in pytmx support for loading maps created with Tiled. """ def __init__(self, data, size, clamp_camera=True, colorkey=None, alpha=False, time_source=time.time, scaling_function=pygame.transform.scale): # default options self.map_rect = None # pygame rect of entire map self.data = data # reference to data source self.clamp_camera = clamp_camera # if clamped, cannot scroll past map edge self.time_source = time_source # determines how tile animations are processed self.scaling_function = scaling_function # what function to use when zooming self.default_shape_texture_gid = 1 # [experimental] texture to draw shapes with self.default_shape_color = 0, 255, 0 # [experimental] color to fill polygons with # internal private defaults self._alpha = False if colorkey and alpha: print('cannot select both colorkey and alpha. choose one.') raise ValueError elif colorkey: self._clear_color = colorkey else: self._clear_color = None # private attributes self._size = None # size that the camera/viewport is on screen, kinda self._redraw_cutoff = None # size of dirty tile edge that will trigger full redraw self._x_offset = None # offsets are used to scroll map in sub-tile increments self._y_offset = None self._buffer = None # complete rendering of tilemap self._tile_view = None # this rect represents each tile on the buffer self._half_width = None # 'half x' attributes are used to reduce division ops. self._half_height = None self._tile_queue = None # tiles queued to be draw onto buffer self._animation_queue = None # heap queue of animation token. schedules tile changes self._animation_map = None # map of GID to other GIDs in an animation self._last_time = None # used for scheduling animations self._layer_quadtree = None # used to draw tiles that overlap optional surfaces self._zoom_buffer = None # used to speed up zoom operations self._zoom_level = 1.0 # negative numbers make map smaller, positive: bigger # this represents the viewable pixels, aka 'camera' self.view_rect = Rect(0, 0, 0, 0) self.reload_animations() self.set_size(size) def _update_time(self): self._last_time = time.time() * 1000 def reload_animations(self): """ Reload animation information """ self._update_time() self._animation_map = dict() self._animation_queue = list() for gid, frame_data in self.data.get_animations(): frames = list() for frame_gid, frame_duration in frame_data: image = self.data.get_tile_image_by_gid(frame_gid) frames.append(AnimationFrame(image, frame_duration)) ani = AnimationToken(gid, frames) ani.next += self._last_time self._animation_map[ani.gid] = ani.frames[ani.index].image heappush(self._animation_queue, ani) def _process_animation_queue(self): self._update_time() requires_redraw = False # test if the next scheduled tile change is ready while self._animation_queue[0].next <= self._last_time: requires_redraw = True token = heappop(self._animation_queue) # advance the animation index, looping by default if token.index == len(token.frames) - 1: token.index = 0 else: token.index += 1 next_frame = token.frames[token.index] token.next = next_frame.duration + self._last_time self._animation_map[token.gid] = next_frame.image heappush(self._animation_queue, token) if requires_redraw: # TODO: record the tiles that changed and update only affected tiles self.redraw_tiles() pass def _calculate_zoom_buffer_size(self, value): if value <= 0: print('zoom level cannot be zero or less') raise ValueError value = 1.0 / value return [int(round(i * value)) for i in self._size] @property def zoom(self): """ Zoom the map in or out. Increase this number to make map appear to come closer to camera. Decrease this number to make map appear to move away from camera. Default value is 1.0 This value cannot be negative or 0.0 :return: float """ return self._zoom_level @zoom.setter def zoom(self, value): self._zoom_level = value buffer_size = self._calculate_zoom_buffer_size(value) self._initialize_buffers(buffer_size) def set_size(self, size): """ Set the size of the map in pixels This is an expensive operation, do only when absolutely needed. :param size: (width, height) pixel size of camera/view of the group """ self._size = size buffer_size = self._calculate_zoom_buffer_size(self._zoom_level) self._initialize_buffers(buffer_size) def _create_buffers(self, view_size, buffer_size): """ Create the buffers, taking in account pixel alpha or colorkey :param view_size: pixel size of the view :param buffer_size: pixel size of the buffer """ requires_zoom_buffer = not view_size == buffer_size self._zoom_buffer = None if self._clear_color: if requires_zoom_buffer: self._zoom_buffer = Surface(view_size, flags=pygame.RLEACCEL) self._zoom_buffer.set_colorkey(self._clear_color) self._buffer = Surface(buffer_size, flags=pygame.RLEACCEL) self._buffer.set_colorkey(self._clear_color) self._buffer.fill(self._clear_color) elif self._alpha: if requires_zoom_buffer: self._zoom_buffer = Surface(view_size, flags=pygame.SRCALPHA) self._buffer = Surface(buffer_size, flags=pygame.SRCALPHA) else: if requires_zoom_buffer: self._zoom_buffer = Surface(view_size) self._buffer = Surface(buffer_size) def _initialize_buffers(self, size): """ Create the buffers to cache tile drawing :param size: (int, int): size of the draw area :return: None """ tw, th = self.data.tile_size mw, mh = self.data.map_size buffer_tile_width = int(math.ceil(size[0] / tw) + 2) buffer_tile_height = int(math.ceil(size[1] / th) + 2) buffer_pixel_size = buffer_tile_width * tw, buffer_tile_height * th self.map_rect = Rect(0, 0, mw * tw, mh * th) self.view_rect.size = size self._tile_view = Rect(0, 0, buffer_tile_width, buffer_tile_height) self._redraw_cutoff = min(buffer_tile_width, buffer_tile_height) self._create_buffers(size, buffer_pixel_size) self._half_width = size[0] // 2 self._half_height = size[1] // 2 self._x_offset = 0 self._y_offset = 0 def make_rect(x, y): return Rect((x * tw, y * th), (tw, th)) rects = [make_rect(*i) for i in product(range(buffer_tile_width), range(buffer_tile_height))] # TODO: figure out what depth -actually- does self._layer_quadtree = quadtree.FastQuadTree(rects, 4) self.redraw_tiles() def scroll(self, vector): """ scroll the background in pixels :param vector: (int, int) """ self.center((vector[0] + self.view_rect.centerx, vector[1] + self.view_rect.centery)) def center(self, coords): """ center the map on a pixel float numbers will be rounded. :param coords: (number, number) """ x, y = [round(i, 0) for i in coords] self.view_rect.center = x, y if self.clamp_camera: self.view_rect.clamp_ip(self.map_rect) x, y = self.view_rect.center # calc the new position in tiles and offset tw, th = self.data.tile_size left, self._x_offset = divmod(x - self._half_width, tw) top, self._y_offset = divmod(y - self._half_height, th) # adjust the view if the view has changed without a redraw dx = int(left - self._tile_view.left) dy = int(top - self._tile_view.top) view_change = max(abs(dx), abs(dy)) if view_change <= self._redraw_cutoff: self._buffer.scroll(-dx * tw, -dy * th) self._tile_view.move_ip((dx, dy)) self._queue_edge_tiles(dx, dy) self._flush_tile_queue() elif view_change > self._redraw_cutoff: logger.info('scrolling too quickly. redraw forced') self._tile_view.move_ip((dx, dy)) self.redraw_tiles() def _queue_edge_tiles(self, dx, dy): """ Queue edge tiles and clear edge areas on buffer if needed :param dx: Edge along X axis to enqueue :param dy: Edge along Y axis to enqueue :return: None """ v = self._tile_view fill = partial(self._buffer.fill, self._clear_color) tw, th = self.data.tile_size self._tile_queue = iter([]) def append(rect): self._tile_queue = chain(self._tile_queue, self.data.get_tile_images_by_rect(rect)) if self._clear_color: fill(((rect[0] - self._tile_view.left) * tw, (rect[1] - self._tile_view.top) * th, rect[2] * tw, rect[3] * th)) if dx > 0: # right side append((v.right - dx, v.top, dx, v.height)) elif dx < 0: # left side append((v.left, v.top, -dx, v.height)) if dy > 0: # bottom side append((v.left, v.bottom - dy, v.width, dy)) elif dy < 0: # top side append((v.left, v.top, v.width, -dy)) def draw(self, surface, rect, surfaces=None): """ Draw the map onto a surface pass a rect that defines the draw area for: drawing to an area smaller that the whole window/screen surfaces may optionally be passed that will be blitted onto the surface. this must be a list of tuples containing a layer number, image, and rect in screen coordinates. surfaces will be drawn in order passed, and will be correctly drawn with tiles from a higher layer overlapping the surface. surfaces list should be in the following format: [ (layer, surface, rect), ... ] :param surface: pygame surface to draw to :param rect: area to draw to :param surfaces: optional sequence of surfaces to interlace into tiles """ if self._zoom_level == 1.0: self._render_map(surface, rect, surfaces) else: self._render_map(self._zoom_buffer, self._zoom_buffer.get_rect(), surfaces) self.scaling_function(self._zoom_buffer, rect.size, surface) def _render_map(self, surface, rect, surfaces): """ Render the map and optional surfaces to destination surface :param surface: pygame surface to draw to :param rect: area to draw to :param surfaces: optional sequence of surfaces to interlace into tiles """ if self._animation_queue: self._process_animation_queue() if rect.width > self.map_rect.width: x = (rect.width - self.map_rect.width) // 4 print(x) self._x_offset += x if rect.height > self.map_rect.height: pass # need to set clipping otherwise the map will draw outside its area with surface_clipping_context(surface, rect): # draw the entire map to the surface taking in account the scrolling offset surface.blit(self._buffer, (-self._x_offset - rect.left, -self._y_offset - rect.top)) if surfaces: self._draw_surfaces(surface, rect, surfaces) def _draw_surfaces(self, surface, rect, surfaces): """ Draw surfaces onto map, then redraw tiles that cover them :param surface: destination :param rect: clipping area :param surfaces: sequence of surfaces to blit """ surface_blit = surface.blit left, top = self._tile_view.topleft ox = self._x_offset - rect.left oy = self._y_offset - rect.top hit = self._layer_quadtree.hit get_tile = self.data.get_tile_image tile_layers = tuple(self.data.visible_tile_layers) dirty = [(surface_blit(i[0], i[1]), i[2]) for i in surfaces] for dirty_rect, layer in dirty: for r in hit(dirty_rect.move(ox, oy)): x, y, tw, th = r for l in [i for i in tile_layers if gt(i, layer)]: tile = get_tile((x // tw + left, y // th + top, l)) if tile: surface_blit(tile, (x - ox, y - oy)) def _draw_objects(self): """ Totally unoptimized drawing of objects to the map [probably broken] """ tw, th = self.data.tile_size buff = self._buffer blit = buff.blit map_gid = self.data.tmx.map_gid default_color = self.default_shape_color get_image_by_gid = self.data.get_tile_image_by_gid _draw_textured_poly = pygame.gfxdraw.textured_polygon _draw_poly = pygame.draw.polygon _draw_lines = pygame.draw.lines ox = self._tile_view.left * tw oy = self._tile_view.top * th def draw_textured_poly(texture, points): try: _draw_textured_poly(buff, points, texture, tw, th) except pygame.error: pass def draw_poly(color, points, width=0): _draw_poly(buff, color, points, width) def draw_lines(color, points, width=2): _draw_lines(buff, color, False, points, width) def to_buffer(pt): return pt[0] - ox, pt[1] - oy for layer in self.data.visible_object_layers: for o in (o for o in layer if o.visible): texture_gid = getattr(o, "texture", None) color = getattr(o, "color", default_color) # BUG: this is not going to be completely accurate, because it # does not take into account times where texture is flipped. if texture_gid: texture_gid = map_gid(texture_gid)[0][0] texture = get_image_by_gid(int(texture_gid)) if hasattr(o, 'points'): points = [to_buffer(i) for i in o.points] if o.closed: if texture_gid: draw_textured_poly(texture, points) else: draw_poly(color, points) else: draw_lines(color, points) elif o.gid: tile = get_image_by_gid(o.gid) if tile: pt = to_buffer((o.x, o.y)) blit(tile, pt) else: x, y = to_buffer((o.x, o.y)) points = ((x, y), (x + o.width, y), (x + o.width, y + o.height), (x, y + o.height)) if texture_gid: draw_textured_poly(texture, points) else: draw_poly(color, points) def _flush_tile_queue(self): """ Blit the queued tiles and block until the tile queue is empty """ tw, th = self.data.tile_size ltw = self._tile_view.left * tw tth = self._tile_view.top * th blit = self._buffer.blit map_get = self._animation_map.get for x, y, l, tile, gid in self._tile_queue: blit(map_get(gid, tile), (x * tw - ltw, y * th - tth)) def redraw_tiles(self): """ redraw the visible portion of the buffer -- it is slow. """ if self._clear_color: self._buffer.fill(self._clear_color) elif self._alpha: self._buffer.fill(0) self._tile_queue = self.data.get_tile_images_by_rect(self._tile_view) self._flush_tile_queue() def get_center_offset(self): """ Return x, y pair that will change world coords to screen coords :return: int, int """ return (-self.view_rect.centerx + self._half_width, -self.view_rect.centery + self._half_height)
class Player: def __init__(self, window: pygame.Surface, x_pos: float, y_pos: float, color: Tuple[int, int, int]): """ Create a player :param window: The pygame window that player will be displayed :param x_pos: Player's initial x position :param y_pos: Player's initial y position :param color: The color of the player """ self._window: pygame.Surface = window self._DEFAULT_X_POS: float = x_pos self._DEFAULT_Y_POS: float = y_pos self._rect = Rect(self._DEFAULT_X_POS, self._DEFAULT_Y_POS, PLAYER_WIDTH, PLAYER_HEIGHT) self._color: Tuple[int, int, int] = color self._y_speed: float = 0 def x_pos(self) -> int: """ Return the x position of the player :return: x position of the player """ return self._rect.x def y_pos(self) -> int: """ Return the y position of the player :return: y position of the player """ return self._rect.y def y_speed(self) -> float: """ Returns the y speed of the player :return: The y speed of the player """ return self._y_speed def is_moving_up(self) -> bool: """ Check if the player is moving up :return: True if the player is, False otherwise """ return self._y_speed < 0 def is_moving_down(self) -> bool: """ Check if the player is moving down :return: True if the player is, False otherwise """ return self._y_speed > 0 def move_up(self) -> None: """ Move the player upward based on the current speed """ # check if the player is moving down if self.is_moving_down(): self.stop() self._move() # accelerate upward if self._y_speed > -PLAYER_MAX_Y_SPEED: self._y_speed -= 0.7 # make sure the current speed doesn't exceed the maximum speed if self._y_speed < -PLAYER_MAX_Y_SPEED: self._y_speed = -PLAYER_MAX_Y_SPEED def move_down(self) -> None: """ Move the player downward based on the current speed """ # check if the player is moving up if self.is_moving_up(): self.stop() self._move() # accelerate downward if self._y_speed < PLAYER_MAX_Y_SPEED: self._y_speed += 0.7 # make sure the current speed doesn't exceed the maximum speed if self._y_speed > PLAYER_MAX_Y_SPEED: self._y_speed = PLAYER_MAX_Y_SPEED def _move(self) -> None: """ Move the player based on the current speed """ self._rect.move_ip(0, self._y_speed) def stop(self) -> None: """ Stop the player's movement """ self._y_speed = 0 def is_collided(self, x_pos: float, y_pos: float) -> bool: """ Check if the given positions are within the player's area :param x_pos: The x position to check :param y_pos: The y position to check :return: True if the given positions are within the player's area, False otherwise """ return self.x_pos( ) <= x_pos <= self.x_pos() + PLAYER_WIDTH and self.y_pos( ) <= y_pos <= self.y_pos() + PLAYER_HEIGHT def reset(self) -> None: """ Reset the player to the initial state """ self._rect.x = self._DEFAULT_X_POS self._rect.y = self._DEFAULT_Y_POS self.stop() def draw(self) -> None: """ Draw the player """ draw.rect(self._window, self._color, self._rect)
class Chromossome: def __init__(self, position, size, _range, cic=3, angl=[]): self.size = size self.initialp = position self.position = position self.xposition = (self.position[0] + self.size[0] // 2), (self.position[1] + self.size[1] // 2) self.rect = Rect(position, size) self.angles = [angles[randint(0, 7)] for i in range(cic)] if angl == [] else angl self._range = _range self.cic = cic self.cicles = -1 def crossover(self, other): i1 = randint(0, self.cic - 1) newchromo1 = Chromossome(self.initialp, self.size, self._range, self.cic, self.angles[:i1] + other.angles[i1:]) newchromo2 = Chromossome(self.initialp, self.size, self._range, self.cic, other.angles[:i1] + self.angles[i1:]) return newchromo1, newchromo2 def move(self, ang): if ang >= pi: if (type(tan(ang)) == float): self.rect.move_ip(-self._range, round(tan(ang) * (-self._range))) self.position = (self.position[0] - self._range, self.position[1] + round(tan(ang) * (-self._range))) else: self.rect.move_ip(0, -self._range) self.position = (self.position[0], self.position[1] - self._range) else: if type(tan(ang)) == float: self.rect.move_ip(self._range, round(tan(ang) * (self._range))) self.position = (self.position[0] + self._range, self.position[1] + round(tan(ang) * (self._range))) else: self.rect.move_ip(0, self._range) self.position = (self.position[0], self.position[1] + self._range) self.xposition = (self.position[0] + self.size[0] // 2), (self.position[1] + self.size[1] // 2) def mutate(self, p=0.03): if random() <= p: self.angles[randint(0, self.cic - 1)] = angles[randint(0, 7)] def getCicl(self, nzombies, zombieargs, dist): if self.cicles > -1: return initialp = self.position, self.xposition zs = [ Zombie(zombieargs[0], [zombieargs[1][0] + i, zombieargs[1][1] + dist * i], zombieargs[2]) for i in range(nzombies) ] k = 0 while True: for z in zs: z.move(self.xposition) self.move(self.angles[k]) k += 1 if ((self.rect.collidelist([z.rect for z in zs])) > -1) or ( self.xposition[0] >= 100 or self.xposition[1] >= 100) or ( self.xposition[0] <= 0 or self.xposition[1] <= 0): ##print(k) self.cicles = k break self.position, self.xposition = initialp
class MapEntity(Sprite): """MapEntity is a sprite that knows how to interact with a map. It contains code that can be run periodically, the default is to do nothing. There are also methods for each type of map event. To do anything useful, subclass this""" def __init__(self, image_map=None): self.name = self.__class__.__name__ if image_map is not None: self.init(image_map) def __getstate__(self): dict = self.__dict__.copy() dict.pop('map') dict.pop('_Sprite__g') dict.pop('image') return dict def __setstate__(self, dict): self.__dict__ = dict Sprite.__init__(self) self.image = util.load_image(CHARACTERS_DIR, *self.image_args) self.animation_speed = 6 def init(self,image_map,tile_x=0,tile_y=0,color_key=None): """MapEntity(Surface, tile_x, tile_y, direction) Surface should be obtained from util.load_image tile_x & tile_y specify what image map to use if you join multiple images into one map (Characters, Entities, etc) legal values are positive integers representing zero based index direction is what direction the entity should face, can also be set later with MapEntity.face(direction) legal values are map.NORTH, map.EAST, map.SOUTH, & map.WEST""" image = util.load_image(CHARACTERS_DIR, image_map, True, color_key) Sprite.__init__(self) self.image_args = (image_map, True, color_key) self.pos = (0,0) self.map = None self.image = image self.image_base_x = tile_x * 3 * TILE_SIZE self.image_base_y = tile_y * 4 * TILE_SIZE self.frame = 0 self.image_tile = Rect( self.image_base_x, self.image_base_y, TILE_SIZE, TILE_SIZE ) self.rect = Rect(0,0,TILE_SIZE,TILE_SIZE) self.face(NORTH) self.next_frame() self.velocity = (0,0) self.speed = 4 self.moving = False # For tile based motion self.always_animate = False self.animation_count = 1 self.animation_speed = 6 self.entered_tile = False self.can_trigger_actions = 0 def speed(self, pixels_per_update): """speed(int) Set the movement speed, currently in pixels per update""" self.speed = pixels_per_update def face(self,direction): self.facing = direction self.image_tile.top = self.image_base_y + (self.facing * TILE_SIZE) def next_frame(self): self.frame = self.frame + 1 if self.frame > 3: self.frame = 0 if self.frame == 3: self.image_tile.left = self.image_base_x + (1 * TILE_SIZE) else: self.image_tile.left = self.image_base_x + (self.frame * TILE_SIZE) def move_to(self, pos): """Moves the entity to a location without running triggers or changing the direction its facing""" x, y = pos self.pos = pos self.rect.top = TILE_SIZE * (y - self.map.viewport.top) self.rect.left = TILE_SIZE * (x - self.map.viewport.left) def move(self, direction, face_dir=True): """move(direction, face_direction=True) Start moving if not already""" if not self.moving: target = add(self.pos, MOVE_VECTORS[direction]) if face_dir: self.face(direction) if self.map.move_ok(target, self): self.direction = MOVE_VECTORS[direction] self.pos = target self.moving = True self.pixels_left_to_move = TILE_SIZE self.velocity = (MOVE_VECTORS[direction][0] * self.speed, \ MOVE_VECTORS[direction][1] * self.speed) def update(self): if self.moving: self.pixels_left_to_move = self.pixels_left_to_move - self.speed self.animation_count = self.animation_count + 1 if self.animation_count % self.animation_speed == 0: self.animation_count = 1 self.next_frame() self.rect.move_ip(self.velocity) if self.pixels_left_to_move < self.speed: # Move the remaining pixels self.velocity = (self.direction[0] * self.pixels_left_to_move, \ self.direction[1] * self.pixels_left_to_move) self.rect.move_ip(self.velocity) self.entered_tile = True self.moving = False else: if self.always_animate: self.animation_count = self.animation_count + 1 if self.animation_count % self.animation_speed == 0: self.animation_count = 1 self.next_frame() ################## The following are all action methods ######################## ############ These are the ones you will override most commonly ################ #TODO Decide if these methods should have a prefix like action_ def activate(self): """Called when the character hits the action key while facing entity""" pass def touch(self): """Called when the character makes contact with the entity This can be one of the following conditions: The character attempts to move into the entity The entity attempts to move into the character This will not be triggered if the sprites overlap temporarily. If it should, add an enhancement request and I'll fix it""" pass def enter_map(self): pass def leave_map(self): pass
screen.blit(original_background, background_dimensions) pygame.display.update() scale = 1 prev_scale = 1 running = True while running: redraw = False normalize = False for event in pygame.event.get(): if event.type == pygame.QUIT: running = False if event.type == pygame.KEYDOWN: redraw = True if event.key == pygame.K_UP: background_dimensions.move_ip(0, 100) elif event.key == pygame.K_DOWN: background_dimensions.move_ip(0, -100) elif event.key == pygame.K_LEFT: background_dimensions.move_ip(100, 0) elif event.key == pygame.K_RIGHT: background_dimensions.move_ip(-100, 0) elif event.key == pygame.K_SPACE: normalize = True else: redraw = False if event.type == pygame.MOUSEBUTTONDOWN: redraw = True if event.button == 4: scale *= 1.1 elif event.button == 5: