Skip to content

Commit b1892f1

Browse files
Separated the applyForces faction into several smaller functions
1 parent f4e58f7 commit b1892f1

File tree

1 file changed

+138
-130
lines changed

1 file changed

+138
-130
lines changed

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

+138-130
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@ import com.fasterxml.jackson.annotation.JsonProperty
77
import net.minecraft.core.Direction
88
import net.minecraft.network.chat.TranslatableComponent
99
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.*
1411
import org.valkyrienskies.core.api.VSBeta
1512
import org.valkyrienskies.core.api.ships.PhysShip
1613
import org.valkyrienskies.core.api.ships.ServerShip
@@ -119,10 +116,10 @@ class EurekaShipControl : ShipForcesInducer, ServerTickListener {
119116
// [x] Rewrite Alignment code
120117
// [x] Revisit Elevation code
121118
// [x] Balloon limiter
122-
// [ ] Add Cruise code
123-
// [ ] Rotation based of shipsize
119+
// [x] Add Cruise code
120+
// [x] Rotation based of ship size
124121
// [x] Engine consumption
125-
// [ ] Fix elevation sensititvity
122+
// [x] Fix elevation sensitivity
126123

127124
// region Aligning
128125

@@ -155,11 +152,8 @@ class EurekaShipControl : ShipForcesInducer, ServerTickListener {
155152
val validPlayer = controllingPlayer != null && !anchored
156153

157154
if (isCruising && anchored) {
158-
isCruising = false;
159-
if (controllingPlayer != null)
160-
{
161-
showCruiseStatus();
162-
}
155+
isCruising = false
156+
showCruiseStatus()
163157
}
164158

165159
stabilize(
@@ -176,22 +170,8 @@ class EurekaShipControl : ShipForcesInducer, ServerTickListener {
176170

177171
if (validPlayer) {
178172
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)
195175

196176
if (!isCruising) {
197177
// only take the latest control data if the player is not cruising
@@ -208,135 +188,163 @@ class EurekaShipControl : ShipForcesInducer, ServerTickListener {
208188
}
209189

210190
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+
}
217194

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
223206

224-
dist
225-
}.coerceIn(0.5, EurekaConfig.SERVER.maxSizeForTurnSpeedPenalty)
207+
physShip.isStatic = anchored
208+
}
226209

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+
}
229226

230-
// acceleration = alpha * r
231-
// therefore: maxAlpha = maxAcceleration / r
232-
val maxOmegaY = maxLinearSpeed / largestDistance
233-
val maxAlphaY = maxLinearAcceleration / largestDistance
227+
return currentControlData
228+
}
234229

235-
val isBelowMaxTurnSpeed = abs(omega.y()) < maxOmegaY
230+
private fun applyPlayerControl(control: ControlData, physShip: PhysShipImpl) {
236231

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
240236

241-
val idealAlphaY = normalizedAlphaYMultiplier * maxAlphaY
237+
// region Player controlled rotation
238+
val moiTensor = physShip.inertia.momentOfInertiaTensor
239+
val omega: Vector3dc = physShip.poseVel.omega
242240

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()))
246246

247-
val torque = Vector3d(angularImpulse)
248-
physShip.applyInvariantTorque(torque)
249-
// endregion
247+
dist
248+
}.coerceIn(0.5, EurekaConfig.SERVER.maxSizeForTurnSpeedPenalty)
250249

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
253252

254-
physShip.poseVel.transformDirection(rotationVector)
253+
// acceleration = alpha * r
254+
// therefore: maxAlpha = maxAcceleration / r
255+
val maxOmegaY = maxLinearSpeed / largestDistance
256+
val maxAlphaY = maxLinearAcceleration / largestDistance
255257

256-
rotationVector.y = 0.0
258+
val isBelowMaxTurnSpeed = abs(omega.y()) < maxOmegaY
257259

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)
259263

260-
physShip.poseVel.rot.transform(
261-
moiTensor.transform(
262-
physShip.poseVel.rot.transformInverse(rotationVector)
263-
)
264-
)
264+
val idealAlphaY = normalizedAlphaYMultiplier * maxAlphaY
265265

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))
268270

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+
}
274273

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)
278283
)
284+
)
279285

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+
}
283288

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 {
287291

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
291294

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()
295299

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+
)
298304

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)
302308

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())
322311

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)
333315

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+
}
337327

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)
340348
}
341349

342350
private fun showCruiseStatus() {
@@ -404,7 +412,7 @@ class EurekaShipControl : ShipForcesInducer, ServerTickListener {
404412
powerLinear = 0.0
405413

406414
extraForceAngular = powerAngular
407-
powerAngular = 0.0;
415+
powerAngular = 0.0
408416

409417
consumed = physConsumption * /* should be physics ticks based*/ 0.1f
410418
physConsumption = 0.0f

0 commit comments

Comments
 (0)