Compare commits

..
19 changed files with 364 additions and 232 deletions
+2
View File
@@ -13,6 +13,8 @@
- Added `visibility/hidden`, `visibility/hat` and `visibility/face` tags to control where and how certain items are rendered.
- Added Pokémon markings, toggleable within the summary.
- Added `/boxcount` command to change PC boxes amount
- Added `/transformmodelpart (position|rotation|scale) <modelPart> <transform: x y z>` command that can add transformations to a pokemon's model part.
- The player executing the command must be facing the target pokemon entity. Transformations are not persistent and will revert when resources are reloaded.
### Changes
- Renamed `chargeGainedPerTick` config to `secondsToChargeHealingMachine`.
@@ -563,6 +563,8 @@ object Cobblemon {
this.implementation.registerCommandArgument(cobblemonResource("npc_class"), NPCClassArgumentType::class, SingletonArgumentInfo.contextFree(NPCClassArgumentType::npcClass))
this.implementation.registerCommandArgument(cobblemonResource("wallpaper"), UnlockablePCBoxWallpaperArgumentType::class, SingletonArgumentInfo.contextFree(UnlockablePCBoxWallpaperArgumentType::wallpaper))
this.implementation.registerCommandArgument(cobblemonResource("mark"), MarkArgumentType::class, SingletonArgumentInfo.contextFree(MarkArgumentType::mark))
this.implementation.registerCommandArgument(cobblemonResource("transform_type"), TransformTypeArgumentType::class, SingletonArgumentInfo.contextFree(TransformTypeArgumentType::transformType))
this.implementation.registerCommandArgument(cobblemonResource("model_part"), ModelPartArgumentType::class, SingletonArgumentInfo.contextFree(ModelPartArgumentType::modelPart))
}
}
@@ -23,7 +23,7 @@ object CobblemonCommands {
ChangeEyeHeight.register(dispatcher)
ChangeScaleAndSize.register(dispatcher)
ChangeWalkSpeed.register(dispatcher)
ChangeJointScaleCommand.register(dispatcher)
TransformModelPartCommand.register(dispatcher)
TestCommand.register(dispatcher)
ReloadShowdownCommand.register(dispatcher)
ClickTextCommand.register(dispatcher)
@@ -20,7 +20,7 @@ object CobblemonPermissions {
@JvmStatic
val CHANGE_WALK_SPEED = this.create("${COMMAND_PREFIX}changewalkspeed", PermissionLevel.ALL_COMMANDS)
@JvmStatic
val CHANGE_JOINT_SCALE = this.create("${COMMAND_PREFIX}changejointscale", PermissionLevel.ALL_COMMANDS)
val TRANSFORM_MODEL_PART = this.create("${COMMAND_PREFIX}transformmodelpart", PermissionLevel.ALL_COMMANDS)
@JvmStatic
val CHECKSPAWNS = this.create("${COMMAND_PREFIX}checkspawns", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
@@ -35,7 +35,7 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
override val key: ResourceLocation = KEY
val poseProvider = PoseProvider<BirdAirSettings, BirdAirState>(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVel.get().z > 0.1 })
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVel.get().z > 0.2 })
override fun isActive(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity): Boolean {
return true
@@ -96,7 +96,7 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
upForce -= 0.3
}
val altitudeLimit = vehicle.runtime.resolveDouble(settings.altitudeExpr)
val altitudeLimit = vehicle.runtime.resolveDouble(settings.jumpExpr)
//Only limit altitude if altitude is not infinite
if (!vehicle.runtime.resolveBoolean(settings.infiniteAltitude)) {
@@ -113,9 +113,9 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
*/
fun calculateRideSpaceVel(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity, driver: Player) {
//retrieve stats
val topSpeed = vehicle.runtime.resolveDouble(settings.topSpeedExpr)
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glideTopSpeedExpr)
val accel = vehicle.runtime.resolveDouble(settings.accelExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
val staminaStat = vehicle.runtime.resolveDouble(settings.staminaExpr)
var glideSpeedChange = 0.0
@@ -237,11 +237,9 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
}
//air resistance
newVelocity = Vec3(
newVelocity.x,
newVelocity.y,
lerp( newVelocity.z,0.0, topSpeed / ( 20.0 * 30.0))
)
if( abs(newVelocity.z) > 0) {
newVelocity = newVelocity.subtract(0.0, 0.0, min(0.003 * newVelocity.z.sign, newVelocity.z))
}
state.rideVel.set(newVelocity)
}
@@ -257,8 +255,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
//TODO: Tie into handling
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.topSpeedExpr)
val rotationChangeRate = 10.0
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val rotationChangeRate = 10.0
var yawForce = rotationChangeRate * sin(Math.toRadians(controller.roll.toDouble()))
//for a bit of correction on the rolls limit it to a quarter the amount
@@ -287,14 +285,16 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
val controller = (driver as OrientationControllable).orientationController
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.topSpeedExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
//Smooth out mouse input.
val smoothingSpeed = 4
val smoothingSpeed = 4.0
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
val xInput = mouseXSmoother.getNewDeltaValue(mouseX * 0.1 * invertRoll, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseY * 0.1 * invertPitch, deltaTime * smoothingSpeed);
val mouseXc = (mouseX).coerceIn(-60.0, 60.0)
val mouseYc = (mouseY).coerceIn(-60.0, 60.0)
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1 * invertRoll, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1 * invertPitch, deltaTime * smoothingSpeed);
//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
@@ -374,8 +374,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
vehicle: PokemonEntity,
driver: Player
): Float {
val topSpeed = vehicle.runtime.resolveDouble(settings.topSpeedExpr)
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glideTopSpeedExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
//Must I ensure that topspeed is greater than minimum?
val normalizedGlideSpeed = normalizeVal(state.rideVel.get().length(), topSpeed, glideTopSpeed)
@@ -440,52 +440,41 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
class BirdAirSettings : RidingBehaviourSettings {
override val key = BirdAirBehaviour.KEY
//max y level for the ride
var altitudeExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 200.0, 128.0)".asExpression()
private set
var infiniteAltitude: Expression = "false".asExpression()
private set
var infiniteStamina: Expression = "false".asExpression()
private set
//max y level for the ride
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 200.0, 128.0)".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', 1.0, 0.35)".asExpression()
private set
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 speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.0, 0.35)".asExpression()
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 3.0)), (1.0 / (20.0 * 8.0)))".asExpression()
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 120.0, 20.0)".asExpression()
var glidespeedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 2.0, 1.0)".asExpression()
private set
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeExpression(altitudeExpr)
buffer.writeExpression(infiniteAltitude)
buffer.writeExpression(infiniteStamina)
buffer.writeExpression(glidespeedExpr)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(handlingExpr)
buffer.writeExpression(topSpeedExpr)
buffer.writeExpression(glideTopSpeedExpr)
buffer.writeExpression(accelExpr)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
altitudeExpr = buffer.readExpression()
infiniteAltitude = buffer.readExpression()
infiniteStamina = buffer.readExpression()
glidespeedExpr = buffer.readExpression()
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
topSpeedExpr = buffer.readExpression()
glideTopSpeedExpr = buffer.readExpression()
accelExpr = buffer.readExpression()
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
}
}
@@ -46,7 +46,7 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
override fun speed(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity, driver: Player): Float {
//retrieve stats
val topSpeed = vehicle.runtime.resolveDouble(settings.topSpeedExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val staminaStat = vehicle.runtime.resolveDouble(settings.staminaExpr)
//retrieve minSpeed
@@ -137,8 +137,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
vehicle: PokemonEntity,
driver: Player
) {
val topSpeed = vehicle.runtime.resolveDouble(settings.topSpeedExpr)
val accel = vehicle.runtime.resolveDouble(settings.accelExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
val altitudeLimit = vehicle.runtime.resolveDouble(settings.jumpExpr)
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
val speed = state.rideVel.get().length()
@@ -281,7 +281,7 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
vehicle: PokemonEntity,
driver: Player
): Float {
val topSpeed = vehicle.runtime.resolveDouble(settings.topSpeedExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
//Must I ensure that topspeed is greater than minimum?
@@ -345,27 +345,9 @@ class JetAirSettings : RidingBehaviourSettings {
var minSpeed: Expression = "1.2".asExpression()
private set
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('STAMINA', 'AIR', 60.0, 10.0)".asExpression()
private set
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 300.0, 128.0)".asExpression()
private set
// Make configurable by json
var infiniteStamina: Expression = "false".asExpression()
private set
@@ -373,30 +355,41 @@ class JetAirSettings : RidingBehaviourSettings {
var infiniteAltitude: Expression = "false".asExpression()
private set
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 300.0, 128.0)".asExpression()
private set
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 140.0, 20.0)".asExpression()
private set
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 2.5, 1.0)".asExpression()
private set
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 1.0)), (1.0 / (20.0 * 5.0)))".asExpression()
private set
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 60.0, 10.0)".asExpression()
private set
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeExpression(gravity)
buffer.writeExpression(minSpeed)
buffer.writeExpression(handlingExpr)
buffer.writeExpression(handlingYawExpr)
buffer.writeExpression(topSpeedExpr)
buffer.writeExpression(accelExpr)
buffer.writeExpression(staminaExpr)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(infiniteStamina)
buffer.writeExpression(infiniteAltitude)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(handlingExpr)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
gravity = buffer.readExpression()
minSpeed = buffer.readExpression()
handlingExpr = buffer.readExpression()
handlingYawExpr = buffer.readExpression()
topSpeedExpr = buffer.readExpression()
accelExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
jumpExpr = buffer.readExpression()
infiniteStamina = buffer.readExpression()
infiniteAltitude = buffer.readExpression()
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
}
}
@@ -78,8 +78,15 @@ class JsonPose(model: PosableModel, json: JsonObject) {
it[2].asDouble
)
} ?: Vec3.ZERO
val scale = it.get("scale")?.asJsonArray?.let {
Vec3(
it[0].asDouble,
it[1].asDouble,
it[2].asDouble
)
} ?: Vec3(1.0,1.0,1.0)
val isVisible = it.get("isVisible")?.asString?.asExpressionLike()
return@map part.withPosition(position.x, position.y, position.z).withRotationDegrees(rotation.x, rotation.y, rotation.z).also { if (isVisible != null) it.withVisibility(isVisible) }
return@map part.withPosition(position.x, position.y, position.z).withRotationDegrees(rotation.x, rotation.y, rotation.z).withScale(scale.x, scale.y, scale.z).also { if (isVisible != null) it.withVisibility(isVisible) }
}?.toTypedArray() ?: arrayOf()
val idleAnimations = (json.get("animations")?.asJsonArray ?: JsonArray()).asJsonArray.mapNotNull {
@@ -532,7 +532,7 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
/** Puts the model back to its original location and rotations. */
fun setDefault() {
defaultPositions.forEach { it.set() }
transformedParts.forEach { it.set() }
transformedParts.forEach { it.apply() }
}
/**
@@ -733,8 +733,14 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
transformationMatrix.rotate(controller.getRenderOrientation(state.getPartialTicks()))
transformationMatrix.translate(center.negate())
matrixStack.mulPose(transformationMatrix)
} else if( entity.passengers.isNotEmpty()) {
// rotate a ridden but non rollable pokemon correctly
val driver = entity.controllingPassenger ?: entity
var yRot = Mth.lerp(state.getPartialTicks(), driver.yRotO, driver.yRot)
yRot = Mth.wrapDegrees(yRot)
matrixStack.mulPose(Axis.YP.rotationDegrees(180 - yRot))
} else {
var yRot = Mth.lerp(state.getPartialTicks(), entity.yBodyRotO, entity.yBodyRot)
var yRot = Mth.lerp(state.getPartialTicks(), entity.yRotO, entity.yRot)
yRot = Mth.wrapDegrees(yRot)
matrixStack.mulPose(Axis.YP.rotationDegrees(180 - yRot))
}
@@ -46,7 +46,7 @@ class ModelPartTransformation(val modelPart: ModelPart) {
var visibility: ExpressionLike? = null
/** Applies the transformation to the model part. */
fun apply(state: PosableState, intensity: Float) {
fun apply(state: PosableState? = null, intensity: Float = 1f) {
modelPart.x += position[0] * intensity
modelPart.y += position[1] * intensity
modelPart.z += position[2] * intensity
@@ -56,7 +56,7 @@ class ModelPartTransformation(val modelPart: ModelPart) {
modelPart.xScale *= ((1 - scale[0]).absoluteValue * intensity) * (scale[0] - 1).sign + 1
modelPart.yScale *= ((1 - scale[1]).absoluteValue * intensity) * (scale[1] - 1).sign + 1
modelPart.zScale *= ((1 - scale[2]).absoluteValue * intensity) * (scale[2] - 1).sign + 1
visibility?.let { modelPart.visible = state.runtime.resolveBoolean(it) }
visibility?.let { modelPart.visible = state?.runtime?.resolveBoolean(it)?: defaultRuntime.resolveBoolean(it) }
}
fun set() {
@@ -76,12 +76,13 @@ object MountedPlayerRenderer {
//Rotates player
if (player is OrientationControllable && player.orientationController.active && !disableRollableRenderDebug) {
val center = Vector3f(0f, player.bbHeight / 2, 0f)
val yaw = Mth.rotLerp(partialTicks, entity.yBodyRotO, entity.yBodyRot)
val transformationMatrix = Matrix4f()
transformationMatrix.translate(center)
transformationMatrix.rotate(player.orientationController.getRenderOrientation(partialTicks))
transformationMatrix.translate(center.negate(Vector3f()))
//Pre-Undo Yaw
transformationMatrix.rotate(Axis.YP.rotationDegrees(yBodyRot+180f))
transformationMatrix.rotate(Axis.YP.rotationDegrees(yaw+180f))
matrix.mul(transformationMatrix)
}
matrix.translate(0f, 0.25f, 0f)
@@ -178,7 +178,7 @@ class PokemonRenderer(
buffer: MultiBufferSource,
packedLight: Int
) {
val driver = entity.firstPassenger ?: return
val driver = entity.controllingPassenger ?: return
val rollable = driver as? OrientationControllable ?: return
val controller = rollable.orientationController
poseMatrix.pushPose()
@@ -198,6 +198,10 @@ class PokemonRenderer(
//Pre-undo yaw rotation
transformationMatrix.rotate(Axis.YP.rotationDegrees(yaw+180f))
matrix.mul(transformationMatrix)
} else {
//Align rotation for non rollable pokemon
entity.yBodyRot = driver.yRot
entity.yBodyRotO = driver.yRotO
}
super.render(entity, 0f, partialTicks, poseMatrix, buffer, packedLight)
@@ -1,79 +0,0 @@
/*
* 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.command
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.client.render.models.blockbench.createTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.permission
import com.cobblemon.mod.common.util.traceFirstEntityCollision
import com.mojang.brigadier.Command
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.arguments.FloatArgumentType
import com.mojang.brigadier.arguments.StringArgumentType
import com.mojang.brigadier.context.CommandContext
import net.minecraft.commands.CommandSourceStack
import net.minecraft.commands.Commands
object ChangeJointScaleCommand {
fun register(dispatcher : CommandDispatcher<CommandSourceStack>) {
val command = Commands.literal("changejointscale")
.permission(CobblemonPermissions.CHANGE_JOINT_SCALE)
.then(
Commands.argument("joint", StringArgumentType.string())
.then(
Commands.argument("x", FloatArgumentType.floatArg())
.then(Commands.argument("y", FloatArgumentType.floatArg())
.then(
Commands.argument("z", FloatArgumentType.floatArg())
.executes(::execute)
)
)
)
.executes(::execute))
dispatcher.register(command)
}
private fun execute(context: CommandContext<CommandSourceStack>) : Int {
val jointName = StringArgumentType.getString(context, "joint")
val x = FloatArgumentType.getFloat(context, "x")
val y = FloatArgumentType.getFloat(context, "y")
val z = FloatArgumentType.getFloat(context, "z")
val player = context.source.playerOrException
val targetEntity = player.traceFirstEntityCollision(entityClass = PokemonEntity::class.java)
if (targetEntity == null) {
player.sendSystemMessage("No pokemon".red())
return 0
}
val state = FloatingState()
state.currentAspects = targetEntity.aspects
val model = VaryingModelRepository.getPoser(name = targetEntity.exposedSpecies.resourceIdentifier, state = state)
val part = model.relevantPartsByName[jointName]
?: run {
player.sendSystemMessage("No joint named $jointName".red())
return 0
}
val existing = model.transformedParts.find { it.modelPart == part }
if (existing == null) {
model.transformedParts = model.transformedParts + part.createTransformation().withScale(x, y, z)
} else {
existing.withScale(x, y, z)
}
player.sendSystemMessage("Changed scale of $jointName to $x, $y, $z".text())
return Command.SINGLE_SUCCESS
}
}
@@ -0,0 +1,72 @@
/*
* 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.command
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.render.models.blockbench.createTransformation
import com.cobblemon.mod.common.command.argument.ModelPartArgumentType
import com.cobblemon.mod.common.command.argument.TransformTypeArgumentType
import com.cobblemon.mod.common.command.argument.TransformTypeArgumentType.Companion.TransformType
import com.cobblemon.mod.common.util.permission
import com.mojang.brigadier.Command
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.arguments.FloatArgumentType
import com.mojang.brigadier.context.CommandContext
import net.minecraft.commands.CommandSourceStack
import net.minecraft.commands.Commands
object TransformModelPartCommand {
fun register(dispatcher : CommandDispatcher<CommandSourceStack>) {
val command = Commands.literal("transformmodelpart")
.permission(CobblemonPermissions.TRANSFORM_MODEL_PART)
.then(Commands.argument("transform", TransformTypeArgumentType.transformType())
.then(Commands.argument("part", ModelPartArgumentType.modelPart())
.then(Commands.argument("x", FloatArgumentType.floatArg())
.then(Commands.argument("y", FloatArgumentType.floatArg())
.then(Commands.argument("z", FloatArgumentType.floatArg())
.executes(::execute)
)
)
)
.executes(::execute))
.executes(::execute))
dispatcher.register(command)
}
private fun execute(context: CommandContext<CommandSourceStack>) : Int {
val transformType = TransformTypeArgumentType.getTransform(context, "transform")
val targetModelPart = ModelPartArgumentType.getModelPart(context, "part")?: return 0
val x = FloatArgumentType.getFloat(context, "x")
val y = FloatArgumentType.getFloat(context, "y")
val z = FloatArgumentType.getFloat(context, "z")
val model = ModelPartArgumentType.getModel(context)?: return 0
val existing = model.transformedParts.find { it.modelPart == targetModelPart }
when (transformType) {
TransformType.POSITION -> {
if (existing != null) existing.withPosition(x, y, z)
else model.transformedParts += targetModelPart.createTransformation().withPosition(x, y, z)
}
TransformType.ROTATION -> {
if (existing != null) existing.withRotation(x, y, z)
else model.transformedParts += targetModelPart.createTransformation().withRotation(x, y, z)
}
TransformType.SCALE -> {
if (existing != null) existing.withScale(x, y, z)
else model.transformedParts += targetModelPart.createTransformation().withScale(x, y, z)
}
}
context.source.playerOrException.sendSystemMessage("Changed ${transformType.name.lowercase()} of ${context.getArgument("part", String::class.java )} to $x, $y, $z".text())
return Command.SINGLE_SUCCESS
}
}
@@ -0,0 +1,75 @@
/*
* 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.command.argument
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.mojang.brigadier.StringReader
import com.mojang.brigadier.arguments.ArgumentType
import com.mojang.brigadier.context.CommandContext
import com.mojang.brigadier.suggestion.Suggestions
import com.mojang.brigadier.suggestion.SuggestionsBuilder
import net.minecraft.client.Minecraft
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.commands.CommandSourceStack
import net.minecraft.commands.SharedSuggestionProvider
import net.minecraft.world.phys.EntityHitResult
import net.minecraft.world.phys.HitResult.Type
import java.util.concurrent.CompletableFuture
class ModelPartArgumentType(): ArgumentType<String> {
override fun parse(reader: StringReader): String {
return reader.readString()
}
override fun <S : Any> listSuggestions(
context: CommandContext<S>,
builder: SuggestionsBuilder
): CompletableFuture<Suggestions> {
return SharedSuggestionProvider.suggest(getModelParts(context).keys.toList(),builder)
}
companion object {
fun modelPart() = ModelPartArgumentType()
fun <S: Any> getModel(context: CommandContext<S> ): PosableModel? {
if (context.source is SharedSuggestionProvider) {
val targetEntity = (Minecraft.getInstance().hitResult as? EntityHitResult)
?.takeIf { it.type == Type.ENTITY }
?.entity
//Could probably work for NPCs too with some changes
if (targetEntity is PokemonEntity) {
val state = FloatingState()
state.currentAspects = targetEntity.aspects
return VaryingModelRepository.getPoser(name = targetEntity.exposedSpecies.resourceIdentifier, state = state)
}
}
return null
}
private fun <S: Any> getModelParts(context: CommandContext<S>): MutableMap<String, ModelPart> {
val modelParts = getModel(context)?.relevantPartsByName?:mutableMapOf()
return modelParts
}
fun getModelPart(context: CommandContext<CommandSourceStack>, name: String): ModelPart? {
val partName = context.getArgument(name, String::class.java )
val part = getModelParts(context)[partName]
?: run {
context.source.playerOrException.sendSystemMessage("No joint named $partName".red())
return null
}
return part
}
}
}
@@ -0,0 +1,49 @@
/*
* 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.command.argument
import com.mojang.brigadier.StringReader
import com.mojang.brigadier.arguments.ArgumentType
import com.mojang.brigadier.context.CommandContext
import com.mojang.brigadier.exceptions.CommandSyntaxException
import com.mojang.brigadier.suggestion.SuggestionsBuilder
import net.minecraft.commands.CommandSourceStack
import net.minecraft.commands.SharedSuggestionProvider
class TransformTypeArgumentType: ArgumentType<TransformTypeArgumentType.Companion.TransformType>
{
override fun parse(reader: StringReader): TransformType {
try {
reader.readString()?.let { return TransformType.valueOf(it.uppercase()) }
}
catch (_: Exception) { }
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherParseException().create(reader)
}
override fun getExamples() = EXAMPLES
override fun <S : Any> listSuggestions(context: CommandContext<S>, builder: SuggestionsBuilder) = SharedSuggestionProvider.suggest(EXAMPLES, builder)
companion object {
enum class TransformType {
POSITION,
ROTATION,
SCALE
}
fun transformType() = TransformTypeArgumentType()
fun getTransform(context: CommandContext<CommandSourceStack>, name: String): TransformType {
return context.getArgument(name, TransformType::class.java )
}
private val EXAMPLES = TransformType.entries.map { it.name.lowercase() }
}
}
@@ -146,7 +146,9 @@ import net.minecraft.world.level.material.FluidState
import net.minecraft.world.level.pathfinder.PathType
import net.minecraft.world.phys.AABB
import net.minecraft.world.phys.Vec3
import org.joml.AxisAngle4f
import org.joml.Matrix3f
import org.joml.Quaternionf
import org.joml.Vector3f
@Suppress("unused")
@@ -252,7 +254,6 @@ open class PokemonEntity(
var enablePoseTypeRecalculation = true
// override var ridingController: RideController? = null
var previousRidingState: RidingBehaviourState? = null
var ridingState: RidingBehaviourState? = null
@@ -1536,6 +1537,7 @@ open class PokemonEntity(
}
// TODO: jackowes look over this so I don't accidentally break anything
// TODO: Talk to landon about why this was needed
this.deltaMovement = this.deltaMovement.lerp(v, inertia)
var pos = this.deltaMovement.scale(this.speed.toDouble())
if (super.onGround() && this.deltaMovement.y == 0.0) {
@@ -1563,14 +1565,35 @@ open class PokemonEntity(
if (beamMode != 3) { // Don't let Pokémon move during recall
//Prevent current travel logic when riding a pokemon.
/*
val riders = this.passengers.filterIsInstance<LivingEntity>()
if ( riders.isEmpty() ) {
if ( riders.isEmpty() || this.controllingPassenger == null) {
super.travel(movementInput)
}
*/
else {
val inp = ifRidingAvailableSupply(fallback = Vec3.ZERO) { behaviour, settings, state ->
behaviour.velocity(settings, state, this, this.controllingPassenger as Player, deltaMovement)
}
// Rotate velocity vector to face the current y rotation
val f = Mth.sin(this.getYRot() * 0.017453292f)
val g = Mth.cos(this.getYRot() * 0.017453292f)
val v = Vec3(
inp.x * g.toDouble() - inp.z * f.toDouble(),
inp.y,
inp.z * g.toDouble() + inp.x * f.toDouble()
)
val diff = v.subtract(this.deltaMovement)
val inertia = ifRidingAvailableSupply(fallback = 0.5) { behaviour, settings, state ->
behaviour.inertia(settings, state,this)
}
this.deltaMovement = this.deltaMovement.add( diff.scale(inertia) )
this.move(MoverType.SELF, this.deltaMovement)
}
super.travel(movementInput)
this.updateBlocksTraveled(prevBlockPos)
}
@@ -43,7 +43,14 @@ object ModelPartTransformationAdapter : JsonDeserializer<ModelPartTransformation
it[2].asDouble
)
} ?: Vec3.ZERO
val scale = json.get("scale")?.asJsonArray?.let {
Vec3(
it[0].asDouble,
it[1].asDouble,
it[2].asDouble
)
} ?: Vec3(1.0,1.0,1.0)
val isVisible = json.get("isVisible")?.asString?.asExpressionLike()
return part.withPosition(position.x, position.y, position.z).withRotationDegrees(rotation.x, rotation.y, rotation.z).also { if (isVisible != null) it.withVisibility(isVisible) }
return part.withPosition(position.x, position.y, position.z).withRotationDegrees(rotation.x, rotation.y, rotation.z).withScale(scale.x, scale.y, scale.z).also { if (isVisible != null) it.withVisibility(isVisible) }
}
}
@@ -15,6 +15,14 @@
"name": "lapras",
"pivot": [0, 0, 0]
},
{
"name": "locator_root",
"parent": "lapras",
"pivot": [0, 0, 0],
"locators": {
"root": [0, 0, 0]
}
},
{
"name": "body",
"parent": "lapras",
@@ -29,11 +37,11 @@
}
},
{
"name": "locator_middle",
"name": "locator_body",
"parent": "body",
"pivot": [0, 0, 0],
"locators": {
"middle": [0, 14, 0]
"body": [0, 14, 0]
}
},
{
@@ -60,14 +68,6 @@
{"origin": [-15.25, 13.5, -7], "size": [3, 3, 3], "pivot": [-12.5, 13.5, -4.5], "rotation": [0, -15, 5], "uv": [12, 120], "mirror": true}
]
},
{
"name": "locator_seat_1",
"parent": "shell",
"pivot": [0, 24, 0],
"locators": {
"seat_1": [0, 24, 0]
}
},
{
"name": "shell2",
"parent": "shell",
@@ -138,31 +138,6 @@
{"origin": [-5.5, 30, -14.5], "size": [11, 10, 8], "inflate": 0.01, "uv": [0, 0]}
]
},
{
"name": "locator_item_face",
"parent": "head_angle",
"pivot": [0, 37.25, -21],
"rotation": [-22.5, 0, 0],
"locators": {
"item_face": [0, 37.25, -21]
}
},
{
"name": "locator_face",
"parent": "head_angle",
"pivot": [0, 36, -24.1],
"locators": {
"face": [0, 36, -24.1]
}
},
{
"name": "locator_item_hat",
"parent": "head_angle",
"pivot": [0, 40, -12],
"locators": {
"item_hat": [0, 40, -12]
}
},
{
"name": "locator_head",
"parent": "head_angle",
@@ -185,6 +160,14 @@
"parent": "head_angle",
"pivot": [0, 30.75, -11.15]
},
{
"name": "locator_face",
"parent": "eyes",
"pivot": [0, 36, -24.1],
"locators": {
"face": [0, 36, -24.1]
}
},
{
"name": "eye_right",
"parent": "eyes",
@@ -323,14 +306,6 @@
"jaw_lower": [0, 32.15, -21.5]
}
},
{
"name": "locator_item",
"parent": "jaw",
"pivot": [0, 32, -22.5],
"locators": {
"item": [0, 32, -22.5]
}
},
{
"name": "tongue",
"parent": "jaw",
@@ -370,15 +345,6 @@
{"origin": [-6.5, 1.5, 11], "size": [13, 10, 7], "uv": [78, 40]}
]
},
{
"name": "locator_tail",
"parent": "tail",
"pivot": [0, 5, 17],
"rotation": [0, 180, 0],
"locators": {
"tail": [0, 5, 17]
}
},
{
"name": "tail2",
"parent": "tail",
@@ -533,19 +499,26 @@
]
},
{
"name": "locator_root",
"parent": "lapras",
"name": "bone2",
"pivot": [0, 0, 0]
},
{
"name": "bone4",
"parent": "bone2",
"pivot": [0, 0, 0]
},
{
"name": "bone",
"pivot": [0, 0, 0],
"locators": {
"root": [0, 0, 0]
"locator5": [0, 0, 0]
}
},
{
"name": "locator_top",
"parent": "lapras",
"pivot": [0, 45.2, -6],
"name": "bone5",
"pivot": [0, 0, 0],
"locators": {
"top": [0, 45.2, -6]
"locator6": [0, 0, 0]
}
}
]
@@ -51,16 +51,24 @@
"fixed": false
},
"riding": {
"behaviour": {
"key": "cobblemon:swim/generic"
},
"behaviour": null,
"seats": [
{
"offset": {
"x": 0.0,
"y": 0.4,
"z": 0.0
}
},
"poseAnimations": [
{
"animations": [
{
"fileName": "trainer_generic",
"animationName": "animation.trainer_generic.idle"
}
]
}
]
},
{
"offset": {