From 42c0c49782a81a321112d07b85f770cb296046ae Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sat, 14 May 2022 15:18:50 +0700 Subject: [PATCH] Plasma weapons but better:tm: --- .../ru/dbotthepony/mc/otm/core/EuclidMath.kt | 21 ++++ .../mc/otm/entity/PlasmaProjectile.kt | 2 - .../mc/otm/item/weapon/AbstractWeaponItem.kt | 117 +++++++++++++++--- .../mc/otm/item/weapon/PlasmaRifleItem.kt | 7 +- 4 files changed, 130 insertions(+), 17 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/EuclidMath.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/EuclidMath.kt index 441f4deb0..041aee99f 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/EuclidMath.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/EuclidMath.kt @@ -329,3 +329,24 @@ fun bezierCurve(t: Double, vararg values: Vector): Vector { } } } +fun bezierCurve(t: Double, values: DoubleArray): Double { + when (values.size) { + 0, 1 -> throw IllegalArgumentException("Provided array has only ${values.size} entries in it") + else -> { + @Suppress("NAME_SHADOWING") val values = values.clone() + + // construct prime - 1 + for (length in values.size - 2 downTo 0) { + // search prime + for (j in 0 .. length) { + val point1 = values[j] + val point2 = values[j + 1] + + values[j] = linearInterpolation(t, point1, point2) + } + } + + return values[0] + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/entity/PlasmaProjectile.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/entity/PlasmaProjectile.kt index 42daf6adb..43096f523 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/entity/PlasmaProjectile.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/entity/PlasmaProjectile.kt @@ -43,8 +43,6 @@ class PlasmaProjectile(level: Level) : Projectile(MEntityTypes.PLASMA as EntityT override fun onHitBlock(p_37258_: BlockHitResult) { super.onHitBlock(p_37258_) - - println("Play sound at ${position()}") } override fun tick() { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/item/weapon/AbstractWeaponItem.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/item/weapon/AbstractWeaponItem.kt index 0393dc894..85cc3014f 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/item/weapon/AbstractWeaponItem.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/item/weapon/AbstractWeaponItem.kt @@ -27,6 +27,7 @@ import ru.dbotthepony.mc.otm.network.MatteryNetworking import java.util.* import java.util.function.Supplier import kotlin.math.PI +import kotlin.math.abs import kotlin.reflect.KClass import kotlin.reflect.full.isSubclassOf import kotlin.reflect.full.primaryConstructor @@ -99,6 +100,29 @@ open class WeaponDataTable(val tag: CompoundTag) { var nextPrimaryFire by tag.ints var nextSecondaryFire by tag.ints var uuid by tag.uuids + + var fireAnim = 0.0 + var fireAnimDeviation = Angle.ZERO + + fun doFireAnim(amount: Double = 1.0, deviation: Angle? = null) { + fireAnim = (fireAnim + amount).coerceAtLeast(0.0).coerceAtMost(1.0) + + if (deviation != null) { + fireAnimDeviation = Angle( + deviation.pitch * RANDOM.nextGaussian(), + deviation.yaw * RANDOM.nextGaussian(), + deviation.roll * RANDOM.nextGaussian(), + ) + } else { + fireAnimDeviation = Angle.ZERO + } + } + + var deployTime = 0.0 + + companion object { + private val RANDOM = Random() + } } abstract class AbstractWeaponItem(val tables: KClass, rarity: Rarity = Rarity.UNCOMMON) : Item( @@ -144,10 +168,16 @@ abstract class AbstractWeaponItem(val tables: KClass, ra val roundsPerSecond get() = roundsPerMinute / 60 val fireCooldown get() = (1200 / roundsPerMinute).coerceAtLeast(1) + open val positionHolstered get() = Vector(1.0, -2.5, -1.0) open val positionIdle get() = Vector(1.0, -0.5, -1.0) open val positionIronSights get() = Vector(0.0, -0.23, -1.0) open val rotIdle get() = Angle(PI / 36, PI / 18) open val rotIronSights get() = Angle.ZERO + open val rotFireAnim get() = Angle.deg(20.0, 0.0, 0.0) + open val rotFireAnimDeviation get() = Angle.deg(2.0, 1.0, 1.0) + + open val fireAnimRecovery = 1.0 + open val deployAnimSpeed = 1.0 open val primaryAutomatic = true open val secondaryAutomatic = true @@ -156,9 +186,9 @@ abstract class AbstractWeaponItem(val tables: KClass, ra fun ironSightsProgress(itemStack: ItemStack, partialTicks: Double = 0.0, dt: D = dataTable(itemStack)): Double { if (!dt.wantsToScope) - return (dt.scopeTicks.toDouble() - partialTicks).coerceAtLeast(0.0) / scopingTime.toDouble() + return (((dt.scopeTicks.toDouble() - partialTicks).coerceAtLeast(0.0) / scopingTime.toDouble()) * 1.4).coerceAtMost(1.0) - return (dt.scopeTicks.toDouble() + partialTicks) / scopingTime.toDouble() + return (((dt.scopeTicks.toDouble() + partialTicks) / scopingTime.toDouble()) * 1.4).coerceAtMost(1.0) } abstract val scopingTime: Int @@ -228,25 +258,39 @@ abstract class AbstractWeaponItem(val tables: KClass, ra // client only private var inRender = false var predictedData: WeaponDataTable? = null + private var lastFov = 1.0 + private var lastFovTime = 0L @SubscribeEvent @Suppress("unused") fun fovHook(event: EntityViewRenderEvent.FieldOfView) { val player = event.camera.entity as? Player ?: return - (player.mainHandItem.item as? AbstractWeaponItem<*>)?.let { + val it = (player.mainHandItem.item as? AbstractWeaponItem<*>) + + if (it != null) { @Suppress("unchecked_cast") if (it.compatibleDataTable(predictedData)) (it as AbstractWeaponItem).dataTable = predictedData - event.fov /= linearInterpolation( + val interp = linearInterpolation( it.ironSightsProgress( player.mainHandItem, event.partialTicks ) * 1.7 - 0.6, 1.0, it.ironSightsFOV ) + val time = System.nanoTime() + val diff = abs(lastFovTime - time) + lastFov = linearInterpolation(diff.coerceAtLeast(0).coerceAtMost(10_000_000L).toDouble() / 10_000_000.0, lastFov, interp).coerceAtLeast(0.001) + lastFovTime = time + + event.fov /= lastFov + it.dataTable = null + } else { + lastFov = 1.0 + lastFovTime = 0L } } @@ -270,6 +314,29 @@ abstract class AbstractWeaponItem(val tables: KClass, ra } } + private var lastClientRender = 0L + + private val fireAnimInterp = doubleArrayOf( + 0.0, + 0.01, + 0.03, + 0.04, + 0.05, + 0.06, + 0.07, + 0.1, + 0.15, + 0.3, + 0.45, + 0.6, + 0.8, + 0.85, + 0.9, + 1.0, + 0.9, + 0.6, + ) + @SubscribeEvent @Suppress("unused", "unchecked_cast") fun renderViewModel(event: RenderHandEvent) { @@ -277,6 +344,10 @@ abstract class AbstractWeaponItem(val tables: KClass, ra return } + val time = System.nanoTime() + val diff = abs(lastClientRender - time) + lastClientRender = time + val stack = event.itemStack val item = stack.item as? AbstractWeaponItem<*> ?: return @@ -285,6 +356,9 @@ abstract class AbstractWeaponItem(val tables: KClass, ra (item as AbstractWeaponItem).dataTable = predictedData + predictedData?.deployTime = (predictedData!!.deployTime + diff.toDouble() / (300_000_000.0 * item.deployAnimSpeed)).coerceAtMost(1.0).coerceAtLeast(0.0) + predictedData?.fireAnim = (predictedData!!.fireAnim - diff.toDouble() / (300_000_000.0 * item.fireAnimRecovery)).coerceAtMost(1.0).coerceAtLeast(0.0) + val player = Minecraft.getInstance().player!! val pose = event.poseStack val itemInHandRenderer = Minecraft.getInstance().itemInHandRenderer @@ -293,20 +367,25 @@ abstract class AbstractWeaponItem(val tables: KClass, ra val progress = item.ironSightsProgress(stack, event.partialTicks.toDouble()) - val (x, y, z) = bezierCurve( - progress, - item.positionIdle, - item.positionIdle, - item.positionIdle, - Vector(0.0, -1.0, -1.0), - item.positionIronSights, - item.positionIronSights, - item.positionIronSights, + val (x, y, z) = linearInterpolation( + // 1.0 - event.equipProgress.toDouble(), + item.dataTable?.deployTime ?: 0.0, + item.positionHolstered, + bezierCurve( + progress, + item.positionIdle, + item.positionIdle, + item.positionIdle, + Vector(0.0, -1.0, -1.0), + item.positionIronSights, + item.positionIronSights, + item.positionIronSights, + ) ) pose.translate(x, y, z) - val (pitch, yaw, roll) = bezierCurve( + var (pitch, yaw, roll) = bezierCurve( progress, item.rotIdle, item.rotIdle, @@ -319,6 +398,16 @@ abstract class AbstractWeaponItem(val tables: KClass, ra item.rotIronSights, ) + val fireAnim = bezierCurve( + predictedData?.fireAnim ?: 0.0, + fireAnimInterp + ) + + val rotFire = item.rotFireAnim + pitch += (rotFire.pitch + (predictedData?.fireAnimDeviation?.pitch ?: 0.0)) * fireAnim + yaw += (rotFire.yaw + (predictedData?.fireAnimDeviation?.yaw ?: 0.0)) * fireAnim + roll += (rotFire.roll + (predictedData?.fireAnimDeviation?.roll ?: 0.0)) * fireAnim + pose.mulPose(Vector3f.ZP.rotation(roll.toFloat())) pose.mulPose(Vector3f.YP.rotation(yaw.toFloat())) pose.mulPose(Vector3f.XP.rotation(pitch.toFloat())) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/item/weapon/PlasmaRifleItem.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/item/weapon/PlasmaRifleItem.kt index 8bb7453b5..82b3da95b 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/item/weapon/PlasmaRifleItem.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/item/weapon/PlasmaRifleItem.kt @@ -118,8 +118,9 @@ class VelocityCalculation( } class PlasmaRifleItem : PlasmaWeaponItem(WeaponDataTable::class, ImpreciseFraction(200_000)) { - override val roundsPerMinute: Int = 400 + override val roundsPerMinute: Int = 200 override val scopingTime: Int = 7 + override val positionIdle: Vector get() = Vector(0.2,-0.4,-0.6) override val positionIronSights: Vector @@ -137,7 +138,11 @@ class PlasmaRifleItem : PlasmaWeaponItem(WeaponDataTable::class val calc = VelocityCalculation(player, force = 4.0, deviation = 0.3) calc.load(arrow) + arrow.owner = player + player.level.addFreshEntity(arrow) + } else { + dt.doFireAnim(deviation = rotFireAnimDeviation) } return true