예제 #1
0
def collision_resolution(entity, x, y):

    # basic platformer collision resolution routine
    # TODO fix spacing. for some reason certain collisions allow for a gap in
    # space (between 1 and 2 pixels wide)

    vel_x = entity.velocity_x
    vel_y = entity.velocity_y

    collision_results = collides_solid(entity.container, entity.at_point(x, y))
    for result in collision_results:
        colliding = result.function
        other = result.shape

        # Check if collision is resolved, on y axis, or on x axis
        test_a = (other, entity.at_point(x, y))
        test_b = (other, entity.at_point(x, entity.y))
        test_c = (other, entity.at_point(entity.x, y))

        if not result.is_first:
            test_a = test_a[1], test_a[0]
            test_b = test_b[1], test_b[0]
            test_c = test_c[1], test_c[0]

        # collision already resolved
        if not colliding(*test_a):
            continue

        # collision resolved by reverting y axis
        elif not colliding(*test_b):
            if y - entity.height - vel_y < other.y and vel_y > 0:
                y = other.y - entity.height
            elif y + vel_y > other.y - other.height and vel_y < 0:
                y = other.y + other.height
            vel_y = 0

        # collision resolved by reverting x axis
        elif not colliding(*test_c):
            if x - entity.width - vel_x < other.x and vel_x > 0:
                x = other.x - entity.width
            elif x + vel_x > other.x - other.width and vel_x < 0:
                x = other.x + other.width
            vel_x = 0

        # collision resoultion not found, revert to original space
        else:
            x = entity.x
            y = entity.y
            vel_x = 0
            vel_y = 0
            break

    collision_occurred = len(collision_results) > 0
    return collision_occurred, x, y, vel_x, vel_y
예제 #2
0
    def update(self):
        temp_x = self.x
        temp_y = self.y
        has_solid_below = solid_below(self, self.x, self.y)

        # Key polling
        keydown_up = Key.down('up')
        keydown_down = Key.down('down')
        keydown_left = Key.down('left')
        keydown_right = Key.down('right')
        keydown_run = False  # Key.down('lshift')
        keypressed_up = Key.pressed('up')
        keypressed_down = Key.pressed('down')
        keypressed_left = Key.pressed('left')
        keypressed_right = Key.pressed('right')
        keypressed_jump = Key.pressed('space')
        keypressed_gadget = Key.pressed('x')

        if self.state == Player.STATE_STANDARD:
            # Run/Walk
            if keydown_left == keydown_right:
                self.velocity_x = 0
                self.walking_sound.stop()
            else:
                if keydown_left:
                    if keydown_run:
                        self.velocity_x = -Player.SPEED_FAST
                    else:
                        self.velocity_x = -Player.SPEED
                    self.facing_x = -1
                if keydown_right:
                    if keydown_run:
                        self.velocity_x = Player.SPEED_FAST
                    else:
                        self.velocity_x = Player.SPEED
                    self.facing_x = 1

                # Play walking sound effect
                if has_solid_below:
                    self.walking_sound.play(-1)
                else:
                    self.walking_sound.stop()

                # Push block
                block = peachy.collision.collides_group(
                    self.container, 'block',
                    self.at_point(temp_x + self.velocity_x, self.y))
                if len(block) == 1 and has_solid_below:
                    block = block[0]
                    block_temp_x = block.x + self.velocity_x

                    if not peachy.collision.collides_solid(
                            self.container,
                            block.at_point(block_temp_x, block.y)):
                        self.change_state(Player.STATE_PUSHING, block=block)
                        if self.x < block.x:
                            self.x = block.x - self.width
                        else:
                            self.x = block.x + block.width
                        return

            # Jump
            if keypressed_jump and has_solid_below:
                self.velocity_y = -Player.JUMP_FORCE

            # Wall Grab
            if self.velocity_y < 4 and self.velocity_y > 0:
                climb_wall = []
                if keydown_left:
                    climb_wall = solid_left(self, self.x, self.y)
                if keydown_right:
                    climb_wall = solid_right(self, self.x, self.y)

                ledge = None
                for wall in climb_wall:
                    if wall.y - self.y < 3 and not wall.member_of('block'):
                        if self.y <= wall.y:
                            ledge = wall
                        else:
                            ledge = None

                if ledge is not None:
                    test = Rect(self.x, ledge.y - Player.HEIGHT_CROUCH,
                                self.width, Player.HEIGHT_CROUCH)
                    test.container = self.container

                    if test.x + test.width <= ledge.x:
                        test.x = ledge.x
                    elif test.x >= ledge.x + ledge.width:
                        test.x = ledge.x + ledge.width - test.width

                    if not collides_solid(self.container, test):
                        self.change_state(Player.STATE_LEDGEGRAB, ledge=ledge)
                        return

            # Interact
            if keydown_up:
                interactables = collides_group(self.container, 'interact',
                                               self)
                for interact in interactables:
                    if interact.member_of('rope'):
                        vertical = False
                        if interact.width == 0:
                            vertical = True
                        elif interact.height == 0:
                            vertical = False

                        self.change_state(Player.STATE_CLIMBING,
                                          handle=interact,
                                          vertical=vertical)

                        interact.attach(self)
                        return

                    elif interact.member_of('ladder'):
                        self.x = interact.x
                        self.change_state(Player.STATE_CLIMBING,
                                          handle=interact,
                                          vertical=True)
                        return

                    elif keypressed_up:
                        if interact.member_of('button'):
                            interact.press()

                        elif interact.member_of('door'):
                            if self.gadget.state == gadgets.Gadget.STATE_ACTIVE:
                                self.gadget.cancel()
                            interact.enter()

                        elif interact.member_of('lever'):
                            interact.pull()

                        elif interact.member_of('hiding-spot'):
                            self.change_state(Player.STATE_HIDDEN,
                                              hiding_spot=interact)
                            return

                        elif interact.member_of('message-box'):
                            interact.activate()
                            return

            # Climb down ladder
            if keypressed_down:
                ladder = collides_group(self.container, 'ladder',
                                        self.at_point(self.x, self.y + 1))
                if ladder:
                    ladder = ladder[0]
                    if self.y + self.height < ladder.y + ladder.height:
                        self.x = ladder.x
                        if self.y < ladder.y:
                            self.y = ladder.y
                        self.change_state(Player.STATE_CLIMBING,
                                          handle=ladder,
                                          vertical=True)
                        return

            # Pickup
            if keypressed_up:
                pickups = collides_group(self.container, 'pickup', self)
                for pickup in pickups:
                    if pickup.member_of('gadget'):
                        self.change_gadget(pickup.gadget)
                    elif pickup.member_of('key'):
                        self.obtained_keys.append(pickup.tag)
                        self.key_count += 1
                        pickup.destroy()

            # Crouching
            if keypressed_down and has_solid_below:
                self.change_state(Player.STATE_CROUCHING)
                return

            # Gravity
            if self.velocity_y < config.MAX_GRAVITY:
                self.velocity_y += config.GRAVITY

        elif self.state == Player.STATE_CLIMBING:
            if keypressed_jump:
                self.velocity_y = -Player.JUMP_FORCE
                self.change_state(Player.STATE_STANDARD)
                return

            handle = self.state_args['handle']
            vertical = self.state_args['vertical']

            if vertical:
                if keydown_up == keydown_down:
                    self.velocity_y = 0
                else:
                    if keydown_up:
                        if self.y > handle.y:
                            self.velocity_y = -Player.SPEED_SLOW
                        else:
                            self.velocity_y = 0
                            if handle.member_of('ladder'):
                                self.y = handle.y - self.height
                                self.change_state(Player.STATE_STANDARD)
                                return
                    if keydown_down:
                        if self.y + self.height < handle.y + handle.height:
                            self.velocity_y = Player.SPEED_SLOW
                        else:
                            self.velocity_y = 0
                            self.change_state(Player.STATE_STANDARD)
                            return
            else:
                if keypressed_down:
                    self.change_state(Player.STATE_STANDARD)
                    return

                if keydown_left == keydown_right:
                    self.velocity_x = 0
                else:
                    if keydown_left:
                        if self.x > handle.x:
                            self.velocity_x = -Player.SPEED_SLOW
                        else:
                            self.velocity_x = 0
                    if keydown_right:
                        if self.x + self.width < handle.x + handle.width:
                            self.velocity_x = Player.SPEED_SLOW
                        else:
                            self.velocity_x = 0

        elif self.state == Player.STATE_CROUCHING:
            if keydown_left == keydown_right:
                self.velocity_x = 0
            else:
                if keydown_left:
                    self.velocity_x = -Player.SPEED_SLOW
                    self.facing_x = -1
                if keydown_right:
                    self.velocity_x = Player.SPEED_SLOW
                    self.facing_x = 1

                # attempt to grab ledge when crawling off one
                tx = self.velocity_x + self.x
                if not solid_below(self, tx, self.y):
                    ledges = solid_below(self, self.x, self.y)
                    try:
                        ledge = ledges[0]

                        if self.velocity_x > 0:
                            test = Rect(ledge.x + ledge.width, ledge.y,
                                        self.width, Player.HEIGHT_STANDARD)
                        else:
                            test = Rect(ledge.x - self.width, ledge.y,
                                        self.width, Player.HEIGHT_STANDARD)

                        test.container = self.container

                        if not collides_solid(self.container, test):
                            self.x = test.x
                            self.facing_x *= -1
                            self.change_state(Player.STATE_LEDGEGRAB,
                                              ledge=ledge)
                            return
                    except IndexError:
                        pass

            attempt_stand = False

            if keypressed_jump and has_solid_below:
                self.velocity_y = -Player.JUMP_FORCE
                attempt_stand = True
            if not keydown_down:
                attempt_stand = True

            if attempt_stand:
                self.height = Player.HEIGHT_STANDARD
                temp_y = self.y - \
                    abs(Player.HEIGHT_STANDARD - Player.HEIGHT_CROUCH)
                if collides_solid(self.container,
                                  self.at_point(self.x, temp_y)):
                    self.height = Player.HEIGHT_CROUCH
                    temp_y = self.y
                    self.velocity_y = 0
                else:
                    self.state = Player.STATE_STANDARD
                    self.y = temp_y

            self.height = Player.HEIGHT_STANDARD
            if collides_solid(self.container, self.at_point(temp_x, temp_y)):
                self.height = Player.HEIGHT_CROUCH
            else:
                self.state = Player.STATE_STANDARD

        elif self.state == Player.STATE_HIDDEN:
            if keypressed_up or keypressed_left or keypressed_right:
                self.change_state(Player.STATE_STANDARD)

        elif self.state == Player.STATE_LEDGEGRAB:
            # Climb up ledge
            if keypressed_up or keypressed_left or keypressed_right:
                ledges = None
                if keydown_up:
                    try:
                        ledges = solid_left(self, self.x, self.y)
                        assert ledges[0].y == self.y
                    except (AssertionError, IndexError):
                        ledges = solid_right(self, self.x, self.y)
                elif keydown_left:
                    ledges = solid_left(self, self.x, self.y)
                elif keydown_right:
                    ledges = solid_right(self, self.x, self.y)

                try:
                    ledge = ledges[0]
                    assert ledge.y == self.y

                    test = peachy.geo.Rect(self.x,
                                           ledge.y - Player.HEIGHT_CROUCH,
                                           self.width, Player.HEIGHT_CROUCH)
                    test.container = self.container

                    if test.x + test.width <= ledge.x:
                        test.x = ledge.x
                    elif test.x >= ledge.x + ledge.width:
                        test.x = ledge.x + ledge.width - test.width

                    if not collides_solid(self.container, test):
                        self.change_state(Player.STATE_CROUCHING)
                        self.x = test.x
                        self.y = test.y
                        return
                except (AssertionError, IndexError):
                    pass

            # Let go of ledge
            if keypressed_left and not solid_left(self, self.x, self.y) or \
               keypressed_right and not solid_right(self, self.x, self.y) or \
               keypressed_down:
                self.state = Player.STATE_STANDARD
            if keypressed_jump:
                self.state = Player.STATE_STANDARD
                self.velocity_y = -Player.JUMP_FORCE

        elif self.state == Player.STATE_PUSHING:
            block = self.state_args['block']

            if self.x > block.x and keydown_left:
                self.velocity_x = -Player.SPEED_SLOW
            elif self.x < block.x and keydown_right:
                self.velocity_x = Player.SPEED_SLOW
            else:
                self.change_state(Player.STATE_STANDARD)
                return

            if keypressed_jump:
                self.velocity_y = -Player.JUMP_FORCE
                self.change_state(Player.STATE_STANDARD)
                return

            test_x = temp_x + self.velocity_x
            if not rect_rect(self.at_point(test_x, self.y), block):
                self.change_state(Player.STATE_STANDARD)
                return

            block_temp_x = block.x + self.velocity_x
            if not collides_solid(self.container,
                                  block.at_point(block_temp_x, block.y)):
                block.x = block_temp_x

        # GADGET
        if self.state != Player.STATE_DEAD:
            if keypressed_gadget and self.gadget.name:
                self.gadget.use()

        # ADVANCE
        temp_x += self.velocity_x
        temp_y += self.velocity_y

        # LOCKED DOOR
        lock = collides_group(self.container, 'locked-door',
                              self.at_point(temp_x, temp_y))
        if lock:
            lock = lock[0]
            if self.key_count > 0:
                self.key_count -= 1
                self.container.unlocked_doors.append(lock.tag)
                lock.destroy()

        # FINALIZE
        c, self.x, self.y, self.velocity_x, self.velocity_y = \
            collision_resolution(self, temp_x, temp_y)

        self.gadget.update()
예제 #3
0
def solid_left(entity, x, y):
    return collides_solid(entity.container, entity.at_point(x - 1, y))
예제 #4
0
def solid_right(entity, x, y):
    return collides_solid(entity.container, entity.at_point(x + 1, y))
예제 #5
0
def solid_below(entity, x, y):
    return collides_solid(entity.container, entity.at_point(x, y + 1))