partially working conveyor

This commit is contained in:
minjaesong
2025-03-15 21:40:14 +09:00
parent 18feeb1826
commit b577df0155
4 changed files with 161 additions and 16 deletions

View File

@@ -0,0 +1,196 @@
package net.torvald.terrarum.modulebasegame.gameactors
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.App
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.inUse
import net.torvald.terrarum.worlddrawer.WorldCamera
import java.util.*
import kotlin.math.*
/**
* Created by minjaesong on 2025-03-10.
*/
class ActorConveyors : ActorWithBody {
// make it savegame-reloadable
private constructor() {
x1 = -1
y1 = -1
x2 = -1
y2 = -1
s = 0.0
di = 0.0
dd = 0.0
cx1 = 0.0
cy1 = 0.0
cx2 = 0.0
cy2 = 0.0
r = 0.5 * TILE_SIZED.minus(2)
c = 0
btx1 = 0.0
bty1 = 0.0
btx2 = 0.0
bty2 = 0.0
bbx1 = 0.0
bby1 = 0.0
bbx2 = 0.0
bby2 = 0.0
}
val x1: Int // can be negative when the conveyor crosses the world border
val y1: Int
val x2: Int // always positive
val y2: Int
private val s: Double // belt length
private val c: Int // segment counts
private val di: Double // inclination deg
private val dd: Double // declination deg
private val cx1: Double // centre of the left spindle
private val cy1: Double
private val cx2: Double // centre of the right spindle
private val cy2: Double
private val r: Double // radius
private val btx1: Double // line points of the top belt
private val bty1: Double
private val btx2: Double
private val bty2: Double
private val bbx1: Double // line points of the bottom bolt
private val bby1: Double
private val bbx2: Double
private val bby2: Double
/**
* xy1 is always the starting point, and the starting point's x-value is always lower than the end points',
* even when the conveyor crosses the edge of the world border, in which case the x-value is negative.
*/
constructor(x1: Int, y1: Int, x2: Int, y2: Int) {
this.x1 = x1
this.y1 = y1
this.x2 = x2
this.y2 = y2
s = calcBeltLength(x1, y1, x2, y2)
di = atan2(this.y2.toDouble() - this.y1, this.x2.toDouble() - this.x1)
dd = atan2(this.y1.toDouble() - this.y2, this.x1.toDouble() - this.x2)
cx1 = (this.x1 + 0.5) * TILE_SIZED
cy1 = (this.y1 + 0.5) * TILE_SIZED
cx2 = (this.x2 + 0.5) * TILE_SIZED
cy2 = (this.y2 + 0.5) * TILE_SIZED
r = 0.5 * TILE_SIZED.minus(2)
c = (s / 8).roundToInt() * 2 // 4px segments rounded towards nearest even number
btx1 = cx1 + r * sin(di)
bty1 = cy1 + r * cos(di)
btx2 = cx2 + r * sin(di)
bty2 = cy2 + r * cos(di)
bbx1 = cx1 + r * sin(dd)
bby1 = cy1 + r * cos(dd)
bbx2 = cx2 + r * sin(dd)
bby2 = cy2 + r * cos(dd)
}
private fun calcBeltLength(x1: Int, y1: Int, x2: Int, y2: Int) =
2 * (hypot((x2 - x1) * TILE_SIZED, (y2 - y1) * TILE_SIZED) + Math.PI * TILE_SIZED / 2)
override fun drawBody(frameDelta: Float, batch: SpriteBatch) {
App.shapeRender.inUse {
it.color = Color.RED
// belt top
drawLineOnWorld(btx1, bty1, btx2, bty2)
// belt bottom
drawLineOnWorld(bbx1, bby1, bbx2, bby2)
// left arc
drawArcOnWorld(cx1, cy1, r, dd, Math.PI)
// right arc
drawArcOnWorld(cx2, cy2, r, di, Math.PI)
}
}
private fun drawLineOnWorld(x1: Double, y1: Double, x2: Double, y2: Double) {
val w = 2.0f
App.shapeRender.rectLine(
x1.toFloat() - WorldCamera.x, y1.toFloat() - WorldCamera.y,
x2.toFloat() - WorldCamera.x, y2.toFloat() - WorldCamera.y,
w
)
}
private fun drawArcOnWorld(xc: Double, yc: Double, r: Double, arcStart: Double, arcDeg: Double) {
// dissect the circle
// val pathLen = arcDeg * r
//// estimated number of segments. pathLen divided by sqrt(2)
// val segments = Math.round(pathLen / Double.fromBits(0x3FF6A09E667F3BCDL)).coerceAtLeast(1L).toInt()
val segments = 12 * 8
for (i in 0 until segments) {
val degStart = (i.toDouble() / segments) * arcDeg + arcStart
val degEnd = ((i + 1.0) / segments) * arcDeg + arcStart
val x1 = r * sin(degStart) + xc
val y1 = r * cos(degStart) + yc
val x2 = r * sin(degEnd) + xc
val y2 = r * cos(degEnd) + yc
drawLineOnWorld(x1, y1, x2, y2)
}
}
/** Real time, in nanoseconds */
@Transient var spawnRequestedTime: Long = 0L
protected set
internal var actorThatInstalledThisFixture: UUID? = null
open fun spawn(installerUUID: UUID?): Boolean {
this.isVisible = true
val posXtl = minOf(x1, x2).toDouble()
val posYtl = minOf(y1, y2).toDouble()
val posXbr = maxOf(x1, x2).toDouble()
val posYbr = maxOf(y1, y2).toDouble()
this.hitbox.setFromTwoPoints(posXtl * TILE_SIZED, posYtl * TILE_SIZED, (posXbr+1) * TILE_SIZED, (posYbr+1) * TILE_SIZED)
this.setHitboxDimension(this.hitbox.width.toInt(), this.hitbox.height.toInt(), 0, 1)
this.intTilewiseHitbox.setFromTwoPoints(posXtl, posYtl, posXbr, posYbr)
// actually add this actor into the world
INGAME.queueActorAddition(this)
spawnRequestedTime = System.nanoTime()
actorThatInstalledThisFixture = installerUUID
//makeNoiseAndDust(posXtl, posYtl)
onSpawn()
return true
}
/**
* Callend whenever the fixture was spawned successfully.
*/
open fun onSpawn() {}
}

View File

@@ -104,10 +104,7 @@ open class FixtureItemBase(originalID: ItemID, val fixtureClassName: String) : G
// return true when placed, false when cannot be placed
}
override fun startSecondaryUse(actor: ActorWithBody, delta: Float) = mouseInInteractableRange(actor) { mwx, mwy, mtx, mty ->
(INGAME as TerrarumIngame).pickupFixtureOrDroppedItem(actor, delta, mwx, mwy, mtx, mty, false)
-1
}
override fun startSecondaryUse(actor: ActorWithBody, delta: Float) = fixturePickupFun(actor, delta)
/**
* Also see: [net.torvald.terrarum.modulebasegame.gameactors.FixtureBase.Companion]
@@ -128,5 +125,10 @@ open class FixtureItemBase(originalID: ItemID, val fixtureClassName: String) : G
TextureRegion(Texture(ModMgr.getGdxFile(module, path)))
} as TextureRegion
}
fun fixturePickupFun(actor: ActorWithBody, delta: Float) = mouseInInteractableRange(actor) { mwx, mwy, mtx, mty ->
(INGAME as TerrarumIngame).pickupFixtureOrDroppedItem(actor, delta, mwx, mwy, mtx, mty, false)
-1
}
}
}

View File

@@ -0,0 +1,95 @@
package net.torvald.terrarum.modulebasegame.gameitems
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.Point2i
import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.gameitems.GameItem
import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.gameitems.mouseInInteractableRange
import net.torvald.terrarum.modulebasegame.gameactors.ActorConveyors
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
/**
* Created by minjaesong on 2025-03-15.
*/
open class ItemConveyorBelt(originalID: ItemID) : GameItem(originalID) {
override var baseMass = 10.0
override var baseToolSize: Double? = null
override var inventoryCategory = Category.FIXTURE
override val canBeDynamic = false
override val materialId = ""
override var equipPosition = EquipPosition.HAND_GRIP
override var originalName = "CONVEYOR_BELT"
protected val AXLE_COUNT = 2
private var currentStatus = 0 // 0: initial, 1+: place n-th axle
private val occupiedPoints = HashSet<Point2i>()
override fun startPrimaryUse(actor: ActorWithBody, delta: Float) = mouseInInteractableRange(actor) { _, _, mtx, mty ->
val ret = when (currentStatus) {
0 -> placeFirstAxle(mtx, mty)
1 -> placeSecondAxleAndFinalise(mtx, mty)
else -> {
currentStatus = 0
-1L
}
}
currentStatus += 1
ret
}
override fun startSecondaryUse(actor: ActorWithBody, delta: Float): Long {
return when (currentStatus) {
0 -> FixtureItemBase.fixturePickupFun(actor, delta)
else -> {
currentStatus = -1
-1L
}
}
}
private fun placeFirstAxle(mx: Int, my: Int): Long {
occupiedPoints.add(Point2i(mx, my))
return 0L
}
private fun placeSecondAxleAndFinalise(mx: Int, my: Int): Long {
Point2i(mx, my).let { p2 ->
if (!occupiedPoints.contains(p2)) {
occupiedPoints.add(p2)
}
else {
currentStatus = -1
return 0L
}
// sort occupiedPoints by its x value
val points = occupiedPoints.toMutableList().also { it.sortBy { it.x } }.also { list ->
// check for ROUNDWORLD
val xMin = list[0].x
val xMax = list[1].x
// normalise it by making the x value for left spindle to negative
if (xMin >= 0 && xMin < INGAME.world.width / 2 && xMax >= INGAME.world.width / 2) {
list[0].x -= INGAME.world.width
}
}
val conveyors = ActorConveyors(points[0].x, points[0].y, points[1].x, points[1].y)
conveyors.spawn((INGAME.actorNowPlaying as? IngamePlayer)?.uuid)
}
occupiedPoints.clear()
currentStatus = -1
return 1L
}
}