extends GutTest ## The grid is the foundation for collision, bullets, fog and interest ## management, so its edge cases are worth pinning down directly rather than ## discovering them as strange behaviour three systems away. var grid: MapGrid func before_each() -> void: # 10x10 room: solid border, open interior. grid = MapGrid.new(10, 10, MapGrid.Kind.WALL) grid.fill_rect(Rect2i(1, 1, 8, 8), MapGrid.Kind.FLOOR) func test_out_of_bounds_reads_as_wall() -> void: assert_eq(grid.at(-1, 5), MapGrid.Kind.WALL, "the world has to be sealed without every caller bounds-checking") assert_eq(grid.at(999, 999), MapGrid.Kind.WALL) func test_tile_flags_are_independent() -> void: # The whole reason for three flags rather than one "solid" bit. assert_true(grid.BLOCKS_MOVE[MapGrid.Kind.PIT], "you cannot walk over a pit") assert_false(grid.BLOCKS_BULLET[MapGrid.Kind.PIT], "but you can shoot over it") assert_false(grid.BLOCKS_SIGHT[MapGrid.Kind.PIT], "and see over it") assert_true(grid.BLOCKS_MOVE[MapGrid.Kind.BARRICADE]) assert_true(grid.BLOCKS_BULLET[MapGrid.Kind.BARRICADE]) assert_false(grid.BLOCKS_SIGHT[MapGrid.Kind.BARRICADE], "chest height: you see over it but cannot shoot or walk through") func test_world_and_tile_coordinates_round_trip() -> void: assert_eq(grid.to_tile(Vector2(0.0, 0.0)), Vector2i(0, 0)) assert_eq(grid.to_tile(Vector2(MapGrid.TILE * 3.5, MapGrid.TILE * 2.5)), Vector2i(3, 2)) assert_eq(grid.tile_centre(3, 2), Vector2(MapGrid.TILE * 3.5, MapGrid.TILE * 2.5)) assert_eq(grid.world_size(), Vector2(10.0, 10.0) * MapGrid.TILE) func test_negative_world_positions_map_outside_the_grid() -> void: # floor(), not truncation -- int() would fold -0.5 onto tile 0 and let an # actor stand half a tile outside the map. assert_eq(grid.to_tile(Vector2(-1.0, -1.0)), Vector2i(-1, -1)) func test_circle_collision_against_a_wall() -> void: var open := grid.tile_centre(4, 4) assert_false(grid.circle_blocked(open, 6.0)) # Hard against the left wall: tile 0 is solid, so a circle at tile 1's # centre minus most of a tile overlaps it. assert_true(grid.circle_blocked(Vector2(MapGrid.TILE + 2.0, grid.tile_centre(1, 4).y), 6.0)) func test_sliding_along_a_wall_preserves_the_free_axis() -> void: # Moving diagonally into the top wall should keep the horizontal motion. var start := grid.tile_centre(4, 1) var moved := grid.slide_circle(start, Vector2(8.0, -40.0), 6.0) assert_almost_eq(moved.x, start.x + 8.0, 0.001, "x is unobstructed and must not be lost") assert_lt(absf(moved.y - start.y), 40.0, "y is blocked by the wall") func test_a_circle_cannot_be_pushed_through_a_wall() -> void: var start := grid.tile_centre(4, 1) for _i in 60: start = grid.slide_circle(start, Vector2(0.0, -100.0), 6.0) assert_gt(start.y, MapGrid.TILE, "no amount of shoving may cross a solid tile") func test_bullets_stop_at_walls_but_cross_pits() -> void: grid.set_tile(4, 4, MapGrid.Kind.PIT) assert_false(grid.bullet_blocked(grid.tile_centre(4, 4)), "bullets fly over pits") assert_true(grid.bullet_blocked(grid.tile_centre(0, 0)), "and stop at walls") func test_line_of_sight_is_blocked_by_walls_and_not_by_pits() -> void: var a := grid.tile_centre(1, 4) var b := grid.tile_centre(8, 4) assert_true(grid.has_line_of_sight(a, b), "clear floor between them") grid.set_tile(4, 4, MapGrid.Kind.PIT) assert_true(grid.has_line_of_sight(a, b), "a pit is a hole, not a screen") grid.set_tile(4, 4, MapGrid.Kind.PILLAR) assert_false(grid.has_line_of_sight(a, b), "a pillar blocks it") grid.set_tile(4, 4, MapGrid.Kind.BARRICADE) assert_true(grid.has_line_of_sight(a, b), "you can see over a barricade") func test_line_of_sight_is_symmetric() -> void: grid.set_tile(5, 4, MapGrid.Kind.WALL) var a := grid.tile_centre(1, 4) var b := grid.tile_centre(8, 4) assert_eq(grid.has_line_of_sight(a, b), grid.has_line_of_sight(b, a), "asymmetric sight would mean an enemy can shoot you from cover you " + "cannot shoot back into") func test_standing_inside_a_wall_can_still_see_out() -> void: # Endpoints must not block, or an actor clipped into geometry goes blind # and its aggro check silently fails. var inside := grid.tile_centre(0, 0) var outside := grid.tile_centre(1, 1) assert_true(grid.has_line_of_sight(inside, outside))