Files

213 lines
6.9 KiB
Python

import math
from ..constants import tileSize
from .geometry import Aabb
slopeStick = 6.0
groundProbe = 2.0
def bodyAabb(actor):
return Aabb(actor.x, actor.y, actor.w, actor.h)
def cellRange(box):
leftCol = int(math.floor(box.left / tileSize))
rightCol = int(math.floor((box.right - 1e-6) / tileSize))
topRow = int(math.floor(box.top / tileSize))
bottomRow = int(math.floor((box.bottom - 1e-6) / tileSize))
return leftCol, rightCol, topRow, bottomRow
def solidTilesOverlapping(terrain, box):
leftCol, rightCol, topRow, bottomRow = cellRange(box)
hits = []
for row in range(topRow, bottomRow + 1):
for col in range(leftCol, rightCol + 1):
if terrain.solidAt(col, row):
hits.append(terrain.rectFor(col, row))
return hits
def oneWayTilesOverlapping(terrain, box):
leftCol, rightCol, topRow, bottomRow = cellRange(box)
hits = []
for row in range(topRow, bottomRow + 1):
for col in range(leftCol, rightCol + 1):
if terrain.oneWayAt(col, row):
hits.append(terrain.rectFor(col, row))
return hits
def slopeSurfaceY(col, row, sampleX, kind):
tileLeft = col * tileSize
tileTop = row * tileSize
localX = sampleX - tileLeft
if localX < 0.0:
localX = 0.0
elif localX > tileSize:
localX = float(tileSize)
if kind == "ascendRight":
return tileTop + (tileSize - localX)
return tileTop + localX
def moveHorizontal(actor, terrain, platforms):
actor.x += actor.vx
box = bodyAabb(actor)
actor.onWallLeft = False
actor.onWallRight = False
for tileBox in solidTilesOverlapping(terrain, box):
if box.intersects(tileBox):
if actor.vx > 0:
actor.x = tileBox.left - actor.w
actor.onWallRight = True
elif actor.vx < 0:
actor.x = tileBox.right
actor.onWallLeft = True
actor.vx = 0.0
box = bodyAabb(actor)
for platform in platforms:
if platform.oneWay:
continue
platformBox = platform.aabb
if box.intersects(platformBox):
if actor.vx > 0:
actor.x = platformBox.left - actor.w
actor.onWallRight = True
elif actor.vx < 0:
actor.x = platformBox.right
actor.onWallLeft = True
actor.vx = 0.0
box = bodyAabb(actor)
def moveVertical(actor, terrain, platforms, dropThrough=False):
prevBottom = actor.y + actor.h
actor.y += actor.vy
box = bodyAabb(actor)
actor.onGround = False
actor.onCeiling = False
actor.groundPlatform = None
for tileBox in solidTilesOverlapping(terrain, box):
if box.intersects(tileBox):
if actor.vy > 0:
actor.y = tileBox.top - actor.h
actor.onGround = True
elif actor.vy < 0:
actor.y = tileBox.bottom
actor.onCeiling = True
actor.vy = 0.0
box = bodyAabb(actor)
if actor.vy >= 0 and not dropThrough:
for tileBox in oneWayTilesOverlapping(terrain, box):
top = tileBox.top
if prevBottom <= top + 1.0 and box.bottom >= top and box.right > tileBox.left and box.left < tileBox.right:
actor.y = top - actor.h
actor.vy = 0.0
actor.onGround = True
box = bodyAabb(actor)
for platform in platforms:
platformBox = platform.aabb
if platform.oneWay:
if actor.vy >= 0 and not dropThrough:
top = platformBox.top
horizontal = box.right > platformBox.left and box.left < platformBox.right
if prevBottom <= top + 1.0 and box.bottom >= top and horizontal:
actor.y = top - actor.h
actor.vy = 0.0
actor.onGround = True
actor.groundPlatform = platform
box = bodyAabb(actor)
else:
if box.intersects(platformBox):
if actor.vy > 0:
actor.y = platformBox.top - actor.h
actor.onGround = True
actor.groundPlatform = platform
elif actor.vy < 0:
actor.y = platformBox.bottom
actor.onCeiling = True
actor.vy = 0.0
box = bodyAabb(actor)
def resolveSlope(actor, terrain, wasOnGround):
box = bodyAabb(actor)
sampleX = box.centerX
col = int(math.floor(sampleX / tileSize))
topRow = int(math.floor((box.top - tileSize) / tileSize))
bottomRow = int(math.floor((box.bottom + slopeStick) / tileSize))
for row in range(topRow, bottomRow + 1):
kind = terrain.slopeAt(col, row)
if not kind:
continue
surfaceY = slopeSurfaceY(col, row, sampleX, kind)
if surfaceY <= box.top:
continue
gap = surfaceY - box.bottom
penetrating = gap <= 0.0 and gap >= -float(tileSize) - slopeStick
clinging = wasOnGround and 0.0 < gap <= slopeStick
if actor.vy >= 0.0 and (penetrating or clinging):
actor.y = surfaceY - actor.h
actor.vy = 0.0
actor.onGround = True
return True
return False
def touchingSolidSide(actor, terrain, platforms, direction):
probe = Aabb(
actor.x + (actor.w if direction > 0 else -groundProbe),
actor.y + 2.0,
groundProbe,
actor.h - 4.0,
)
for tileBox in solidTilesOverlapping(terrain, probe):
if probe.intersects(tileBox):
return True
for platform in platforms:
if platform.oneWay:
continue
if probe.intersects(platform.aabb):
return True
return False
def hasGroundBelow(actor, terrain, platforms):
probe = Aabb(actor.x + 1.0, actor.y + actor.h, actor.w - 2.0, groundProbe)
for tileBox in solidTilesOverlapping(terrain, probe):
if probe.intersects(tileBox):
return True
for tileBox in oneWayTilesOverlapping(terrain, probe):
if probe.intersects(tileBox):
return True
for platform in platforms:
if probe.intersects(platform.aabb):
return True
return False
def overlapsLadder(actor, terrain):
box = bodyAabb(actor)
leftCol, rightCol, topRow, bottomRow = cellRange(box)
for row in range(topRow, bottomRow + 1):
for col in range(leftCol, rightCol + 1):
if terrain.ladderAt(col, row):
return True
return False
def hazardOverlap(actor, terrain):
box = bodyAabb(actor).inflated(-1.0, -1.0)
leftCol, rightCol, topRow, bottomRow = cellRange(box)
worstDamage = 0
for row in range(topRow, bottomRow + 1):
for col in range(leftCol, rightCol + 1):
if terrain.hazardAt(col, row):
worstDamage = max(worstDamage, terrain.hazardDamageAt(col, row))
return worstDamage