152 lines
3.8 KiB
Python
152 lines
3.8 KiB
Python
"""Player movement & collision."""
|
|
|
|
from conftest import step, run, place, hold
|
|
from game import settings as S
|
|
|
|
FLAT = """
|
|
name: t
|
|
tile_size: 32
|
|
map: |
|
|
##########
|
|
#........#
|
|
#........#
|
|
#........#
|
|
#P......G#
|
|
##########
|
|
"""
|
|
|
|
|
|
def test_falls_and_lands_on_floor(make_game):
|
|
g = make_game(FLAT)
|
|
place(g, 4, 1) # up in the air
|
|
run(g, 60)
|
|
assert g.player.on_ground
|
|
assert g.player.rect.bottom == 5 * 32 # floor is row 5's top (y=160)
|
|
|
|
|
|
def test_terminal_velocity(make_game):
|
|
g = make_game(FLAT)
|
|
place(g, 4, 1)
|
|
for _ in range(200):
|
|
step(g)
|
|
assert g.player.vy <= S.MAX_FALL + 1
|
|
|
|
|
|
def test_jump_gains_height_then_returns(make_game):
|
|
g = make_game(FLAT)
|
|
run(g, 30) # settle on floor
|
|
ground = g.player.rect.bottom
|
|
peak = ground
|
|
for i in range(60):
|
|
step(g, hold(jump_pressed=(i == 0), jump_held=True))
|
|
peak = min(peak, g.player.rect.bottom)
|
|
assert peak < ground - 32 # rose at least a tile
|
|
|
|
|
|
def test_variable_jump_height(make_game):
|
|
# Full-hold jump should out-climb a 1-frame tap.
|
|
def peak(hold_frames):
|
|
g = make_game(FLAT)
|
|
run(g, 30)
|
|
ground = g.player.rect.bottom
|
|
hi = ground
|
|
for i in range(60):
|
|
held = i < hold_frames
|
|
step(g, hold(jump_pressed=(i == 0), jump_held=held))
|
|
hi = min(hi, g.player.rect.bottom)
|
|
return ground - hi
|
|
assert peak(60) > peak(1) + 8
|
|
|
|
|
|
def test_coyote_time_allows_jump_after_leaving_ledge(make_game):
|
|
# Walk off a ledge, then jump within the coyote window -> should rise.
|
|
g = make_game("""
|
|
name: t
|
|
tile_size: 32
|
|
map: |
|
|
#######
|
|
#.....#
|
|
#.....#
|
|
##....#
|
|
#P#...#
|
|
#######
|
|
""")
|
|
place(g, 1, 3) # standing on the little step at [1,3] top
|
|
# walk right off the step for a couple frames, then jump
|
|
run(g, 4, lambda i: hold(right=True))
|
|
y_before = g.player.rect.bottom
|
|
step(g, hold(right=True, jump_pressed=True, jump_held=True))
|
|
step(g, hold(right=True, jump_held=True))
|
|
assert g.player.vy < 0 # a jump actually started
|
|
|
|
|
|
def test_walls_stop_horizontal_movement(make_game):
|
|
g = make_game(FLAT)
|
|
place(g, 1, 4)
|
|
run(g, 120, lambda i: hold(right=True))
|
|
assert g.player.rect.right <= 9 * 32 # right wall inner edge (x=288)
|
|
run(g, 120, lambda i: hold(left=True))
|
|
assert g.player.rect.left >= 1 * 32 # left wall inner edge (x=32)
|
|
|
|
|
|
def test_oneway_platform_land_from_above_pass_from_below(make_game):
|
|
g = make_game("""
|
|
name: t
|
|
tile_size: 32
|
|
map: |
|
|
#######
|
|
#.....#
|
|
#.....#
|
|
#--...#
|
|
#.....#
|
|
#P...G#
|
|
#######
|
|
""")
|
|
# Fall onto the one-way from above -> lands on it.
|
|
place(g, 1, 1) # open air above the one-way (row 3)
|
|
run(g, 60)
|
|
assert g.player.on_ground and g.player.rect.bottom == 3 * 32
|
|
|
|
# From below, jumping up passes through it (doesn't block the head).
|
|
g2 = make_game("""
|
|
name: t
|
|
tile_size: 32
|
|
map: |
|
|
#######
|
|
#.....#
|
|
#--...#
|
|
#.....#
|
|
#P...G#
|
|
#######
|
|
""")
|
|
place(g2, 1, 4) # on the floor, below the one-way (row 2)
|
|
run(g2, 5) # settle so on_ground is set before jumping
|
|
passed = False
|
|
for i in range(40):
|
|
step(g2, hold(jump_pressed=(i == 0), jump_held=(i < 12)))
|
|
if g2.player.rect.top < 2 * 32: # rose above the one-way row
|
|
passed = True
|
|
assert passed
|
|
|
|
|
|
def test_drop_through_oneway(make_game):
|
|
g = make_game("""
|
|
name: t
|
|
tile_size: 32
|
|
map: |
|
|
#######
|
|
#.....#
|
|
#.....#
|
|
#--...#
|
|
#.....#
|
|
#....G#
|
|
#######
|
|
""")
|
|
place(g, 1, 2) # standing on the one-way at row 3
|
|
run(g, 20)
|
|
assert g.player.on_ground
|
|
# press down + jump to drop through
|
|
for i in range(30):
|
|
step(g, hold(down=True, jump_pressed=(i == 0)))
|
|
assert g.player.rect.top > 3 * 32 # fell below the one-way
|