@@ -7,10 +7,7 @@ import com.fasterxml.jackson.annotation.JsonProperty
7
7
import net.minecraft.core.Direction
8
8
import net.minecraft.network.chat.TranslatableComponent
9
9
import net.minecraft.world.entity.player.Player
10
- import org.joml.AxisAngle4d
11
- import org.joml.Quaterniond
12
- import org.joml.Vector3d
13
- import org.joml.Vector3dc
10
+ import org.joml.*
14
11
import org.valkyrienskies.core.api.VSBeta
15
12
import org.valkyrienskies.core.api.ships.PhysShip
16
13
import org.valkyrienskies.core.api.ships.ServerShip
@@ -119,10 +116,10 @@ class EurekaShipControl : ShipForcesInducer, ServerTickListener {
119
116
// [x] Rewrite Alignment code
120
117
// [x] Revisit Elevation code
121
118
// [x] Balloon limiter
122
- // [ ] Add Cruise code
123
- // [ ] Rotation based of shipsize
119
+ // [x ] Add Cruise code
120
+ // [x ] Rotation based of ship size
124
121
// [x] Engine consumption
125
- // [ ] Fix elevation sensititvity
122
+ // [x ] Fix elevation sensitivity
126
123
127
124
// region Aligning
128
125
@@ -155,11 +152,8 @@ class EurekaShipControl : ShipForcesInducer, ServerTickListener {
155
152
val validPlayer = controllingPlayer != null && ! anchored
156
153
157
154
if (isCruising && anchored) {
158
- isCruising = false ;
159
- if (controllingPlayer != null )
160
- {
161
- showCruiseStatus();
162
- }
155
+ isCruising = false
156
+ showCruiseStatus()
163
157
}
164
158
165
159
stabilize(
@@ -176,22 +170,8 @@ class EurekaShipControl : ShipForcesInducer, ServerTickListener {
176
170
177
171
if (validPlayer) {
178
172
val player = controllingPlayer!!
179
- val currentControlData = ControlData .create(player)
180
-
181
- // If the player is currently controlling the ship
182
- if (! wasCruisePressed && player.cruise) {
183
- // the player pressed the cruise button
184
- isCruising = ! isCruising
185
- showCruiseStatus()
186
- } else if (! player.cruise &&
187
- isCruising &&
188
- (player.leftImpulse != 0.0f || player.sprintOn || player.upImpulse != 0.0f || player.forwardImpulse != 0.0f ) &&
189
- currentControlData != controlData
190
- ) {
191
- // The player pressed another button
192
- isCruising = false
193
- showCruiseStatus()
194
- }
173
+
174
+ val currentControlData = getControlData(player)
195
175
196
176
if (! isCruising) {
197
177
// only take the latest control data if the player is not cruising
@@ -208,135 +188,163 @@ class EurekaShipControl : ShipForcesInducer, ServerTickListener {
208
188
}
209
189
210
190
controlData?.let { control ->
211
- // region Player controlled rotation
212
- val transform = physShip.transform
213
- val aabb = ship.worldAABB
214
- val center = transform.positionInWorld
215
- val stw = transform.shipToWorld
216
- val wts = transform.worldToShip
191
+ applyPlayerControl(control, physShip)
192
+ idealUpwardVel = getPlayerUpwardVel(control, mass)
193
+ }
217
194
218
- val largestDistance = run {
219
- var dist = center.distance(aabb.minX(), center.y(), aabb.minZ())
220
- dist = max(dist, center.distance(aabb.minX(), center.y(), aabb.maxZ()))
221
- dist = max(dist, center.distance(aabb.maxX(), center.y(), aabb.minZ()))
222
- dist = max(dist, center.distance(aabb.maxX(), center.y(), aabb.maxZ()))
195
+ // region Elevation
196
+ val idealUpwardForce = (idealUpwardVel.y() - vel.y() - (GRAVITY / EurekaConfig .SERVER .elevationSnappiness)) *
197
+ mass * EurekaConfig .SERVER .elevationSnappiness
198
+
199
+ physShip.applyInvariantForce(Vector3d (0.0 ,
200
+ min(balloonForceProvided, max(idealUpwardForce, 0.0 )) +
201
+ // Add drag to the y-component
202
+ vel.y() * - mass,
203
+ 0.0 )
204
+ )
205
+ // endregion
223
206
224
- dist
225
- }.coerceIn( 0.5 , EurekaConfig . SERVER .maxSizeForTurnSpeedPenalty)
207
+ physShip.isStatic = anchored
208
+ }
226
209
227
- val maxLinearAcceleration = EurekaConfig .SERVER .turnAcceleration
228
- val maxLinearSpeed = EurekaConfig .SERVER .turnSpeed + extraForceAngular
210
+ private fun getControlData (player : SeatedControllingPlayer ): ControlData {
211
+
212
+ val currentControlData = ControlData .create(player)
213
+
214
+ if (! wasCruisePressed && player.cruise) {
215
+ // the player pressed the cruise button
216
+ isCruising = ! isCruising
217
+ showCruiseStatus()
218
+ } else if (! player.cruise && isCruising &&
219
+ (player.leftImpulse != 0.0f || player.sprintOn || player.upImpulse != 0.0f || player.forwardImpulse != 0.0f ) &&
220
+ currentControlData != controlData
221
+ ) {
222
+ // The player pressed another button
223
+ isCruising = false
224
+ showCruiseStatus()
225
+ }
229
226
230
- // acceleration = alpha * r
231
- // therefore: maxAlpha = maxAcceleration / r
232
- val maxOmegaY = maxLinearSpeed / largestDistance
233
- val maxAlphaY = maxLinearAcceleration / largestDistance
227
+ return currentControlData
228
+ }
234
229
235
- val isBelowMaxTurnSpeed = abs(omega.y()) < maxOmegaY
230
+ private fun applyPlayerControl ( control : ControlData , physShip : PhysShipImpl ) {
236
231
237
- val normalizedAlphaYMultiplier =
238
- if (isBelowMaxTurnSpeed && control.leftImpulse != 0.0f ) control.leftImpulse.toDouble()
239
- else - omega.y().coerceIn(- 1.0 , 1.0 )
232
+ val ship = ship ? : return
233
+ val transform = physShip.transform
234
+ val aabb = ship.worldAABB
235
+ val center = transform.positionInWorld
240
236
241
- val idealAlphaY = normalizedAlphaYMultiplier * maxAlphaY
237
+ // region Player controlled rotation
238
+ val moiTensor = physShip.inertia.momentOfInertiaTensor
239
+ val omega: Vector3dc = physShip.poseVel.omega
242
240
243
- val alpha = Vector3d (0.0 , idealAlphaY, 0.0 )
244
- val angularImpulse =
245
- stw.transformDirection(moiTensor.transform(wts.transformDirection(Vector3d (alpha))))
241
+ val largestDistance = run {
242
+ var dist = center.distance(aabb.minX(), center.y(), aabb.minZ())
243
+ dist = max(dist, center.distance(aabb.minX(), center.y(), aabb.maxZ()))
244
+ dist = max(dist, center.distance(aabb.maxX(), center.y(), aabb.minZ()))
245
+ dist = max(dist, center.distance(aabb.maxX(), center.y(), aabb.maxZ()))
246
246
247
- val torque = Vector3d (angularImpulse)
248
- physShip.applyInvariantTorque(torque)
249
- // endregion
247
+ dist
248
+ }.coerceIn(0.5 , EurekaConfig .SERVER .maxSizeForTurnSpeedPenalty)
250
249
251
- // region Player controlled banking
252
- val rotationVector = control.seatInDirection.normal.toJOMLD()
250
+ val maxLinearAcceleration = EurekaConfig . SERVER .turnAcceleration
251
+ val maxLinearSpeed = EurekaConfig . SERVER .turnSpeed + extraForceAngular
253
252
254
- physShip.poseVel.transformDirection(rotationVector)
253
+ // acceleration = alpha * r
254
+ // therefore: maxAlpha = maxAcceleration / r
255
+ val maxOmegaY = maxLinearSpeed / largestDistance
256
+ val maxAlphaY = maxLinearAcceleration / largestDistance
255
257
256
- rotationVector.y = 0.0
258
+ val isBelowMaxTurnSpeed = abs(omega.y()) < maxOmegaY
257
259
258
- rotationVector.mul(idealAlphaY * - 1.5 )
260
+ val normalizedAlphaYMultiplier =
261
+ if (isBelowMaxTurnSpeed && control.leftImpulse != 0.0f ) control.leftImpulse.toDouble()
262
+ else - omega.y().coerceIn(- 1.0 , 1.0 )
259
263
260
- physShip.poseVel.rot.transform(
261
- moiTensor.transform(
262
- physShip.poseVel.rot.transformInverse(rotationVector)
263
- )
264
- )
264
+ val idealAlphaY = normalizedAlphaYMultiplier * maxAlphaY
265
265
266
- physShip.applyInvariantTorque(rotationVector)
267
- // endregion
266
+ physShip.applyInvariantTorque(moiTensor.transform(Vector3d (0.0 , idealAlphaY, 0.0 )))
267
+ // endregion
268
+
269
+ physShip.applyInvariantTorque(getPlayerControlledBanking(control, physShip, moiTensor, - idealAlphaY))
268
270
269
- // region Player controlled forward and backward thrust
270
- val forwardVector = control.seatInDirection.normal.toJOMLD()
271
- physShip.poseVel.rot.transform(forwardVector)
272
- // forwardVector.y *= 0.1 // Reduce vertical thrust
273
- forwardVector.normalize()
271
+ physShip.applyInvariantForce(getPlayerForwardVel(control, physShip))
272
+ }
274
273
275
- val s = 1 / smoothingATanMax(
276
- EurekaConfig .SERVER .linearMaxMass,
277
- physShip.inertia.shipMass * EurekaConfig .SERVER .linearMassScaling + EurekaConfig .SERVER .linearBaseMass
274
+ private fun getPlayerControlledBanking (control : ControlData , physShip : PhysShipImpl , moiTensor : Matrix3dc , strength : Double ): Vector3d {
275
+ val rotationVector = control.seatInDirection.normal.toJOMLD()
276
+ physShip.poseVel.transformDirection(rotationVector)
277
+ rotationVector.y = 0.0
278
+ rotationVector.mul(strength * 1.5 )
279
+
280
+ physShip.poseVel.rot.transform(
281
+ moiTensor.transform(
282
+ physShip.poseVel.rot.transformInverse(rotationVector)
278
283
)
284
+ )
279
285
280
- val maxSpeed = EurekaConfig .SERVER .linearMaxSpeed / 15 ;
281
- oldSpeed = max(min(oldSpeed * (1 - s) + control.forwardImpulse.toDouble() * s, maxSpeed), - maxSpeed)
282
- forwardVector.mul(oldSpeed)
286
+ return rotationVector
287
+ }
283
288
284
- val playerUpDirection = physShip.poseVel.transformDirection(Vector3d (0.0 , 1.0 , 0.0 ))
285
- val velOrthogonalToPlayerUp =
286
- vel.sub(playerUpDirection.mul(playerUpDirection.dot(vel), Vector3d ()), Vector3d ())
289
+ // Player controlled forward and backward thrust
290
+ private fun getPlayerForwardVel (control : ControlData , physShip : PhysShipImpl ): Vector3d {
287
291
288
- // This is the speed that the ship is always allowed to go out, without engines
289
- val baseForwardVel = Vector3d (forwardVector).mul(EurekaConfig .SERVER .baseSpeed)
290
- val baseForwardForce = Vector3d (baseForwardVel).sub(velOrthogonalToPlayerUp).mul(mass * 10 )
292
+ val mass10 = physShip.inertia.shipMass * 10
293
+ val vel: Vector3dc = physShip.poseVel.vel
291
294
292
- // This is the maximum speed we want to go in any scenario (when not sprinting)
293
- val idealForwardVel = Vector3d (forwardVector).mul(EurekaConfig .SERVER .maxCasualSpeed)
294
- val idealForwardForce = Vector3d (idealForwardVel).sub(velOrthogonalToPlayerUp).mul(mass * 10 )
295
+ // region Player controlled forward and backward thrust
296
+ val forwardVector = control.seatInDirection.normal.toJOMLD()
297
+ physShip.poseVel.rot.transform(forwardVector)
298
+ forwardVector.normalize()
295
299
296
- val extraForceNeeded = Vector3d (idealForwardForce).sub(baseForwardForce)
297
- val actualExtraForce = Vector3d (baseForwardForce)
300
+ val s = 1 / smoothingATanMax(
301
+ EurekaConfig .SERVER .linearMaxMass,
302
+ physShip.inertia.shipMass * EurekaConfig .SERVER .linearMassScaling + EurekaConfig .SERVER .linearBaseMass
303
+ )
298
304
299
- if (extraForceLinear != 0.0 ) {
300
- actualExtraForce.fma(min(extraForceLinear / extraForceNeeded.length(), 1.0 ), extraForceNeeded )
301
- }
305
+ val maxSpeed = EurekaConfig . SERVER .linearMaxSpeed / 15
306
+ oldSpeed = max(min(oldSpeed * ( 1 - s) + control.forwardImpulse.toDouble() * s, maxSpeed ), - maxSpeed )
307
+ forwardVector.mul(oldSpeed)
302
308
303
- physShip.applyInvariantForce(actualExtraForce)
304
- // endregion
305
-
306
- // Player controlled elevation
307
- if (control.upImpulse != 0.0f ) {
308
- idealUpwardVel = Vector3d (0.0 , 1.0 , 0.0 )
309
- .mul(control.upImpulse.toDouble())
310
- .mul(
311
- if (control.upImpulse < 0.0f ) {
312
- EurekaConfig .SERVER .baseImpulseDescendRate
313
- }
314
- else {
315
- EurekaConfig .SERVER .baseImpulseElevationRate +
316
- // Smoothing for how the elevation scales as you approaches the balloonElevationMaxSpeed
317
- smoothing(2.0 , EurekaConfig .SERVER .balloonElevationMaxSpeed, balloonForceProvided / mass)
318
- }
319
- )
320
- }
321
- }
309
+ val playerUpDirection = physShip.poseVel.transformDirection(Vector3d (0.0 , 1.0 , 0.0 ))
310
+ val velOrthogonalToPlayerUp = vel.sub(playerUpDirection.mul(playerUpDirection.dot(vel)), Vector3d ())
322
311
323
- // region Elevation
324
- val idealUpwardForce = Vector3d (
325
- 0.0 ,
326
- idealUpwardVel.y() - vel.y() - (GRAVITY / EurekaConfig .SERVER .elevationSnappiness),
327
- 0.0
328
- ).mul(mass * EurekaConfig .SERVER .elevationSnappiness)
329
-
330
- val actualUpwardForce = Vector3d (0.0 , min(balloonForceProvided, max(idealUpwardForce.y(), 0.0 )), 0.0 )
331
- physShip.applyInvariantForce(actualUpwardForce)
332
- // endregion
312
+ // This is the speed that the ship is always allowed to go out, without engines
313
+ val baseForwardVel = Vector3d (forwardVector).mul(EurekaConfig .SERVER .baseSpeed)
314
+ val forwardForce = Vector3d (baseForwardVel).sub(velOrthogonalToPlayerUp).mul(mass10)
333
315
334
- // region Anchor
335
- physShip.isStatic = anchored
336
- // endregion
316
+ if (extraForceLinear != 0.0 ) {
317
+ // This is the maximum speed we want to go in any scenario (when not sprinting)
318
+ val idealForwardVel = Vector3d (forwardVector).mul(EurekaConfig .SERVER .maxCasualSpeed)
319
+ val idealForwardForce = Vector3d (idealForwardVel).sub(velOrthogonalToPlayerUp).mul(mass10)
320
+
321
+ val extraForceNeeded = Vector3d (idealForwardForce).sub(forwardForce)
322
+ forwardForce.fma(min(extraForceLinear / extraForceNeeded.length(), 1.0 ), extraForceNeeded)
323
+ }
324
+
325
+ return forwardForce
326
+ }
337
327
338
- // Add drag to the y-component
339
- physShip.applyInvariantForce(Vector3d (0.0 , vel.y(),0.0 ).mul(- mass))
328
+ // Player controlled elevation
329
+ private fun getPlayerUpwardVel (control : ControlData , mass : Double ): Vector3d {
330
+ if (control.upImpulse != 0.0f ) {
331
+
332
+ val balloonForceProvided = balloons * forcePerBalloon
333
+
334
+ return Vector3d (0.0 , 1.0 , 0.0 )
335
+ .mul(control.upImpulse.toDouble())
336
+ .mul(
337
+ if (control.upImpulse < 0.0f ) {
338
+ EurekaConfig .SERVER .baseImpulseDescendRate
339
+ }
340
+ else {
341
+ EurekaConfig .SERVER .baseImpulseElevationRate +
342
+ // Smoothing for how the elevation scales as you approaches the balloonElevationMaxSpeed
343
+ smoothing(2.0 , EurekaConfig .SERVER .balloonElevationMaxSpeed, balloonForceProvided / mass)
344
+ }
345
+ )
346
+ }
347
+ return Vector3d (0.0 , 0.0 , 0.0 )
340
348
}
341
349
342
350
private fun showCruiseStatus () {
@@ -404,7 +412,7 @@ class EurekaShipControl : ShipForcesInducer, ServerTickListener {
404
412
powerLinear = 0.0
405
413
406
414
extraForceAngular = powerAngular
407
- powerAngular = 0.0 ;
415
+ powerAngular = 0.0
408
416
409
417
consumed = physConsumption * /* should be physics ticks based*/ 0.1f
410
418
physConsumption = 0.0f
0 commit comments