forked from mirrors/cobblemon
Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
308c9d61c6 | ||
|
|
07ad22608d | ||
|
|
b50f924ce0 | ||
|
|
c0b55ebb6f | ||
|
|
cdc0a790fb | ||
|
|
13e8084f0d | ||
|
|
3daa3d31a3 | ||
|
|
585371bb08 | ||
|
|
aee97ff96e | ||
|
|
fbbd450d0d | ||
|
|
83612b50fc | ||
|
|
434faea210 | ||
|
|
6e24ee6b4e | ||
|
|
3d6a3fbfec | ||
|
|
708b810e6b | ||
|
|
509016e133 | ||
|
|
690183b994 | ||
|
|
33d56124f5 | ||
|
|
a5a65abe2b | ||
|
|
f73ab3b001 | ||
|
|
9a571984db |
+12
@@ -9,8 +9,10 @@
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.client.CobblemonClient;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
@@ -30,5 +32,15 @@ public class AbstractClientPlayerMixin {
|
||||
if (usageContext.getScanningGuiOpen()) {
|
||||
cir.setReturnValue(usageContext.getFovMultiplier());
|
||||
}
|
||||
|
||||
//TODO: find a better solution that just tagging this logic on here. Likely just change it to a more general
|
||||
//name?
|
||||
LocalPlayer player = (LocalPlayer) (Object) this;
|
||||
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
|
||||
PokemonEntity ride = (PokemonEntity) player.getVehicle();
|
||||
|
||||
//Return custom fov mult
|
||||
cir.setReturnValue(ride.rideFovMult());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.world.effect.MobEffect
|
||||
import net.minecraft.world.entity.EntityDimensions
|
||||
import net.minecraft.world.item.Item
|
||||
@@ -108,6 +109,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
|
||||
.registerTypeAdapter(RideController::class.java, RideControllerAdapter)
|
||||
.registerTypeAdapter(RidingStatDefinition::class.java, RidingStatDefinitionAdapter)
|
||||
.registerTypeAdapter(SoundEvent::class.java, SoundEventAdapter)
|
||||
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
|
||||
.disableHtmlEscaping()
|
||||
.enableComplexMapKeySerialization()
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.riding.controller.RideController
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.toProperties
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import net.minecraft.ChatFormatting
|
||||
import net.minecraft.network.chat.Component
|
||||
@@ -25,6 +26,7 @@ import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.sin
|
||||
|
||||
data class RidingManager(val entity: PokemonEntity) {
|
||||
var lastSpeed = 0F
|
||||
@@ -40,8 +42,13 @@ data class RidingManager(val entity: PokemonEntity) {
|
||||
it.environment.query.addFunction("get_ride_stats") { params ->
|
||||
val rideStat = RidingStat.valueOf(params.getString(0).uppercase())
|
||||
val rideStyle = RidingStyle.valueOf(params.getString(1).uppercase())
|
||||
val maxVal = params.getDouble(2)
|
||||
val minVal = params.getDouble(3)
|
||||
//TODO: Use the mons actual boost once implemented
|
||||
DoubleValue(entity.rideProp.calculate(rideStat, rideStyle, 0))
|
||||
val normalizedStat = entity.rideProp.calculate(rideStat, rideStyle, 0) / 100.0f
|
||||
val trueStatVal = (normalizedStat * (maxVal - minVal)) + minVal
|
||||
|
||||
DoubleValue(trueStatVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,6 +82,11 @@ data class RidingManager(val entity: PokemonEntity) {
|
||||
//val speedEntity = entity.deltaMovement.length() //* 20 * 60 * 60) / ( 1000 )
|
||||
//println(speedEntity)
|
||||
//driver.displayClientMessage(Component.literal("Speed: ").withStyle { it.withColor(ChatFormatting.GREEN) }.append(Component.literal("${String.format("%.2f", speedEntity)} km/h")), true)
|
||||
//val speedPlayer = driver.deltaMovement.horizontalDistance()
|
||||
//driver.displayClientMessage(Component.literal("Speed player: ").withStyle { it.withColor(ChatFormatting.GREEN) }.append(Component.literal("$speedPlayer b/t")), true)
|
||||
//val speedEntity = entity.deltaMovement.length() //* 20 * 60 * 60) / ( 1000 )
|
||||
//println(speedEntity)
|
||||
//driver.displayClientMessage(Component.literal("Speed: ").withStyle { it.withColor(ChatFormatting.GREEN) }.append(Component.literal("${String.format("%.2f", speedEntity)} km/h")), true)
|
||||
|
||||
}
|
||||
|
||||
@@ -112,6 +124,11 @@ data class RidingManager(val entity: PokemonEntity) {
|
||||
return controller.setRideBar(entity, driver)
|
||||
}
|
||||
|
||||
fun rideFovMult(entity: PokemonEntity, driver: Player): Float {
|
||||
val controller = getController(entity) ?: return 1.0f
|
||||
return controller.rideFovMult(entity, driver)
|
||||
}
|
||||
|
||||
fun canJump(entity: PokemonEntity, driver: Player): Boolean {
|
||||
val controller = getController(entity) ?: return false
|
||||
return controller.canJump(entity, driver)
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding
|
||||
|
||||
import net.minecraft.client.player.LocalPlayer
|
||||
import net.minecraft.client.resources.sounds.AbstractTickableSoundInstance
|
||||
import net.minecraft.client.resources.sounds.SoundInstance
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.sounds.SoundSource
|
||||
|
||||
/**
|
||||
* put what it is here
|
||||
*
|
||||
* @author Jackowes
|
||||
* @since March 4th, 2025
|
||||
*/
|
||||
class RidingOnPlayerSoundEventInstance(
|
||||
private val player: LocalPlayer,
|
||||
soundEvent: SoundEvent
|
||||
) : AbstractTickableSoundInstance(soundEvent, SoundSource.PLAYERS, SoundInstance.createUnseededRandom()) {
|
||||
|
||||
|
||||
init {
|
||||
this.looping = true
|
||||
this.delay = 0
|
||||
this.volume = 0.1f
|
||||
}
|
||||
|
||||
fun setPitch( pitch: Float ){
|
||||
this.pitch = pitch
|
||||
}
|
||||
|
||||
fun setVolume( volume: Float){
|
||||
this.volume = volume
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
//TODO: implement effectively
|
||||
//For now it should just play the sound?
|
||||
setPitch(1.0f)
|
||||
println("ticking")
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -96,8 +96,9 @@ interface RideController : Encodable, Decodable {
|
||||
|
||||
fun setRideBar(entity: PokemonEntity, driver: Player): Float = 0.0f
|
||||
|
||||
fun canJump(entity: PokemonEntity, driver: Player) : Boolean
|
||||
fun rideFovMult(entity: PokemonEntity, driver: Player): Float = 1.0f
|
||||
|
||||
fun canJump(entity: PokemonEntity, driver: Player) : Boolean
|
||||
fun jumpForce(entity: PokemonEntity, driver: Player, jumpStrength: Int) : Vec3
|
||||
|
||||
fun gravity(entity: PokemonEntity, regularGravity: Double) : Double? = null
|
||||
|
||||
@@ -1417,6 +1417,7 @@ open class PokemonEntity(
|
||||
return this.deltaMovement
|
||||
}
|
||||
|
||||
/*
|
||||
override fun shouldDiscardFriction(): Boolean {
|
||||
val riders = this.passengers.filterIsInstance<LivingEntity>()
|
||||
if (riders.isEmpty()) {
|
||||
@@ -1425,6 +1426,7 @@ open class PokemonEntity(
|
||||
return true
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
override fun travel(movementInput: Vec3) {
|
||||
val prevBlockPos = this.blockPosition()
|
||||
@@ -1690,6 +1692,22 @@ open class PokemonEntity(
|
||||
this.yBodyRot = this.yRot
|
||||
this.yRotO = this.yRot
|
||||
|
||||
val riders = this.passengers.filterIsInstance<LivingEntity>()
|
||||
|
||||
//These are the only functions that matter and I have isolated them to
|
||||
//avoid minecrafts changes they do to velocity so that the ride controllers
|
||||
//can be tested without that interference.
|
||||
if ( false ) {
|
||||
val v = Entity.getInputVector(riding.velocity(this, driver, Vec3.ZERO), 1.0f, this.getYRot());
|
||||
//changing this will give the ride more or less inertia/handling/drift?
|
||||
//A bit of overlap with other things but.
|
||||
this.setDeltaMovement(this.deltaMovement.lerp(v, riding.inertia(this)));
|
||||
|
||||
//maybe move this to the travel function. The jump below never gets properly used
|
||||
this.move( MoverType.SELF, this.deltaMovement );
|
||||
|
||||
}
|
||||
|
||||
if (this.riding.canJump(this, driver)) {
|
||||
if (this.onGround()) {
|
||||
if (this.jumpInputStrength > 0) {
|
||||
@@ -1893,6 +1911,11 @@ open class PokemonEntity(
|
||||
return this.riding.setRideBar(this, driver)
|
||||
}
|
||||
|
||||
fun rideFovMult(): Float {
|
||||
val driver = this.controllingPassenger as? Player ?: return 1.0f
|
||||
return this.riding.rideFovMult(this, driver)
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility method to resolve the [Codec] of [Pokemon] aware if the [world] is client sided or not.
|
||||
*
|
||||
|
||||
+222
-87
@@ -9,18 +9,18 @@
|
||||
package com.cobblemon.mod.common.pokemon.riding.controllers
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.cobblemon.mod.common.Rollable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.controller.RideController
|
||||
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.riding.states.BirdAirState
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
@@ -33,7 +33,16 @@ class BirdAirController : RideController {
|
||||
override val poseProvider = PoseProvider(PoseType.HOVER)
|
||||
.with(PoseOption(PoseType.FLY) {
|
||||
val player = it.passengers.firstOrNull() as? Player ?: return@PoseOption false
|
||||
it.deltaMovement.length() > 0.3 })
|
||||
it.deltaMovement.length() > 0.5 })
|
||||
/*
|
||||
.with(PoseOption(PoseType.FLY) {
|
||||
val player = it.passengers.firstOrNull() as? Player ?: return@PoseOption false
|
||||
player.zza > 0.0f // Checks if the player is actively attempting to move forward
|
||||
})
|
||||
*/
|
||||
.with(PoseOption(PoseType.FLY) {
|
||||
val player = it.passengers.firstOrNull() as? Player ?: return@PoseOption false
|
||||
it.deltaMovement.length() > 0.5 })
|
||||
/*
|
||||
.with(PoseOption(PoseType.FLY) {
|
||||
val player = it.passengers.firstOrNull() as? Player ?: return@PoseOption false
|
||||
@@ -43,8 +52,6 @@ class BirdAirController : RideController {
|
||||
|
||||
override val condition: (PokemonEntity) -> Boolean = { true }
|
||||
|
||||
var jumpVector = listOf("0".asExpression(), "0.3".asExpression(), "0".asExpression())
|
||||
private set
|
||||
var gravity: Expression = "0".asExpression()
|
||||
private set
|
||||
var horizontalAcceleration: Expression = "0.1".asExpression()
|
||||
@@ -52,62 +59,29 @@ class BirdAirController : RideController {
|
||||
var verticalAcceleration: Expression = "0.1".asExpression()
|
||||
private set
|
||||
|
||||
// Where 90 is the width of the range and 45 is the offset.
|
||||
// So the handling interpolates between 135 and 45
|
||||
var handlingExpr: Expression = "(q.get_ride_stats('SKILL', 'AIR') / 100) * 90 + 45".asExpression()
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 135.0, 45.0)".asExpression()
|
||||
private set
|
||||
var topSpeedExpr: Expression = "(q.get_ride_stats('SPEED', 'AIR') / 100) * (1.0 - 0.35) + 0.35".asExpression()
|
||||
var topSpeedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.0, 0.35)".asExpression()
|
||||
private set
|
||||
//Reaches max top speed from a standstill at minimum 8 seconds and max 3 seconds.
|
||||
//This is a bit of a confusing expression and likely should be cleaned up to be more readable
|
||||
var accelExpr: Expression = "(q.get_ride_stats('ACCELERATION', 'AIR') / 100) * (0.01666666666 - 0.00625) + 0.00625".asExpression()
|
||||
var glideTopSpeedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 2.0, 1.0)".asExpression()
|
||||
private set
|
||||
// Max accel is a whole 1.0 in 3 seconds. The conversion in the function below is to convert seconds to ticks
|
||||
var accelExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 3.0)), (1.0 / (20.0 * 8.0)))".asExpression()
|
||||
private set
|
||||
//Seconds self propelled flight (glide is not self propelled in this case)
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 60.0, 20.0)".asExpression()
|
||||
private set
|
||||
|
||||
var infiniteStamina: Expression = "false".asExpression()
|
||||
private set
|
||||
|
||||
override fun speed(entity: PokemonEntity, driver: Player): Float {
|
||||
|
||||
//Retrieve the current bird controller state
|
||||
//val state = entity.riding.getState(KEY) { e -> BirdAirControllerState(e) }
|
||||
//Retrieve the current bird controller state and stats
|
||||
val state = getState(entity, ::BirdAirState)
|
||||
|
||||
//retrieve stats
|
||||
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
|
||||
val accel = getRuntime(entity).resolveDouble(accelExpr)
|
||||
|
||||
val maxGlideSpeed = topSpeed * 2
|
||||
var glideSpeedChange = 0.00
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza > 0.0 && state.currSpeed < topSpeed) {
|
||||
state.currSpeed = min(state.currSpeed + accel , topSpeed)
|
||||
} else if (driver.zza < 0.0 && state.currSpeed > 0.0) {
|
||||
//Decelerate is currently always a constant half of max acceleration.
|
||||
state.currSpeed = max(state.currSpeed - (0.0166 / 2), 0.0)
|
||||
}
|
||||
|
||||
val rollable = driver as? Rollable
|
||||
if (rollable != null) {
|
||||
glideSpeedChange = sin(Math.toRadians(rollable.pitch.toDouble()))
|
||||
glideSpeedChange = glideSpeedChange.pow(3)
|
||||
|
||||
//TODO: Possibly create a deadzone around parallel where speed isn't taken away from?
|
||||
if (glideSpeedChange <= 0.0) {
|
||||
//Ensures that a propelling force is still able to be applied when
|
||||
//climbing in height
|
||||
if (driver.zza <= 0) {
|
||||
//speed decrease should be 2x speed increase?
|
||||
state.currSpeed = max(state.currSpeed + (0.0166) * glideSpeedChange, 0.0 )
|
||||
}
|
||||
} else {
|
||||
// only add to the speed if it hasn't exceeded the current
|
||||
//glide angles maximum amount of speed that it can give.
|
||||
state.currSpeed = min(state.currSpeed + ((0.0166 * 2) * glideSpeedChange), maxGlideSpeed)
|
||||
}
|
||||
}
|
||||
|
||||
//air resistance
|
||||
state.currSpeed = max(state.currSpeed - 0.005, 0.0)
|
||||
|
||||
return state.currSpeed.toFloat()
|
||||
return state.rideVel.length().toFloat()
|
||||
}
|
||||
|
||||
override fun rotation(entity: PokemonEntity, driver: LivingEntity): Vec2 {
|
||||
@@ -119,57 +93,54 @@ class BirdAirController : RideController {
|
||||
var leftForce = 0.0
|
||||
var upForce = 0.0
|
||||
var forwardForce = 0.0
|
||||
val state = getState(entity, ::BirdAirState)
|
||||
|
||||
//Perform ride velocity update
|
||||
calculateRideSpaceVel(entity, driver, state)
|
||||
|
||||
//Translate ride space velocity to world space velocity.
|
||||
val rollable = driver as? Rollable
|
||||
if (rollable != null) {
|
||||
//Need to deadzone this when straight up or down
|
||||
// test with and without strafing added. May be wanted while moving
|
||||
leftForce = -1.0 * sin(Math.toRadians(rollable.roll.toDouble()))
|
||||
|
||||
upForce = -1.0 * sin(Math.toRadians(rollable.pitch.toDouble()))
|
||||
upForce += -1.0 * sin(Math.toRadians(rollable.pitch.toDouble())) * state.rideVel.z
|
||||
forwardForce += cos(Math.toRadians(rollable.pitch.toDouble())) * state.rideVel.z
|
||||
|
||||
forwardForce = cos(Math.toRadians(rollable.pitch.toDouble()))
|
||||
upForce += cos(Math.toRadians(rollable.pitch.toDouble())) * state.rideVel.y
|
||||
forwardForce += sin(Math.toRadians(rollable.pitch.toDouble())) * state.rideVel.y
|
||||
}
|
||||
|
||||
//TODO: Add in an upwards and downwards force when pressing space maybe?
|
||||
//TODO: implement altitude limit
|
||||
|
||||
//Leave out tap to flap right now
|
||||
/*
|
||||
var jumpVector = Vec3.ZERO
|
||||
if (entity.jumpInputStrength > 0) {
|
||||
val runtime = getRuntime(entity)
|
||||
runtime.environment.query.addFunction("jump_strength") { DoubleValue(entity.jumpInputStrength.toDouble()) }
|
||||
val jumpForces = this.jumpVector.map { runtime.resolveFloat(it) }
|
||||
jumpVector = Vec3(jumpForces[0].toDouble(), jumpForces[1].toDouble(), jumpForces[2].toDouble())
|
||||
jumpVector = jumpVector.toVector3f().mul((driver as Rollable).orientation).toVec3d()
|
||||
entity.jumpInputStrength = 0
|
||||
entity.addDeltaMovement(jumpVector)
|
||||
//Bring the ride out of the sky when stamina is depleted.
|
||||
if (state.stamina <= 0.0) {
|
||||
upForce -= 0.3
|
||||
}
|
||||
*/
|
||||
//Retrieve the current composite controller state
|
||||
val state = getState(entity, ::BirdAirState)
|
||||
|
||||
val velocity = Vec3(0.0 , upForce * state.currSpeed, forwardForce * state.currSpeed)
|
||||
|
||||
val velocity = Vec3(state.rideVel.x , upForce, forwardForce)
|
||||
return velocity
|
||||
}
|
||||
|
||||
override fun angRollVel(entity: PokemonEntity, driver: Player, deltaTime: Double): Vec3 {
|
||||
|
||||
val rollable = driver as? Rollable
|
||||
val state = getState(entity, ::BirdAirState)
|
||||
|
||||
//TODO: Tie in handling
|
||||
val handling = getRuntime(entity).resolveDouble(handlingExpr)
|
||||
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
|
||||
val rotationChangeRate = 10.0
|
||||
|
||||
if (rollable != null) {
|
||||
var yawForce = rotationChangeRate * sin(Math.toRadians(rollable.roll.toDouble()))
|
||||
//for a bit of correction on the rolls limit it to a quarter the amount
|
||||
var pitchForce = -0.25 * rotationChangeRate * abs(sin(Math.toRadians(rollable.roll.toDouble())))
|
||||
var pitchForce = -0.35 * rotationChangeRate * abs(sin(Math.toRadians(rollable.roll.toDouble())))
|
||||
|
||||
//limit rotation modulation when pitched up heavily or pitched down heavily
|
||||
yawForce *= abs(cos(Math.toRadians(rollable.pitch.toDouble())))
|
||||
pitchForce *= abs(cos(Math.toRadians(rollable.pitch.toDouble()))) * 1.5
|
||||
|
||||
|
||||
//Create a deadzone for easier leveling out
|
||||
if (rollable.roll <= 0.0 && rollable.roll >= -0.0) {
|
||||
yawForce = 0.0
|
||||
@@ -192,40 +163,54 @@ class BirdAirController : RideController {
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is Rollable) return Vec3.ZERO
|
||||
|
||||
val rollable = driver as Rollable
|
||||
|
||||
val state = getState(entity, ::BirdAirState)
|
||||
val handling = getRuntime(entity).resolveDouble(handlingExpr)
|
||||
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
|
||||
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4
|
||||
var xInput = xMouseSmoother.getNewDeltaValue(xMouse * 0.1, deltaTime * smoothingSpeed);
|
||||
val YInput = yMouseSmoother.getNewDeltaValue(yMouse * 0.1, deltaTime * smoothingSpeed);
|
||||
val xInput = xMouseSmoother.getNewDeltaValue(xMouse * 0.1, deltaTime * smoothingSpeed);
|
||||
val yInput = yMouseSmoother.getNewDeltaValue(yMouse * 0.1, deltaTime * smoothingSpeed);
|
||||
|
||||
//limit rolling based on healing
|
||||
val rotLimit = handling
|
||||
//limit rolling based on handling and current speed.
|
||||
//modulated by speed so that when flapping idle in air you are ont wobbling around to look around
|
||||
val rotMin = 15.0
|
||||
var rollForce = xInput
|
||||
val rotLimit = max(handling * normalizeVal(state.rideVel.length(), 0.0, topSpeed).pow(3), rotMin)
|
||||
|
||||
//Limit roll by non linearly decreasing inputs towards
|
||||
// a rotation limit based on the current distance from
|
||||
// that rotation limit
|
||||
if (abs(rollable.roll + xInput) < rotLimit) {
|
||||
if (sign(xInput) == sign(rollable.roll).toDouble()) {
|
||||
if (abs(rollable.roll + rollForce) < rotLimit) {
|
||||
if (sign(rollForce) == sign(rollable.roll).toDouble()) {
|
||||
val d = abs(abs(rollable.roll) - rotLimit)
|
||||
xInput *= (d.pow(2)) / (rotLimit.pow(2))
|
||||
rollForce *= (d.pow(2)) / (rotLimit.pow(2))
|
||||
}
|
||||
} else if (sign(xInput) == sign(rollable.roll).toDouble()) {
|
||||
xInput = 0.0
|
||||
} else if (sign(rollForce) == sign(rollable.roll).toDouble()) {
|
||||
rollForce = 0.0
|
||||
}
|
||||
|
||||
//Give the ability to yaw with x mouse input when at low speeds.
|
||||
val yawForce = xInput * ( 1.0 - normalizeVal(state.rideVel.length(), 0.0, topSpeed).pow(3))
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, YInput, xInput)
|
||||
return Vec3(yawForce, yInput, rollForce)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
entity: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setRideBar(entity: PokemonEntity, driver: Player): Float {
|
||||
//Retrieve stamina from state
|
||||
val state = getState(entity, ::BirdAirState)
|
||||
|
||||
return (state.stamina / 1.0f)
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
@@ -242,6 +227,19 @@ class BirdAirController : RideController {
|
||||
|
||||
override fun shouldRoll(entity: PokemonEntity) = true
|
||||
|
||||
override fun rideFovMult(entity: PokemonEntity, driver: Player): Float {
|
||||
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
|
||||
val glideTopSpeed = getRuntime(entity).resolveDouble(glideTopSpeedExpr)
|
||||
val state = getState(entity, ::BirdAirState)
|
||||
|
||||
//Must I ensure that topspeed is greater than minimum?
|
||||
val normalizedGlideSpeed = normalizeVal(state.rideVel.length(), topSpeed, glideTopSpeed)
|
||||
|
||||
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
|
||||
return 1.0f + normalizedGlideSpeed.pow(2).toFloat() * 0.2f
|
||||
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(entity: PokemonEntity): Boolean = false
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
@@ -252,6 +250,9 @@ class BirdAirController : RideController {
|
||||
buffer.writeString(topSpeedExpr.toString())
|
||||
buffer.writeString(accelExpr.toString())
|
||||
buffer.writeString(handlingExpr.toString())
|
||||
buffer.writeString(topSpeedExpr.toString())
|
||||
buffer.writeString(accelExpr.toString())
|
||||
buffer.writeString(handlingExpr.toString())
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
@@ -261,10 +262,144 @@ class BirdAirController : RideController {
|
||||
topSpeedExpr = buffer.readString().asExpression()
|
||||
accelExpr = buffer.readString().asExpression()
|
||||
handlingExpr = buffer.readString().asExpression()
|
||||
topSpeedExpr = buffer.readString().asExpression()
|
||||
accelExpr = buffer.readString().asExpression()
|
||||
handlingExpr = buffer.readString().asExpression()
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
val KEY = cobblemonResource("air/bird")
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalizes the given value between a min and a max.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents x is at or below min
|
||||
* and 1.0 represents x is at or above it.
|
||||
*/
|
||||
fun normalizeVal(x: Double, min: Double, max: Double): Double {
|
||||
require(max > min) { "max must be greater than min" }
|
||||
return ((x - min) / (max - min)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates the change in the ride space vector due to player input and ride state
|
||||
*/
|
||||
fun calculateRideSpaceVel( entity: PokemonEntity, driver: Player, state: BirdAirState){
|
||||
|
||||
|
||||
//retrieve stats
|
||||
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
|
||||
val glideTopSpeed = getRuntime(entity).resolveDouble(glideTopSpeedExpr)
|
||||
val accel = getRuntime(entity).resolveDouble(accelExpr)
|
||||
val staminaStat = getRuntime(entity).resolveDouble(staminaExpr)
|
||||
|
||||
var glideSpeedChange = 0.00
|
||||
|
||||
val currSpeed = state.rideVel.length()
|
||||
|
||||
//Flag for determining if stamina should be lost
|
||||
var loseStamina = false
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza != 0.0f && state.stamina > 0.0) {
|
||||
//make sure it can't exceed top speed
|
||||
val forwardInput = when {
|
||||
driver.zza > 0 && state.rideVel.z > topSpeed -> 0.0
|
||||
driver.zza < 0 && state.rideVel.z < (-topSpeed / 3.0) -> 0.0
|
||||
else -> driver.zza.sign
|
||||
}
|
||||
|
||||
state.rideVel = Vec3(
|
||||
state.rideVel.x,
|
||||
state.rideVel.y,
|
||||
(state.rideVel.z + (accel * forwardInput.toDouble())))
|
||||
|
||||
loseStamina = true
|
||||
}
|
||||
|
||||
val rollable = driver as? Rollable
|
||||
if (rollable != null) {
|
||||
//Base glide speed change on current pitch of the ride.
|
||||
glideSpeedChange = sin(Math.toRadians(rollable.pitch.toDouble()))
|
||||
glideSpeedChange = glideSpeedChange.pow(3) * 0.5
|
||||
|
||||
//TODO: Possibly create a deadzone around parallel where glide doesn't affect speed?
|
||||
if (glideSpeedChange <= 0.0) {
|
||||
//Ensures that a propelling force is still able to be applied when
|
||||
//climbing in height
|
||||
if (driver.zza <= 0) {
|
||||
//speed decrease should be 2x speed increase?
|
||||
//state.currSpeed = max(state.currSpeed + (0.0166) * glideSpeedChange, 0.0 )
|
||||
state.rideVel = Vec3(
|
||||
state.rideVel.x,
|
||||
state.rideVel.y,
|
||||
lerp( state.rideVel.z, 0.0,glideSpeedChange * -0.0166 * 2 ))
|
||||
}
|
||||
} else {
|
||||
// only add to the speed if it hasn't exceeded the current
|
||||
//glide angles maximum amount of speed that it can give.
|
||||
//state.currSpeed = min(state.currSpeed + ((0.0166 * 2) * glideSpeedChange), maxGlideSpeed)
|
||||
state.rideVel = Vec3(
|
||||
state.rideVel.x,
|
||||
state.rideVel.y,
|
||||
min(state.rideVel.z + ((0.0166 * 2) * glideSpeedChange), glideTopSpeed))
|
||||
}
|
||||
}
|
||||
|
||||
//Lateral movement based on driver input.
|
||||
val latTopSpeed = topSpeed / 2.0
|
||||
if (driver.xxa != 0.0f && state.stamina > 0.0) {
|
||||
state.rideVel = Vec3(
|
||||
(state.rideVel.x + (accel * driver.xxa)).coerceIn(-latTopSpeed, latTopSpeed),
|
||||
state.rideVel.y,
|
||||
state.rideVel.z)
|
||||
loseStamina = true
|
||||
}
|
||||
else {
|
||||
state.rideVel = Vec3(
|
||||
lerp(state.rideVel.x, 0.0, latTopSpeed / 20.0),
|
||||
state.rideVel.y,
|
||||
state.rideVel.z)
|
||||
}
|
||||
|
||||
//Vertical movement based on driver input.
|
||||
val vertTopSpeed = topSpeed / 2.0
|
||||
val vertInput = when {
|
||||
Minecraft.getInstance().options.keyJump.isDown() -> 1.0
|
||||
Minecraft.getInstance().options.keyShift.isDown() -> -1.0
|
||||
else -> 0.0
|
||||
}
|
||||
|
||||
if (vertInput != 0.0 && state.stamina > 0.0) {
|
||||
state.rideVel = Vec3(
|
||||
state.rideVel.x,
|
||||
(state.rideVel.y + (accel * vertInput)).coerceIn(-vertTopSpeed, vertTopSpeed),
|
||||
state.rideVel.z)
|
||||
loseStamina = true
|
||||
}
|
||||
else {
|
||||
state.rideVel = Vec3(
|
||||
state.rideVel.x,
|
||||
lerp(state.rideVel.y, 0.0, vertTopSpeed / 20.0),
|
||||
state.rideVel.z)
|
||||
}
|
||||
|
||||
//air resistance
|
||||
//TODO: This should not affect the ride if the player is inputting a force?
|
||||
//state.currSpeed = max(state.currSpeed - 0.005, 0.0)
|
||||
//Ensure it would take ten whole seconds to come to a complete stop through air resistance.
|
||||
|
||||
if ( loseStamina ) {
|
||||
state.stamina -= (0.05 / staminaStat).toFloat()
|
||||
}
|
||||
|
||||
//Lose a base amount of stamina just for being airborne
|
||||
state.stamina -= (0.01 / staminaStat).toFloat()
|
||||
|
||||
state.rideVel = Vec3(
|
||||
state.rideVel.x,
|
||||
state.rideVel.y,
|
||||
lerp( state.rideVel.z,0.0, topSpeed / ( 20.0 * 30.0)))
|
||||
}
|
||||
}
|
||||
+82
-87
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.pokemon.riding.controllers
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Rollable
|
||||
import com.cobblemon.mod.common.api.riding.controller.RideController
|
||||
@@ -16,6 +17,7 @@ import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.riding.states.DolphinState
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
@@ -25,33 +27,13 @@ import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.sqrt
|
||||
import kotlin.math.*
|
||||
|
||||
class DolphinController : RideController {
|
||||
companion object {
|
||||
val KEY: ResourceLocation = cobblemonResource("swim/dolphin")
|
||||
}
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
private set
|
||||
var jumpVector = listOf("0".asExpression(), "0.3".asExpression(), "0".asExpression())
|
||||
private set
|
||||
var speed = "1.0".asExpression()
|
||||
private set
|
||||
var driveFactor = "1.0".asExpression()
|
||||
private set
|
||||
var reverseDriveFactor = "0.25".asExpression()
|
||||
private set
|
||||
var strafeFactor = "0.2".asExpression()
|
||||
private set
|
||||
var lastVelocity = Vec3(0.0,0.0,0.0)
|
||||
private set
|
||||
|
||||
private val rollSmoother = SmoothDouble()
|
||||
|
||||
override val key: ResourceLocation = KEY
|
||||
override val poseProvider: PoseProvider = PoseProvider(PoseType.FLOAT)
|
||||
.with(PoseOption(PoseType.SWIM) { it.deltaMovement.length() >= 0.05 })
|
||||
@@ -69,9 +51,43 @@ class DolphinController : RideController {
|
||||
}
|
||||
}
|
||||
|
||||
var handlingExpr: Expression = "(q.get_ride_stats('SKILL', 'LAND') / 100) * (140.0 - 20.0) + 20.0".asExpression()
|
||||
private set
|
||||
var topSpeedExpr: Expression = "(q.get_ride_stats('SPEED', 'LAND') / 100) * (1.0 - 0.5) + 0.5".asExpression()
|
||||
private set
|
||||
var accelExpr: Expression = ("(q.get_ride_stats('ACCELERATION', 'LAND') / 100.0) * " +
|
||||
"( (1.0 / (1.0 * 2)) - (1.0 / (20 * 6.0))) + (1.0 / (20 * 6.0))").asExpression()
|
||||
private set
|
||||
var staminaExpr: Expression = "(q.get_ride_stats('SKILL', 'LAND') / 100) * (60.0 - 10.0) + 10.0".asExpression()
|
||||
private set
|
||||
var jumpExpr: Expression = "(q.get_ride_stats('SKILL', 'LAND') / 100) * (300.0 - 128.0) + 128.0".asExpression()
|
||||
private set
|
||||
|
||||
|
||||
override fun speed(entity: PokemonEntity, driver: Player): Float {
|
||||
return getRuntime(entity).resolveFloat(speed)
|
||||
//Retrieve Vehicle state
|
||||
val state = getState(entity, ::DolphinState)
|
||||
|
||||
//retrieve stats
|
||||
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr) * 2
|
||||
val accel = getRuntime(entity).resolveDouble(accelExpr) * 2
|
||||
|
||||
//speed up and slow down based on input
|
||||
if( driver.zza > 0.0 && state.currSpeed < topSpeed)
|
||||
{
|
||||
state.currSpeed = min( state.currSpeed + accel , topSpeed)
|
||||
}
|
||||
else if ( driver.zza < 0.0 && state.currSpeed > 0.0)
|
||||
{
|
||||
//Decelerate is currently always half of accel
|
||||
state.currSpeed = max( state.currSpeed - (accel * 2) , 0.0)
|
||||
}
|
||||
//If neither forcing deccel or accel then apply water resistance
|
||||
else{
|
||||
state.currSpeed = max( state.currSpeed - (accel / 4.0) , 0.0)
|
||||
}
|
||||
|
||||
return state.currSpeed.toFloat()
|
||||
}
|
||||
|
||||
override fun rotation(entity: PokemonEntity, driver: LivingEntity): Vec2 {
|
||||
@@ -84,7 +100,7 @@ class DolphinController : RideController {
|
||||
|
||||
override fun inertia(entity: PokemonEntity ): Double
|
||||
{
|
||||
return 0.05
|
||||
return 0.1
|
||||
}
|
||||
|
||||
override fun velocity(entity: PokemonEntity, driver: Player, input: Vec3): Vec3 {
|
||||
@@ -92,20 +108,12 @@ class DolphinController : RideController {
|
||||
|
||||
if (!entity.isInWater && !entity.isUnderWater)
|
||||
{
|
||||
lastVelocity = Vec3(lastVelocity.x, lastVelocity.y - 0.035, lastVelocity.z)
|
||||
return lastVelocity
|
||||
}
|
||||
|
||||
val driveFactor = runtime.resolveFloat(this.driveFactor)
|
||||
val strafeFactor = runtime.resolveFloat(this.strafeFactor)
|
||||
val f = driver.xxa * strafeFactor
|
||||
var g = driver.zza * driveFactor
|
||||
if (g <= 0.0f) {
|
||||
g *= runtime.resolveFloat(this.reverseDriveFactor)
|
||||
return Vec3(entity.deltaMovement.x, entity.deltaMovement.x - 0.035, entity.deltaMovement.z )
|
||||
}
|
||||
|
||||
var yComp = 0.0
|
||||
|
||||
//TODO:Determine a better way to move around at low speed.
|
||||
//Get roll to add a left and right strafe during roll
|
||||
//Not sure if this is desired? Will need to mess around with this
|
||||
//a bit more
|
||||
@@ -114,50 +122,52 @@ class DolphinController : RideController {
|
||||
if (rollable != null)
|
||||
{
|
||||
//Can be used again if I better figure out how to deadzone it near the tops?
|
||||
zComp = -1.0 * g.toDouble() * sin(Math.toRadians(rollable.roll.toDouble()))
|
||||
yComp = -1.0 * g.toDouble() * sin(Math.toRadians(rollable.pitch.toDouble()))
|
||||
zComp = -1.0 * sin(Math.toRadians( rollable.roll.toDouble() ) )
|
||||
yComp = -1.0 * sin(Math.toRadians( rollable.pitch.toDouble() ) )
|
||||
}
|
||||
|
||||
val currVelocity = Vec3(0.0, yComp , g.toDouble())
|
||||
lastVelocity = currVelocity
|
||||
val currVelocity = Vec3(0.0, yComp , 1.0)
|
||||
return currVelocity
|
||||
}
|
||||
|
||||
override fun angRollVel(entity: PokemonEntity, driver: Player, deltaTime: Double): Vec3 {
|
||||
|
||||
if (!entity.isInWater && !entity.isUnderWater)
|
||||
{
|
||||
return Vec3(0.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
val rollable = driver as? Rollable
|
||||
|
||||
//this should be changed to be speed maybe?
|
||||
val movingForce = driver.zza
|
||||
if (rollable != null) {
|
||||
var yawAngVel = 3 * sin(Math.toRadians(rollable.roll.toDouble())).toFloat()
|
||||
var pitchAngVel = -1 * Math.abs(sin(Math.toRadians(rollable.roll.toDouble())).toFloat())
|
||||
//TODO: Tie in handling
|
||||
val handling = getRuntime(entity).resolveDouble(handlingExpr)
|
||||
val rotationChangeRate = 10.0
|
||||
|
||||
if( rollable != null )
|
||||
{
|
||||
var yawForce = rotationChangeRate * sin( Math.toRadians( rollable.roll.toDouble() ))
|
||||
//for a bit of correction on the rolls limit it to a quarter the amount
|
||||
var pitchForce = -0.25 * rotationChangeRate * abs(sin( Math.toRadians( rollable.roll.toDouble() )))
|
||||
|
||||
//limit rotation modulation when pitched up heavily or pitched down heavily
|
||||
yawAngVel *= (abs(cos(Math.toRadians(rollable.pitch.toDouble())))).toFloat()
|
||||
pitchAngVel *= (abs(cos(Math.toRadians(rollable.pitch.toDouble())))).toFloat()
|
||||
//if you are not pressing forward then don't turn
|
||||
//yawAngVel *= movingForce
|
||||
//Ignore x,y,z its angular velocity:
|
||||
//yaw, pitch, roll
|
||||
return Vec3(yawAngVel.toDouble(), pitchAngVel.toDouble(), 0.0)
|
||||
yawForce *= abs(cos( Math.toRadians( rollable.pitch.toDouble() )))
|
||||
pitchForce *= abs(cos( Math.toRadians( rollable.pitch.toDouble() ))) * 1.5
|
||||
|
||||
//Create a deadzone for easier leveling out
|
||||
if( rollable.roll <= 0.0 && rollable.roll >= -0.0)
|
||||
{
|
||||
yawForce = 0.0
|
||||
pitchForce = 0.0
|
||||
}
|
||||
return Vec3( yawForce, pitchForce, 0.0)
|
||||
}
|
||||
|
||||
return Vec3(0.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
override fun canJump(entity: PokemonEntity, driver: Player) = getRuntime(entity).resolveBoolean(canJump)
|
||||
override fun canJump(entity: PokemonEntity, driver: Player) = false
|
||||
|
||||
override fun jumpForce(entity: PokemonEntity, driver: Player, jumpStrength: Int): Vec3 {
|
||||
val runtime = getRuntime(entity)
|
||||
runtime.environment.query.addFunction("jump_strength") { DoubleValue(jumpStrength.toDouble()) }
|
||||
val jumpVector = jumpVector.map { runtime.resolveFloat(it) }
|
||||
return Vec3(jumpVector[0].toDouble(), jumpVector[1].toDouble(), jumpVector[2].toDouble())
|
||||
override fun jumpForce(
|
||||
entity: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun shouldRoll(entity: PokemonEntity) = true
|
||||
@@ -168,35 +178,20 @@ class DolphinController : RideController {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeString(this.speed.getString())
|
||||
buffer.writeString(this.canJump.getString())
|
||||
buffer.writeString(this.jumpVector[0].getString())
|
||||
buffer.writeString(this.jumpVector[1].getString())
|
||||
buffer.writeString(this.jumpVector[2].getString())
|
||||
buffer.writeString(this.driveFactor.getString())
|
||||
buffer.writeString(this.reverseDriveFactor.getString())
|
||||
buffer.writeString(this.strafeFactor.getString())
|
||||
buffer.writeDouble(this.lastVelocity.x)
|
||||
buffer.writeDouble(this.lastVelocity.y)
|
||||
buffer.writeDouble(this.lastVelocity.z)
|
||||
buffer.writeString(this.handlingExpr.getString())
|
||||
buffer.writeString(this.staminaExpr.getString())
|
||||
buffer.writeString(this.jumpExpr.getString())
|
||||
buffer.writeString(this.topSpeedExpr.getString())
|
||||
buffer.writeString(this.accelExpr.getString())
|
||||
}
|
||||
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
this.speed = buffer.readString().asExpression()
|
||||
this.canJump = buffer.readString().asExpression()
|
||||
this.jumpVector = listOf(
|
||||
buffer.readString().asExpression(),
|
||||
buffer.readString().asExpression(),
|
||||
buffer.readString().asExpression()
|
||||
)
|
||||
this.driveFactor = buffer.readString().asExpression()
|
||||
this.reverseDriveFactor = buffer.readString().asExpression()
|
||||
this.strafeFactor = buffer.readString().asExpression()
|
||||
this.strafeFactor = buffer.readString().asExpression()
|
||||
val x = buffer.readDouble()
|
||||
val y = buffer.readDouble()
|
||||
val z = buffer.readDouble()
|
||||
this.lastVelocity = Vec3(x, y, z)
|
||||
this.handlingExpr = buffer.readString().asExpression()
|
||||
this.staminaExpr = buffer.readString().asExpression()
|
||||
this.jumpExpr = buffer.readString().asExpression()
|
||||
this.topSpeedExpr = buffer.readString().asExpression()
|
||||
this.accelExpr = buffer.readString().asExpression()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+110
-16
@@ -8,30 +8,27 @@
|
||||
|
||||
package com.cobblemon.mod.common.pokemon.riding.controllers
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Rollable
|
||||
import com.cobblemon.mod.common.api.riding.controller.RideController
|
||||
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.riding.states.GenericLandState
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.getString
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.resolveFloat
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import com.cobblemon.mod.common.pokemon.riding.states.JetAirState
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.*
|
||||
|
||||
class GenericLandController : RideController {
|
||||
companion object {
|
||||
@@ -53,6 +50,20 @@ class GenericLandController : RideController {
|
||||
var strafeFactor = "0.2".asExpression()
|
||||
private set
|
||||
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 140.0, 20.0)".asExpression()
|
||||
private set
|
||||
var topSpeedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.3)".asExpression()
|
||||
private set
|
||||
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
|
||||
var accelExpr: Expression = "q.get_ride_stats('ACCELERATION', 'LAND', (1.0 / (20.0 * 0.5)), (1.0 / (20.0 * 5.0)))".asExpression()
|
||||
private set
|
||||
// Between 30 seconds and 10 seconds at the lowest when at full speed.
|
||||
var staminaExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 30.0, 10.0)".asExpression()
|
||||
private set
|
||||
//Between a one block jump and a ten block jump
|
||||
var jumpExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 10.0, 1.0)".asExpression()
|
||||
private set
|
||||
|
||||
@Transient
|
||||
override val key: ResourceLocation = KEY
|
||||
@Transient
|
||||
@@ -78,8 +89,7 @@ class GenericLandController : RideController {
|
||||
|
||||
override fun speed(entity: PokemonEntity, driver: Player): Float {
|
||||
val state = getState(entity, ::GenericLandState)
|
||||
state.currSpeed = 1.0
|
||||
return state.currSpeed.toFloat()
|
||||
return state.rideVel.length().toFloat()
|
||||
}
|
||||
|
||||
override fun rotation(entity: PokemonEntity, driver: LivingEntity): Vec2 {
|
||||
@@ -97,16 +107,54 @@ class GenericLandController : RideController {
|
||||
if (g <= 0.0f) {
|
||||
g *= runtime.resolveFloat(this.reverseDriveFactor)
|
||||
}
|
||||
val gravity = -0.8
|
||||
val gravity = -1.0
|
||||
|
||||
val state = getState(entity, ::GenericLandState)
|
||||
|
||||
val velocity = Vec3(f.toDouble() * state.currSpeed, gravity, g.toDouble() * state.currSpeed)
|
||||
|
||||
return velocity
|
||||
calculateRideSpaceVel(entity, driver, state)
|
||||
|
||||
//Jump the thang!
|
||||
if (Minecraft.getInstance().options.keyJump.isDown() && entity.onGround()) {
|
||||
state.rideVel = Vec3(state.rideVel.x, 2.0, state.rideVel.z)
|
||||
}
|
||||
|
||||
//This is cheap.
|
||||
//Also make it stop quicker the slower it is.
|
||||
val maxSpeed = 1.0
|
||||
//state.currVel = state.currVel.lerp( Vec3(0.0, state.currVel.y, 0.0), 1.0/20.0 )
|
||||
|
||||
|
||||
//entity.deltaMovement = entity.deltaMovement.lerp(velocity, 1.0)
|
||||
//state.currVel = state.currVel.add( f.toDouble() / 20.0, gravity / 20.0 , g.toDouble() / 20.0)
|
||||
return state.rideVel
|
||||
}
|
||||
|
||||
override fun canJump(entity: PokemonEntity, driver: Player) = getRuntime(entity).resolveBoolean(canJump)
|
||||
override fun shouldRoll(entity: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
entity: PokemonEntity,
|
||||
driver: Player,
|
||||
yMouse: Double,
|
||||
xMouse: Double,
|
||||
yMouseSmoother: SmoothDouble,
|
||||
xMouseSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4
|
||||
val xInput = xMouseSmoother.getNewDeltaValue(xMouse * 0.1, deltaTime * smoothingSpeed);
|
||||
val yInput = yMouseSmoother.getNewDeltaValue(yMouse * 0.1, deltaTime * smoothingSpeed);
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, yInput, xInput)
|
||||
}
|
||||
|
||||
override fun canJump(entity: PokemonEntity, driver: Player) = false
|
||||
|
||||
//TODO: bring in stamina stats
|
||||
override fun setRideBar(entity: PokemonEntity, driver: Player): Float {
|
||||
@@ -155,4 +203,50 @@ class GenericLandController : RideController {
|
||||
this.reverseDriveFactor = buffer.readString().asExpression()
|
||||
this.strafeFactor = buffer.readString().asExpression()
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalizes the current speed between minSpeed and maxSpeed.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents minSpeed and 1.0 represents maxSpeed.
|
||||
*/
|
||||
fun normalizeSpeed(currSpeed: Double, minSpeed: Double, maxSpeed: Double): Double {
|
||||
require(maxSpeed > minSpeed) { "maxSpeed must be greater than minSpeed" }
|
||||
return ((currSpeed - minSpeed) / (maxSpeed - minSpeed)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
fun calculateRideSpaceVel( entity: PokemonEntity, driver: Player, state: GenericLandState){
|
||||
|
||||
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
|
||||
val accel = getRuntime(entity).resolveDouble(accelExpr)
|
||||
val speed = state.rideVel.length()
|
||||
|
||||
//Minimum speed aka how fast you can go backwards.
|
||||
//val minSpeed = -( topSpeed / 3.0)
|
||||
val minSpeed = 0.0
|
||||
|
||||
//speed up and slow down based on input
|
||||
if( driver.zza > 0.0 && speed <= topSpeed && state.stamina > 0.0f ) {
|
||||
//modify acceleration to be slower when at closer speeds to top speed
|
||||
val accelMod = max( (normalizeSpeed(speed, minSpeed, topSpeed) - 1).pow(2), 0.1)
|
||||
state.rideVel = Vec3(state.rideVel.x, state.rideVel.y, min( state.rideVel.z + (accel * accelMod) , topSpeed))
|
||||
}
|
||||
else if( driver.zza >= 0.0 && (state.stamina == 0.0f) || speed > topSpeed) {
|
||||
state.rideVel = Vec3(state.rideVel.x, state.rideVel.y, max( state.rideVel.z - ((accel) / 4) , minSpeed))
|
||||
}
|
||||
//The minimumspeed you can
|
||||
else if ( driver.zza < 0.0 && speed > minSpeed) {
|
||||
//modify deccel to be slower when at closer speeds to minimum speed
|
||||
val deccelMod = max( -(normalizeSpeed(speed, minSpeed, topSpeed)) + 1, 0.0)
|
||||
|
||||
//Decelerate currently always a constant half of max acceleration.
|
||||
state.rideVel = Vec3(state.rideVel.x, state.rideVel.y, max( state.rideVel.z - ((accel * deccelMod) / 2) , minSpeed))
|
||||
}
|
||||
|
||||
state.rideVel= Vec3(state.rideVel.x, max(state.rideVel.y - 0.2, -1.0) , state.rideVel.z)
|
||||
|
||||
if( entity.onGround())
|
||||
{
|
||||
state.rideVel= Vec3(state.rideVel.x, 0.0 , state.rideVel.z)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+165
-70
@@ -35,52 +35,63 @@ class JetAirController : RideController {
|
||||
|
||||
override val condition: (PokemonEntity) -> Boolean = { true }
|
||||
|
||||
var jumpVector = listOf("0".asExpression(), "0.3".asExpression(), "0".asExpression())
|
||||
private set
|
||||
var gravity: Expression = "0".asExpression()
|
||||
private set
|
||||
var horizontalAcceleration: Expression = "0.1".asExpression()
|
||||
|
||||
var minSpeed: Expression = "1.2".asExpression()
|
||||
private set
|
||||
var verticalAcceleration: Expression = "0.1".asExpression()
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 140.0, 20.0)".asExpression()
|
||||
private set
|
||||
var handlingYawExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 25.0, 8.0)".asExpression()
|
||||
private set
|
||||
var topSpeedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 2.5, 1.0)".asExpression()
|
||||
private set
|
||||
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
|
||||
var accelExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 1.0)), (1.0 / (20.0 * 5.0)))".asExpression()
|
||||
private set
|
||||
// Between 60 seconds and 10 seconds at the lowest when at full speed.
|
||||
var staminaExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 60.0, 10.0)".asExpression()
|
||||
private set
|
||||
var jumpExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 300.0, 128.0)".asExpression()
|
||||
private set
|
||||
|
||||
//5 bl/s. Minimum speed so long as jets have no gravity.
|
||||
var minSpeed: Expression = "0.8".asExpression()
|
||||
// Make configurable by json
|
||||
var infiniteStamina: Expression = "false".asExpression()
|
||||
private set
|
||||
var handlingExpr: Expression = "(q.get_ride_stats('SKILL', 'AIR') / 100) * (140.0 - 20.0) + 20.0".asExpression()
|
||||
private set
|
||||
var handlingYawExpr: Expression = "(q.get_ride_stats('SKILL', 'AIR') / 100) * (16.0 - 8.0) + 20.0".asExpression()
|
||||
private set
|
||||
var topSpeedExpr: Expression = "(q.get_ride_stats('SPEED', 'AIR') / 100) * (2.5 - 1.0) + 1.0".asExpression()
|
||||
private set
|
||||
//Same equation as above except the minimum accel is 8 seconds from the minimum accel to 1 second.
|
||||
//This likely needs simplified to probably just the amount of seconds to reach max from zero?
|
||||
var accelExpr: Expression = ("(q.get_ride_stats('ACCELERATION', 'AIR') / 100.0) * " +
|
||||
"( ((2.5 - 0.35) / 20) - ((2.5 - 0.35) / (20 * 5))) + ((2.5 - 0.35) / (20 * 5))").asExpression()
|
||||
var infiniteAltitude: Expression = "false".asExpression()
|
||||
private set
|
||||
|
||||
override fun speed(entity: PokemonEntity, driver: Player): Float {
|
||||
|
||||
//retrieve stats
|
||||
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
|
||||
val accel = getRuntime(entity).resolveDouble(accelExpr)
|
||||
val staminaStat = getRuntime(entity).resolveDouble(staminaExpr)
|
||||
|
||||
//retrieve state
|
||||
val state = getState(entity, ::JetAirState)
|
||||
|
||||
//retrieve minSpeed
|
||||
val mSpeed = getRuntime(entity).resolveDouble(minSpeed)
|
||||
val minSpeed = getRuntime(entity).resolveDouble(minSpeed)
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza > 0.0 && state.currSpeed < topSpeed) {
|
||||
state.currSpeed = min(state.currSpeed + accel, topSpeed)
|
||||
} else if (driver.zza < 0.0 && state.currSpeed > getRuntime(entity).resolveDouble(minSpeed)) {
|
||||
//Decelerate currently always a constant half of max acceleration.
|
||||
state.currSpeed = max(state.currSpeed - (((2.5 - 0.35) / 20) / 2), 1.2)
|
||||
//Reduce stamina unless stamina is infinite
|
||||
if( !getRuntime(entity).resolveBoolean(infiniteStamina)) {
|
||||
//Calculate stamina loss due to speed
|
||||
//At max speed it will tick down 0.1 a second so the stamina will last ten seconds
|
||||
//There has got to be a better way to express this equation. It interpolates between 0.5 and 1.0
|
||||
var staminaRate = (normalizeSpeed(state.rideVel.length(), minSpeed, topSpeed))
|
||||
|
||||
//interpolate between 0.25 and 1.0 so that you always have at least a min of 0.25 stam loss
|
||||
staminaRate = 0.25 + (0.75 * staminaRate.pow(3))
|
||||
|
||||
//Calculate stamina loss in seconds achievable at top speed
|
||||
val staminaLoss = staminaRate * (1.0 / (20.0 * staminaStat))
|
||||
state.stamina = max(state.stamina - staminaLoss, 0.0).toFloat()
|
||||
}
|
||||
else{
|
||||
state.stamina = 1.0f
|
||||
}
|
||||
|
||||
//Returning is redundant as the logic is not connected currently
|
||||
return state.currSpeed.toFloat()
|
||||
return state.rideVel.length().toFloat()
|
||||
}
|
||||
|
||||
override fun rotation(entity: PokemonEntity, driver: LivingEntity): Vec2 {
|
||||
@@ -88,19 +99,41 @@ class JetAirController : RideController {
|
||||
}
|
||||
|
||||
override fun velocity(entity: PokemonEntity, driver: Player, input: Vec3): Vec3 {
|
||||
|
||||
var upForce = 0.0
|
||||
var forwardForce = 0.0
|
||||
val rollable = driver as? Rollable
|
||||
|
||||
if (rollable != null) {
|
||||
upForce = -1.0 * sin(Math.toRadians(rollable.pitch.toDouble()))
|
||||
forwardForce = cos(Math.toRadians(rollable.pitch.toDouble()))
|
||||
}
|
||||
|
||||
//Gather state information
|
||||
val state = getState(entity, ::JetAirState)
|
||||
|
||||
val velocity = Vec3(0.0 , (upForce)* state.currSpeed, forwardForce * state.currSpeed)
|
||||
val rollable = driver as? Rollable
|
||||
|
||||
//Calculate ride space velocity
|
||||
calculateRideSpaceVel(entity, driver, state)
|
||||
|
||||
//Translate ride space velocity to world space velocity.
|
||||
if( rollable != null ) {
|
||||
|
||||
upForce = -1.0 * sin(Math.toRadians( rollable.pitch.toDouble() )) * state.rideVel.z
|
||||
|
||||
forwardForce = cos(Math.toRadians( rollable.pitch.toDouble() )) * state.rideVel.z
|
||||
|
||||
}
|
||||
|
||||
//If stamina has run out then initiate forced glide down.
|
||||
upForce = if(state.stamina > 0.0) upForce else -0.7
|
||||
|
||||
val altitudeLimit = getRuntime(entity).resolveDouble(jumpExpr)
|
||||
val pushingHeightLimit = if(getRuntime(entity).resolveBoolean(infiniteAltitude)) false
|
||||
else (entity.y >= 128 && entity.xRot <= 0)
|
||||
|
||||
|
||||
//Incorporate the altitude limiter.
|
||||
//Apply downwards force if limit is reached. Resist upwards force on a buffer of 30 blocks past the limit.
|
||||
upForce = if(pushingHeightLimit) upForce - upForce * ((entity.y - altitudeLimit) / 30) - 0.5
|
||||
else upForce
|
||||
|
||||
val velocity = Vec3(0.0 , upForce, forwardForce )
|
||||
|
||||
return velocity
|
||||
}
|
||||
@@ -108,18 +141,25 @@ class JetAirController : RideController {
|
||||
override fun useAngVelSmoothing(entity: PokemonEntity): Boolean = true
|
||||
|
||||
override fun angRollVel(entity: PokemonEntity, driver: Player, deltaTime: Double): Vec3 {
|
||||
//Retrieve state
|
||||
val state = getState(entity, ::JetAirState)
|
||||
|
||||
//Cap at a rate of 5fps so frame skips dont lead to huge jumps
|
||||
val cappedDeltaTime = min( deltaTime, 0.2)
|
||||
|
||||
//Get handling in degrees per second
|
||||
val yawRotRate = getRuntime(entity).resolveDouble(handlingYawExpr)
|
||||
|
||||
//Base the change off of deltatime.
|
||||
val handlingYaw = yawRotRate * (deltaTime)
|
||||
var handlingYaw = yawRotRate * (cappedDeltaTime)
|
||||
|
||||
//apply stamina debuff if applicable
|
||||
handlingYaw *= if(state.stamina > 0.0) 1.0 else 0.5
|
||||
|
||||
//A+D to yaw
|
||||
//TODO: Currently doubled, check if this makes the most sense to have it this high.
|
||||
val yawForce = -2.0 * driver.xxa * handlingYaw
|
||||
val yawForce = driver.xxa * handlingYaw * -1
|
||||
|
||||
return Vec3(yawForce, 0.0, 0.0)
|
||||
return Vec3( yawForce, 0.0, 0.0)
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY
|
||||
@@ -133,32 +173,35 @@ class JetAirController : RideController {
|
||||
deltaTime: Double ): Vec3
|
||||
{
|
||||
if(driver !is Rollable) return Vec3.ZERO
|
||||
|
||||
//TODO: figure out a cleaner solution to this issue of large jumps when skipping frames or lagging
|
||||
//Cap at a rate of 5fps so frame skips dont lead to huge jumps
|
||||
val cappedDeltaTime = min(deltaTime, 0.2)
|
||||
val cappedDeltaTime = min( deltaTime, 0.2)
|
||||
|
||||
//Retrieve state
|
||||
val state = getState(entity, ::JetAirState)
|
||||
|
||||
// Accumulate the mouse input
|
||||
state.currMouseXForce = (state.currMouseXForce + (0.0015 * xMouse)).coerceIn(-1.0, 1.0)
|
||||
state.currMouseYForce = (state.currMouseYForce + (0.0015 * yMouse)).coerceIn(-1.0, 1.0)
|
||||
state.currMouseXForce = (state.currMouseXForce + ( 0.0015 * xMouse )).coerceIn(-1.0, 1.0)
|
||||
state.currMouseYForce = (state.currMouseYForce + ( 0.0015 * yMouse )).coerceIn(-1.0, 1.0)
|
||||
|
||||
//Get handling in degrees per second
|
||||
var handling = getRuntime(entity).resolveDouble(handlingExpr)
|
||||
|
||||
//convert it to partial ticks
|
||||
handling *= (deltaTime)
|
||||
//convert it to delta time
|
||||
handling *= (cappedDeltaTime)
|
||||
|
||||
//apply stamina debuff if applicable
|
||||
handling *= if(state.stamina > 0.0) 1.0 else 0.5
|
||||
|
||||
val poke = driver.vehicle as? PokemonEntity
|
||||
|
||||
//TODO: reevaluate if deadzones are needed and if they are still causing issues.
|
||||
//create deadzones for the constant input values.
|
||||
val xInput = remapWithDeadzone(state.currMouseXForce, 0.025, 1.0)
|
||||
val yInput = remapWithDeadzone(state.currMouseYForce, 0.025, 1.0)
|
||||
//val xInput = remapWithDeadzone(state.currMouseXForce, 0.025, 1.0)
|
||||
//val yInput = remapWithDeadzone(state.currMouseYForce, 0.025, 1.0)
|
||||
|
||||
val pitchRot = handling * state.currMouseYForce
|
||||
|
||||
//Roll is 1.5 times as fast as pitch
|
||||
val rollRot = handling * 1.5 * state.currMouseXForce
|
||||
|
||||
@@ -166,19 +209,11 @@ class JetAirController : RideController {
|
||||
return Vec3(0.0, pitchRot, rollRot)
|
||||
}
|
||||
|
||||
//TODO: bring in stamina stats
|
||||
override fun setRideBar(entity: PokemonEntity, driver: Player): Float {
|
||||
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
|
||||
val mSpeed = getRuntime(entity).resolveDouble(minSpeed)
|
||||
|
||||
//Retrieve stamina from state and tick down based on current speed vs top speed.
|
||||
//Retrieve stamina from state
|
||||
val state = getState(entity, ::JetAirState)
|
||||
//For now 1.0 is a temp min speed.
|
||||
//At max speed it will tick down 0.1 a second so the stamina will last ten seconds
|
||||
//There has got to be a better way to express this equation. It interpolates between 0.5 and 1.0
|
||||
val staminaLoss = (((state.currSpeed - 1.2) / (topSpeed - 1.2)) / 2 + 0.5) * ((1.0 / 20.0) * 0.1)
|
||||
state.stamina = max(state.stamina - staminaLoss, 0.0).toFloat()
|
||||
return state.stamina / 1.0f
|
||||
|
||||
return (state.stamina / 1.0f)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
@@ -197,47 +232,105 @@ class JetAirController : RideController {
|
||||
}
|
||||
|
||||
override fun gravity(entity: PokemonEntity, regularGravity: Double): Double {
|
||||
|
||||
var grav = 0.0
|
||||
|
||||
//TODO: create a better gravity equation.
|
||||
//if (maxSpeed <= 0.5) grav = 0.5 - maxSpeed
|
||||
|
||||
val grav = 0.0
|
||||
return grav
|
||||
}
|
||||
|
||||
override fun shouldRoll(entity: PokemonEntity) = true
|
||||
|
||||
override fun rideFovMult(entity: PokemonEntity, driver: Player): Float {
|
||||
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
|
||||
val minSpeed = getRuntime(entity).resolveDouble(minSpeed)
|
||||
val state = getState(entity, ::JetAirState)
|
||||
|
||||
//Must I ensure that topspeed is greater than minimum?
|
||||
val normalizedSpeed = normalizeSpeed(state.rideVel.length(), minSpeed, topSpeed)
|
||||
|
||||
//TODO: Determine if this should be based on max possible speed instead of top speed.
|
||||
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
|
||||
return 1.0f + normalizedSpeed.pow(2).toFloat() * 0.2f
|
||||
|
||||
}
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeString(gravity.toString())
|
||||
buffer.writeString(horizontalAcceleration.toString())
|
||||
buffer.writeString(verticalAcceleration.toString())
|
||||
buffer.writeString(minSpeed.toString())
|
||||
buffer.writeString(handlingExpr.toString())
|
||||
buffer.writeString(handlingYawExpr.toString())
|
||||
buffer.writeString(topSpeedExpr.toString())
|
||||
buffer.writeString(accelExpr.toString())
|
||||
buffer.writeString(jumpExpr.toString())
|
||||
buffer.writeString(staminaExpr.toString())
|
||||
buffer.writeString(infiniteStamina.toString())
|
||||
buffer.writeString(infiniteAltitude.toString())
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
gravity = buffer.readString().asExpression()
|
||||
horizontalAcceleration = buffer.readString().asExpression()
|
||||
verticalAcceleration = buffer.readString().asExpression()
|
||||
minSpeed = buffer.readString().asExpression()
|
||||
handlingExpr = buffer.readString().asExpression()
|
||||
handlingYawExpr = buffer.readString().asExpression()
|
||||
topSpeedExpr = buffer.readString().asExpression()
|
||||
accelExpr = buffer.readString().asExpression()
|
||||
jumpExpr = buffer.readString().asExpression()
|
||||
staminaExpr = buffer.readString().asExpression()
|
||||
infiniteStamina = buffer.readString().asExpression()
|
||||
infiniteAltitude = buffer.readString().asExpression()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val KEY = cobblemonResource("air/jet")
|
||||
}
|
||||
|
||||
//This likely needs moved to somewhere that all ride contollers can share it. Maybe a ride utilities file?
|
||||
//Creates a deadzone around zero that extends out to deadzoneLow in both +- and then remaps
|
||||
//the value linearly between -1.0 and 1.0 outside of the deadzone
|
||||
/*
|
||||
* Calculates the change in the ride space vector due to player input and ride state
|
||||
*/
|
||||
fun calculateRideSpaceVel( entity: PokemonEntity, driver: Player, state: JetAirState){
|
||||
|
||||
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
|
||||
val accel = getRuntime(entity).resolveDouble(accelExpr)
|
||||
val altitudeLimit = getRuntime(entity).resolveDouble(jumpExpr)
|
||||
val minSpeed = getRuntime(entity).resolveDouble(minSpeed)
|
||||
val speed = state.rideVel.length()
|
||||
|
||||
//Give no altitude limit if at max jump stat.
|
||||
val pushingHeightLimit = if(getRuntime(entity).resolveBoolean(infiniteStamina)) false
|
||||
else (entity.y >= altitudeLimit && entity.xRot <= 0)
|
||||
|
||||
|
||||
//speed up and slow down based on input
|
||||
if( driver.zza > 0.0 && speed < topSpeed && state.stamina > 0.0f && !pushingHeightLimit) {
|
||||
//modify acceleration to be slower when at closer speeds to top speed
|
||||
val accelMod = max( -(normalizeSpeed(speed, minSpeed, topSpeed)) + 1, 0.0)
|
||||
state.rideVel = Vec3(state.rideVel.x, state.rideVel.y, min( state.rideVel.z + (accel * accelMod) , topSpeed))
|
||||
}
|
||||
else if( driver.zza >= 0.0 && (state.stamina == 0.0f || pushingHeightLimit) ) {
|
||||
state.rideVel = Vec3(state.rideVel.x, state.rideVel.y, max( state.rideVel.z - ((accel) / 4) , minSpeed))
|
||||
}
|
||||
else if ( driver.zza < 0.0 && speed > minSpeed) {
|
||||
//modify deccel to be slower when at closer speeds to minimum speed
|
||||
val deccelMod = max( (normalizeSpeed(speed, minSpeed, topSpeed) - 1).pow(2), 0.1)
|
||||
|
||||
//Decelerate currently always a constant half of max acceleration.
|
||||
state.rideVel = Vec3(state.rideVel.x, state.rideVel.y, max( state.rideVel.z - ((accel * deccelMod) / 2) , minSpeed))
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Move these functions to a riding util class.
|
||||
/*
|
||||
* Normalizes the current speed between minSpeed and maxSpeed.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents minSpeed and 1.0 represents maxSpeed.
|
||||
*/
|
||||
fun normalizeSpeed(currSpeed: Double, minSpeed: Double, maxSpeed: Double): Double {
|
||||
require(maxSpeed > minSpeed) { "maxSpeed must be greater than minSpeed" }
|
||||
return ((currSpeed - minSpeed) / (maxSpeed - minSpeed)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a deadzone around zero that extends out to deadzoneLow in both +- and then remaps
|
||||
* the value linearly between -1.0 and 1.0 outside of the deadzone
|
||||
*/
|
||||
fun remapWithDeadzone(value: Double, deadzoneLow: Double = 0.2, deadzoneHigh: Double = 1.0): Double {
|
||||
// If the absolute value is below the deadzone, return 0.
|
||||
if (kotlin.math.abs(value) < deadzoneLow) return 0.0
|
||||
@@ -247,4 +340,6 @@ class JetAirController : RideController {
|
||||
//Attach the sign back to the vale
|
||||
return adjusted * sign
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+35
-16
@@ -23,6 +23,7 @@ import com.cobblemon.mod.common.util.getString
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.resolveInt
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
@@ -74,12 +75,32 @@ class RunUpToFlightCompositeController : RideController {
|
||||
if (state.activeController == flightController && entity.onGround() && state.timeTransitioned + 20 < entity.level().gameTime) {
|
||||
|
||||
//Pass the speed to the next state
|
||||
val flightState = flightController.getState(entity, ::BirdAirState)
|
||||
val groundState = landController.getState(entity, ::GenericLandState)
|
||||
val flightState = flightController.getState( entity, ::BirdAirState )
|
||||
val groundState = landController.getState( entity, ::GenericLandState )
|
||||
groundState.currSpeed = flightState.currSpeed
|
||||
groundState.rideVel = flightState.rideVel
|
||||
groundState.stamina = flightState.stamina
|
||||
|
||||
getState(entity, ::RunUpToFlightCompositeState).activeController = landController
|
||||
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
|
||||
getState(entity, ::RunUpToFlightCompositeState).let {
|
||||
it.activeController = landController
|
||||
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
|
||||
it.timeTransitioned = entity.level().gameTime
|
||||
}
|
||||
}
|
||||
else if (state.activeController == landController && Minecraft.getInstance().options.keyJump.isDown && state.timeTransitioned + 20 < entity.level().gameTime) {
|
||||
|
||||
//Pass the speed to the next state
|
||||
val flightState = flightController.getState( entity, ::BirdAirState )
|
||||
val groundState = landController.getState( entity, ::GenericLandState )
|
||||
flightState.currSpeed = groundState.currSpeed
|
||||
flightState.rideVel = groundState.rideVel
|
||||
flightState.stamina = groundState.stamina
|
||||
|
||||
getState(entity, ::RunUpToFlightCompositeState).let {
|
||||
it.activeController = flightController
|
||||
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
|
||||
it.timeTransitioned = entity.level().gameTime
|
||||
}
|
||||
}
|
||||
|
||||
return getActiveController(entity).speed(entity, driver)
|
||||
@@ -101,6 +122,14 @@ class RunUpToFlightCompositeController : RideController {
|
||||
return getActiveController(entity).angRollVel(entity, driver, deltaTime)
|
||||
}
|
||||
|
||||
override fun rideFovMult(entity: PokemonEntity, driver: Player): Float {
|
||||
return getActiveController(entity).rideFovMult(entity, driver)
|
||||
}
|
||||
|
||||
override fun setRideBar(entity: PokemonEntity, driver: Player): Float {
|
||||
return getActiveController(entity).setRideBar(entity, driver)
|
||||
}
|
||||
|
||||
override fun canJump(entity: PokemonEntity, driver: Player): Boolean {
|
||||
return getActiveController(entity).canJump(entity, driver)
|
||||
}
|
||||
@@ -111,19 +140,9 @@ class RunUpToFlightCompositeController : RideController {
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
val controller = getActiveController(entity)
|
||||
if (controller == landController && jumpStrength >= getRuntime(entity).resolveInt(minimumJump)) {
|
||||
val state = getState(entity, ::CompositeState)
|
||||
val isJumping = Minecraft.getInstance().options.keyJump.isDown
|
||||
|
||||
//Pass the speed to the next state
|
||||
val flightState = flightController.getState(entity, ::BirdAirState )
|
||||
val groundState = landController.getState(entity, ::GenericLandState)
|
||||
flightState.currSpeed = groundState.currSpeed
|
||||
|
||||
getState(entity, ::RunUpToFlightCompositeState).let {
|
||||
it.activeController = flightController
|
||||
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
|
||||
it.timeTransitioned = entity.level().gameTime
|
||||
}
|
||||
}
|
||||
return controller.jumpForce(entity, driver, jumpStrength)
|
||||
}
|
||||
|
||||
|
||||
+16
-23
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.getString
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.resolveInt
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
@@ -73,14 +74,15 @@ class RunUpToJetFlightCompositeController : RideController {
|
||||
driver: Player
|
||||
): Float {
|
||||
val state = getState(entity, ::RunUpToFlightCompositeState)
|
||||
if ((state.activeController == flightController && entity.onGround() && state.timeTransitioned + 20 < entity.level().gameTime) ||
|
||||
(Minecraft.getInstance().options.keySprint.isDown && state.activeController == flightController && state.timeTransitioned + 20 < entity.level().gameTime)) {
|
||||
if (( state.activeController == flightController && entity.onGround() && state.timeTransitioned + 20 < entity.level().gameTime) ||
|
||||
( Minecraft.getInstance().options.keySprint.isDown() && state.activeController == flightController && state.timeTransitioned + 20 < entity.level().gameTime)) {
|
||||
|
||||
//Pass the data to the next state
|
||||
val flightState = flightController.getState(entity, ::JetAirState)
|
||||
val groundState = landController.getState(entity, ::GenericLandState)
|
||||
val flightState = flightController.getState( entity, ::JetAirState )
|
||||
val groundState = landController.getState( entity, ::GenericLandState )
|
||||
groundState.currSpeed = flightState.currSpeed
|
||||
groundState.stamina = flightState.stamina
|
||||
groundState.rideVel = flightState.rideVel
|
||||
|
||||
//Clear accumulated rotation input when transferring to ground input
|
||||
flightState.currMouseXForce = 0.0
|
||||
@@ -102,10 +104,11 @@ class RunUpToJetFlightCompositeController : RideController {
|
||||
)
|
||||
{
|
||||
//Pass the data to the next state
|
||||
val flightState = flightController.getState(entity, ::JetAirState)
|
||||
val groundState = landController.getState(entity, ::GenericLandState)
|
||||
val flightState = flightController.getState( entity, ::JetAirState )
|
||||
val groundState = landController.getState( entity, ::GenericLandState )
|
||||
flightState.currSpeed = groundState.currSpeed
|
||||
flightState.stamina = groundState.stamina
|
||||
flightState.rideVel = groundState.rideVel
|
||||
|
||||
getState(entity, ::RunUpToFlightCompositeState).let {
|
||||
it.activeController = flightController
|
||||
@@ -141,28 +144,18 @@ class RunUpToJetFlightCompositeController : RideController {
|
||||
return getActiveController(entity).setRideBar(entity, driver)
|
||||
}
|
||||
|
||||
override fun rideFovMult(entity: PokemonEntity, driver: Player): Float {
|
||||
return getActiveController(entity).rideFovMult(entity, driver)
|
||||
}
|
||||
|
||||
|
||||
|
||||
override fun jumpForce(
|
||||
entity: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
val controller = getActiveController(entity)
|
||||
/*
|
||||
if (controller == landController && jumpStrength >= getRuntime(entity).resolveInt(minimumJump)) {
|
||||
|
||||
//Pass the data to the next state
|
||||
val flightState = flightController.getState( entity, ::JetAirState )
|
||||
val groundState = landController.getState( entity, ::GenericLandState )
|
||||
flightState.currSpeed = groundState.currSpeed
|
||||
flightState.stamina = groundState.stamina
|
||||
|
||||
getState(entity, ::RunUpToFlightCompositeState).let {
|
||||
it.activeController = flightController
|
||||
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
|
||||
it.timeTransitioned = entity.level().gameTime
|
||||
}
|
||||
}
|
||||
*/
|
||||
return controller.jumpForce(entity, driver, jumpStrength)
|
||||
}
|
||||
|
||||
@@ -227,4 +220,4 @@ class RunUpToJetFlightCompositeController : RideController {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("composite/run_up_to_jet_flight")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+157
-45
@@ -8,54 +8,35 @@
|
||||
|
||||
package com.cobblemon.mod.common.pokemon.riding.controllers
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.controller.RideController
|
||||
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.riding.RidingOnPlayerSoundEventInstance
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.riding.controllers.BirdAirController.Companion
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.getString
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.resolveFloat
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import kotlin.math.absoluteValue
|
||||
import com.cobblemon.mod.common.pokemon.riding.states.LandVehicleState
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.player.LocalPlayer
|
||||
import net.minecraft.client.resources.sounds.Sound
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.*
|
||||
|
||||
class VehicleLandController : RideController {
|
||||
companion object {
|
||||
val KEY: ResourceLocation = cobblemonResource("land/vehicle")
|
||||
|
||||
val MAXTOPSPEED = 1.0 // 20 bl/s
|
||||
val MINTOPSPEED = 0.35 // 7 bl/s
|
||||
//val MINSPEED = 0.25 // 5 bl/s
|
||||
|
||||
//Accel will lie between 1.0 second and 5.0 seconds
|
||||
val MAXACCEL = (MAXTOPSPEED ) / (20*3) //3.0 second to max speed
|
||||
val MINACCEL = (MAXTOPSPEED ) / (20*8) // 8 seconds to max speed
|
||||
|
||||
//Can rotate 90 degrees
|
||||
val MAXHANDLING = 90.0
|
||||
val MINHANDLING = 30.0
|
||||
|
||||
|
||||
val MAXYAWHANDLING = 16.0
|
||||
val MINYAWHANDLING = 8.0
|
||||
}
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
@@ -69,6 +50,9 @@ class VehicleLandController : RideController {
|
||||
var reverseDriveFactor = "0.25".asExpression()
|
||||
private set
|
||||
|
||||
var sound = "minecraft:item.elytra.flying".asExpression()
|
||||
private set
|
||||
|
||||
var minimumSpeedToTurn = "0.1".asExpression()
|
||||
private set
|
||||
var rotationSpeed = "45/20".asExpression()
|
||||
@@ -76,8 +60,20 @@ class VehicleLandController : RideController {
|
||||
var lookYawLimit = "101".asExpression()
|
||||
private set
|
||||
|
||||
var currSpeed = 0.0
|
||||
// TODO: Tweak these stats to a workable state.
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 140.0, 20.0)".asExpression()
|
||||
private set
|
||||
var topSpeedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.0, 0.5)".asExpression()
|
||||
private set
|
||||
// Max accel is a whole 1.0 in 2 seconds. The conversion in the function below is to convert seconds to ticks
|
||||
var accelExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 2.0)), (1.0 / (20.0 * 6.0)))".asExpression()
|
||||
private set
|
||||
// Between 60 seconds and 10 seconds at the lowest when at full speed.
|
||||
var staminaExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 60.0, 10.0)".asExpression()
|
||||
private set
|
||||
var jumpExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 300.0, 128.0)".asExpression()
|
||||
private set
|
||||
|
||||
|
||||
@Transient
|
||||
override val key: ResourceLocation = KEY
|
||||
@@ -93,47 +89,132 @@ class VehicleLandController : RideController {
|
||||
if (entity.isInWater || entity.isUnderWater) {
|
||||
return@any false
|
||||
}
|
||||
/*
|
||||
//This might not actually work, depending on what the yPos actually is. yPos of the middle of the entity? the feet?
|
||||
if (it.y.toDouble() == (entity.position().y)) {
|
||||
val blockState = entity.level().getBlockState(it.below())
|
||||
return@any !(!blockState.isAir && blockState.fluidState.isEmpty)
|
||||
return@any (!blockState.isAir && blockState.fluidState.isEmpty)
|
||||
}
|
||||
*/
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun speed(entity: PokemonEntity, driver: Player): Float {
|
||||
|
||||
val topSpeed = entity.getRideStat(RidingStat.SPEED, RidingStyle.AIR, MINTOPSPEED, MAXTOPSPEED)
|
||||
val accel = entity.getRideStat(RidingStat.ACCELERATION, RidingStyle.AIR, MINACCEL, MAXACCEL)
|
||||
//Retrieve Vehicle state
|
||||
val state = getState(entity, ::LandVehicleState)
|
||||
|
||||
//retrieve stats
|
||||
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr) * 2
|
||||
val accel = getRuntime(entity).resolveDouble(accelExpr) * 2
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza > 0.0 && currSpeed < topSpeed) {
|
||||
currSpeed = min(currSpeed + accel , topSpeed)
|
||||
} else if (driver.zza < 0.0 && currSpeed > 0.0) {
|
||||
//Decelerate is now always a constant half of max acceleration.
|
||||
currSpeed = max(currSpeed - (MAXACCEL / 2), 0.0)
|
||||
if( driver.zza > 0.0 && state.currSpeed < topSpeed)
|
||||
{
|
||||
state.currSpeed = min( state.currSpeed + accel , topSpeed)
|
||||
}
|
||||
else if ( driver.zza < 0.0 && state.currSpeed > 0.0)
|
||||
{
|
||||
//Decelerate is currently always half of accel
|
||||
state.currSpeed = max( state.currSpeed - (accel * 2) , 0.0)
|
||||
}
|
||||
else{
|
||||
state.currSpeed = max( state.currSpeed - (accel / 2.0) , 0.0)
|
||||
}
|
||||
|
||||
return currSpeed.toFloat()
|
||||
return state.currSpeed.toFloat()
|
||||
}
|
||||
|
||||
override fun rotation(entity: PokemonEntity, driver: LivingEntity): Vec2 {
|
||||
//Retrieve Vehicle state
|
||||
val state = getState(entity, ::LandVehicleState)
|
||||
|
||||
val rotationDegrees = driver.xxa * getRuntime(entity).resolveFloat(this.rotationSpeed)
|
||||
return Vec2(driver.xRot, entity.yRot - rotationDegrees)
|
||||
|
||||
if( Minecraft.getInstance().options.keyJump.isDown() ){
|
||||
//If the current drift has not been set and the car is rotating then set drift
|
||||
if( state.currDrift == 0.0 && rotationDegrees != 0.0f) {
|
||||
//Set to base value which will be half.
|
||||
state.currDrift = sign(rotationDegrees) * 2.5
|
||||
state.turnSmoother.reset()
|
||||
}
|
||||
|
||||
//Calculate any changes to drift
|
||||
val driftChangeRate = (1.0 / (20.0 * 0.15))
|
||||
|
||||
//If Drift is negative already
|
||||
if(state.currDrift < 0) {
|
||||
state.currDrift = (state.currDrift + (sign(rotationDegrees) * driftChangeRate)).coerceIn(-5.0, -1.0)
|
||||
}else{
|
||||
state.currDrift = (state.currDrift + (sign(rotationDegrees) * driftChangeRate)).coerceIn(1.0, 5.0)
|
||||
}
|
||||
return Vec2(driver.xRot, entity.yRot - state.currDrift.toFloat())
|
||||
}
|
||||
//If the drift key is not held then reset the curr drift to 0 and use normal rotation
|
||||
else{
|
||||
//If the drift key has just been released perform boost addition and drift reset
|
||||
if( state.currDrift != 0.0){
|
||||
state.turnSmoother.reset()
|
||||
state.currDrift = 0.0
|
||||
state.driftBoost = min(state.driftBoost + 1.0, 2.0)
|
||||
driver.level().playSound(
|
||||
null as Player?,
|
||||
driver.getX(),
|
||||
driver.getY(),
|
||||
driver.getZ(), SoundEvents.PLAYER_ATTACK_WEAK,
|
||||
driver.getSoundSource(), 1.0f, 0.2f
|
||||
)
|
||||
|
||||
|
||||
val lp = driver as? LocalPlayer
|
||||
lp?.let {
|
||||
//val resourceLocation = ResourceLocation.tryParse(getRuntime(entity).resolveString(sound))
|
||||
//val soundEvent = BuiltInRegistries.SOUND_EVENT.getOptional(resourceLocation).orElse(null)
|
||||
val soundInstance = RidingOnPlayerSoundEventInstance(it, SoundEvents.AMBIENT_UNDERWATER_LOOP)
|
||||
Minecraft.getInstance().soundManager.play(soundInstance)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Restrict turning while boost is active
|
||||
val boostRotDebuff = if( state.driftBoost != 0.0 ) 0.5f else 1.0f
|
||||
|
||||
return Vec2(driver.xRot, entity.yRot - (rotationDegrees * boostRotDebuff) )
|
||||
//return Vec2(driver.xRot, state.turnSmoother.getNewDeltaValue((entity.yRot - rotationDegrees).toDouble(), 1.0).toFloat())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun velocity(entity: PokemonEntity, driver: Player, input: Vec3): Vec3 {
|
||||
//Retrieve Vehicle state
|
||||
val state = getState(entity, ::LandVehicleState)
|
||||
|
||||
val runtime = getRuntime(entity)
|
||||
val driveFactor = runtime.resolveFloat(this.driveFactor)
|
||||
var g = driver.zza * driveFactor
|
||||
if (g <= 0.0f) {
|
||||
g *= runtime.resolveFloat(this.reverseDriveFactor)
|
||||
}
|
||||
if (driver.xxa != 0F && g.absoluteValue < runtime.resolveFloat(this.minimumSpeedToTurn)) {
|
||||
driver.xxa = 0F
|
||||
val grav = -1.0
|
||||
var velocity = Vec3(0.0,0.0,0.0)
|
||||
|
||||
//If the vehicle is currently drifting then do a "poor mans" rotation of the velocity vector to offset it
|
||||
//from the front of the vehicle. This likely needs cleaned up to be
|
||||
if( state.currDrift != 0.0){
|
||||
//Drift speed should be slower the higher the drift is
|
||||
//normalize the drift speed
|
||||
val driftSpeed = (1.0 - ((abs(state.currDrift) - 1.0) / (5.0 - 1.0))) * 0.5 + 0.3
|
||||
|
||||
velocity = Vec3((1.0 * state.currSpeed) * (driftSpeed / 2.0) * -sign(state.currDrift) + min(state.driftBoost, 0.5) * 1.25,
|
||||
grav,
|
||||
(1.0 * state.currSpeed) * driftSpeed + min(state.driftBoost, 0.5) * 1.25)
|
||||
}else{
|
||||
velocity = Vec3(0.0 , grav, state.currSpeed + min(state.driftBoost, 0.5) * 1.25)
|
||||
}
|
||||
val velocity = Vec3(0.0 /* Maybe do drifting here later */, 0.0, g.toDouble() * currSpeed)
|
||||
|
||||
//tick down boost
|
||||
state.driftBoost = max( state.driftBoost - (1.0 / (20.0 * 2)), 0.0)
|
||||
|
||||
return velocity
|
||||
}
|
||||
@@ -146,7 +227,7 @@ class VehicleLandController : RideController {
|
||||
override fun clampPassengerRotation(entity: PokemonEntity, driver: LivingEntity) {
|
||||
val f = Mth.wrapDegrees(driver.yRot - entity.yRot)
|
||||
val runtime = getRuntime(entity)
|
||||
val lookYawLimit = runtime.resolveFloat(this.lookYawLimit)
|
||||
val lookYawLimit = 120.0f
|
||||
val g = Mth.clamp(f, -lookYawLimit, lookYawLimit)
|
||||
driver.yRotO += g - f
|
||||
driver.yRot = driver.yRot + g - f
|
||||
@@ -162,6 +243,27 @@ class VehicleLandController : RideController {
|
||||
return Vec3(jumpVector[0].toDouble(), jumpVector[1].toDouble(), jumpVector[2].toDouble())
|
||||
}
|
||||
|
||||
override fun inertia(entity: PokemonEntity ) : Double {
|
||||
val state = getState(entity, ::LandVehicleState)
|
||||
|
||||
//val inertia = if(Minecraft.getInstance().options.keyJump.isDown()) 0.1 else 0.5
|
||||
|
||||
return 0.1
|
||||
}
|
||||
|
||||
override fun rideFovMult(entity: PokemonEntity, driver: Player): Float {
|
||||
val state = getState(entity, ::LandVehicleState)
|
||||
|
||||
//Only increase fov when actively using the boost.
|
||||
val fovIncrease = (min( state.driftBoost, 0.5 ) * 0.3).toFloat()
|
||||
return 1.0f + fovIncrease
|
||||
|
||||
}
|
||||
|
||||
override fun shouldRoll(entity: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeString(this.speed.getString())
|
||||
@@ -174,6 +276,11 @@ class VehicleLandController : RideController {
|
||||
buffer.writeString(this.minimumSpeedToTurn.getString())
|
||||
buffer.writeString(this.rotationSpeed.getString())
|
||||
buffer.writeString(this.lookYawLimit.getString())
|
||||
buffer.writeString(this.handlingExpr.getString())
|
||||
buffer.writeString(this.staminaExpr.getString())
|
||||
buffer.writeString(this.jumpExpr.getString())
|
||||
buffer.writeString(this.topSpeedExpr.getString())
|
||||
buffer.writeString(this.accelExpr.getString())
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
@@ -189,6 +296,11 @@ class VehicleLandController : RideController {
|
||||
this.minimumSpeedToTurn = buffer.readString().asExpression()
|
||||
this.rotationSpeed = buffer.readString().asExpression()
|
||||
this.lookYawLimit = buffer.readString().asExpression()
|
||||
this.handlingExpr = buffer.readString().asExpression()
|
||||
this.staminaExpr = buffer.readString().asExpression()
|
||||
this.jumpExpr = buffer.readString().asExpression()
|
||||
this.topSpeedExpr = buffer.readString().asExpression()
|
||||
this.accelExpr = buffer.readString().asExpression()
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -14,5 +14,6 @@ import net.minecraft.world.phys.Vec3
|
||||
|
||||
data class BirdAirState(val entity: PokemonEntity) : RidingState {
|
||||
var currSpeed: Double = 0.0
|
||||
var lastVelocity: Vec3 = Vec3(0.0, 0.0, 0.0)
|
||||
var stamina: Float = 1.0f
|
||||
var rideVel: Vec3 = Vec3.ZERO
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.pokemon.riding.states
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.RidingState
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
|
||||
data class DolphinState(val entity: PokemonEntity) : RidingState {
|
||||
var currSpeed: Double = 0.0
|
||||
}
|
||||
+1
@@ -14,5 +14,6 @@ import net.minecraft.world.phys.Vec3
|
||||
|
||||
data class GenericLandState(val entity: PokemonEntity) : RidingState {
|
||||
var currSpeed: Double = 0.0
|
||||
var rideVel: Vec3 = Vec3.ZERO
|
||||
var stamina: Float = 1.0f
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import net.minecraft.world.phys.Vec3
|
||||
data class JetAirState(val entity: PokemonEntity) : RidingState {
|
||||
var currSpeed: Double = 0.0
|
||||
var stamina: Float = 1.0f
|
||||
var rideVel: Vec3 = Vec3.ZERO
|
||||
var currMouseXForce: Double = 0.0
|
||||
var currMouseYForce: Double = 0.0
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.pokemon.riding.states
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.RidingState
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
data class LandVehicleState(val entity: PokemonEntity) : RidingState {
|
||||
var currSpeed: Double = 0.0
|
||||
var stamina: Float = 1.0f
|
||||
var currMouseXForce: Double = 0.0
|
||||
var currMouseYForce: Double = 0.0
|
||||
//I am thinking this will either be a ranging from -1.0 to 1.0
|
||||
//When drift is first activated while a turn is initiated it will go to (the sign of the turn)*0.5.
|
||||
//Then you are able to change it between +-1.0 and 0.25 which will determine how much forced rotation
|
||||
//The drift gives you. The second component is the offset of the velocity vector. Might simply be able
|
||||
//to add an equal amount of x velocity as the current z velocity? Or maybe just a transformation to turn
|
||||
//it to the right?? This will be the hardest part actually... Because if you add a similar one then the
|
||||
//magnitude willbe increased? Maybe just half both and then that effectively slows it? idk.
|
||||
var currDrift: Double = 0.0
|
||||
//Determines how much boost is left.
|
||||
var driftBoost: Double = 0.0
|
||||
var turnSmoother: SmoothDouble = SmoothDouble()
|
||||
|
||||
}
|
||||
@@ -48,6 +48,7 @@ object RideControllerAdapter : JsonDeserializer<RideController> {
|
||||
VehicleLandController.KEY to VehicleLandController::class.java,
|
||||
DolphinController.KEY to DolphinController::class.java,
|
||||
JetAirController.KEY to JetAirController::class.java
|
||||
|
||||
)
|
||||
|
||||
override fun deserialize(element: JsonElement, type: Type, context: JsonDeserializationContext): RideController {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.util.adapters
|
||||
|
||||
import com.google.gson.*
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import java.lang.reflect.Type
|
||||
|
||||
/**
|
||||
* Adapter for deserializing SoundEvent types.
|
||||
*
|
||||
* @author Jackowes
|
||||
* @since March 5th, 2025
|
||||
*/
|
||||
|
||||
object SoundEventAdapter : JsonDeserializer<SoundEvent> {
|
||||
override fun deserialize(json: JsonElement, type: Type, context: JsonDeserializationContext): SoundEvent {
|
||||
val soundId = json.asString
|
||||
|
||||
// Attempt to grab the sound resource location. If incorrect then return null
|
||||
val resourceLocation = ResourceLocation.read(soundId).result().orElse(null)
|
||||
?: throw JsonParseException("Invalid sound event resource location: $soundId")
|
||||
|
||||
// Attempt to retrieve the SoundEvent from the registry
|
||||
return BuiltInRegistries.SOUND_EVENT.getOptional(resourceLocation)
|
||||
.orElseThrow { JsonParseException("Unknown sound event: $soundId") }
|
||||
}
|
||||
}
|
||||
+34
-5
@@ -124,8 +124,14 @@
|
||||
},
|
||||
"pokeball": {
|
||||
"scale": {
|
||||
"0.4583": {"pre": [0, 0, 0], "post": [0.3, 0.3, 0.3]},
|
||||
"0.5417": {"pre": [0.3, 0.3, 0.3], "post": [0.3, 0.3, 0.3]},
|
||||
"0.4583": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0.3, 0.3, 0.3]
|
||||
},
|
||||
"0.5417": {
|
||||
"pre": [0.3, 0.3, 0.3],
|
||||
"post": [0.3, 0.3, 0.3]
|
||||
},
|
||||
"0.7083": [1, 1, 1]
|
||||
}
|
||||
},
|
||||
@@ -622,12 +628,21 @@
|
||||
},
|
||||
"pokeball": {
|
||||
"scale": {
|
||||
"0.9375": {"pre": [0, 0, 0], "post": [0.3, 0.3, 0.3]},
|
||||
"1.0": {"pre": [0.3, 0.3, 0.3], "post": [0.3, 0.3, 0.3]},
|
||||
"0.9375": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0.3, 0.3, 0.3]
|
||||
},
|
||||
"1.0": {
|
||||
"pre": [0.3, 0.3, 0.3],
|
||||
"post": [0.3, 0.3, 0.3]
|
||||
},
|
||||
"1.1875": [1, 1, 1],
|
||||
"3.0625": [1, 1, 1],
|
||||
"3.25": [0.4, 0.4, 0.4],
|
||||
"3.375": {"pre": [0.4, 0.4, 0.4], "post": [0, 0, 0]}
|
||||
"3.375": {
|
||||
"pre": [0.4, 0.4, 0.4],
|
||||
"post": [0, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"leg_right": {
|
||||
@@ -996,6 +1011,20 @@
|
||||
"scale": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.trainer_generic.ride_test": {
|
||||
"loop": true,
|
||||
"bones": {
|
||||
"body": {
|
||||
"position": [0, -8.4, 0]
|
||||
},
|
||||
"leg_right": {
|
||||
"rotation": [-90, 5, 0]
|
||||
},
|
||||
"leg_left": {
|
||||
"rotation": [-90, -5, 0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -26,7 +26,10 @@
|
||||
"pivot": [0, 24.5, -1],
|
||||
"cubes": [
|
||||
{"origin": [-6, 18.5, -13.75], "size": [12, 12, 26], "uv": [52, 39]}
|
||||
]
|
||||
],
|
||||
"locators": {
|
||||
"seat_1": [0, 24, 0]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "neck",
|
||||
|
||||
@@ -175,39 +175,7 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"stats": {
|
||||
"SPEED": {
|
||||
"ranges": {
|
||||
"LAND": "10-20",
|
||||
"AIR": "25-50"
|
||||
}
|
||||
},
|
||||
"ACCELERATION": {
|
||||
"ranges": {
|
||||
"LAND": "90-100",
|
||||
"AIR": "20-40"
|
||||
}
|
||||
},
|
||||
"SKILL": {
|
||||
"ranges": {
|
||||
"LAND": "15-30",
|
||||
"AIR": "50-100"
|
||||
}
|
||||
},
|
||||
"JUMP": {
|
||||
"ranges": {
|
||||
"LAND": "10-20",
|
||||
"AIR": "25-50"
|
||||
}
|
||||
},
|
||||
"STAMINA": {
|
||||
"ranges": {
|
||||
"LAND": "15-30",
|
||||
"AIR": "35-70"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"behaviour": {
|
||||
"resting": {
|
||||
|
||||
@@ -88,6 +88,67 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"riding": {
|
||||
"stats": {
|
||||
"SPEED": {
|
||||
"displayName": "Speed",
|
||||
"description": "Its top speed",
|
||||
"ranges": {
|
||||
"LIQUID": "100-100"
|
||||
}
|
||||
},
|
||||
"ACCELERATION": {
|
||||
"displayName": "Acceleration",
|
||||
"description": "How fast it can speed up",
|
||||
"ranges": {
|
||||
"LIQUID": "100-100"
|
||||
}
|
||||
},
|
||||
"SKILL": {
|
||||
"displayName": "Handling",
|
||||
"description": "How fast it can turn",
|
||||
"ranges": {
|
||||
"LIQUID": "100-100"
|
||||
}
|
||||
},
|
||||
"JUMP": {
|
||||
"displayName": "Altitude",
|
||||
"description": "How high it can fly",
|
||||
"ranges": {
|
||||
"LIQUID": "100-100"
|
||||
}
|
||||
},
|
||||
"STAMINA": {
|
||||
"displayName": "Fuel/Stamina",
|
||||
"description": "How long it can stay off the ground",
|
||||
"ranges": {
|
||||
"LIQUID": "100-100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"controller": {
|
||||
"key": "cobblemon:swim/dolphin"
|
||||
},
|
||||
"seats": [
|
||||
{
|
||||
"offset": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"poseAnimations": [
|
||||
{
|
||||
"animations": [
|
||||
{
|
||||
"fileName": "trainer_generic",
|
||||
"animationName": "animation.trainer_generic.ride_test"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"moves": [
|
||||
"1:firepunch",
|
||||
"1:thunderpunch",
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
"displayName": "Speed",
|
||||
"description": "Its top speed",
|
||||
"ranges": {
|
||||
"LAND": "2-10",
|
||||
"LAND": "100-101",
|
||||
"AIR": "100-101"
|
||||
}
|
||||
},
|
||||
@@ -64,7 +64,7 @@
|
||||
"displayName": "Acceleration",
|
||||
"description": "How fast it can speed up",
|
||||
"ranges": {
|
||||
"LAND": "2-10",
|
||||
"LAND": "100-101",
|
||||
"AIR": "100-101"
|
||||
}
|
||||
},
|
||||
@@ -72,7 +72,7 @@
|
||||
"displayName": "Handling",
|
||||
"description": "How fast it can turn",
|
||||
"ranges": {
|
||||
"LAND": "2-10",
|
||||
"LAND": "100-101",
|
||||
"AIR": "100-101"
|
||||
}
|
||||
},
|
||||
@@ -80,7 +80,7 @@
|
||||
"displayName": "Altitude",
|
||||
"description": "How high it can fly",
|
||||
"ranges": {
|
||||
"LAND": "2-10",
|
||||
"LAND": "100-101",
|
||||
"AIR": "100-101"
|
||||
}
|
||||
},
|
||||
@@ -88,7 +88,7 @@
|
||||
"displayName": "Fuel/Stamina",
|
||||
"description": "How long it can stay off the ground",
|
||||
"ranges": {
|
||||
"LAND": "2-10",
|
||||
"LAND": "100-101",
|
||||
"AIR": "100-101"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,67 @@
|
||||
"eggCycles": 20,
|
||||
"baseFriendship": 50,
|
||||
"baseScale": 1,
|
||||
"riding": {
|
||||
"stats": {
|
||||
"SPEED": {
|
||||
"displayName": "Speed",
|
||||
"description": "Its top speed",
|
||||
"ranges": {
|
||||
"LAND": "100-101"
|
||||
}
|
||||
},
|
||||
"ACCELERATION": {
|
||||
"displayName": "Acceleration",
|
||||
"description": "How fast it can speed up",
|
||||
"ranges": {
|
||||
"LAND": "100-101"
|
||||
}
|
||||
},
|
||||
"SKILL": {
|
||||
"displayName": "Handling",
|
||||
"description": "How fast it can turn",
|
||||
"ranges": {
|
||||
"LAND": "100-101"
|
||||
}
|
||||
},
|
||||
"JUMP": {
|
||||
"displayName": "Altitude",
|
||||
"description": "How high it can fly",
|
||||
"ranges": {
|
||||
"LAND": "100-101"
|
||||
}
|
||||
},
|
||||
"STAMINA": {
|
||||
"displayName": "Fuel/Stamina",
|
||||
"description": "How long it can sprint for.",
|
||||
"ranges": {
|
||||
"LAND": "100-101"
|
||||
}
|
||||
}
|
||||
},
|
||||
"controller": {
|
||||
"key": "cobblemon:land/generic"
|
||||
},
|
||||
"seats": [
|
||||
{
|
||||
"offset": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"poseAnimations": [
|
||||
{
|
||||
"animations": [
|
||||
{
|
||||
"fileName": "trainer_generic",
|
||||
"animationName": "animation.trainer_generic.ride_test"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"hitbox": {
|
||||
"width": 1.3,
|
||||
"height": 2.6,
|
||||
|
||||
@@ -42,6 +42,68 @@
|
||||
"catchRate": 75,
|
||||
"eggCycles": 20,
|
||||
"baseFriendship": 50,
|
||||
"riding": {
|
||||
"stats": {
|
||||
"SPEED": {
|
||||
"displayName": "Speed",
|
||||
"description": "Its top speed",
|
||||
"ranges": {
|
||||
"LAND": "100-101"
|
||||
}
|
||||
},
|
||||
"ACCELERATION": {
|
||||
"displayName": "Acceleration",
|
||||
"description": "How fast it can speed up",
|
||||
"ranges": {
|
||||
"LAND": "100-101"
|
||||
}
|
||||
},
|
||||
"SKILL": {
|
||||
"displayName": "Handling",
|
||||
"description": "How fast it can turn",
|
||||
"ranges": {
|
||||
"LAND": "100-101"
|
||||
}
|
||||
},
|
||||
"JUMP": {
|
||||
"displayName": "Altitude",
|
||||
"description": "How high it can fly",
|
||||
"ranges": {
|
||||
"LAND": "100-101"
|
||||
}
|
||||
},
|
||||
"STAMINA": {
|
||||
"displayName": "Fuel/Stamina",
|
||||
"description": "How long it can stay off the ground",
|
||||
"ranges": {
|
||||
"LAND": "100-101"
|
||||
}
|
||||
}
|
||||
},
|
||||
"controller": {
|
||||
"key": "cobblemon:land/vehicle",
|
||||
"sound": "minecraft:entity.elder_guardian.flop"
|
||||
},
|
||||
"seats": [
|
||||
{
|
||||
"offset": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"poseAnimations": [
|
||||
{
|
||||
"animations": [
|
||||
{
|
||||
"fileName": "trainer_generic",
|
||||
"animationName": "animation.trainer_generic.ride_test"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"behaviour": {
|
||||
"resting": {
|
||||
"canSleep": true,
|
||||
|
||||
Reference in New Issue
Block a user