mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 12:21:52 +09:00
platform wip2
This commit is contained in:
@@ -15,6 +15,8 @@ import org.dyn4j.geometry.Vector2
|
|||||||
*
|
*
|
||||||
* Subclasses must set [platformVelocity] before calling `super.updateImpl(delta)`.
|
* Subclasses must set [platformVelocity] before calling `super.updateImpl(delta)`.
|
||||||
*
|
*
|
||||||
|
* TODO: in the future this must be generalised as a PhysContraption
|
||||||
|
*
|
||||||
* Created by minjaesong on 2022-02-28.
|
* Created by minjaesong on 2022-02-28.
|
||||||
*/
|
*/
|
||||||
open class ActorMovingPlatform() : ActorWithBody() {
|
open class ActorMovingPlatform() : ActorWithBody() {
|
||||||
@@ -23,6 +25,11 @@ open class ActorMovingPlatform() : ActorWithBody() {
|
|||||||
|
|
||||||
constructor(newTilewiseWidth: Int) : this() {
|
constructor(newTilewiseWidth: Int) : this() {
|
||||||
this.tilewiseWidth = newTilewiseWidth
|
this.tilewiseWidth = newTilewiseWidth
|
||||||
|
|
||||||
|
physProp = PhysProperties.MOBILE_OBJECT()
|
||||||
|
collisionType = COLLISION_KINEMATIC
|
||||||
|
|
||||||
|
setHitboxDimension(TILE_SIZE * newTilewiseWidth, TILE_SIZE, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Actors currently riding this platform, stored by ActorID for serialisation. */
|
/** Actors currently riding this platform, stored by ActorID for serialisation. */
|
||||||
@@ -34,8 +41,11 @@ open class ActorMovingPlatform() : ActorWithBody() {
|
|||||||
/** Actual displacement applied this tick (after clampHitbox). */
|
/** Actual displacement applied this tick (after clampHitbox). */
|
||||||
@Transient private val appliedVelocity = Vector2(0.0, 0.0)
|
@Transient private val appliedVelocity = Vector2(0.0, 0.0)
|
||||||
|
|
||||||
/** Tolerance in pixels for "feet on top of platform" detection. */
|
/** Tolerance above platform top for "feet on top" detection (pixels). */
|
||||||
@Transient private val MOUNT_TOLERANCE_Y = 2.0
|
@Transient private val MOUNT_TOLERANCE_ABOVE = (TILE_SIZE / 2).toDouble()//2.0
|
||||||
|
|
||||||
|
/** Tolerance below platform top — how far feet can sink before dismount (pixels). */
|
||||||
|
@Transient private val MOUNT_TOLERANCE_BELOW = (TILE_SIZE / 2).toDouble()
|
||||||
|
|
||||||
/** Minimum combined Y velocity to count as "jumping up" (prevents mount while jumping). */
|
/** Minimum combined Y velocity to count as "jumping up" (prevents mount while jumping). */
|
||||||
@Transient private val JUMP_THRESHOLD_Y = -0.5
|
@Transient private val JUMP_THRESHOLD_Y = -0.5
|
||||||
@@ -86,12 +96,25 @@ open class ActorMovingPlatform() : ActorWithBody() {
|
|||||||
// Compute actual displacement (clampHitbox may have wrapped coordinates)
|
// Compute actual displacement (clampHitbox may have wrapped coordinates)
|
||||||
appliedVelocity.set(hitbox.startX - oldX, hitbox.startY - oldY)
|
appliedVelocity.set(hitbox.startX - oldX, hitbox.startY - oldY)
|
||||||
|
|
||||||
// --- Mount detection and rider management ---
|
// --- Step 1: Move existing riders BEFORE mount detection ---
|
||||||
|
// This keeps riders aligned with the platform's new position so the
|
||||||
|
// mount check doesn't fail when the platform is moving fast.
|
||||||
|
for (riderId in actorsRiding.toList()) {
|
||||||
|
val rider = INGAME.getActorByID(riderId) as? ActorWithBody
|
||||||
|
if (rider != null) {
|
||||||
|
rider.hitbox.translate(appliedVelocity)
|
||||||
|
rider.hitbox.setPositionY(hitbox.startY - rider.hitbox.height)
|
||||||
|
if (rider.externalV.y > 0.0) {
|
||||||
|
rider.externalV.y = 0.0
|
||||||
|
}
|
||||||
|
rider.walledBottom = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Step 2: Mount detection (riders are now at correct positions) ---
|
||||||
|
|
||||||
val ridersToRemove = ArrayList<ActorID>()
|
val ridersToRemove = ArrayList<ActorID>()
|
||||||
|
val currentRiders = ArrayList<ActorWithBody>()
|
||||||
// Build set of actors currently on top of this platform
|
|
||||||
val newRiders = ArrayList<ActorWithBody>()
|
|
||||||
|
|
||||||
// Check all active actors + actorNowPlaying
|
// Check all active actors + actorNowPlaying
|
||||||
val candidates = ArrayList<ActorWithBody>()
|
val candidates = ArrayList<ActorWithBody>()
|
||||||
@@ -104,10 +127,15 @@ open class ActorMovingPlatform() : ActorWithBody() {
|
|||||||
|
|
||||||
for (actor in candidates) {
|
for (actor in candidates) {
|
||||||
val feetY = actor.hitbox.endY
|
val feetY = actor.hitbox.endY
|
||||||
|
val headY = actor.hitbox.startY
|
||||||
val platTop = hitbox.startY
|
val platTop = hitbox.startY
|
||||||
|
|
||||||
// Check vertical proximity: feet within tolerance of platform top
|
// Feet are near platform top: slightly above or sunk partway in
|
||||||
val verticallyAligned = Math.abs(feetY - platTop) <= MOUNT_TOLERANCE_Y
|
val feetNearPlatTop = feetY >= platTop - MOUNT_TOLERANCE_ABOVE &&
|
||||||
|
feetY <= platTop + MOUNT_TOLERANCE_BELOW
|
||||||
|
|
||||||
|
// Actor's head must be above platform top (prevents mounting from below)
|
||||||
|
val comingFromAbove = headY < platTop
|
||||||
|
|
||||||
// Check horizontal overlap
|
// Check horizontal overlap
|
||||||
val horizontalOverlap = actor.hitbox.endX > hitbox.startX && actor.hitbox.startX < hitbox.endX
|
val horizontalOverlap = actor.hitbox.endX > hitbox.startX && actor.hitbox.startX < hitbox.endX
|
||||||
@@ -116,18 +144,24 @@ open class ActorMovingPlatform() : ActorWithBody() {
|
|||||||
val combinedVelY = actor.externalV.y + (actor.controllerV?.y ?: 0.0)
|
val combinedVelY = actor.externalV.y + (actor.controllerV?.y ?: 0.0)
|
||||||
val notJumping = combinedVelY >= JUMP_THRESHOLD_Y
|
val notJumping = combinedVelY >= JUMP_THRESHOLD_Y
|
||||||
|
|
||||||
if (verticallyAligned && horizontalOverlap && notJumping) {
|
if (feetNearPlatTop && comingFromAbove && horizontalOverlap && notJumping) {
|
||||||
if (!actorsRiding.contains(actor.referenceID)) {
|
if (!actorsRiding.contains(actor.referenceID)) {
|
||||||
|
// New rider — mount and snap
|
||||||
mount(actor)
|
mount(actor)
|
||||||
|
actor.hitbox.setPositionY(hitbox.startY - actor.hitbox.height)
|
||||||
|
if (actor.externalV.y > 0.0) {
|
||||||
|
actor.externalV.y = 0.0
|
||||||
|
}
|
||||||
|
actor.walledBottom = true
|
||||||
}
|
}
|
||||||
newRiders.add(actor)
|
currentRiders.add(actor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dismount actors that are no longer on top
|
// --- Step 3: Dismount actors no longer on top ---
|
||||||
val newRiderIds = newRiders.map { it.referenceID }.toSet()
|
val currentRiderIds = currentRiders.map { it.referenceID }.toSet()
|
||||||
for (riderId in actorsRiding.toList()) {
|
for (riderId in actorsRiding.toList()) {
|
||||||
if (riderId !in newRiderIds) {
|
if (riderId !in currentRiderIds) {
|
||||||
val rider = INGAME.getActorByID(riderId)
|
val rider = INGAME.getActorByID(riderId)
|
||||||
if (rider is ActorWithBody) {
|
if (rider is ActorWithBody) {
|
||||||
dismount(rider)
|
dismount(rider)
|
||||||
@@ -138,18 +172,6 @@ open class ActorMovingPlatform() : ActorWithBody() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ridersToRemove.forEach { actorsRiding.remove(it) }
|
ridersToRemove.forEach { actorsRiding.remove(it) }
|
||||||
|
|
||||||
// Move riders and suppress their gravity
|
|
||||||
for (rider in newRiders) {
|
|
||||||
// Translate rider by platform's actual displacement
|
|
||||||
rider.hitbox.translate(appliedVelocity)
|
|
||||||
|
|
||||||
// Snap rider's feet to platform top
|
|
||||||
rider.hitbox.setPositionY(hitbox.startY - rider.hitbox.height)
|
|
||||||
|
|
||||||
// Suppress gravity for this tick
|
|
||||||
rider.walledBottom = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -582,7 +582,7 @@ open class ActorWithBody : Actor {
|
|||||||
|
|
||||||
// --> Apply more forces <-- //
|
// --> Apply more forces <-- //
|
||||||
// Actors are subject to the gravity and the buoyancy if they are not levitating
|
// Actors are subject to the gravity and the buoyancy if they are not levitating
|
||||||
if (!isNoSubjectToGrav) {
|
if (!isNoSubjectToGrav && platformsRiding.isEmpty()) {
|
||||||
applyGravitation()
|
applyGravitation()
|
||||||
applyBuoyancy()
|
applyBuoyancy()
|
||||||
}
|
}
|
||||||
@@ -603,7 +603,14 @@ open class ActorWithBody : Actor {
|
|||||||
* If and only if:
|
* If and only if:
|
||||||
* This body is NON-STATIC and the other body is STATIC
|
* This body is NON-STATIC and the other body is STATIC
|
||||||
*/
|
*/
|
||||||
if (!isNoCollideWorld) {
|
if (platformsRiding.isNotEmpty()) {
|
||||||
|
// Riding a platform: skip displaceHitbox entirely.
|
||||||
|
// The CCD/collision solver doesn't know about platforms and
|
||||||
|
// will corrupt the rider's position (bounce, stair-step, etc.).
|
||||||
|
// Only apply horizontal movement; the platform owns Y.
|
||||||
|
hitbox.translate(vecSum.x, 0.0)
|
||||||
|
}
|
||||||
|
else if (!isNoCollideWorld) {
|
||||||
val (collisionStatus, collisionDamage) = displaceHitbox(true)
|
val (collisionStatus, collisionDamage) = displaceHitbox(true)
|
||||||
|
|
||||||
|
|
||||||
@@ -665,7 +672,7 @@ open class ActorWithBody : Actor {
|
|||||||
walledLeft = isWalled(hitbox, COLLIDING_LEFT)
|
walledLeft = isWalled(hitbox, COLLIDING_LEFT)
|
||||||
walledRight = isWalled(hitbox, COLLIDING_RIGHT)
|
walledRight = isWalled(hitbox, COLLIDING_RIGHT)
|
||||||
walledTop = isWalled(hitbox, COLLIDING_TOP)
|
walledTop = isWalled(hitbox, COLLIDING_TOP)
|
||||||
walledBottom = isWalled(hitbox, COLLIDING_BOTTOM)
|
walledBottom = isWalled(hitbox, COLLIDING_BOTTOM) || platformsRiding.isNotEmpty()
|
||||||
colliding = isColliding(hitbox)
|
colliding = isColliding(hitbox)
|
||||||
|
|
||||||
if (isNoCollideWorld) {
|
if (isNoCollideWorld) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package net.torvald.terrarum.modulebasegame.console
|
|||||||
|
|
||||||
import net.torvald.terrarum.INGAME
|
import net.torvald.terrarum.INGAME
|
||||||
import net.torvald.terrarum.Terrarum
|
import net.torvald.terrarum.Terrarum
|
||||||
|
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
|
||||||
import net.torvald.terrarum.console.ConsoleAlias
|
import net.torvald.terrarum.console.ConsoleAlias
|
||||||
import net.torvald.terrarum.console.ConsoleCommand
|
import net.torvald.terrarum.console.ConsoleCommand
|
||||||
import net.torvald.terrarum.console.Echo
|
import net.torvald.terrarum.console.Echo
|
||||||
@@ -13,12 +14,12 @@ import net.torvald.terrarum.modulebasegame.gameactors.ActorTestPlatform
|
|||||||
@ConsoleAlias("spawnplatform")
|
@ConsoleAlias("spawnplatform")
|
||||||
internal object SpawnMovingPlatform : ConsoleCommand {
|
internal object SpawnMovingPlatform : ConsoleCommand {
|
||||||
override fun execute(args: Array<String>) {
|
override fun execute(args: Array<String>) {
|
||||||
val mouseX = Terrarum.mouseX
|
val mouseX = Terrarum.mouseTileX * TILE_SIZED
|
||||||
val mouseY = Terrarum.mouseY
|
val mouseY = Terrarum.mouseTileY * TILE_SIZED
|
||||||
|
|
||||||
val platform = ActorTestPlatform()
|
val platform = ActorTestPlatform()
|
||||||
// setPosition places bottom-centre at the given point; offset Y so the platform is centred at cursor
|
// setPosition places bottom-centre at the given point; offset Y so the platform is centred at cursor
|
||||||
platform.setPosition(mouseX, mouseY + platform.hitbox.height / 2.0)
|
platform.setPosition(mouseX, mouseY)
|
||||||
|
|
||||||
INGAME.queueActorAddition(platform)
|
INGAME.queueActorAddition(platform)
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import kotlin.math.sin
|
|||||||
class ActorTestPlatform : ActorMovingPlatform(8) {
|
class ActorTestPlatform : ActorMovingPlatform(8) {
|
||||||
|
|
||||||
/** Movement pattern index (0-3). */
|
/** Movement pattern index (0-3). */
|
||||||
private val pattern: Int = (0..3).random()
|
private val pattern: Int = 1//(0..3).random()
|
||||||
|
|
||||||
/** Speed in pixels per tick (2.0 to 4.0). */
|
/** Speed in pixels per tick (2.0 to 4.0). */
|
||||||
private val speed: Double = 2.0 + Math.random() * 2.0
|
private val speed: Double = 2.0 + Math.random() * 2.0
|
||||||
|
|||||||
Reference in New Issue
Block a user