def apply(self, subject: Player): previous_position = subject.get_position() w, h = self.environment.get_tile_dimensions() start_x = w - 1 if self.direction == CardinalDirection.EAST else 0 shift = -1 if self.direction == CardinalDirection.EAST else 1 y = subject.get_position().y position = Position(start_x, y) while self.environment.contains(position): if (not self.to_vegetation and self.environment.tile_at(position).is_walkable()): subject.set_position(position) subject.set_direction( self.get_direction(subject, previous_position.x, position.x)) return if (self.to_vegetation and (self.environment.tile_at(position).is_grass() or self.environment.tile_at(position).is_stone())): subject.set_position(position) subject.set_direction( self.get_direction(subject, previous_position.x, position.x)) return position = Position(position.x + shift, position.y) raise ActionException("ContourJump not possible")
def test_bounds_of_the_environment(self): dummy_map = "S....\n" \ "....." e = Environment(self.settings, dummy_map) self.assertTrue(e.contains(Position(0, 0))) self.assertTrue(e.contains(Position(2, 2))) self.assertFalse(e.contains(Position(3, 3))) self.assertFalse(e.contains(Position(500, 300))) self.assertFalse(e.contains(Position(-2, 1)))
def apply(self, subject: Player): w, h = self.environment.get_tile_dimensions() previous_position = subject.get_position() if self.steps >= 0: if self.direction == CardinalDirection.SOUTH: start_y = previous_position.y + self.steps if start_y > h - 1: # if the jump goes out of boundaries, do reverse search start_y = h - 1 shift = -1 else: # otherwise do standard search in that direction shift = 1 else: start_y = previous_position.y - self.steps if start_y < 0: # if the jump goes out of boundaries, do reverse search start_y = 0 shift = 1 else: # otherwise do standard search in that direction shift = -1 else: # for negative step values do reverse search to get to top/bottom if self.direction == CardinalDirection.SOUTH: start_y = h - 1 shift = -1 else: start_y = 0 shift = 1 position = Position(previous_position.x, start_y) while self.environment.contains(position): simulated_player = Player() simulated_player.set_position(position) try: simulated_player.apply_action( ContourJumpAction(self.environment, CardinalDirection.WEST, to_vegetation=True)) subject.set_position(simulated_player.get_position()) subject.set_direction(simulated_player.direction) return except ActionException: position = Position(position.x, position.y + shift) raise ActionException("VerticalJump not possible")
def _construct_tiles(self, encoded_map: CharacterEncodedMap) -> List[List[Tile]]: """ :param encoded_map: constructs the tiles from a character encoded map :return: matrix of tiles """ character_matrix = [[char for char in line] for line in encoded_map.split("\n")] if len(character_matrix) < 1: raise EnvironmentException("Not a valid map encoding - bad size") height = len(character_matrix) + 1 width = len(character_matrix[0]) for row in character_matrix: if len(row) != width: raise EnvironmentException( "Not a valid map encoding - all rows must have the same size") tile_matrix = [[None for _ in range(width)] for _ in range(height)] tile_matrix = self._construct_bottom_row(tile_matrix) for y in range(height - 1): for x in range(width): tile_matrix[y][x] = self._construct_tile(Position(x, y), character_matrix) return tile_matrix
def create_shard_collected(scene: 'GameScene', position: Position) -> 'ParticleSprite': return ParticleSprite( scene, scene.animation_manager.get_animation("particle-shard-collected"), position, px_offset=Position(0, -TILE_SIZE_PX) )
def create_blink_in(scene: 'GameScene', position: Position) -> 'ParticleSprite': return ParticleSprite( scene, scene.animation_manager.get_animation("particle-blink-in"), position, px_offset=Position(0, -TILE_SIZE_PX // 4) )
def create_dash_right(scene: 'GameScene', position: Position) -> 'ParticleSprite': return ParticleSprite( scene, scene.animation_manager.get_animation("particle-dash-right"), position, px_offset=Position(TILE_SIZE_PX // 2, 1) )
def create_blink_out(scene: 'GameScene', position: Position, direction: CardinalDirection) -> 'ParticleSprite': return ParticleSprite( scene, scene.animation_manager.get_animation("particle-blink-out"), position, px_offset=Position(0, 0), flip_x=direction != CardinalDirection.EAST )
def create_descent(scene: 'GameScene', position: Position, direction: CardinalDirection): animation_name = "particle-descent-left" \ if direction == CardinalDirection.WEST else "particle-descent-right" return ParticleSprite( scene, scene.animation_manager.get_animation(animation_name), position, px_offset=Position(0, -TILE_SIZE_PX // 2) )
def _prepare_surfaces_for_tiles(self) -> List[List[List[Surface]]]: tile_matrix = self.environment.get_tile_matrix() surfaces = [[None for _ in row] for row in tile_matrix] for y in range(len(tile_matrix)): for x in range(len(tile_matrix[y])): surfaces[y][x] = \ self._prepare_surfaces_for_tile(Position(x, y), tile_matrix) return surfaces
def _get_starting_position(self, encoded_map) -> Position: starting_positions = [] for y, row in enumerate(encoded_map.split("\n")): for x, character in enumerate(row): if character == "S": starting_positions.append(Position(x, y)) if len(starting_positions) != 1: raise EnvironmentException( f"Wrong number of starting positions (expected 1, got {len(starting_positions)})") return starting_positions[0]
def _increment_position_with_direction(self, position: Position) -> Position: x, y = position width = self.environment.get_tile_dimensions()[0] x = x + 1 if self.direction == CardinalDirection.EAST else x - 1 # if x is out of bounds, jump to the next/previous row if x < 0: x = width - 1 y -= 1 if x > width - 1: x = 0 y += 1 return Position(x, y)
def test_should_serialize_correctly(self): r = Recording() r.start(10_000) r.record_text_input(10_100, "kLhi") r.record_text_input(10_200, "k") r.record_shard_spawn(10_300, Position(1, 1)) r.record_text_input(10_500, "I") output = r.serialize() self.assertEqual(5, len(output.split("\n"))) self.assertEqual( f"V 0 {VERSION}\nI 100 kLhi\nI 200 k\nS 300 1 1\nI 500 I", output)
def test_standard_environment_creation(self): dummy_map = "/o/..\n" \ "S.../" e = Environment(self.settings, dummy_map) self.assertEqual(15, len(e.get_all_tiles())) # First tile is walkable grass and not dirt first_tile = e.tile_at(Position(0, 0)) self.assertTrue(first_tile.is_walkable()) self.assertFalse(first_tile.is_dirt()) self.assertTrue(first_tile.is_grass()) # Second tile is walkable stone and not dirt second_tile = e.tile_at(Position(1, 0)) self.assertTrue(second_tile.is_walkable()) self.assertFalse(second_tile.is_dirt()) self.assertTrue(second_tile.is_stone()) # Last tile is walkable grass with dirt last_tile = e.tile_at(Position(4, 1)) self.assertTrue(last_tile.is_walkable()) self.assertTrue(last_tile.is_dirt()) self.assertTrue(last_tile.is_grass())
def spawn_shard(self, position: Position) -> None: """Spawns a shard at a given position. :param position: where to spawn a shard """ shard = Shard(position) self.shard_group.add(ShardSprite(self, shard)) self.recording.record_shard_spawn(pygame.time.get_ticks(), position) if shard.position.y > self.vertical_shift + HEIGHT_IN_TILES: # shard is below the screen particle = ParticleSprite.create_shard_pointer( self, Position(shard.position.x, self.vertical_shift + HEIGHT_IN_TILES - 2), CardinalDirection.SOUTH ) self.particle_group.add(particle) elif shard.position.y < self.vertical_shift: # shard is above the screen particle = ParticleSprite.create_shard_pointer( self, Position(shard.position.x, self.vertical_shift + 1), CardinalDirection.NORTH ) self.particle_group.add(particle)
def apply(self, subject: Any): player: Player = subject current_position = player.get_position() current_y = current_position.y future_y = current_y + self.steps if 0 <= future_y < self.environment.get_tile_dimensions()[1] - 1: future_position = Position(player.get_position().x, future_y) if self.environment.tile_at(future_position).is_walkable(): player.set_position(future_position) else: raise ActionException(f"Vertical move would end up on non-" f"walkable position {future_position}") else: raise ActionException( f"Vertical move outside of bounds: {future_y}")
def test_should_record_correctly(self): r = Recording() r.start(10_000) r.record_text_input(10_100, "kLhi") r.record_text_input(10_200, "k") r.record_shard_spawn(10_300, Position(1, 1)) r.record_text_input(10_500, "I") self.assertEqual(5, len(r.data)) self.assertEqual(0, r.data[0].time) self.assertIsInstance(r.data[0], VersionInfo) self.assertEqual(100, r.data[1].time) self.assertIsInstance(r.data[1], TextInput) self.assertEqual(200, r.data[2].time) self.assertIsInstance(r.data[2], TextInput) self.assertEqual(300, r.data[3].time) self.assertIsInstance(r.data[3], ShardSpawn) self.assertEqual(500, r.data[4].time) self.assertIsInstance(r.data[4], TextInput)
def __init__(self, scene: 'GameScene', animation: Animation, position: Position, px_offset: Position = Position(0, 0), flip_x: bool = False, flip_y: bool = False, *groups: AbstractGroup): super().__init__(*groups) self.scene = scene self.animation = animation self.position = position self.px_offset = px_offset self.flip_x = flip_x self.flip_y = flip_y # automatically start the animation on creation self.animation.start(pygame.time.get_ticks()) self._update_image() self.rect = self.image.get_rect() self._update_rectangle_based_on_vertical_shift()
def apply(self, subject: Player): current_position = subject.get_position() current_x = current_position.x future_x = current_x + self.steps if 0 <= future_x < self.environment.get_tile_dimensions()[0]: future_position = Position(future_x, subject.get_position().y) if self.environment.tile_at(future_position).is_walkable(): subject.set_position(future_position) future_direction = CardinalDirection.EAST \ if self.steps > 0 else CardinalDirection.WEST subject.set_direction(future_direction) else: raise ActionException(f"Horizontal move would end up on non-" f"walkable position {future_position}") else: raise ActionException( f"Horizontal move outside of bounds: {future_x}")
def __init__(self): self.position = Position(0, 0) self.direction = CardinalDirection.EAST
def parse(text: str) -> 'ShardSpawn': parts = text.split() if parts[0] != "S" or len(parts) != 4: raise ParsingException("Not a ShardSpawn event") return ShardSpawn(int(parts[1]), Position(int(parts[2]), int(parts[3])))
def get_random_position(environment: 'Environment') -> Position: w, h = environment.get_tile_dimensions() x = random.randint(0, w - 1) y = random.randint(0, h - 2) return Position(x, y)