Skip to content

Commit 88b99b0

Browse files
Fix issue where rotating the ship sometimes causes blocks to rotate in odd ways (#315)
* add kotlin for forge to build.gradle so forge can build * use ship rotation instead of buggy `aligningTo` * update shipAssembler to take a rotation and make snapRotation public * move logic into ShipAssembler * remove buggy aligningTo, demote alignTarget to a local * fix float comparisons * Removed unnecessary imports (millennIumAMbiguity) --------- Co-authored-by: millennIumAMbiguity <37588844+millennIumAMbiguity@users.noreply.github.com>
1 parent 1567606 commit 88b99b0

File tree

4 files changed

+43
-24
lines changed

4 files changed

+43
-24
lines changed

build.gradle

+4
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ allprojects {
9999

100100
repositories {
101101
mavenLocal()
102+
maven {
103+
name = 'Kotlin for Forge'
104+
url = 'https://thedarkcolour.github.io/KotlinForForge/'
105+
}
102106
maven {
103107
name = "Valkyrien Skies Internal"
104108
url = project.vs_maven_url ?: 'https://maven.valkyrienskies.org'

common/src/main/kotlin/org/valkyrienskies/eureka/blockentity/ShipHelmBlockEntity.kt

+1-4
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ class ShipHelmBlockEntity(pos: BlockPos, state: BlockState) :
8484
}
8585

8686
fun startRiding(player: Player, force: Boolean, blockPos: BlockPos, state: BlockState, level: ServerLevel): Boolean {
87-
8887
for (i in seats.size - 1 downTo 0) {
8988
if (!seats[i].isVehicle) {
9089
seats[i].kill()
@@ -126,7 +125,7 @@ class ShipHelmBlockEntity(pos: BlockPos, state: BlockState) :
126125
) { !it.isAir && !EurekaConfig.SERVER.blockBlacklist.contains(BuiltInRegistries.BLOCK.getKey(it.block).toString()) }
127126

128127
if (builtShip == null) {
129-
player.displayClientMessage(Component.translatable("Ship is too big! Max size is ${EurekaConfig.SERVER.maxShipBlocks} blocks (changable in the config)"), true)
128+
player.displayClientMessage(Component.translatable("Ship is too big! Max size is ${EurekaConfig.SERVER.maxShipBlocks} blocks (changeable in the config)"), true)
130129
logger.warn("Failed to assemble ship for ${player.name.string}")
131130
}
132131
}
@@ -148,7 +147,6 @@ class ShipHelmBlockEntity(pos: BlockPos, state: BlockState) :
148147
ShipAssembler.unfillShip(
149148
level as ServerLevel,
150149
ship,
151-
control.aligningTo,
152150
this.blockPos,
153151
BlockPos.containing(inWorld.x, inWorld.y, inWorld.z)
154152
)
@@ -163,7 +161,6 @@ class ShipHelmBlockEntity(pos: BlockPos, state: BlockState) :
163161
}
164162

165163
override fun setRemoved() {
166-
167164
if (level?.isClientSide == false) {
168165
for (i in seats.indices) {
169166
seats[i].kill()

common/src/main/kotlin/org/valkyrienskies/eureka/ship/EurekaShipControl.kt

+1-3
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,11 @@ class EurekaShipControl : ShipForcesInducer, ServerTickListener {
5050

5151
private var angleUntilAligned = 0.0
5252
private var positionUntilAligned = Vector3d()
53-
private var alignTarget = 0
5453
val canDisassemble
5554
get() = ship != null &&
5655
disassembling &&
5756
abs(angleUntilAligned) < DISASSEMBLE_THRESHOLD &&
5857
positionUntilAligned.distanceSquared(this.ship!!.transform.positionInWorld) < 4.0
59-
val aligningTo: Direction get() = Direction.from2DDataValue(alignTarget)
6058
var consumed = 0f
6159
private set
6260

@@ -132,7 +130,7 @@ class EurekaShipControl : ShipForcesInducer, ServerTickListener {
132130
val invRotation = physShip.poseVel.rot.invert(Quaterniond())
133131
val invRotationAxisAngle = AxisAngle4d(invRotation)
134132
// Floor makes a number 0 to 3, which corresponds to direction
135-
alignTarget = floor((invRotationAxisAngle.angle / (PI * 0.5)) + 4.5).toInt() % 4
133+
val alignTarget = floor((invRotationAxisAngle.angle / (PI * 0.5)) + 4.5).toInt() % 4
136134
angleUntilAligned = (alignTarget.toDouble() * (0.5 * PI)) - invRotationAxisAngle.angle
137135
if (disassembling) {
138136
val pos = ship.transform.positionInWorld

common/src/main/kotlin/org/valkyrienskies/eureka/util/ShipAssembler.kt

+37-17
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,7 @@ import org.valkyrienskies.mod.util.logger
2626
import org.valkyrienskies.mod.util.relocateBlock
2727
import org.valkyrienskies.mod.util.updateBlock
2828
import kotlin.collections.set
29-
import kotlin.math.PI
30-
import kotlin.math.abs
31-
import kotlin.math.round
32-
import kotlin.math.sign
29+
import kotlin.math.*
3330

3431
object ShipAssembler {
3532
fun collectBlocks(level: ServerLevel, center: BlockPos, predicate: (BlockState) -> Boolean): ServerShip? {
@@ -62,9 +59,44 @@ object ShipAssembler {
6259
}
6360
}
6461

65-
fun unfillShip(level: ServerLevel, ship: ServerShip, direction: Direction, shipCenter: BlockPos, center: BlockPos) {
62+
private fun rotationFromAxisAngle(axis: AxisAngle4d): Rotation {
63+
if (axis.y.absoluteValue < 0.1) {
64+
// if the axis isn't Y, either we're tilted up/down (which should not happen often) or we haven't moved and it's
65+
// along the z axis with a magnitude of 0 for some reason. In these cases, we don't rotate.
66+
return Rotation.NONE
67+
}
68+
69+
// normalize into counterclockwise rotation (i.e. positive y-axis, according to testing + right hand rule)
70+
if (axis.y.sign < 0.0) {
71+
axis.y = 1.0
72+
// the angle is always positive and < 2pi coming in
73+
axis.angle = 2.0 * PI - axis.angle
74+
axis.angle %= (2.0 * PI)
75+
}
76+
77+
val eps = 0.001
78+
if (axis.angle < eps)
79+
return Rotation.NONE
80+
else if ((axis.angle - PI / 2.0).absoluteValue < eps)
81+
return Rotation.COUNTERCLOCKWISE_90
82+
else if ((axis.angle - PI).absoluteValue < eps)
83+
return Rotation.CLOCKWISE_180
84+
else if ((axis.angle - 3.0 * PI / 2.0).absoluteValue < eps)
85+
return Rotation.CLOCKWISE_90
86+
else {
87+
logger.warn("failed to convert $axis into a rotation")
88+
return Rotation.NONE
89+
}
90+
}
91+
92+
fun unfillShip(level: ServerLevel, ship: ServerShip, shipCenter: BlockPos, center: BlockPos) {
6693
ship.isStatic = true
6794

95+
val rotation: Rotation = ship.transform.shipToWorldRotation
96+
.let(::AxisAngle4d)
97+
.let(ShipAssembler::snapRotation)
98+
.let(::rotationFromAxisAngle)
99+
68100
// ship's rotation rounded to nearest 90*
69101
val shipToWorld = ship.transform.run {
70102
Matrix4d()
@@ -76,18 +108,6 @@ object ShipAssembler {
76108

77109
val alloc0 = Vector3d()
78110

79-
// Direction comes from direction ship is aligning to
80-
// We can assume that the ship in shipspace is always facing north, because it has to be
81-
val rotation: Rotation = when (direction) {
82-
Direction.SOUTH -> Rotation.NONE // Bug in Direction.from2DDataValue() can return south/north as opposite
83-
Direction.NORTH -> Rotation.CLOCKWISE_180
84-
Direction.EAST -> Rotation.CLOCKWISE_90
85-
Direction.WEST -> Rotation.COUNTERCLOCKWISE_90
86-
else -> {
87-
Rotation.NONE
88-
}
89-
}
90-
91111
val chunksToBeUpdated = mutableMapOf<ChunkPos, Pair<ChunkPos, ChunkPos>>()
92112

93113
ship.activeChunksSet.forEach { chunkX, chunkZ ->

0 commit comments

Comments
 (0)