reorientable gearbox because input and output cannot be on the same location

This commit is contained in:
minjaesong
2024-10-06 14:06:04 +09:00
parent 9a33375286
commit 02db6b8fed
11 changed files with 118 additions and 82 deletions

View File

@@ -119,6 +119,10 @@ class Point2i() {
}
override fun hashCode(): Int = XXHash32.hashGeoCoord(x, y)
override fun equals(other: Any?) = if (other is Point2i)
this.x == other.x && this.y == other.y
else
false
override fun toString() = "Point2i($x, $y)"

View File

@@ -630,7 +630,7 @@ object WorldSimulator {
INGAME.getActorsAt(point.x * TILE_SIZED, point.y * TILE_SIZED).filterIsInstance<Electric>().firstOrNull().let { found ->
if (found != null) {
// get offset from the fixture's origin
tileOffsetFromFixture = found.intTilewiseHitbox.let { Point2i(it.startX.toInt(), it.startY.toInt()) } - tilePoint
tileOffsetFromFixture = found.worldBlockPos!! - tilePoint
// println("$tilePoint; ${found.javaClass.canonicalName}, $tileOffsetFromFixture, ${found.getWireSinkAt(tileOffsetFromFixture!!)}")

View File

@@ -100,8 +100,8 @@ open class Electric : FixtureBase {
open fun updateSignal() {}
fun getWireStateAt(offsetX: Int, offsetY: Int, sinkType: WireEmissionType): Vector2 {
val wx = offsetX + intTilewiseHitbox.startX.toInt()
val wy = offsetY + intTilewiseHitbox.startY.toInt()
val wx = offsetX + worldBlockPos!!.x
val wy = offsetY + worldBlockPos!!.y
return WireCodex.getAllWiresThatAccepts(sinkType).fold(Vector2()) { acc, (id, _) ->
INGAME.world.getWireEmitStateOf(wx, wy, id).let {
@@ -141,8 +141,8 @@ open class Electric : FixtureBase {
open fun updateOnWireGraphTraversal(offsetX: Int, offsetY: Int, sinkType: WireEmissionType) {
val index = pointToBlockBoxIndex(offsetX, offsetY)
val wx = offsetX + intTilewiseHitbox.startX.toInt()
val wy = offsetY + intTilewiseHitbox.startY.toInt()
val wx = offsetX + worldBlockPos!!.x
val wy = offsetY + worldBlockPos!!.y
val new2 = WireCodex.getAllWiresThatAccepts(wireSinkTypes[index] ?: "").fold(Vector2()) { acc, (id, _) ->
INGAME.world.getWireEmitStateOf(wx, wy, id).let {
@@ -168,8 +168,8 @@ open class Electric : FixtureBase {
// get indices of "rising edges"
// get indices of "falling edges"
val wx = x + intTilewiseHitbox.startX.toInt()
val wy = y + intTilewiseHitbox.startY.toInt()
val wx = x + worldBlockPos!!.x
val wy = y + worldBlockPos!!.y
val new = WireCodex.getAllWiresThatAccepts(getWireSinkAt(x, y) ?: "").fold(Vector2()) { acc, (id, _) ->
INGAME.world.getWireEmitStateOf(wx, wy, id).let {
Vector2(acc.x + (it?.x ?: 0.0), acc.y + (it?.y ?: 0.0))

View File

@@ -183,6 +183,8 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange {
return (posX until posX + blockBox.width).toList().cartesianProduct((posY until posY + blockBox.height).toList())
}
open fun spawnCustomGetSpawningOffset() = 0 to 0
/**
* Returns BlockBox definition as a list of individual blocks, absolute position in the world.
*
@@ -306,8 +308,12 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange {
// posY: bottom of the blockBox
// using the actor's hitbox
val posX = (posX0 - blockBox.width.minus(1).div(2)) fmod world!!.width // width.minus(1) so that spawning position would be same as the ghost's position
val posY = posY0 - blockBox.height + 1
val (ox, oy) = spawnCustomGetSpawningOffset()
val posX0 = posX0 + ox
val posY0 = posY0 + oy
val posX = ox + (posX0 - blockBox.width.minus(1).div(2)) fmod world!!.width // width.minus(1) so that spawning position would be same as the ghost's position
val posY = oy + posY0 - blockBox.height + 1
if (!canSpawnHere(posX0, posY0)) {
printdbg(this, "cannot spawn fixture1 ${nameFun()} at F${INGAME.WORLD_UPDATE_TIMER}, has tile collision; xy=($posX,$posY) tDim=(${blockBox.width},${blockBox.height})")
@@ -317,10 +323,12 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange {
printdbg(this, "spawn fixture ${nameFun()} at F${INGAME.WORLD_UPDATE_TIMER}, xy=($posX,$posY) tDim=(${blockBox.width},${blockBox.height})")
// at this point, worldBlockPos was set by the canSpawnHere() function
// fill the area with the filler blocks
placeActorBlocks()
this.isVisible = true
this.hitbox.setFromWidthHeight(
posX * TILE_SIZED,
@@ -358,7 +366,7 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange {
* @param thbh tile-wise Hitbox height
* @return true if successfully spawned, false if was not (e.g. space to spawn is occupied by something else)
*/
open fun spawn(posX0: Int, posY0: Int, installersUUID: UUID?, thbw: Int, thbh: Int): Boolean {
open fun spawnUsingCustomBoxSize(posX0: Int, posY0: Int, installersUUID: UUID?, thbw: Int, thbh: Int): Boolean {
val posX = (posX0 - thbw.minus(1).div(2)) fmod world!!.width // width.minus(1) so that spawning position would be same as the ghost's position
val posY = posY0 - thbh + 1
@@ -453,6 +461,9 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange {
* This function MUST BE super-called for make despawn call to work at all.
*/
override fun updateImpl(delta: Float) {
actorValue.set(AVKey.SCALE, 1.0)
actorValue.set(AVKey.SCALEBUFF, 1.0)
////////////////////////////////////////////////////////////
super.updateImpl(delta)
chunkAnchoring = inOperation

View File

@@ -1,11 +1,14 @@
package net.torvald.terrarum.modulebasegame.gameactors
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.spriteanimation.SheetSpriteAnimation
import net.torvald.terrarum.App
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.Point2i
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.gameworld.fmod
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.gameactors.FixtureInductionMotor.Companion.MASS
import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase
@@ -68,7 +71,7 @@ class FixtureInductionMotor : Electric {
/**
* Created by minjaesong on 2024-10-05.
*/
class FixtureGearbox : Electric {
class FixtureGearbox : Electric, Reorientable {
@Transient override val spawnNeedsFloor = true
@Transient override val spawnNeedsWall = false
@@ -82,95 +85,104 @@ class FixtureGearbox : Electric {
return listOf(posX+1 to posY+1)
}
override fun spawnCustomGetSpawningOffset() = 0 to 1
override fun orientClockwise() {
orientation = 1 - orientation
reorient(); setEmitterAndSink(); updateSignal()
}
override fun orientAnticlockwise() {
orientation = 1 - orientation
reorient(); setEmitterAndSink(); updateSignal()
}
override var orientation = 0 // 0 or 1
private fun reorient() {
(sprite as SheetSpriteAnimation).currentRow = orientation
}
private fun setEmitterAndSink() {
clearStatus()
when (orientation) {
0 -> {
posVecsIn.forEach { (x, y) ->
setWireSinkAt(x, y, "axle")
}
posVecsOut.forEach { (x, y) ->
setWireEmitterAt(x, y, "axle")
}
}
1 -> {
posVecsIn.forEach { (x, y) ->
setWireEmitterAt(x, y, "axle")
}
posVecsOut.forEach { (x, y) ->
setWireSinkAt(x, y, "axle")
}
}
}
}
init {
val itemImage = FixtureItemBase.getItemImageFromSingleImage("basegame", "sprites/fixtures/gearbox.tga")
density = 7874.0
setHitboxDimension(TILE_SIZE, TILE_SIZE, 0, TILE_SIZE)
setHitboxDimension(TILE_SIZE, TILE_SIZE, 0, 0)
makeNewSprite(TextureRegionPack(itemImage.texture, TILE_SIZE, TILE_SIZE+1)).let {
it.setRowsAndFrames(1,1)
it.setRowsAndFrames(2,1)
}
actorValue[AVKey.BASEMASS] = MASS
posVecs.forEach { (x, y) ->
setWireEmitterAt(x, y, "axle")
setWireSinkAt(x, y, "axle")
}
setEmitterAndSink()
}
override fun updateImpl(delta: Float) {
super.updateImpl(delta)
// animate the sprite
val worldX = intTilewiseHitbox.startX.toInt()
val worldY = intTilewiseHitbox.startY.toInt()
val targetTick = (App.TICK_SPEED / speedMax).let {
it.coerceIn(0.0, Long.MAX_VALUE.toDouble())
}.roundToLong()
val sprite = (sprite as SheetSpriteAnimation)
val phaseShift = if (worldY % 2 == 0)
(worldX % 2 == 1)
else
(worldX % 2 == 0)
if (targetTick > 0) {
(INGAME.WORLD_UPDATE_TIMER % (targetTick * 2)).let {
sprite.currentFrame =
if (phaseShift)
(it < targetTick).toInt()
else
(it >= targetTick).toInt()
}
// re-position the hitbox because wtf
worldBlockPos?.let { (x, y) ->
val x = (x+1) * TILE_SIZED; val y = (y+1) * TILE_SIZED
hitbox.setFromTwoPoints(x, y, x + TILE_SIZED, y + TILE_SIZED)
}
super.updateImpl(delta)
}
@Transient private var speedMax = 0.0
private var maxSpeedLoc = ArrayList<Point2i>()
private var minTorqueLoc = ArrayList<Point2i>()
@Transient private val newMaxSpeedLoc = ArrayList<Point2i>()
@Transient private val newMinTorqueLoc = ArrayList<Point2i>()
override fun updateSignal() {
val a = when (orientation) {
0 -> posVecsIn
1 -> posVecsOut
else -> throw InternalError()
}
val b = when (orientation) {
0 -> posVecsOut
1 -> posVecsIn
else -> throw InternalError()
}
var torqueMin = Double.POSITIVE_INFINITY
newMaxSpeedLoc.clear()
newMinTorqueLoc.clear()
speedMax = 0.0
posVecs.forEach {
a.forEach {
val vec = getWireStateAt(it.x, it.y, "axle")
if (!maxSpeedLoc.contains(it) && vec.x >= speedMax) {
newMaxSpeedLoc.add(Point2i(it.x, it.y))
if (vec.x.absoluteValue >= ELECTRIC_EPSILON_GENERIC && vec.x >= speedMax) {
speedMax = vec.x
}
if (!minTorqueLoc.contains(it) && vec.y.absoluteValue >= ELECTRIC_EPSILON_GENERIC && vec.y <= torqueMin) {
newMinTorqueLoc.add(Point2i(it.x, it.y))
if (vec.y.absoluteValue >= ELECTRIC_EPSILON_GENERIC && vec.y <= torqueMin) {
torqueMin = if (vec.y == Double.POSITIVE_INFINITY) 0.0 else vec.y
}
// FIXME: intTilewiseHitbox discrepancy with spawn position.
val wx = it.x + intTilewiseHitbox.startX.toInt()
val wy = it.y + intTilewiseHitbox.startY.toInt()
print("$wx,$wy\t")
if (maxSpeedLoc.contains(it))
println("$it*\t$vec")
else
println("$it\t$vec")
}
println("--------")
maxSpeedLoc.clear(); maxSpeedLoc.addAll(newMaxSpeedLoc)
minTorqueLoc.clear(); minTorqueLoc.addAll(newMinTorqueLoc)
if (torqueMin == Double.POSITIVE_INFINITY) torqueMin = 0.0
posVecs.forEach { (x, y) ->
b.forEach { (x, y) ->
setWireEmissionAt(x, y, Vector2(speedMax, torqueMin))
}
}
@@ -180,10 +192,17 @@ class FixtureGearbox : Electric {
}
companion object {
@Transient val posVecs = listOf(
Point2i(1, 0),
@Transient val posVecsIn = listOf(
// Point2i(1, 0),
Point2i(0, 1),
Point2i(2, 1),
// Point2i(1, 2),
)
@Transient val posVecsOut = listOf(
Point2i(1, 0),
// Point2i(0, 1),
// Point2i(2, 1),
Point2i(1, 2),
)
}

View File

@@ -97,7 +97,7 @@ class FixtureLogicSignalAdder : Electric, Reorientable {
private val I: Boolean
get() = when (orientation) {
// 0 -> getWireStateAt(0, 0, "digital_bit").x >= ELECTRIC_THRESHOLD_HIGH
0 -> getWireStateAt(0, 0, "digital_bit").x >= ELECTRIC_THRESHOLD_HIGH
1 -> getWireStateAt(1, 0, "digital_bit").x >= ELECTRIC_THRESHOLD_HIGH
2 -> getWireStateAt(1, 1, "digital_bit").x >= ELECTRIC_THRESHOLD_HIGH
3 -> getWireStateAt(0, 1, "digital_bit").x >= ELECTRIC_THRESHOLD_HIGH

View File

@@ -143,7 +143,7 @@ open class FixtureSwingingDoorBase : FixtureBase {
reload()
}
override fun spawn(posX: Int, posY: Int, installersUUID: UUID?): Boolean = spawn(posX, posY, installersUUID, tilewiseHitboxWidth, tilewiseHitboxHeight)
override fun spawn(posX: Int, posY: Int, installersUUID: UUID?): Boolean = spawnUsingCustomBoxSize(posX, posY, installersUUID, tilewiseHitboxWidth, tilewiseHitboxHeight)
override fun makeNoiseAndDust(posX: Int, posY: Int) {
val posYb = posY + blockBox.height - 1

View File

@@ -50,7 +50,7 @@ internal class FixtureTapestry : FixtureBase {
reload()
}
override fun spawn(posX: Int, posY: Int, installersUUID: UUID?): Boolean = spawn(posX, posY, installersUUID, tilewiseHitboxWidth, tilewiseHitboxHeight)
override fun spawn(posX: Int, posY: Int, installersUUID: UUID?): Boolean = spawnUsingCustomBoxSize(posX, posY, installersUUID, tilewiseHitboxWidth, tilewiseHitboxHeight)
override fun reload() {
super.reload()
@@ -111,7 +111,7 @@ internal class FixtureTapestry : FixtureBase {
// must be re-spawned on reload to make it visible after load
spawn(
spawnUsingCustomBoxSize(
intTilewiseHitbox.canonicalX.toInt(),
intTilewiseHitbox.canonicalY.toInt(),
actorThatInstalledThisFixture,

View File

@@ -18,7 +18,6 @@ import net.torvald.terrarum.itemproperties.Item
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.gameitems.ItemTextSignCopper
import net.torvald.terrarum.modulebasegame.ui.UIItemInventoryCellCommonRes.tooltipShowing
import net.torvald.terrarum.ui.Toolkit
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import net.torvald.unicode.TIMES
import org.dyn4j.geometry.Vector2
@@ -71,7 +70,7 @@ class FixtureTextSignCopper : Electric {
return item.dynamicID
}
override fun spawn(posX: Int, posY: Int, installersUUID: UUID?): Boolean = spawn(posX, posY, installersUUID, panelCount.coerceAtLeast(2), 2)
override fun spawn(posX: Int, posY: Int, installersUUID: UUID?): Boolean = spawnUsingCustomBoxSize(posX, posY, installersUUID, panelCount.coerceAtLeast(2), 2)
override fun reload() {
super.reload()
@@ -86,7 +85,7 @@ class FixtureTextSignCopper : Electric {
}
// must be re-spawned on reload to make it visible after load
spawn(
spawnUsingCustomBoxSize(
intTilewiseHitbox.canonicalX.toInt(),
intTilewiseHitbox.canonicalY.toInt(),
actorThatInstalledThisFixture,