forked from mirrors/cobblemon
Completed pokeball capturing outside of battle, improved pokeball collision and motion, added some useful maths for parabolas
This commit is contained in:
Binary file not shown.
+34
@@ -0,0 +1,34 @@
|
|||||||
|
package com.cablemc.pokemoncobbled.client.entity
|
||||||
|
|
||||||
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.PoseableEntityState
|
||||||
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.frame.PokeBallFrame
|
||||||
|
import com.cablemc.pokemoncobbled.client.render.pokeball.animation.OpenAnimation
|
||||||
|
import com.cablemc.pokemoncobbled.client.render.pokeball.animation.ShakeAnimation
|
||||||
|
import com.cablemc.pokemoncobbled.common.api.entity.EntitySideDelegate
|
||||||
|
import com.cablemc.pokemoncobbled.common.api.reactive.Observable.Companion.emitWhile
|
||||||
|
import com.cablemc.pokemoncobbled.common.entity.pokeball.EmptyPokeBallEntity
|
||||||
|
import com.cablemc.pokemoncobbled.common.entity.pokeball.EmptyPokeBallEntity.CaptureState
|
||||||
|
import com.cablemc.pokemoncobbled.common.entity.pokeball.EmptyPokeBallEntity.CaptureState.HIT
|
||||||
|
import com.cablemc.pokemoncobbled.common.entity.pokeball.EmptyPokeBallEntity.CaptureState.SHAKE
|
||||||
|
import com.cablemc.pokemoncobbled.common.entity.pokeball.PokeBallDelegate
|
||||||
|
|
||||||
|
class EmptyPokeBallClientDelegate : PoseableEntityState<EmptyPokeBallEntity>(), PokeBallDelegate<EmptyPokeBallEntity> {
|
||||||
|
override fun initialize(entity: EmptyPokeBallEntity) {
|
||||||
|
entity.captureState.subscribe {
|
||||||
|
when (CaptureState.values()[it.toInt()]) {
|
||||||
|
HIT -> {
|
||||||
|
statefulAnimations.add(OpenAnimation())
|
||||||
|
}
|
||||||
|
CaptureState.FALL -> {
|
||||||
|
statefulAnimations.clear()
|
||||||
|
}
|
||||||
|
SHAKE -> {
|
||||||
|
entity.shakeEmitter
|
||||||
|
.pipe(emitWhile { entity.captureState.get() == SHAKE.ordinal.toByte() })
|
||||||
|
.subscribe { statefulAnimations.add(ShakeAnimation(0.8F)) }
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package com.cablemc.pokemoncobbled.client.entity
|
||||||
|
|
||||||
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.PoseableEntityState
|
||||||
|
import com.cablemc.pokemoncobbled.common.entity.pokeball.OccupiedPokeBallEntity
|
||||||
|
import com.cablemc.pokemoncobbled.common.entity.pokeball.PokeBallDelegate
|
||||||
|
|
||||||
|
class OccupiedPokeBallClientDelegate : PoseableEntityState<OccupiedPokeBallEntity>(), PokeBallDelegate<OccupiedPokeBallEntity>
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package com.cablemc.pokemoncobbled.client.entity
|
|
||||||
|
|
||||||
import com.cablemc.pokemoncobbled.client.render.models.blockbench.PoseableEntityState
|
|
||||||
import com.cablemc.pokemoncobbled.client.render.models.blockbench.frame.PokeBallFrame
|
|
||||||
import com.cablemc.pokemoncobbled.client.render.pokeball.animation.OpenAnimation
|
|
||||||
import com.cablemc.pokemoncobbled.common.api.entity.EntitySideDelegate
|
|
||||||
import com.cablemc.pokemoncobbled.common.entity.pokeball.EmptyPokeBallEntity
|
|
||||||
import com.cablemc.pokemoncobbled.common.entity.pokeball.EmptyPokeBallEntity.CaptureState
|
|
||||||
import com.cablemc.pokemoncobbled.common.entity.pokeball.EmptyPokeBallEntity.CaptureState.HIT
|
|
||||||
import com.cablemc.pokemoncobbled.common.entity.pokeball.EmptyPokeBallEntity.CaptureState.SHAKE
|
|
||||||
import com.cablemc.pokemoncobbled.common.entity.pokeball.PokeBallEntity
|
|
||||||
|
|
||||||
class PokeBallClientDelegate : PoseableEntityState<PokeBallEntity>(), EntitySideDelegate<PokeBallEntity> {
|
|
||||||
override fun initialize(entity: PokeBallEntity) {
|
|
||||||
if (entity is EmptyPokeBallEntity) {
|
|
||||||
entity.captureState.subscribe {
|
|
||||||
when (CaptureState.values()[it.toInt()]) {
|
|
||||||
HIT -> {
|
|
||||||
statefulAnimations.add(OpenAnimation(currentModel as PokeBallFrame))
|
|
||||||
}
|
|
||||||
CaptureState.FALL -> {
|
|
||||||
|
|
||||||
}
|
|
||||||
SHAKE -> {
|
|
||||||
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-1
@@ -98,7 +98,6 @@ abstract class PoseableEntityModel<T : Entity> : EntityModel<T>(), ModelFrame {
|
|||||||
if (previousPose != null && pose.transformTicks > 0) {
|
if (previousPose != null && pose.transformTicks > 0) {
|
||||||
state.statefulAnimations.add(
|
state.statefulAnimations.add(
|
||||||
PoseTransitionAnimation(
|
PoseTransitionAnimation(
|
||||||
frame = this,
|
|
||||||
beforePose = previousPose,
|
beforePose = previousPose,
|
||||||
afterPose = pose,
|
afterPose = pose,
|
||||||
durationTicks = pose.transformTicks
|
durationTicks = pose.transformTicks
|
||||||
|
|||||||
+2
-2
@@ -10,7 +10,7 @@ import net.minecraft.world.entity.Entity
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the entity-specific state for a poseable model. The implementation is responsible for
|
* Represents the entity-specific state for a poseable model. The implementation is responsible for
|
||||||
* handling all of the state for an entity's model, and needs to be conscious of the fact that the
|
* handling all the state for an entity's model, and needs to be conscious of the fact that the
|
||||||
* model may change without this state changing.
|
* model may change without this state changing.
|
||||||
*
|
*
|
||||||
* @author Hiroku
|
* @author Hiroku
|
||||||
@@ -49,7 +49,7 @@ abstract class PoseableEntityState<T : Entity> {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val animation = PoseTransitionAnimation(model, beforePose, afterPose, durationTicks)
|
val animation = PoseTransitionAnimation(beforePose, afterPose, durationTicks)
|
||||||
statefulAnimations.add(animation)
|
statefulAnimations.add(animation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -16,11 +16,10 @@ import java.lang.Float.min
|
|||||||
* @since December 5th, 2021
|
* @since December 5th, 2021
|
||||||
*/
|
*/
|
||||||
class PoseTransitionAnimation<T : Entity>(
|
class PoseTransitionAnimation<T : Entity>(
|
||||||
frame: ModelFrame,
|
|
||||||
beforePose: Pose<T, *>,
|
beforePose: Pose<T, *>,
|
||||||
val afterPose: Pose<T, *>,
|
val afterPose: Pose<T, *>,
|
||||||
private val durationTicks: Int = 20
|
private val durationTicks: Int = 20
|
||||||
) : StatefulAnimation<T, ModelFrame>(frame) {
|
) : StatefulAnimation<T, ModelFrame> {
|
||||||
var passedTicks = 0F
|
var passedTicks = 0F
|
||||||
|
|
||||||
val transforms = mutableListOf<GradualTransform>()
|
val transforms = mutableListOf<GradualTransform>()
|
||||||
|
|||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
package com.cablemc.pokemoncobbled.client.render.models.blockbench.animation
|
||||||
|
|
||||||
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.PoseableEntityModel
|
||||||
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.frame.PokeBallFrame
|
||||||
|
import com.cablemc.pokemoncobbled.common.entity.pokeball.EmptyPokeBallEntity
|
||||||
|
import com.cablemc.pokemoncobbled.common.entity.pokeball.PokeBallEntity
|
||||||
|
import net.minecraft.util.Mth
|
||||||
|
import net.minecraft.util.Mth.PI
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple idle animation that will point the PokéBall in the direction of the target it hit. Does nothing
|
||||||
|
* for occupied PokéBalls.
|
||||||
|
*
|
||||||
|
* @author Hiroku
|
||||||
|
* @since December 25th, 2021
|
||||||
|
*/
|
||||||
|
class RootPokeBallLookAnimation(frame: PokeBallFrame) : StatelessAnimation<PokeBallEntity, PokeBallFrame>(frame) {
|
||||||
|
override val targetFrame = PokeBallFrame::class.java
|
||||||
|
override fun setAngles(entity: PokeBallEntity?, model: PoseableEntityModel<PokeBallEntity>, limbSwing: Float, limbSwingAmount: Float, ageInTicks: Float, headYaw: Float, headPitch: Float) {
|
||||||
|
val yRot = entity?.let {
|
||||||
|
if (it is EmptyPokeBallEntity) {
|
||||||
|
val dispX = it.hitTargetPosition.get().x - it.x
|
||||||
|
val dispZ = it.hitTargetPosition.get().z - it.z
|
||||||
|
Mth.atan2(-dispZ, dispX) + PI / 2
|
||||||
|
} else {
|
||||||
|
0F
|
||||||
|
}
|
||||||
|
}?.toFloat() ?: 0F
|
||||||
|
model.rootPart.yRot += yRot
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-4
@@ -12,14 +12,14 @@ import net.minecraft.world.entity.Entity
|
|||||||
* @author Hiroku
|
* @author Hiroku
|
||||||
* @since December 5th, 2021
|
* @since December 5th, 2021
|
||||||
*/
|
*/
|
||||||
abstract class StatefulAnimation<T : Entity, F : ModelFrame>(val frame: F) {
|
interface StatefulAnimation<T : Entity, F : ModelFrame> {
|
||||||
/**
|
/**
|
||||||
* Whether this animation should prevent the given idle animation from occurring.
|
* Whether this animation should prevent the given idle animation from occurring.
|
||||||
*
|
*
|
||||||
* This is for cases where this animation and the idle animation work on the same parts
|
* This is for cases where this animation and the idle animation work on the same parts
|
||||||
* of the model and would conflict.
|
* of the model and would conflict.
|
||||||
*/
|
*/
|
||||||
abstract fun preventsIdle(entity: T, idleAnimation: StatelessAnimation<T, *>): Boolean
|
fun preventsIdle(entity: T, idleAnimation: StatelessAnimation<T, *>): Boolean
|
||||||
/** Runs the animation. You can access the frame from the class properties. Returns true if the animation should continue. */
|
/** Runs the animation. You can check that the model fits a particular frame. Returns true if the animation should continue. */
|
||||||
abstract fun run(entity: T, model: PoseableEntityModel<T>): Boolean
|
fun run(entity: T, model: PoseableEntityModel<T>): Boolean
|
||||||
}
|
}
|
||||||
+1
@@ -3,5 +3,6 @@ package com.cablemc.pokemoncobbled.client.render.models.blockbench.frame
|
|||||||
import net.minecraft.client.model.geom.ModelPart
|
import net.minecraft.client.model.geom.ModelPart
|
||||||
|
|
||||||
interface PokeBallFrame : ModelFrame {
|
interface PokeBallFrame : ModelFrame {
|
||||||
|
val subRoot: ModelPart
|
||||||
val lid: ModelPart
|
val lid: ModelPart
|
||||||
}
|
}
|
||||||
+38
-9
@@ -1,11 +1,15 @@
|
|||||||
package com.cablemc.pokemoncobbled.client.render.models.blockbench.pokeball
|
package com.cablemc.pokemoncobbled.client.render.models.blockbench.pokeball
|
||||||
|
|
||||||
import com.cablemc.pokemoncobbled.client.entity.PokeBallClientDelegate
|
|
||||||
import com.cablemc.pokemoncobbled.client.render.models.blockbench.PoseableEntityModel
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.PoseableEntityModel
|
||||||
import com.cablemc.pokemoncobbled.client.render.models.blockbench.animation.RotationFunctionStatelessAnimation
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.PoseableEntityState
|
||||||
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.animation.RootPokeBallLookAnimation
|
||||||
import com.cablemc.pokemoncobbled.client.render.models.blockbench.frame.PokeBallFrame
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.frame.PokeBallFrame
|
||||||
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.getChildOf
|
||||||
import com.cablemc.pokemoncobbled.client.render.models.blockbench.pose.PoseType
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.pose.PoseType
|
||||||
import com.cablemc.pokemoncobbled.client.render.models.blockbench.pose.TransformedModelPart.Companion.Y_AXIS
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.pose.TransformedModelPart.Companion.Y_AXIS
|
||||||
|
import com.cablemc.pokemoncobbled.common.PokemonCobbled
|
||||||
|
import com.cablemc.pokemoncobbled.common.entity.pokeball.EmptyPokeBallEntity
|
||||||
|
import com.cablemc.pokemoncobbled.common.entity.pokeball.OccupiedPokeBallEntity
|
||||||
import com.cablemc.pokemoncobbled.common.entity.pokeball.PokeBallEntity
|
import com.cablemc.pokemoncobbled.common.entity.pokeball.PokeBallEntity
|
||||||
import net.minecraft.client.model.geom.ModelLayerLocation
|
import net.minecraft.client.model.geom.ModelLayerLocation
|
||||||
import net.minecraft.client.model.geom.ModelPart
|
import net.minecraft.client.model.geom.ModelPart
|
||||||
@@ -18,18 +22,38 @@ import net.minecraft.resources.ResourceLocation
|
|||||||
import net.minecraft.util.Mth.PI
|
import net.minecraft.util.Mth.PI
|
||||||
|
|
||||||
class PokeBallModel(root: ModelPart) : PoseableEntityModel<PokeBallEntity>(), PokeBallFrame {
|
class PokeBallModel(root: ModelPart) : PoseableEntityModel<PokeBallEntity>(), PokeBallFrame {
|
||||||
override val rootPart = registerRelevantPart(root.getChild("pokeball"))
|
override val rootPart = registerRelevantPart(root.getChild("root"))
|
||||||
override val lid = registerRelevantPart(root.getChild("pokeball").getChild("pokeball_lid"))
|
override val subRoot = registerRelevantPart(rootPart.getChild("pokeball"))
|
||||||
|
override val lid = registerRelevantPart(subRoot.getChild("pokeball_lid"))
|
||||||
|
|
||||||
override fun registerPoses() {
|
override fun registerPoses() {
|
||||||
registerPose(
|
registerPose(
|
||||||
poseType = PoseType.NONE,
|
poseType = PoseType.NONE,
|
||||||
condition = { true },
|
condition = { it is EmptyPokeBallEntity && it.captureState.get() != EmptyPokeBallEntity.CaptureState.NOT.ordinal.toByte() },
|
||||||
|
idleAnimations = arrayOf(RootPokeBallLookAnimation(this)),
|
||||||
|
transformTicks = 0,
|
||||||
|
transformedParts = arrayOf()
|
||||||
|
)
|
||||||
|
registerPose(
|
||||||
|
poseType = PoseType.WALK,
|
||||||
|
condition = { it is OccupiedPokeBallEntity || (it as EmptyPokeBallEntity).captureState.get() == EmptyPokeBallEntity.CaptureState.NOT.ordinal.toByte() },
|
||||||
|
transformTicks = 0,
|
||||||
idleAnimations = arrayOf(
|
idleAnimations = arrayOf(
|
||||||
rootPart.rotation(
|
rootPart.rotation(
|
||||||
function = { t -> t * PI / 10 }, // 1 rotation per second = 2pi per 20 ticks = 2pi / 20 = pi / 10 per tick
|
function = { t -> t * PI / 10 }, // 1 rotation per second = 2pi per 20 ticks = 2pi / 20 = pi / 10 per tick
|
||||||
axis = Y_AXIS,
|
axis = Y_AXIS,
|
||||||
timeVariable = { _, _, ageInTicks -> ageInTicks }
|
timeVariable = { _, _, ageInTicks -> ageInTicks }
|
||||||
|
),
|
||||||
|
rootPart.translation(
|
||||||
|
function = { t ->
|
||||||
|
if (t > 8) {
|
||||||
|
0F
|
||||||
|
} else {
|
||||||
|
-(8F - t) * 3F
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axis = Y_AXIS,
|
||||||
|
timeVariable = { _, _, ageInTicks -> ageInTicks }
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
transformedParts = arrayOf()
|
transformedParts = arrayOf()
|
||||||
@@ -38,15 +62,20 @@ class PokeBallModel(root: ModelPart) : PoseableEntityModel<PokeBallEntity>(), Po
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// This layer location should be baked with EntityRendererProvider.Context in the entity renderer and passed into this model's constructor
|
// This layer location should be baked with EntityRendererProvider.Context in the entity renderer and passed into this model's constructor
|
||||||
val LAYER_LOCATION = ModelLayerLocation(ResourceLocation("modid", "pokeball"), "main")
|
val LAYER_LOCATION = ModelLayerLocation(ResourceLocation(PokemonCobbled.MODID, "pokeball"), "main")
|
||||||
fun createBodyLayer(): LayerDefinition {
|
fun createBodyLayer(): LayerDefinition {
|
||||||
val meshdefinition = MeshDefinition()
|
val meshdefinition = MeshDefinition()
|
||||||
val partdefinition = meshdefinition.root
|
val partdefinition = meshdefinition.root
|
||||||
val pokeball = partdefinition.addOrReplaceChild(
|
val root = partdefinition.addOrReplaceChild(
|
||||||
|
"root",
|
||||||
|
CubeListBuilder.create(),
|
||||||
|
PartPose.offsetAndRotation(0.0f, 0.0f, 0.0f, PI, 0F, 0F)
|
||||||
|
)
|
||||||
|
val pokeball = root.addOrReplaceChild(
|
||||||
"pokeball",
|
"pokeball",
|
||||||
CubeListBuilder.create().texOffs(0, 0)
|
CubeListBuilder.create().texOffs(0, 0)
|
||||||
.addBox(-4.0f, -4.0f, -4.0f, 8.0f, 4.0f, 8.0f, CubeDeformation(0.0f)),
|
.addBox(-4.0f, -4.0f, -4.0f, 8.0f, 4.0f, 8.0f, CubeDeformation(0.0f)),
|
||||||
PartPose.offsetAndRotation(0.0f, 24.0f, 0.0f, PI, 0F, 0F)
|
PartPose.offsetAndRotation(0.0f, 0.0f, 0.0f, 0F, 0F, 0F)
|
||||||
)
|
)
|
||||||
val pokeball_lid = pokeball.addOrReplaceChild(
|
val pokeball_lid = pokeball.addOrReplaceChild(
|
||||||
"pokeball_lid",
|
"pokeball_lid",
|
||||||
@@ -58,5 +87,5 @@ class PokeBallModel(root: ModelPart) : PoseableEntityModel<PokeBallEntity>(), Po
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getState(entity: PokeBallEntity) = entity.delegate as PokeBallClientDelegate
|
override fun getState(entity: PokeBallEntity) = entity.delegate as PoseableEntityState<PokeBallEntity>
|
||||||
}
|
}
|
||||||
+57
-1
@@ -1,8 +1,11 @@
|
|||||||
package com.cablemc.pokemoncobbled.client.render.models.blockbench.wavefunction
|
package com.cablemc.pokemoncobbled.client.render.models.blockbench.wavefunction
|
||||||
|
|
||||||
import net.minecraft.util.Mth.PI
|
import net.minecraft.util.Mth.PI
|
||||||
|
import net.minecraft.util.Mth.sqrt
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
|
import kotlin.math.pow
|
||||||
|
import kotlin.math.sign
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
|
|
||||||
typealias WaveFunction = (Float) -> Float
|
typealias WaveFunction = (Float) -> Float
|
||||||
@@ -14,12 +17,65 @@ fun gradient(function: WaveFunction, t0: Float, t1: Float) = (function(t1) - fun
|
|||||||
operator fun WaveFunction.plus(other: WaveFunction): WaveFunction = { t -> this(t) + other(t) }
|
operator fun WaveFunction.plus(other: WaveFunction): WaveFunction = { t -> this(t) + other(t) }
|
||||||
operator fun WaveFunction.times(other: WaveFunction): WaveFunction = { t -> this(t) * other(t) }
|
operator fun WaveFunction.times(other: WaveFunction): WaveFunction = { t -> this(t) * other(t) }
|
||||||
fun WaveFunction.aggregate(func: WaveFunction): WaveFunction = { t -> func(this(t)) }
|
fun WaveFunction.aggregate(func: WaveFunction): WaveFunction = { t -> func(this(t)) }
|
||||||
|
fun linearFunction(gradient: Float = 1F, yIntercept: Float = 0F): WaveFunction = { t -> gradient * t + yIntercept }
|
||||||
fun sineFunction(amplitude: Float = 1F, period: Float = 1F, phaseShift: Float = 0F, verticalShift: Float = 0F): WaveFunction = { t -> sin(2*PI/period * (t - phaseShift)) * amplitude + verticalShift }
|
fun sineFunction(amplitude: Float = 1F, period: Float = 1F, phaseShift: Float = 0F, verticalShift: Float = 0F): WaveFunction = { t -> sin(2*PI/period * (t - phaseShift)) * amplitude + verticalShift }
|
||||||
fun cosineFunction(amplitude: Float = 1F, period: Float = 1F, phaseShift: Float = 0F, verticalShift: Float = 0F): WaveFunction = { t -> cos(2*PI/period * (t - phaseShift)) * amplitude + verticalShift }
|
fun cosineFunction(amplitude: Float = 1F, period: Float = 1F, phaseShift: Float = 0F, verticalShift: Float = 0F): WaveFunction = { t -> cos(2*PI/period * (t - phaseShift)) * amplitude + verticalShift }
|
||||||
fun triangleFunction(amplitude: Float = 1F, period: Float = 1F, phaseShift: Float = 0F, verticalShift: Float = 0F): WaveFunction = { t ->
|
fun triangleFunction(amplitude: Float = 1F, period: Float = 1F, phaseShift: Float = 0F, verticalShift: Float = 0F): WaveFunction = { t ->
|
||||||
val timeTerm = ((t + 3 * period / 4 - phaseShift) % period) - period/2
|
var time = t
|
||||||
|
while (time < 0) {
|
||||||
|
time += period
|
||||||
|
}
|
||||||
|
val timeTerm = ((time + 3 * period / 4 - phaseShift) % period) - period/2
|
||||||
val value = 4 * amplitude / period * abs(timeTerm) - amplitude + verticalShift
|
val value = 4 * amplitude / period * abs(timeTerm) - amplitude + verticalShift
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a parabola that intercepts the y axis at zero, intercepts the x axis again
|
||||||
|
* at [period], and at its furthest from y = 0 will reach [peak].
|
||||||
|
*
|
||||||
|
* The maths used to calculate this was a bit confusing but I've added my working to the
|
||||||
|
* docs/parabolaworking.pdf file in the Cobbled repository.
|
||||||
|
*/
|
||||||
|
fun parabolaFunction(
|
||||||
|
peak: Float,
|
||||||
|
period: Float
|
||||||
|
) = parabolaFunction(
|
||||||
|
tightness = -4 * peak / period.pow(2),
|
||||||
|
verticalShift = peak,
|
||||||
|
phaseShift = period / 2
|
||||||
|
)
|
||||||
|
|
||||||
|
fun parabolaFunction(
|
||||||
|
tightness: Float = -1F,
|
||||||
|
phaseShift: Float = 0F,
|
||||||
|
verticalShift: Float = 1F
|
||||||
|
): WaveFunction {
|
||||||
|
/*
|
||||||
|
* To understand the parameters for this, type y=t(x-p)+v into https://www.desmos.com/calculator and play
|
||||||
|
* around with tightness, phase shift, and vertical shift
|
||||||
|
*/
|
||||||
|
|
||||||
|
val a = tightness
|
||||||
|
val b = -2 * phaseShift * tightness
|
||||||
|
val c = tightness * phaseShift * phaseShift + verticalShift
|
||||||
|
|
||||||
|
val root1 = (-b - sqrt(b*b - 4 * a * c)) / (2 * tightness)
|
||||||
|
val root2 = (-b + sqrt(b*b - 4 * a * c)) / (2 * tightness)
|
||||||
|
val tMin = if (root1 < root2) root1 else root2
|
||||||
|
val tMax = if (root1 < root2) root2 else root1
|
||||||
|
val period = tMax - tMin
|
||||||
|
|
||||||
|
return { t ->
|
||||||
|
var time = t
|
||||||
|
while (time < tMin) {
|
||||||
|
time += period
|
||||||
|
}
|
||||||
|
while (time > tMax) {
|
||||||
|
time -= period
|
||||||
|
}
|
||||||
|
|
||||||
|
tightness * (time - phaseShift).pow(2) + verticalShift
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -30,9 +30,9 @@ class PokeBallRenderer<T : PokeBallEntity>(context: EntityRendererProvider.Conte
|
|||||||
) {
|
) {
|
||||||
val model = PokeBallModelRepository.getModel(pEntity.pokeBall).entityModel
|
val model = PokeBallModelRepository.getModel(pEntity.pokeBall).entityModel
|
||||||
pMatrixStack.pushPose()
|
pMatrixStack.pushPose()
|
||||||
pMatrixStack.translate(0.0, -1.0, 0.0)
|
pMatrixStack.scale(0.7F, 0.7F, 0.7F)
|
||||||
val vertexconsumer = ItemRenderer.getFoilBufferDirect(pBuffer, model.renderType(getTextureLocation(pEntity)), false, false)
|
val vertexconsumer = ItemRenderer.getFoilBufferDirect(pBuffer, model.renderType(getTextureLocation(pEntity)), false, false)
|
||||||
model.setupAnim(pEntity, 0f, 0f, pEntity.tickCount + pPartialTicks, pEntity.yRot, pEntity.xRot)
|
model.setupAnim(pEntity, 0f, 0f, pEntity.tickCount + pPartialTicks, pEntity.yRotO, pEntity.xRotO)
|
||||||
model.renderToBuffer(pMatrixStack, vertexconsumer, pPackedLight, OverlayTexture.NO_OVERLAY, 1.0f, 1.0f, 1.0f, 1.0f)
|
model.renderToBuffer(pMatrixStack, vertexconsumer, pPackedLight, OverlayTexture.NO_OVERLAY, 1.0f, 1.0f, 1.0f, 1.0f)
|
||||||
pMatrixStack.popPose()
|
pMatrixStack.popPose()
|
||||||
super.render(pEntity, pEntityYaw, pPartialTicks, pMatrixStack, pBuffer, pPackedLight)
|
super.render(pEntity, pEntityYaw, pPartialTicks, pMatrixStack, pBuffer, pPackedLight)
|
||||||
|
|||||||
-15
@@ -1,15 +0,0 @@
|
|||||||
package com.cablemc.pokemoncobbled.client.render.pokeball.animation
|
|
||||||
|
|
||||||
import net.minecraft.client.model.EntityModel
|
|
||||||
|
|
||||||
interface ModelAnimation<T : EntityModel<*>> {
|
|
||||||
|
|
||||||
/** The current frame the animation is on. If this is null, the animation is not currently running. */
|
|
||||||
val currentFrame: Int?
|
|
||||||
|
|
||||||
/** Animates the model. */
|
|
||||||
fun animate(model: T, limbSwing: Float, limbSwingAmout: Float, ageInTicks: Float, netHeadYaw: Float, headPitch: Float, frame: Int = 0)
|
|
||||||
|
|
||||||
fun resetAnimation()
|
|
||||||
|
|
||||||
}
|
|
||||||
+46
-16
@@ -4,18 +4,38 @@ import com.cablemc.pokemoncobbled.client.render.models.blockbench.PoseableEntity
|
|||||||
import com.cablemc.pokemoncobbled.client.render.models.blockbench.animation.StatefulAnimation
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.animation.StatefulAnimation
|
||||||
import com.cablemc.pokemoncobbled.client.render.models.blockbench.animation.StatelessAnimation
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.animation.StatelessAnimation
|
||||||
import com.cablemc.pokemoncobbled.client.render.models.blockbench.frame.PokeBallFrame
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.frame.PokeBallFrame
|
||||||
import com.cablemc.pokemoncobbled.common.entity.pokeball.PokeBallEntity
|
import com.cablemc.pokemoncobbled.common.entity.pokeball.EmptyPokeBallEntity
|
||||||
import com.cablemc.pokemoncobbled.common.util.math.geometry.toRadians
|
import com.cablemc.pokemoncobbled.common.util.math.geometry.toRadians
|
||||||
|
import com.cablemc.pokemoncobbled.common.util.math.toVec3
|
||||||
|
import net.minecraft.commands.arguments.EntityAnchorArgument
|
||||||
|
import net.minecraft.util.Mth
|
||||||
import net.minecraft.util.Mth.PI
|
import net.minecraft.util.Mth.PI
|
||||||
|
import net.minecraft.world.phys.Vec3
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
class OpenAnimation(frame: PokeBallFrame) : StatefulAnimation<PokeBallEntity, PokeBallFrame>(frame) {
|
/**
|
||||||
|
* Animation that opens a PokéBall upon hitting a Pokémon, and then shuts it.
|
||||||
|
*
|
||||||
|
* @author Hiroku
|
||||||
|
* @since December 24th, 2021
|
||||||
|
*/
|
||||||
|
class OpenAnimation : StatefulAnimation<EmptyPokeBallEntity, PokeBallFrame> {
|
||||||
companion object {
|
companion object {
|
||||||
const val DURATION_SECONDS = 3F
|
const val OPEN_START = 0.1F
|
||||||
|
const val OPEN_END = 0.3F
|
||||||
|
const val CLOSE_START = 1F
|
||||||
|
const val CLOSE_END = 1.2F
|
||||||
|
const val OPEN_ANGLE = PI / 3F
|
||||||
}
|
}
|
||||||
|
|
||||||
var initialized = false
|
var initialized = false
|
||||||
override fun preventsIdle(entity: PokeBallEntity, idleAnimation: StatelessAnimation<PokeBallEntity, *>) = true
|
var startedClosing = false
|
||||||
override fun run(entity: PokeBallEntity, model: PoseableEntityModel<PokeBallEntity>): Boolean {
|
var maxPitch = 0F
|
||||||
|
|
||||||
|
override fun preventsIdle(entity: EmptyPokeBallEntity, idleAnimation: StatelessAnimation<EmptyPokeBallEntity, *>) = true
|
||||||
|
override fun run(entity: EmptyPokeBallEntity, model: PoseableEntityModel<EmptyPokeBallEntity>): Boolean {
|
||||||
|
val frame = model as PokeBallFrame
|
||||||
|
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
model.getState(entity).animationSeconds = 0F
|
model.getState(entity).animationSeconds = 0F
|
||||||
initialized = true
|
initialized = true
|
||||||
@@ -23,19 +43,29 @@ class OpenAnimation(frame: PokeBallFrame) : StatefulAnimation<PokeBallEntity, Po
|
|||||||
|
|
||||||
val animationSeconds = model.getState(entity).animationSeconds
|
val animationSeconds = model.getState(entity).animationSeconds
|
||||||
|
|
||||||
when {
|
entity.lookAt(EntityAnchorArgument.Anchor.EYES, entity.hitTargetPosition.get().toVec3())
|
||||||
animationSeconds <= 1 -> {
|
val xDist = entity.hitTargetPosition.get().x - entity.x
|
||||||
frame.lid.xRot = -animationSeconds * PI / 3
|
val zDist = entity.hitTargetPosition.get().z - entity.z
|
||||||
}
|
frame.rootPart.yRot = Mth.atan2(-zDist, xDist).toFloat() + PI / 2
|
||||||
animationSeconds > 2 -> {
|
frame.rootPart.xRot = -entity.xRotO.toRadians() - PI / 2
|
||||||
frame.lid.xRot = -30f.toRadians() + ((animationSeconds - 2) * (1.5f).toRadians()) / 20F
|
|
||||||
}
|
val minPitch = -PI
|
||||||
else -> {
|
|
||||||
frame.lid.xRot = 30f.toRadians()
|
if (animationSeconds >= OPEN_START && animationSeconds < CLOSE_START) {
|
||||||
return false
|
val portion = min((animationSeconds - OPEN_START)/OPEN_END, 1F)
|
||||||
|
frame.lid.xRot = -portion * OPEN_ANGLE
|
||||||
|
} else if (animationSeconds >= CLOSE_START) {
|
||||||
|
if (!startedClosing) {
|
||||||
|
startedClosing = true
|
||||||
|
maxPitch = frame.rootPart.xRot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val portion = min((animationSeconds - CLOSE_START) / (CLOSE_END - CLOSE_START), 1F)
|
||||||
|
frame.lid.xRot = (portion - 1) * OPEN_ANGLE
|
||||||
|
val dist = maxPitch - minPitch
|
||||||
|
frame.rootPart.xRot = minPitch + (1 - portion) * dist
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return entity.captureState.get() == EmptyPokeBallEntity.CaptureState.HIT.ordinal.toByte()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+42
-38
@@ -1,47 +1,51 @@
|
|||||||
package com.cablemc.pokemoncobbled.client.render.pokeball.animation
|
package com.cablemc.pokemoncobbled.client.render.pokeball.animation
|
||||||
|
|
||||||
import com.cablemc.pokemoncobbled.client.render.models.blockbench.pokeball.PokeBallModel
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.PoseableEntityModel
|
||||||
import com.cablemc.pokemoncobbled.common.util.math.geometry.toRadians
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.addRotation
|
||||||
import net.minecraft.util.Mth
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.animation.StatefulAnimation
|
||||||
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.animation.StatelessAnimation
|
||||||
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.frame.PokeBallFrame
|
||||||
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.pose.TransformedModelPart.Companion.Z_AXIS
|
||||||
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.wavefunction.linearFunction
|
||||||
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.wavefunction.sineFunction
|
||||||
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.wavefunction.times
|
||||||
|
import com.cablemc.pokemoncobbled.common.entity.pokeball.EmptyPokeBallEntity
|
||||||
|
import net.minecraft.util.Mth.PI
|
||||||
|
|
||||||
class ShakeAnimation(val numShakes: Int) : ModelAnimation<PokeBallModel> {
|
/**
|
||||||
|
* Animation that shakes a PokéBall on the specified axis, with the specified force.
|
||||||
|
* This will move it along that axis along a sine function whose highest amplitude is
|
||||||
|
* the force multiplied by PI / 3, meaning at force 1 it rotates 60 degrees to either
|
||||||
|
* side. This shaking will be dampened to nothing over the course of 1 second.
|
||||||
|
*
|
||||||
|
* @author Hiroku
|
||||||
|
* @since December 25th, 2021
|
||||||
|
*/
|
||||||
|
class ShakeAnimation(
|
||||||
|
val force: Float,
|
||||||
|
val axis: Int = Z_AXIS
|
||||||
|
) : StatefulAnimation<EmptyPokeBallEntity, PokeBallFrame> {
|
||||||
|
val shakeFunction = sineFunction(
|
||||||
|
amplitude = force * PI / 3,
|
||||||
|
period = 0.7F
|
||||||
|
) * linearFunction(
|
||||||
|
gradient = -1F,
|
||||||
|
yIntercept = 1F
|
||||||
|
)
|
||||||
|
|
||||||
override var currentFrame: Int? = null
|
var initialized = false
|
||||||
private set
|
|
||||||
private var shakesComplete = 0
|
|
||||||
|
|
||||||
override fun animate(
|
override fun preventsIdle(entity: EmptyPokeBallEntity, idleAnimation: StatelessAnimation<EmptyPokeBallEntity, *>) = false
|
||||||
model: PokeBallModel,
|
override fun run(entity: EmptyPokeBallEntity, model: PoseableEntityModel<EmptyPokeBallEntity>): Boolean {
|
||||||
limbSwing: Float,
|
val frame = model as PokeBallFrame
|
||||||
limbSwingAmout: Float,
|
val state = model.getState(entity)
|
||||||
ageInTicks: Float,
|
if (!initialized) {
|
||||||
netHeadYaw: Float,
|
state.animationSeconds = 0F
|
||||||
headPitch: Float,
|
initialized = true
|
||||||
frame: Int
|
|
||||||
) {
|
|
||||||
currentFrame = frame % 63
|
|
||||||
model.rootPart.y = 20f
|
|
||||||
model.rootPart.xRot = 180f.toRadians()
|
|
||||||
model.rootPart.yRot = 0f
|
|
||||||
model.lid.xRot = 0f
|
|
||||||
if (shakesComplete < numShakes) {
|
|
||||||
if (currentFrame == 62) {
|
|
||||||
currentFrame = 0
|
|
||||||
shakesComplete++
|
|
||||||
}
|
|
||||||
if (currentFrame!! <= 42) {
|
|
||||||
model.rootPart.zRot = Mth.sin(currentFrame!!.toFloat() * 0.15f) * 30f.toRadians()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
model.rootPart.zRot = 0f
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
model.rootPart.zRot = 0f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun resetAnimation() {
|
frame.subRoot.addRotation(axis = axis, differenceInRadians = shakeFunction(state.animationSeconds))
|
||||||
currentFrame = 0
|
|
||||||
|
return state.animationSeconds < 1F
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+4
-3
@@ -5,6 +5,7 @@ import com.cablemc.pokemoncobbled.common.api.pokemon.stats.Stats
|
|||||||
import com.cablemc.pokemoncobbled.common.pokeball.PokeBall
|
import com.cablemc.pokemoncobbled.common.pokeball.PokeBall
|
||||||
import com.cablemc.pokemoncobbled.common.pokemon.Pokemon
|
import com.cablemc.pokemoncobbled.common.pokemon.Pokemon
|
||||||
import net.minecraft.server.level.ServerPlayer
|
import net.minecraft.server.level.ServerPlayer
|
||||||
|
import kotlin.math.pow
|
||||||
import kotlin.random.Random.Default.nextInt
|
import kotlin.random.Random.Default.nextInt
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -22,7 +23,7 @@ class Gen7CaptureCalculator : CaptureCalculator {
|
|||||||
return CaptureContext(isSuccessfulCapture = true, isCriticalCapture = true, numberOfShakes = 1)
|
return CaptureContext(isSuccessfulCapture = true, isCriticalCapture = true, numberOfShakes = 1)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val shakeProbability = (25536 / Math.pow(255 / catchRate.toDouble(), 3.0 / 16))
|
val shakeProbability = (25536 / (255 / catchRate.toDouble()).pow(3.0 / 16))
|
||||||
|
|
||||||
var numShakes = 0
|
var numShakes = 0
|
||||||
for (i in 0..3) {
|
for (i in 0..3) {
|
||||||
@@ -31,13 +32,13 @@ class Gen7CaptureCalculator : CaptureCalculator {
|
|||||||
}
|
}
|
||||||
numShakes++
|
numShakes++
|
||||||
}
|
}
|
||||||
return CaptureContext(isSuccessfulCapture = true, isCriticalCapture = false, numberOfShakes = numShakes)
|
return CaptureContext(isSuccessfulCapture = numShakes == 4, isCriticalCapture = false, numberOfShakes = numShakes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCatchRate(player: ServerPlayer, pokemon: Pokemon, pokeBall: PokeBall): Float {
|
fun getCatchRate(player: ServerPlayer, pokemon: Pokemon, pokeBall: PokeBall): Float {
|
||||||
var catchRate = pokemon.species.catchRate.toFloat()
|
var catchRate = pokemon.species.catchRate.toFloat()
|
||||||
pokeBall.catchRateModifiers.forEach { modifier -> catchRate = modifier.modifyCatchRate(catchRate, player, pokemon) }
|
pokeBall.catchRateModifiers.forEach { catchRate = it.modifyCatchRate(catchRate, player, pokemon) }
|
||||||
val maxHealth = pokemon.getMaxHealth()
|
val maxHealth = pokemon.getMaxHealth()
|
||||||
val currentHealth = pokemon.health
|
val currentHealth = pokemon.health
|
||||||
val statusBonus = getStatusBonus(pokemon)
|
val statusBonus = getStatusBonus(pokemon)
|
||||||
|
|||||||
+16
@@ -1,6 +1,7 @@
|
|||||||
package com.cablemc.pokemoncobbled.common.api.scheduling
|
package com.cablemc.pokemoncobbled.common.api.scheduling
|
||||||
|
|
||||||
import net.minecraft.util.Mth.ceil
|
import net.minecraft.util.Mth.ceil
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
fun after(ticks: Int = 0, seconds: Float = 0F, action: () -> Unit) {
|
fun after(ticks: Int = 0, seconds: Float = 0F, action: () -> Unit) {
|
||||||
ScheduledTaskTracker.addTask(
|
ScheduledTaskTracker.addTask(
|
||||||
@@ -11,4 +12,19 @@ fun after(ticks: Int = 0, seconds: Float = 0F, action: () -> Unit) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun lerp(ticks: Int, action: (Float) -> Unit) {
|
||||||
|
var tick = 0F
|
||||||
|
action(tick / ticks)
|
||||||
|
taskBuilder().interval(1).iterations(Int.MAX_VALUE).execute { task ->
|
||||||
|
tick++
|
||||||
|
action(tick / ticks)
|
||||||
|
if (tick >= ticks) {
|
||||||
|
task.expire()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun lerp(seconds: Float, action: (Float) -> Unit) = lerp(ticks = (seconds * 20F).roundToInt(), action)
|
||||||
|
|
||||||
|
|
||||||
fun taskBuilder() = ScheduledTask.Builder()
|
fun taskBuilder() = ScheduledTask.Builder()
|
||||||
-63
@@ -1,63 +0,0 @@
|
|||||||
package com.cablemc.pokemoncobbled.common.entity.animation
|
|
||||||
|
|
||||||
import com.cablemc.pokemoncobbled.client.render.pokeball.animation.ModelAnimation
|
|
||||||
import net.minecraft.client.model.EntityModel
|
|
||||||
import net.minecraft.world.entity.Entity
|
|
||||||
import net.minecraftforge.eventbus.api.EventPriority
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Controls which animation an entity is currently running for it's model.
|
|
||||||
* The idea here is to register animations with a predicate, to determine when the animation should run.
|
|
||||||
* Any time this is ticked, it will determine if the animation should change or continue based on the predicates.
|
|
||||||
*
|
|
||||||
* This likely will need to be expanded upon in the future or refactored.
|
|
||||||
*
|
|
||||||
* @author landonjw
|
|
||||||
* @since December 3, 2021
|
|
||||||
*/
|
|
||||||
class AnimationController<T : Entity> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Animations registered in the controller based on priority of the animation.
|
|
||||||
* Low priorities should be for animations that will act as default animations, or animations that will very
|
|
||||||
* commonly run.
|
|
||||||
*/
|
|
||||||
private var animationsByPriority = Array(EventPriority.values().size) { mutableListOf<AnimationRegistry<T, ModelAnimation<out EntityModel<T>>>>() }
|
|
||||||
|
|
||||||
/** The current animation that is running. If this is null, there is currently no animation for the model and it will t-pose. */
|
|
||||||
var currentAnimation: ModelAnimation<out EntityModel<T>>? = null
|
|
||||||
private set
|
|
||||||
private var currentAnimationFrame = 0
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ticks the animation controller for a given entity.
|
|
||||||
* If given an animation, it will force the animation onto the entity, ticking it if necessary.
|
|
||||||
* Otherwise, this will determine the appropriate animation based on the priority sets.
|
|
||||||
*/
|
|
||||||
fun tick(entity: T, animation: ModelAnimation<EntityModel<T>>? = null) {
|
|
||||||
val animationToRun = animation ?: getAnimationFromPrioritySet(entity)
|
|
||||||
if (currentAnimation == animationToRun && animationToRun != null) {
|
|
||||||
currentAnimationFrame++
|
|
||||||
} else {
|
|
||||||
currentAnimationFrame = 0
|
|
||||||
currentAnimation = animationToRun
|
|
||||||
}
|
|
||||||
// TODO: Run the animation frame
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getAnimationFromPrioritySet(entity: T): ModelAnimation<out EntityModel<T>>? {
|
|
||||||
for (prioritySet in animationsByPriority) {
|
|
||||||
for (animationRegistry in prioritySet) {
|
|
||||||
if (animationRegistry.predicate(entity)) {
|
|
||||||
return animationRegistry.animation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <M: EntityModel<T>, A : ModelAnimation<M>> registerAnimation(priority: EventPriority, animation: A, animationPredicate: (T) -> Boolean) {
|
|
||||||
animationsByPriority[priority.ordinal].add(AnimationRegistry(animationPredicate, animation))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
package com.cablemc.pokemoncobbled.common.entity.animation
|
|
||||||
|
|
||||||
import com.cablemc.pokemoncobbled.client.render.pokeball.animation.ModelAnimation
|
|
||||||
import net.minecraft.client.model.EntityModel
|
|
||||||
import net.minecraft.world.entity.Entity
|
|
||||||
|
|
||||||
data class AnimationRegistry<T: Entity, A: ModelAnimation<out EntityModel<T>>>(
|
|
||||||
/** Determines if the animation can run or not. For example, a swim animation should only run if the entity is in water. */
|
|
||||||
val predicate: (T) -> Boolean,
|
|
||||||
val animation: A
|
|
||||||
)
|
|
||||||
+118
-20
@@ -2,16 +2,24 @@ package com.cablemc.pokemoncobbled.common.entity.pokeball
|
|||||||
|
|
||||||
import com.cablemc.pokemoncobbled.common.api.pokeball.PokeBalls
|
import com.cablemc.pokemoncobbled.common.api.pokeball.PokeBalls
|
||||||
import com.cablemc.pokemoncobbled.common.api.scheduling.after
|
import com.cablemc.pokemoncobbled.common.api.scheduling.after
|
||||||
|
import com.cablemc.pokemoncobbled.common.api.scheduling.taskBuilder
|
||||||
|
import com.cablemc.pokemoncobbled.common.api.storage.PokemonStoreManager
|
||||||
import com.cablemc.pokemoncobbled.common.entity.EntityRegistry
|
import com.cablemc.pokemoncobbled.common.entity.EntityRegistry
|
||||||
import com.cablemc.pokemoncobbled.common.entity.pokemon.PokemonEntity
|
import com.cablemc.pokemoncobbled.common.entity.pokemon.PokemonEntity
|
||||||
import com.cablemc.pokemoncobbled.common.pokeball.PokeBall
|
import com.cablemc.pokemoncobbled.common.pokeball.PokeBall
|
||||||
|
import com.cablemc.pokemoncobbled.common.util.isServerSide
|
||||||
|
import com.cablemc.pokemoncobbled.common.util.math.toRotations
|
||||||
|
import com.cablemc.pokemoncobbled.mod.PokemonCobbledMod
|
||||||
import net.minecraft.core.Rotations
|
import net.minecraft.core.Rotations
|
||||||
import net.minecraft.network.syncher.EntityDataSerializers
|
import net.minecraft.network.syncher.EntityDataSerializers
|
||||||
import net.minecraft.network.syncher.SynchedEntityData
|
import net.minecraft.network.syncher.SynchedEntityData
|
||||||
|
import net.minecraft.server.level.ServerPlayer
|
||||||
|
import net.minecraft.util.Mth.PI
|
||||||
import net.minecraft.world.entity.EntityType
|
import net.minecraft.world.entity.EntityType
|
||||||
import net.minecraft.world.level.Level
|
import net.minecraft.world.level.Level
|
||||||
import net.minecraft.world.phys.BlockHitResult
|
import net.minecraft.world.phys.BlockHitResult
|
||||||
import net.minecraft.world.phys.EntityHitResult
|
import net.minecraft.world.phys.EntityHitResult
|
||||||
|
import net.minecraft.world.phys.Vec3
|
||||||
|
|
||||||
class EmptyPokeBallEntity(
|
class EmptyPokeBallEntity(
|
||||||
pokeBall: PokeBall,
|
pokeBall: PokeBall,
|
||||||
@@ -28,20 +36,31 @@ class EmptyPokeBallEntity(
|
|||||||
companion object {
|
companion object {
|
||||||
private val CAPTURE_STATE = SynchedEntityData.defineId(EmptyPokeBallEntity::class.java, EntityDataSerializers.BYTE)
|
private val CAPTURE_STATE = SynchedEntityData.defineId(EmptyPokeBallEntity::class.java, EntityDataSerializers.BYTE)
|
||||||
private val HIT_TARGET_POSITION = SynchedEntityData.defineId(EmptyPokeBallEntity::class.java, EntityDataSerializers.ROTATIONS)
|
private val HIT_TARGET_POSITION = SynchedEntityData.defineId(EmptyPokeBallEntity::class.java, EntityDataSerializers.ROTATIONS)
|
||||||
|
private val HIT_VELOCITY = SynchedEntityData.defineId(EmptyPokeBallEntity::class.java, EntityDataSerializers.ROTATIONS)
|
||||||
|
private val SHAKE = SynchedEntityData.defineId(EmptyPokeBallEntity::class.java, EntityDataSerializers.BOOLEAN)
|
||||||
|
|
||||||
|
const val TICKS_BETWEEN_SHAKES = 40
|
||||||
|
const val TICKS_BEFORE_SHAKE = 10
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var capturingPokemon: PokemonEntity? = null
|
||||||
val captureState = addEntityProperty(CAPTURE_STATE, CaptureState.NOT.ordinal.toByte())
|
val captureState = addEntityProperty(CAPTURE_STATE, CaptureState.NOT.ordinal.toByte())
|
||||||
val hitTargetPosition = addEntityProperty(HIT_TARGET_POSITION, Rotations(0F, 0F, 0F))
|
val hitTargetPosition = addEntityProperty(HIT_TARGET_POSITION, Rotations(0F, 0F, 0F))
|
||||||
|
val hitVelocity = addEntityProperty(HIT_VELOCITY, Rotations(0F, 0F, 0F))
|
||||||
|
val shakeEmitter = addEntityProperty(SHAKE, false)
|
||||||
|
|
||||||
|
override val delegate = if (level.isClientSide) {
|
||||||
|
com.cablemc.pokemoncobbled.client.entity.EmptyPokeBallClientDelegate()
|
||||||
|
} else {
|
||||||
|
EmptyPokeBallServerDelegate()
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
delegate.initialize(this)
|
delegate.initialize(this)
|
||||||
captureState.subscribe { state ->
|
captureState.subscribe { state ->
|
||||||
when (CaptureState.values()[state.toInt()]) {
|
when (CaptureState.values()[state.toInt()]) {
|
||||||
CaptureState.NOT -> isNoGravity = false
|
CaptureState.NOT -> isNoGravity = false
|
||||||
CaptureState.HIT -> {
|
CaptureState.HIT -> {}
|
||||||
isNoGravity = true
|
|
||||||
setDeltaMovement(0.0, 0.0, 0.0)
|
|
||||||
}
|
|
||||||
CaptureState.FALL -> isNoGravity = false
|
CaptureState.FALL -> isNoGravity = false
|
||||||
CaptureState.SHAKE -> isNoGravity = false
|
CaptureState.SHAKE -> isNoGravity = false
|
||||||
}
|
}
|
||||||
@@ -51,42 +70,121 @@ class EmptyPokeBallEntity(
|
|||||||
private var isAttemptingCatch = false
|
private var isAttemptingCatch = false
|
||||||
|
|
||||||
constructor(entityType: EntityType<out EmptyPokeBallEntity>, level: Level) : this(PokeBalls.POKE_BALL, entityType, level)
|
constructor(entityType: EntityType<out EmptyPokeBallEntity>, level: Level) : this(PokeBalls.POKE_BALL, entityType, level)
|
||||||
|
|
||||||
constructor(pokeBall: PokeBall, level: Level) : this(pokeBall, EntityRegistry.EMPTY_POKEBALL.get(), level)
|
constructor(pokeBall: PokeBall, level: Level) : this(pokeBall, EntityRegistry.EMPTY_POKEBALL.get(), level)
|
||||||
|
|
||||||
override fun onHitBlock(hitResult: BlockHitResult) {
|
override fun onHitBlock(hitResult: BlockHitResult) {
|
||||||
super.onHitBlock(hitResult)
|
if (captureState.get() == CaptureState.NOT.ordinal.toByte()) {
|
||||||
kill()
|
if (level.isServerSide()) {
|
||||||
spawnAtLocation(defaultItem)
|
super.onHitBlock(hitResult)
|
||||||
|
kill()
|
||||||
|
spawnAtLocation(defaultItem)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isNoGravity = false
|
||||||
|
deltaMovement = Vec3.ZERO
|
||||||
|
isOnGround = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onHitEntity(hitResult: EntityHitResult) {
|
override fun onHitEntity(hitResult: EntityHitResult) {
|
||||||
if (hitResult.entity is PokemonEntity) {
|
if (captureState.get() == CaptureState.NOT.ordinal.toByte()) {
|
||||||
attemptCatch(hitResult.entity as PokemonEntity)
|
if (hitResult.entity is PokemonEntity && level.isServerSide()) {
|
||||||
|
val pokemon = hitResult.entity as PokemonEntity
|
||||||
|
if (pokemon.isBeingCaptured || !pokemon.pokemon.isWild()) {
|
||||||
|
kill()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
capturingPokemon = pokemon
|
||||||
|
hitVelocity.set(deltaMovement.normalize().toRotations())
|
||||||
|
hitTargetPosition.set(hitResult.location.toRotations())
|
||||||
|
attemptCatch(pokemon)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
super.onHitEntity(hitResult)
|
super.onHitEntity(hitResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun tick() {
|
override fun tick() {
|
||||||
delegate.tick(this)
|
delegate.tick(this)
|
||||||
// if (isAttemptingCatch) {
|
|
||||||
// setDeltaMovement(0.0, 0.0, 0.0)
|
|
||||||
// when {
|
|
||||||
// currentAnimation is OpenAnimation && (currentAnimation as OpenAnimation).isComplete() -> {
|
|
||||||
// currentAnimation = ShakeAnimation(3)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
super.tick()
|
super.tick()
|
||||||
|
if (level.isServerSide()) {
|
||||||
|
if (owner == null || !owner!!.isAlive || (captureState.get() != CaptureState.NOT.ordinal.toByte() && capturingPokemon?.isAlive != true)) {
|
||||||
|
kill()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOnGround && captureState.get() == CaptureState.FALL.ordinal.toByte()) {
|
||||||
|
captureState.set(CaptureState.SHAKE.ordinal.toByte())
|
||||||
|
|
||||||
|
val captureResult = PokemonCobbledMod.captureCalculator.processCapture(owner as ServerPlayer, capturingPokemon!!.pokemon, pokeBall)
|
||||||
|
|
||||||
|
var rollsRemaining = captureResult.numberOfShakes
|
||||||
|
if (rollsRemaining == 4) {
|
||||||
|
rollsRemaining--
|
||||||
|
}
|
||||||
|
|
||||||
|
taskBuilder()
|
||||||
|
.iterations(captureResult.numberOfShakes + 1)
|
||||||
|
.delay(TICKS_BEFORE_SHAKE)
|
||||||
|
.interval(TICKS_BETWEEN_SHAKES)
|
||||||
|
.execute {
|
||||||
|
if (capturingPokemon?.isAlive != true) {
|
||||||
|
kill()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isAlive) {
|
||||||
|
it.expire()
|
||||||
|
return@execute
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rollsRemaining <= 0) {
|
||||||
|
if (captureResult.isSuccessfulCapture) {
|
||||||
|
// Do a capture
|
||||||
|
capturingPokemon!!.kill()
|
||||||
|
val party = PokemonStoreManager.getParty(this.owner!!.uuid)
|
||||||
|
party.add(capturingPokemon!!.pokemon)
|
||||||
|
} else {
|
||||||
|
breakFree()
|
||||||
|
}
|
||||||
|
return@execute
|
||||||
|
}
|
||||||
|
|
||||||
|
rollsRemaining--
|
||||||
|
shakeEmitter.set(!shakeEmitter.get())
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun breakFree() {
|
||||||
|
capturingPokemon!!.setPos(position())
|
||||||
|
capturingPokemon!!.isInvisible = false
|
||||||
|
capturingPokemon!!.isBeingCaptured = false
|
||||||
|
kill()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun attemptCatch(pokemonEntity: PokemonEntity) {
|
private fun attemptCatch(pokemonEntity: PokemonEntity) {
|
||||||
// TODO: Validate pokemon is not owned by a player
|
// TODO: Validate pokemon is not owned by a player
|
||||||
if (!isAttemptingCatch) {
|
if (!isAttemptingCatch) {
|
||||||
isAttemptingCatch = true
|
isAttemptingCatch = true
|
||||||
|
pokemonEntity.isBeingCaptured = true
|
||||||
|
val displace = deltaMovement
|
||||||
captureState.set(CaptureState.HIT.ordinal.toByte())
|
captureState.set(CaptureState.HIT.ordinal.toByte())
|
||||||
pokemonEntity.isInvisible = true // TODO this is not the way
|
val mul = if (random.nextBoolean()) 1 else -1
|
||||||
after(seconds = 2F) { captureState.set(CaptureState.FALL.ordinal.toByte()) }
|
deltaMovement = displace.multiply(-1.0, 0.0, -1.0).normalize().yRot(mul * PI/3).multiply(0.1, 0.0, 0.1).add(0.0, 1.0 / 3, 0.0)
|
||||||
|
after(seconds = 0.7F) {
|
||||||
|
deltaMovement = Vec3.ZERO
|
||||||
|
isNoGravity = true
|
||||||
|
}
|
||||||
|
|
||||||
|
after(seconds = 2.2F) {
|
||||||
|
pokemonEntity.isInvisible = true
|
||||||
|
pokemonEntity.setPos(position())
|
||||||
|
// TODO can we turn off collision or something
|
||||||
|
captureState.set(CaptureState.FALL.ordinal.toByte())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
package com.cablemc.pokemoncobbled.common.entity.pokeball
|
||||||
|
|
||||||
|
class EmptyPokeBallServerDelegate : PokeBallDelegate<EmptyPokeBallEntity> {
|
||||||
|
}
|
||||||
+6
@@ -13,6 +13,12 @@ class OccupiedPokeBallEntity(
|
|||||||
entityType: EntityType<out OccupiedPokeBallEntity>,
|
entityType: EntityType<out OccupiedPokeBallEntity>,
|
||||||
level: Level
|
level: Level
|
||||||
) : PokeBallEntity(pokeBall, entityType, level) {
|
) : PokeBallEntity(pokeBall, entityType, level) {
|
||||||
|
override val delegate = if (level.isClientSide) {
|
||||||
|
com.cablemc.pokemoncobbled.client.entity.OccupiedPokeBallClientDelegate()
|
||||||
|
} else {
|
||||||
|
OccupiedPokeBallServerDelegate()
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
delegate.initialize(this)
|
delegate.initialize(this)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
package com.cablemc.pokemoncobbled.common.entity.pokeball
|
||||||
|
|
||||||
|
class OccupiedPokeBallServerDelegate : PokeBallDelegate<OccupiedPokeBallEntity>
|
||||||
+1
-3
@@ -2,6 +2,4 @@ package com.cablemc.pokemoncobbled.common.entity.pokeball
|
|||||||
|
|
||||||
import com.cablemc.pokemoncobbled.common.api.entity.EntitySideDelegate
|
import com.cablemc.pokemoncobbled.common.api.entity.EntitySideDelegate
|
||||||
|
|
||||||
class PokeBallServerDelegate : EntitySideDelegate<PokeBallEntity> {
|
interface PokeBallDelegate<T : PokeBallEntity> : EntitySideDelegate<T>
|
||||||
|
|
||||||
}
|
|
||||||
+5
-13
@@ -1,13 +1,13 @@
|
|||||||
package com.cablemc.pokemoncobbled.common.entity.pokeball
|
package com.cablemc.pokemoncobbled.common.entity.pokeball
|
||||||
|
|
||||||
import com.cablemc.pokemoncobbled.client.render.models.blockbench.pokeball.PokeBallModel
|
|
||||||
import com.cablemc.pokemoncobbled.client.render.pokeball.animation.ModelAnimation
|
|
||||||
import com.cablemc.pokemoncobbled.common.entity.EntityProperty
|
import com.cablemc.pokemoncobbled.common.entity.EntityProperty
|
||||||
import com.cablemc.pokemoncobbled.common.item.ItemRegistry
|
import com.cablemc.pokemoncobbled.common.item.ItemRegistry
|
||||||
import com.cablemc.pokemoncobbled.common.pokeball.PokeBall
|
import com.cablemc.pokemoncobbled.common.pokeball.PokeBall
|
||||||
import net.minecraft.network.protocol.Packet
|
import net.minecraft.network.protocol.Packet
|
||||||
import net.minecraft.network.syncher.EntityDataAccessor
|
import net.minecraft.network.syncher.EntityDataAccessor
|
||||||
|
import net.minecraft.world.entity.EntityDimensions
|
||||||
import net.minecraft.world.entity.EntityType
|
import net.minecraft.world.entity.EntityType
|
||||||
|
import net.minecraft.world.entity.Pose
|
||||||
import net.minecraft.world.entity.projectile.ThrowableItemProjectile
|
import net.minecraft.world.entity.projectile.ThrowableItemProjectile
|
||||||
import net.minecraft.world.item.Item
|
import net.minecraft.world.item.Item
|
||||||
import net.minecraft.world.level.Level
|
import net.minecraft.world.level.Level
|
||||||
@@ -18,18 +18,10 @@ abstract class PokeBallEntity(
|
|||||||
entityType: EntityType<out PokeBallEntity>,
|
entityType: EntityType<out PokeBallEntity>,
|
||||||
level: Level
|
level: Level
|
||||||
) : ThrowableItemProjectile(entityType, level) {
|
) : ThrowableItemProjectile(entityType, level) {
|
||||||
|
val DIMENSIONS = EntityDimensions(0.4F, 0.4F, true)
|
||||||
val entityProperties = mutableListOf<EntityProperty<*>>()
|
val entityProperties = mutableListOf<EntityProperty<*>>()
|
||||||
|
|
||||||
var currentAnimation: ModelAnimation<PokeBallModel>? = null
|
abstract val delegate: PokeBallDelegate<out PokeBallEntity>
|
||||||
protected set
|
|
||||||
|
|
||||||
val delegate = if (level.isClientSide) {
|
|
||||||
// Don't import because scanning for imports is a CI job we'll do later to detect errant access to client from server
|
|
||||||
com.cablemc.pokemoncobbled.client.entity.PokeBallClientDelegate()
|
|
||||||
} else {
|
|
||||||
PokeBallServerDelegate()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getDefaultItem(): Item = ItemRegistry.POKE_BALL.get()
|
override fun getDefaultItem(): Item = ItemRegistry.POKE_BALL.get()
|
||||||
|
|
||||||
@@ -39,10 +31,10 @@ abstract class PokeBallEntity(
|
|||||||
|
|
||||||
override fun tick() {
|
override fun tick() {
|
||||||
super.tick()
|
super.tick()
|
||||||
delegate.tick(this)
|
|
||||||
entityProperties.forEach { it.checkForUpdate() }
|
entityProperties.forEach { it.checkForUpdate() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getDimensions(pPose: Pose) = DIMENSIONS
|
||||||
fun <T> addEntityProperty(accessor: EntityDataAccessor<T>, initialValue: T): EntityProperty<T> {
|
fun <T> addEntityProperty(accessor: EntityDataAccessor<T>, initialValue: T): EntityProperty<T> {
|
||||||
val property = EntityProperty(
|
val property = EntityProperty(
|
||||||
entityData = entityData,
|
entityData = entityData,
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ class PokemonEntity(
|
|||||||
PokemonServerDelegate()
|
PokemonServerDelegate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isBeingCaptured = false
|
||||||
|
|
||||||
val entityProperties = mutableListOf<EntityProperty<*>>()
|
val entityProperties = mutableListOf<EntityProperty<*>>()
|
||||||
|
|
||||||
val dexNumber = addEntityProperty(SPECIES_DEX, pokemon.species.nationalPokedexNumber)
|
val dexNumber = addEntityProperty(SPECIES_DEX, pokemon.species.nationalPokedexNumber)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.cablemc.pokemoncobbled.common.item
|
|||||||
import com.cablemc.pokemoncobbled.common.entity.pokeball.EmptyPokeBallEntity
|
import com.cablemc.pokemoncobbled.common.entity.pokeball.EmptyPokeBallEntity
|
||||||
import com.cablemc.pokemoncobbled.common.item.creativetabs.PokeBallTab
|
import com.cablemc.pokemoncobbled.common.item.creativetabs.PokeBallTab
|
||||||
import com.cablemc.pokemoncobbled.common.pokeball.PokeBall
|
import com.cablemc.pokemoncobbled.common.pokeball.PokeBall
|
||||||
|
import net.minecraft.server.level.ServerPlayer
|
||||||
import net.minecraft.world.InteractionHand
|
import net.minecraft.world.InteractionHand
|
||||||
import net.minecraft.world.InteractionResultHolder
|
import net.minecraft.world.InteractionResultHolder
|
||||||
import net.minecraft.world.entity.player.Player
|
import net.minecraft.world.entity.player.Player
|
||||||
@@ -16,7 +17,7 @@ class PokeBallItem(
|
|||||||
override fun use(level: Level, player: Player, usedHand: InteractionHand): InteractionResultHolder<ItemStack> {
|
override fun use(level: Level, player: Player, usedHand: InteractionHand): InteractionResultHolder<ItemStack> {
|
||||||
val itemStack = player.getItemInHand(usedHand)
|
val itemStack = player.getItemInHand(usedHand)
|
||||||
if (!level.isClientSide) {
|
if (!level.isClientSide) {
|
||||||
throwPokeBall(level, player)
|
throwPokeBall(level, player as ServerPlayer)
|
||||||
}
|
}
|
||||||
if (!player.abilities.instabuild) {
|
if (!player.abilities.instabuild) {
|
||||||
itemStack.shrink(1)
|
itemStack.shrink(1)
|
||||||
@@ -24,12 +25,12 @@ class PokeBallItem(
|
|||||||
return InteractionResultHolder.sidedSuccess(itemStack, level.isClientSide)
|
return InteractionResultHolder.sidedSuccess(itemStack, level.isClientSide)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun throwPokeBall(level: Level, player: Player) {
|
private fun throwPokeBall(level: Level, player: ServerPlayer) {
|
||||||
val pokeBallEntity = EmptyPokeBallEntity(pokeBall, player.level).apply {
|
val pokeBallEntity = EmptyPokeBallEntity(pokeBall, player.level).apply {
|
||||||
setPos(player.x, player.y, player.z)
|
setPos(player.x, player.y + 1.5, player.z)
|
||||||
shootFromRotation(player, player.xRot - 7, player.yRot, 0.0f, 1.5f, 1.0f)
|
shootFromRotation(player, player.xRot - 7, player.yRot, 0.0f, 1.5f, 1.0f)
|
||||||
|
owner = player
|
||||||
}
|
}
|
||||||
level.addFreshEntity(pokeBallEntity)
|
level.addFreshEntity(pokeBallEntity)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -161,5 +161,8 @@ class Pokemon {
|
|||||||
private val _level = registerObservable(SimpleObservable<Int>()) { LevelUpdatePacket(this, it) }
|
private val _level = registerObservable(SimpleObservable<Int>()) { LevelUpdatePacket(this, it) }
|
||||||
private val _health = SimpleObservable<Int>()
|
private val _health = SimpleObservable<Int>()
|
||||||
|
|
||||||
fun getMaxHealth(): Int = TODO("needs to be calculated properly. NOT just the base stat value")
|
val ivHP = 1
|
||||||
|
val evHP = 1
|
||||||
|
|
||||||
|
fun getMaxHealth(): Int = (2 * stats[Stats.HP]!! + ivHP + (evHP / 4) * level) / 100 + level + 10
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.cablemc.pokemoncobbled.common.util
|
|||||||
|
|
||||||
import com.cablemc.pokemoncobbled.common.api.reactive.Observable
|
import com.cablemc.pokemoncobbled.common.api.reactive.Observable
|
||||||
import net.minecraft.util.thread.BlockableEventLoop
|
import net.minecraft.util.thread.BlockableEventLoop
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
import net.minecraftforge.api.distmarker.Dist
|
import net.minecraftforge.api.distmarker.Dist
|
||||||
import net.minecraftforge.fml.DistExecutor
|
import net.minecraftforge.fml.DistExecutor
|
||||||
import net.minecraftforge.fml.LogicalSide
|
import net.minecraftforge.fml.LogicalSide
|
||||||
@@ -42,4 +43,6 @@ internal fun <T> runOnSide(side: LogicalSide, block: () -> T): CompletableFuture
|
|||||||
val future = CompletableFuture<T>()
|
val future = CompletableFuture<T>()
|
||||||
LogicalSidedProvider.WORKQUEUE.get<BlockableEventLoop<*>>(side).submit { future.complete(block()) }
|
LogicalSidedProvider.WORKQUEUE.get<BlockableEventLoop<*>>(side).submit { future.complete(block()) }
|
||||||
return future
|
return future
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Level.isServerSide() = !isClientSide
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
package com.cablemc.pokemoncobbled.common.util.math
|
package com.cablemc.pokemoncobbled.common.util.math
|
||||||
|
|
||||||
|
import net.minecraft.core.Rotations
|
||||||
|
import net.minecraft.world.phys.Vec3
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
infix fun Int.pow(power: Int): Int {
|
infix fun Int.pow(power: Int): Int {
|
||||||
return toDouble().pow(power.toDouble()).toInt()
|
return toDouble().pow(power.toDouble()).toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Vec3.toRotations() = Rotations(x.toFloat(), y.toFloat(), z.toFloat())
|
||||||
|
fun Rotations.toVec3() = Vec3(x.toDouble(), y.toDouble(), z.toDouble())
|
||||||
@@ -5,6 +5,8 @@ import com.cablemc.pokemoncobbled.client.render.models.blockbench.repository.Pok
|
|||||||
import com.cablemc.pokemoncobbled.client.render.models.blockbench.repository.PokemonModelRepository
|
import com.cablemc.pokemoncobbled.client.render.models.blockbench.repository.PokemonModelRepository
|
||||||
import com.cablemc.pokemoncobbled.common.CommandRegistrar
|
import com.cablemc.pokemoncobbled.common.CommandRegistrar
|
||||||
import com.cablemc.pokemoncobbled.common.PokemonCobbled
|
import com.cablemc.pokemoncobbled.common.PokemonCobbled
|
||||||
|
import com.cablemc.pokemoncobbled.common.api.pokeball.catching.calculators.CaptureCalculator
|
||||||
|
import com.cablemc.pokemoncobbled.common.api.pokeball.catching.calculators.Gen7CaptureCalculator
|
||||||
import com.cablemc.pokemoncobbled.common.api.pokemon.PokemonSpecies
|
import com.cablemc.pokemoncobbled.common.api.pokemon.PokemonSpecies
|
||||||
import com.cablemc.pokemoncobbled.common.api.scheduling.ScheduledTaskListener
|
import com.cablemc.pokemoncobbled.common.api.scheduling.ScheduledTaskListener
|
||||||
import com.cablemc.pokemoncobbled.common.api.storage.PokemonStoreManager
|
import com.cablemc.pokemoncobbled.common.api.storage.PokemonStoreManager
|
||||||
@@ -38,6 +40,7 @@ import thedarkcolour.kotlinforforge.forge.MOD_CONTEXT
|
|||||||
object PokemonCobbledMod {
|
object PokemonCobbledMod {
|
||||||
val LOGGER = LogManager.getLogger()
|
val LOGGER = LogManager.getLogger()
|
||||||
val EVENT_BUS = BusBuilder.builder().build()
|
val EVENT_BUS = BusBuilder.builder().build()
|
||||||
|
var captureCalculator: CaptureCalculator = Gen7CaptureCalculator()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
with(MOD_CONTEXT.getKEventBus()) {
|
with(MOD_CONTEXT.getKEventBus()) {
|
||||||
@@ -96,15 +99,4 @@ object PokemonCobbledMod {
|
|||||||
fun on(event: EntityAttributeCreationEvent) {
|
fun on(event: EntityAttributeCreationEvent) {
|
||||||
EntityRegistry.registerAttributes(event)
|
EntityRegistry.registerAttributes(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
// // You can use EventBusSubscriber to automatically subscribe events on the contained class (this is subscribing to the MOD
|
|
||||||
// // Event bus for receiving Registry Events)
|
|
||||||
// @EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD)
|
|
||||||
// object RegistryEvents {
|
|
||||||
// @SubscribeEvent
|
|
||||||
// fun onBlocksRegistry(blockRegistryEvent: Register<Block?>?) {
|
|
||||||
// // register a new block here
|
|
||||||
// LOGGER.info("HELLO from Register Block")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
package com.cablemc.pokemoncobbled.client.render.models.blockbench.animation
|
||||||
|
|
||||||
|
import com.cablemc.pokemoncobbled.common.util.math.geometry.toDegrees
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack
|
||||||
|
import com.mojang.math.Vector3f
|
||||||
|
import net.minecraft.util.Mth.PI
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import kotlin.math.acos
|
||||||
|
import kotlin.math.asin
|
||||||
|
import kotlin.math.atan2
|
||||||
|
import kotlin.math.cos
|
||||||
|
|
||||||
|
internal class RootPokeBallLookAnimationTest {
|
||||||
|
@Test
|
||||||
|
fun `attempts at getting rotation logic working, i am not done trying angryface`() {
|
||||||
|
val xRot = PI / 3
|
||||||
|
val yRot = PI / 3
|
||||||
|
val zRot = 0F
|
||||||
|
|
||||||
|
val desiredZ = PI / 3
|
||||||
|
|
||||||
|
val poseStack = PoseStack()
|
||||||
|
poseStack.mulPose(Vector3f.ZP.rotation(zRot))
|
||||||
|
poseStack.mulPose(Vector3f.YP.rotation(yRot))
|
||||||
|
poseStack.mulPose(Vector3f.XP.rotation(xRot))
|
||||||
|
val zAxis = Vector3f.ZP.copy()
|
||||||
|
zAxis.transform(poseStack.last().normal())
|
||||||
|
poseStack.mulPose(zAxis.rotation(desiredZ))
|
||||||
|
|
||||||
|
val y = Vector3f.YP.copy()
|
||||||
|
y.transform(poseStack.last().normal())
|
||||||
|
val py1 = y.x()
|
||||||
|
|
||||||
|
val z = Vector3f.ZP.copy()
|
||||||
|
z.transform(poseStack.last().normal())
|
||||||
|
val pz1 = z.x()
|
||||||
|
val pz3 = z.z()
|
||||||
|
|
||||||
|
// val beta = atan2(d.y(), d.x())
|
||||||
|
// val alpha = asin(d.z())
|
||||||
|
|
||||||
|
|
||||||
|
val beta = asin(pz1)
|
||||||
|
val alpha = acos(pz3 / cos(beta))
|
||||||
|
val gamma = asin(-py1 / cos(beta))
|
||||||
|
|
||||||
|
val poseStack2 = PoseStack()
|
||||||
|
poseStack2.mulPose(Vector3f.ZP.rotation(gamma))
|
||||||
|
poseStack2.mulPose(Vector3f.YP.rotation(beta))
|
||||||
|
poseStack2.mulPose(Vector3f.XP.rotation(alpha))
|
||||||
|
val y2 = Vector3f(3F, 0F, -4F)
|
||||||
|
y2.transform(poseStack.last().normal())
|
||||||
|
println(y2)
|
||||||
|
|
||||||
|
val alphaD = alpha.toDegrees()
|
||||||
|
val betaD = beta.toDegrees()
|
||||||
|
val gammaD = gamma.toDegrees()
|
||||||
|
|
||||||
|
println("xRot: $alphaD, yRot: $betaD, zRot: $gammaD")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user