class Bro(entity.Entity): MASS = 10 FRICTION = 0 SIZE = 32 JUMP_VECTOR = (0, 2000) SPEED = 100 FREEZE_FADE = 0.3 COLLISION_TYPE = 2 FROZEN_COLLISION_TYPE = 3 @property def color(self): return self._color @color.setter def color(self, val): self._color = val self.red = val.red self.green = val.green self.blue = val.blue @color.deleter def color(self): del self._color self.red = 1 self.green = 1 self.blue = 1 @property def last_tile(self): if self._last_tile is None: return None else: return self._last_tile() @last_tile.setter def last_tile(self, val): # untint old last tile if self._last_tile is not None: self._last_tile().unset_last_tile_for(self) if val is None: self._last_tile = None else: self._last_tile = weakref.ref(val) self._last_tile().set_last_tile_for(self) def __init__(self, player, space, x=0, y=0, old_bro=None, *args, **kwargs): super(Bro, self).__init__(space, x = x, y = y, mass = Bro.MASS, friction = Bro.FRICTION, collision_type = Bro.COLLISION_TYPE, width = Bro.SIZE, height = Bro.SIZE, texture = resources.box) self.player = player self.color = player.color self.dead = False self.frozen = False self.player.bros.append(self) # Model state self.moving_left = False self.moving_right = False self.collide_left = False self.collide_right = False self.shapes_below = WeakSet() self._last_tile = None # get old bro values if old_bro is not None: self.moving_left = old_bro.moving_left self.moving_right = old_bro.moving_right self.last_tile = old_bro.last_tile def update(self, dt): super(Bro, self).update(dt) # Move based on input if self.moving_left: target_velocity = -Bro.SPEED elif self.moving_right: target_velocity = Bro.SPEED else: target_velocity = 0 self.pymunk_body.velocity.x = target_velocity # update player distance self.player.distance = int(max(self.x, self.player.distance)) def freeze(self): self.frozen = True self.static = True self.player.freezes += 1 self.stop() # set collision type for physics callbacks #self.pymunk_shape.collision_type = Bro.FROZEN_COLLISION_TYPE # Fade sprite self.red += Bro.FREEZE_FADE self.green += Bro.FREEZE_FADE self.blue += Bro.FREEZE_FADE def drop(self): if self.frozen: self.static = False def die(self): self.dead = True if not self.frozen: self.player.deaths += 1 # unset last tile self.last_tile = None # remove body from physics space self.space.remove(self.pymunk_body, self.pymunk_shape) def alive(self): return not self.dead and not self.frozen def jump(self): if self.can_jump(): self.pymunk_body.apply_impulse(Bro.JUMP_VECTOR, (0, 0)) # clear the shapes below set for shape in self.shapes_below: self.shapes_below.remove(shape) def can_jump(self): return len(self.shapes_below) > 0 def move_right(self): self.moving_right = True self.moving_left = False def move_left(self): self.moving_left = True self.moving_right = False def stop_right(self): self.moving_right = False def stop_left(self): self.moving_left = False def stop(self): self.stop_right() self.stop_left() @staticmethod def setup_collision_handlers(outer_space): required_overlap = 15 # Prior to solbing collision def pre_solve_handler(space, arbiter): contact = arbiter.contacts[0] a = arbiter.shapes[0] b = arbiter.shapes[1] # if a is above b if abs(a.body.position.x - b.body.position.x) < required_overlap: if a.body.position.y > b.body.position.y: record_shape_below(a, b) # otherwise b is above a else: record_shape_below(b, a) # Allow sliding down walls (sorta, we still catch on 'corners') if round(contact.normal.x) == 1: body = arbiter.shapes[0].body bro = body.entity #body.velocity.x = contact.normal.x * Bro.SPEED return True def record_shape_below(above, below): bro = above.entity if type(bro) is Bro and not bro.frozen: # record this as an object under us bro.shapes_below.add(below) # check if we should record the last tile for this bro t = below.entity if type(t) is tile.Tile and t.static: bro.last_tile = t # When first seperating def separate_handler(space, arbiter): # One less object below bro_shape = arbiter.shapes[0] bro = bro_shape.entity other_shape = arbiter.shapes[1] other = other_shape.entity # Remove object if it was below if other_shape in bro.shapes_below: bro.shapes_below.remove(other_shape) return True # Handlers between bros and tiles outer_space.add_collision_handler( Bro.COLLISION_TYPE, tile.Tile.COLLISION_TYPE, pre_solve = pre_solve_handler, separate = separate_handler ) # Handlers between bros outer_space.add_collision_handler( Bro.COLLISION_TYPE, Bro.COLLISION_TYPE, pre_solve = pre_solve_handler, separate = separate_handler )
class Tile(entity.Entity): MASS = 20 FRICTION = 0.5 SIZE = 32 WHITE_RATIO = 0.5 BRIGHTNESS_MIN = 0.7 BRIGHTNESS_RANGE = 1.0 - BRIGHTNESS_MIN DROP_IMPULSES = [(100, 0), (-100, 0)] DROP_OFFSET = (0, SIZE // 2) COLLISION_TYPE = 1 def __init__(self, x, y, space, *args, **kwargs): self.space = space super(Tile, self).__init__(space, x=x * Tile.SIZE, y=y * Tile.SIZE, angle=0, width=Tile.SIZE, height=Tile.SIZE, friction=Tile.FRICTION, collision_type=Tile.COLLISION_TYPE, mass=Tile.MASS, static=True, texture=resources.box) # random colour if random.random() < Tile.WHITE_RATIO: brightness = random.random( ) % Tile.BRIGHTNESS_RANGE + Tile.BRIGHTNESS_MIN self.rgb = brightness, brightness, brightness self.orig_color = self.rgb self.tints = WeakSet() def drop(self): self.static = False self.untint_all() self.pymunk_body.apply_impulse(random.choice(Tile.DROP_IMPULSES), Tile.DROP_OFFSET) def set_last_tile_for(self, bro): self.tint(bro.color) def unset_last_tile_for(self, bro): self.untint(bro.color) def tint(self, color): self.tints.add(color) self._color() def untint(self, color): if color in self.tints: self.tints.remove(color) self._color() def untint_all(self): for tint in self.tints: self.tints.remove(tint) self._color() def _color(self): # start with orig colour self.red = self.orig_color[0] self.green = self.orig_color[1] self.blue = self.orig_color[2] # overlay each tint for tint in self.tints: t = tint.fade(Tile.BRIGHTNESS_RANGE).invert() self.red -= t.red self.green -= t.green self.blue -= t.blue
class Tile(entity.Entity): MASS = 20 FRICTION = 0.5 SIZE = 32 WHITE_RATIO = 0.5 BRIGHTNESS_MIN = 0.7 BRIGHTNESS_RANGE = 1.0 - BRIGHTNESS_MIN DROP_IMPULSES = [(100, 0), (-100, 0)] DROP_OFFSET = (0, SIZE // 2) COLLISION_TYPE = 1 def __init__(self, x, y, space, *args, **kwargs): self.space = space super(Tile, self).__init__( space, x=x * Tile.SIZE, y=y * Tile.SIZE, angle=0, width=Tile.SIZE, height=Tile.SIZE, friction=Tile.FRICTION, collision_type=Tile.COLLISION_TYPE, mass=Tile.MASS, static=True, texture=resources.box, ) # random colour if random.random() < Tile.WHITE_RATIO: brightness = random.random() % Tile.BRIGHTNESS_RANGE + Tile.BRIGHTNESS_MIN self.rgb = brightness, brightness, brightness self.orig_color = self.rgb self.tints = WeakSet() def drop(self): self.static = False self.untint_all() self.pymunk_body.apply_impulse(random.choice(Tile.DROP_IMPULSES), Tile.DROP_OFFSET) def set_last_tile_for(self, bro): self.tint(bro.color) def unset_last_tile_for(self, bro): self.untint(bro.color) def tint(self, color): self.tints.add(color) self._color() def untint(self, color): if color in self.tints: self.tints.remove(color) self._color() def untint_all(self): for tint in self.tints: self.tints.remove(tint) self._color() def _color(self): # start with orig colour self.red = self.orig_color[0] self.green = self.orig_color[1] self.blue = self.orig_color[2] # overlay each tint for tint in self.tints: t = tint.fade(Tile.BRIGHTNESS_RANGE).invert() self.red -= t.red self.green -= t.green self.blue -= t.blue