Shockwave feature test, some refactoring

This commit is contained in:
DBotThePony 2022-09-21 22:41:08 +07:00
parent f8965e583c
commit 0713b7bb09
Signed by: DBot
GPG Key ID: DCC23B5715498507
21 changed files with 332 additions and 67 deletions

View File

@ -40,3 +40,11 @@ fun ForgeConfigSpec.Builder.appendComment(vararg comments: String): ForgeConfigS
builderContextField.set(context, reconstruct)
return this
}
fun ForgeConfigSpec.Builder.defineInRange(path: String, value: Int, minValue: Int): ForgeConfigSpec.IntValue {
return defineInRange(path, value, minValue, Int.MAX_VALUE)
}
fun ForgeConfigSpec.Builder.defineInRange(path: String, value: Double, minValue: Double): ForgeConfigSpec.DoubleValue {
return defineInRange(path, value, minValue, Double.MAX_VALUE)
}

View File

@ -141,7 +141,28 @@ object ServerConfig {
val NIGHT_VISION_POWER_DRAW by specBuilder.defineImpreciseFraction("nightVisionPowerDraw", ImpreciseFraction(8), ImpreciseFraction.ZERO)
object Shockwave {
init {
specBuilder.comment("Shockwave ability").push("shockwave")
}
val TERMINAL_VELOCITY: Double by specBuilder.comment("In meters per second vertically").defineInRange("terminalVelocity", 5.6, 0.0)
val ACCELERATION: Double by specBuilder.comment("In meters per second vertically").defineInRange("acceleration", 4.0, 0.0)
val COOLDOWN: Int by specBuilder.comment("In ticks").defineInRange("cooldown", 30, 1)
val RADIUS_HORIZONTAL: Double by specBuilder.comment("In meters").defineInRange("radiusHorizontal", 4.0, 0.0)
val RADIUS_VERTICAL: Double by specBuilder.comment("In meters").defineInRange("radiusVertical", 1.0, 0.0)
val BREAK_BLOCKS: Boolean by specBuilder.comment("Break blocks without any blast resistance").define("breakBlocks", true)
val DAMAGE: Double by specBuilder.comment("Max potential damage done by shockwave").defineInRange("damage", 12.0, 0.0, Float.MAX_VALUE.toDouble())
init {
specBuilder.pop()
}
}
init {
// access shockwave class so spec is built
Shockwave
specBuilder.pop()
specBuilder.comment("Tweaking of exosuits").push("exosuitPlayer")

View File

@ -11,7 +11,7 @@ import java.io.DataInputStream
import java.io.InputStream
abstract class AndroidFeature(val type: AndroidFeatureType<*>, val android: MatteryPlayerCapability) : INBTSerializable<CompoundTag> {
val entity get() = android.ply
val ply get() = android.ply
val synchronizer = FieldSynchronizer()
open var level by synchronizer.int(setter = setter@{ value, field, setByRemote ->

View File

@ -22,6 +22,7 @@ abstract class AndroidSwitchableFeature(type: AndroidFeatureType<*>, android: Ma
open val allowToSwitchByPlayer: Boolean get() = true
// TODO: PoseStack is stripped from server dist
abstract fun renderIcon(stack: PoseStack, x: Float, y: Float, width: Float, height: Float)
override fun serializeNBT(): CompoundTag {

View File

@ -9,7 +9,7 @@ import java.util.*
class AttackBoost(android: MatteryPlayerCapability) : AndroidFeature(AndroidFeatures.LIMB_OVERCLOCKING, android) {
override fun applyModifiers() {
val modifier = entity.getAttribute(Attributes.ATTACK_DAMAGE)
val modifier = ply.getAttribute(Attributes.ATTACK_DAMAGE)
if (modifier != null) {
modifier.removePermanentModifier(MODIFIER_ID)
@ -18,7 +18,7 @@ class AttackBoost(android: MatteryPlayerCapability) : AndroidFeature(AndroidFeat
}
override fun removeModifiers() {
entity.getAttribute(Attributes.ATTACK_DAMAGE)?.removePermanentModifier(MODIFIER_ID)
ply.getAttribute(Attributes.ATTACK_DAMAGE)?.removePermanentModifier(MODIFIER_ID)
}
companion object {

View File

@ -12,7 +12,7 @@ class ExtendedReach(android: MatteryPlayerCapability) : AndroidFeature(AndroidFe
if (!ForgeMod.REACH_DISTANCE.isPresent)
return
val reach = entity.getAttribute(ForgeMod.REACH_DISTANCE.get()) ?: return
val reach = ply.getAttribute(ForgeMod.REACH_DISTANCE.get()) ?: return
reach.removePermanentModifier(MODIFIER_ID)
reach.addPermanentModifier(AttributeModifier(MODIFIER_ID, type.displayName.toString(), level + 1.0, AttributeModifier.Operation.ADDITION))
@ -22,7 +22,7 @@ class ExtendedReach(android: MatteryPlayerCapability) : AndroidFeature(AndroidFe
if (!ForgeMod.REACH_DISTANCE.isPresent)
return
entity.getAttribute(ForgeMod.REACH_DISTANCE.get())?.removePermanentModifier(MODIFIER_ID)
ply.getAttribute(ForgeMod.REACH_DISTANCE.get())?.removePermanentModifier(MODIFIER_ID)
}
companion object {

View File

@ -9,14 +9,14 @@ import java.util.*
class LimbOverclocking(android: MatteryPlayerCapability) : AndroidFeature(AndroidFeatures.LIMB_OVERCLOCKING, android) {
override fun applyModifiers() {
val speed = entity.getAttribute(Attributes.MOVEMENT_SPEED)
val speed = ply.getAttribute(Attributes.MOVEMENT_SPEED)
if (speed != null) {
speed.removePermanentModifier(MODIFIER_ID)
speed.addPermanentModifier(AttributeModifier(MODIFIER_ID, type.displayName.toString(), (level + 1) * 0.08, AttributeModifier.Operation.MULTIPLY_TOTAL))
}
val attackSpeed = entity.getAttribute(Attributes.ATTACK_SPEED)
val attackSpeed = ply.getAttribute(Attributes.ATTACK_SPEED)
if (attackSpeed != null) {
attackSpeed.removePermanentModifier(MODIFIER_ID)
@ -25,8 +25,8 @@ class LimbOverclocking(android: MatteryPlayerCapability) : AndroidFeature(Androi
}
override fun removeModifiers() {
entity.getAttribute(Attributes.MOVEMENT_SPEED)?.removePermanentModifier(MODIFIER_ID)
entity.getAttribute(Attributes.ATTACK_SPEED)?.removePermanentModifier(MODIFIER_ID)
ply.getAttribute(Attributes.MOVEMENT_SPEED)?.removePermanentModifier(MODIFIER_ID)
ply.getAttribute(Attributes.ATTACK_SPEED)?.removePermanentModifier(MODIFIER_ID)
}
companion object {

View File

@ -9,7 +9,6 @@ import ru.dbotthepony.mc.otm.capability.extractEnergyInnerExact
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import ru.dbotthepony.mc.otm.registry.StatNames
import ru.dbotthepony.mc.otm.container.set
import ru.dbotthepony.mc.otm.core.set
import kotlin.math.roundToInt
@ -47,7 +46,7 @@ class NanobotsArmor(android: MatteryPlayerCapability) : AndroidFeature(AndroidFe
val powerExtracted = android.androidEnergy.extractEnergyInner(powerRequired, false)
val realAbsorbed = (powerExtracted / ENERGY_PER_HITPOINT).toFloat()
event.amount = event.amount - realAbsorbed
(entity as ServerPlayer?)?.awardStat(StatNames.DAMAGE_ABSORBED, (realAbsorbed * 10f).roundToInt())
(ply as ServerPlayer?)?.awardStat(StatNames.DAMAGE_ABSORBED, (realAbsorbed * 10f).roundToInt())
layers--
}
}

View File

@ -8,7 +8,6 @@ import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import ru.dbotthepony.mc.otm.registry.StatNames
import ru.dbotthepony.mc.otm.container.set
import ru.dbotthepony.mc.otm.core.set
import kotlin.math.roundToInt
@ -17,21 +16,21 @@ class NanobotsRegeneration(android: MatteryPlayerCapability) : AndroidFeature(An
private var healTicks = 0
override fun tickServer() {
if (entity.health > 0f && entity.health < entity.maxHealth) {
if (ply.health > 0f && ply.health < ply.maxHealth) {
ticksPassed++
val waitTime = TICKS_BETWEEN_HEAL.getOrElse(healTicks) { TICKS_BETWEEN_HEAL.last() }
if (ticksPassed > waitTime) {
val missingHealth = entity.maxHealth - entity.health
val missingHealth = ply.maxHealth - ply.health
val power = ENERGY_PER_HITPOINT * missingHealth
val extracted = android.androidEnergy.extractEnergyInner(power, false)
if (extracted.isPositive) {
healTicks = (healTicks + 1).coerceAtMost(level)
val healed = (extracted / ENERGY_PER_HITPOINT).toFloat()
entity.heal(healed)
(entity as ServerPlayer?)?.awardStat(StatNames.HEALTH_REGENERATED, (healed * 10f).roundToInt())
ply.heal(healed)
(ply as ServerPlayer?)?.awardStat(StatNames.HEALTH_REGENERATED, (healed * 10f).roundToInt())
ticksPassed = 0
}
}

View File

@ -3,6 +3,8 @@ package ru.dbotthepony.mc.otm.android.feature
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.world.effect.MobEffectInstance
import net.minecraft.world.effect.MobEffects
import net.minecraftforge.api.distmarker.Dist
import net.minecraftforge.api.distmarker.OnlyIn
import ru.dbotthepony.mc.otm.ServerConfig
import ru.dbotthepony.mc.otm.android.AndroidSwitchableFeature
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability

View File

@ -0,0 +1,153 @@
package ru.dbotthepony.mc.otm.android.feature
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.level.block.Block
import net.minecraft.world.phys.AABB
import ru.dbotthepony.mc.otm.ServerConfig
import ru.dbotthepony.mc.otm.android.AndroidSwitchableFeature
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
import ru.dbotthepony.mc.otm.core.Vector
import ru.dbotthepony.mc.otm.core.getEllipsoidBlockPositions
import ru.dbotthepony.mc.otm.core.getExplosionResistance
import ru.dbotthepony.mc.otm.core.minus
import ru.dbotthepony.mc.otm.core.plus
import ru.dbotthepony.mc.otm.core.position
import ru.dbotthepony.mc.otm.core.roundToIntVector
import ru.dbotthepony.mc.otm.core.times
import ru.dbotthepony.mc.otm.network.MatteryPlayerNetworkChannel
import ru.dbotthepony.mc.otm.network.TriggerShockwavePacket
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import ru.dbotthepony.mc.otm.registry.AndroidResearch
import ru.dbotthepony.mc.otm.registry.ShockwaveDamageSource
import kotlin.math.pow
import kotlin.math.roundToInt
class ShockwaveFeature(capability: MatteryPlayerCapability) : AndroidSwitchableFeature(AndroidFeatures.SHOCKWAVE, capability) {
var cooldown by synchronizer.int()
private var wasMidair = false
private var highestSpeed = 0.0
var airTicks = 0
private set
override fun tickClient() {
if (isActive && ply.isSteppingCarefully && cooldown <= 0) {
ply.deltaMovement += Vector(0.0, -ServerConfig.Shockwave.ACCELERATION / 20.0, 0.0)
}
ticker(true)
}
fun shockwave() {
// TODO: raycasting
val entities = ply.level.getEntities(ply, AABB(
ply.position.x - ServerConfig.Shockwave.RADIUS_HORIZONTAL,
ply.position.y - ServerConfig.Shockwave.RADIUS_VERTICAL,
ply.position.z - ServerConfig.Shockwave.RADIUS_HORIZONTAL,
ply.position.x + ServerConfig.Shockwave.RADIUS_HORIZONTAL,
ply.position.y + ServerConfig.Shockwave.RADIUS_VERTICAL,
ply.position.z + ServerConfig.Shockwave.RADIUS_HORIZONTAL,
)) { (it !is LivingEntity || !it.isSpectator && it.isAlive) && ((it.position - ply.position).let { vec ->
vec.x.pow(2.0) / ServerConfig.Shockwave.RADIUS_HORIZONTAL.pow(2.0) +
vec.y.pow(2.0) / ServerConfig.Shockwave.RADIUS_VERTICAL.pow(2.0) +
vec.z.pow(2.0) / ServerConfig.Shockwave.RADIUS_HORIZONTAL.pow(2.0) <= 1.0
}) || it.boundingBox.center.let { vec ->
(vec.x - ply.position.x).pow(2.0) / ServerConfig.Shockwave.RADIUS_HORIZONTAL.pow(2.0) +
(vec.y - ply.position.y).pow(2.0) / ServerConfig.Shockwave.RADIUS_VERTICAL.pow(2.0) +
(vec.z - ply.position.z).pow(2.0) / ServerConfig.Shockwave.RADIUS_HORIZONTAL.pow(2.0) <= 1.0
} }
for (entity in entities) {
val diff = entity.position - ply.position
val distanceMultiplier = diff.let {
it.x.pow(2.0) / ServerConfig.Shockwave.RADIUS_HORIZONTAL.pow(2.0) +
it.y.pow(2.0) / ServerConfig.Shockwave.RADIUS_VERTICAL.pow(2.0) +
it.z.pow(2.0) / ServerConfig.Shockwave.RADIUS_HORIZONTAL.pow(2.0)
}.coerceAtMost(entity.boundingBox.center.let { vec ->
(vec.x - ply.position.x).pow(2.0) / ServerConfig.Shockwave.RADIUS_HORIZONTAL.pow(2.0) +
(vec.y - ply.position.y).pow(2.0) / ServerConfig.Shockwave.RADIUS_VERTICAL.pow(2.0) +
(vec.z - ply.position.z).pow(2.0) / ServerConfig.Shockwave.RADIUS_HORIZONTAL.pow(2.0)
})
val multiplier = (1.0 - distanceMultiplier).pow(1.25)
// don't hurt items, arrows, etc etc
if (entity is LivingEntity) {
entity.hurt(ShockwaveDamageSource(ply), multiplier.toFloat() * ServerConfig.Shockwave.DAMAGE.toFloat())
entity.deltaMovement += diff.normalize() * (multiplier * 3.0)
} else {
entity.deltaMovement += diff.normalize() * (multiplier * 6.0)
}
}
if (ServerConfig.Shockwave.BREAK_BLOCKS) {
val rounded = ply.position.roundToIntVector()
for (blockPos in getEllipsoidBlockPositions(ServerConfig.Shockwave.RADIUS_HORIZONTAL.roundToInt(), ServerConfig.Shockwave.RADIUS_VERTICAL.roundToInt(), ServerConfig.Shockwave.RADIUS_HORIZONTAL.roundToInt())) {
val newBlockPos = blockPos + rounded
val blockState = ply.level.getBlockState(newBlockPos)
if (!blockState.isAir && blockState.getExplosionResistance(ply.level, newBlockPos) <= 0f && ply.level.getBlockEntity(newBlockPos) == null) {
// Block.dropResources(blockState, ply.level, newBlockPos)
// blockState.block.destroy(ply.level, newBlockPos, blockState)
// ply.level.setBlock(newBlockPos, blockState.fluidState.createLegacyBlock(), Block.UPDATE_ALL)
ply.level.destroyBlock(newBlockPos, true)
}
}
}
}
private fun ticker(isClient: Boolean) {
if (!ply.isOnGround) {
airTicks = (airTicks + 1).coerceAtMost(3)
} else {
airTicks = (airTicks - 1).coerceAtLeast(0)
}
if (isActive && cooldown <= 0) {
val old = wasMidair
wasMidair = !ply.isOnGround
if (wasMidair) {
highestSpeed = (-ply.deltaMovement.y).coerceAtLeast(highestSpeed)
}
if (old != wasMidair && !wasMidair && ServerConfig.Shockwave.TERMINAL_VELOCITY <= (highestSpeed * 20.0) && ply.isSteppingCarefully) {
cooldown = ServerConfig.Shockwave.COOLDOWN
if (isClient) {
// I HATE SELF-UPDATING PLAYERS
// I HATE SELF-UPDATING PLAYERS
// fix "bug" where shockwave doesn't trigger even when player is falling faster than orbiting satellite
MatteryPlayerNetworkChannel.sendToServer(TriggerShockwavePacket)
} else {
shockwave()
}
}
if (!wasMidair) {
highestSpeed = 0.0
}
} else if (!isActive) {
highestSpeed = 0.0
wasMidair = false
}
}
override fun tickServer() {
if (cooldown > 0) {
cooldown--
}
ticker(false)
}
override fun renderIcon(stack: PoseStack, x: Float, y: Float, width: Float, height: Float) {
AndroidResearch.ICON_SHOCKWAVE.render(stack, x, y, width, height)
}
}

View File

@ -12,7 +12,7 @@ class StepAssistFeature(android: MatteryPlayerCapability) : AndroidFeature(Andro
if (!ForgeMod.STEP_HEIGHT_ADDITION.isPresent)
return
val reach = entity.getAttribute(ForgeMod.STEP_HEIGHT_ADDITION.get()) ?: return
val reach = ply.getAttribute(ForgeMod.STEP_HEIGHT_ADDITION.get()) ?: return
reach.removePermanentModifier(MODIFIER_ID)
reach.addPermanentModifier(AttributeModifier(MODIFIER_ID, type.displayName.toString(), level + 1.0, AttributeModifier.Operation.ADDITION))
@ -22,7 +22,7 @@ class StepAssistFeature(android: MatteryPlayerCapability) : AndroidFeature(Andro
if (!ForgeMod.STEP_HEIGHT_ADDITION.isPresent)
return
entity.getAttribute(ForgeMod.STEP_HEIGHT_ADDITION.get())?.removePermanentModifier(MODIFIER_ID)
ply.getAttribute(ForgeMod.STEP_HEIGHT_ADDITION.get())?.removePermanentModifier(MODIFIER_ID)
}
companion object {

View File

@ -31,7 +31,7 @@ import ru.dbotthepony.mc.otm.matter.getMatterValue
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MItems
import ru.dbotthepony.mc.otm.registry.MRegistry
import ru.dbotthepony.mc.otm.container.set
import ru.dbotthepony.mc.otm.core.getSphericalBlockPositions
import ru.dbotthepony.mc.otm.core.set
import kotlin.math.pow
import kotlin.math.roundToInt
@ -302,7 +302,7 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : BlockEn
}
if (gravitationStrength > 0.4) {
val sphere = getSphericalShape((gravitationStrength * 6.0).roundToInt())
val sphere = getSphericalBlockPositions((gravitationStrength * 6.0).roundToInt())
if (sphere.size != lastSphereSizeOuter) {
lastSphereSizeOuter = sphere.size
@ -327,12 +327,14 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : BlockEn
val eResist = try {
getBlock.getExplosionResistance(level, pos, null)
} catch (err: Throwable) {
getBlock.block.getExplosionResistance()
} catch (err: NullPointerException) {
getBlock.block.explosionResistance
// Потому что возможно какой-либо мод не ожидает что Explosion == null
// особенно учитывая что интерфейс IForgeBlock не имеет @ParamsAreNonnullByDefault
// и аргумент не помечен как @Nullable
// тем самым имеет тип Explosion! который указывается как Explosion? .. Explosion!!
} catch (err: IllegalArgumentException) {
getBlock.block.explosionResistance
}
var strengthLinear = sqrt(blockPos.distSqr(pos))
@ -362,49 +364,5 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : BlockEn
const val ITERATIONS = 30_000
val BASELINE_MASS = ImpreciseFraction(1_000)
val HAWKING_MASS_LOSE_STEP = ImpreciseFraction("-0.1")
private val blockShapeCache = HashMap<Int, Array<BlockPos>>()
private fun getSphericalShape(size: Int): Array<BlockPos> {
return blockShapeCache.computeIfAbsent(size) {
val result = ArrayList<BlockPos>((it * it * it * Math.PI * (4.0 / 3.0)).toInt() + 16)
for (x in -it .. it) {
for (y in -it .. it) {
for (z in -it .. it) {
val dist = sqrt(x * x.toDouble() + y * y.toDouble() + z * z.toDouble())
if (dist <= size && dist != 0.0) {
result.add(BlockPos(x, y, z))
}
}
}
}
val arr = result.toTypedArray()
arr.sortWith { a, b ->
val xa = a.x
val ya = a.y
val za = a.z
val distA = sqrt(xa * xa.toDouble() + ya * ya.toDouble() + za * za.toDouble())
val xb = b.x
val yb = b.y
val zb = b.z
val distB = sqrt(xb * xb.toDouble() + yb * yb.toDouble() + zb * zb.toDouble())
if (distA == distB) {
return@sortWith 0
} else if (distA > distB) {
return@sortWith 1
} else {
return@sortWith -1
}
}
return@computeIfAbsent arr
}
}
}
}

View File

@ -2,10 +2,14 @@ package ru.dbotthepony.mc.otm.core
import com.mojang.math.Quaternion
import com.mojang.math.Vector3f
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
import it.unimi.dsi.fastutil.ints.Int2ObjectFunction
import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.Vec3i
import net.minecraft.world.phys.Vec3
import java.lang.ref.SoftReference
import kotlin.math.*
typealias Vector = Vec3
@ -376,6 +380,9 @@ operator fun Vec3i.times(int: Int): Vec3i = this.multiply(int)
operator fun Direction.times(int: Int): Vec3i = this.normal.multiply(int)
operator fun Vec3i.times(double: Double): Vector = Vector(x * double, y * double, z * double)
fun Vec3.toIntVector() = Vec3i(x.toInt(), y.toInt(), z.toInt())
fun Vec3.roundToIntVector() = Vec3i(x.roundToInt(), y.roundToInt(), z.roundToInt())
fun BlockPos.asVector(): Vector {
return Vector(x + 0.5, y + 0.5, z + 0.5)
}
@ -383,3 +390,61 @@ fun BlockPos.asVector(): Vector {
operator fun Direction.unaryMinus(): Direction = this.opposite
operator fun Vec3i.unaryMinus(): Vec3i = Vec3i(-x, -y, -z)
operator fun BlockPos.unaryMinus(): BlockPos = BlockPos(-x, -y, -z)
private data class EllipsoidShapeCacheKey(val x: Int, val y: Int, val z: Int) : Comparable<EllipsoidShapeCacheKey> {
override fun compareTo(other: EllipsoidShapeCacheKey): Int {
return x.compareTo(other.x).let { if (it != 0) it else y.compareTo(other.y).let { if (it != 0) it else z.compareTo(other.z) } }
}
}
private val blockShapeCache = Object2ObjectAVLTreeMap<EllipsoidShapeCacheKey, SoftReference<List<BlockPos>>>(EllipsoidShapeCacheKey::compareTo)
fun getSphericalBlockPositions(size: Int): List<BlockPos> {
return getEllipsoidBlockPositions(size, size, size)
}
fun getEllipsoidBlockPositions(x: Int, y: Int, z: Int): List<BlockPos> {
val getResult = blockShapeCache[EllipsoidShapeCacheKey(x, y, z)]?.get()
if (getResult != null) {
return getResult
}
val result = ArrayList<BlockPos>((x * y * z * Math.PI * (4.0 / 3.0)).toInt() + 16)
val xPow = x.toDouble().pow(2.0)
val yPow = y.toDouble().pow(2.0)
val zPow = z.toDouble().pow(2.0)
for (ellipsoidX in -x..x) {
for (ellipsoidY in -y..y) {
for (ellipsoidZ in -z..z) {
if (
ellipsoidX.toDouble().pow(2.0) / xPow +
ellipsoidY.toDouble().pow(2.0) / yPow +
ellipsoidZ.toDouble().pow(2.0) / zPow <= 1.0
) {
result.add(BlockPos(ellipsoidX, ellipsoidY, ellipsoidZ))
}
}
}
}
result.sortWith { a, b ->
val xa = a.x
val ya = a.y
val za = a.z
val distA = sqrt(xa * xa.toDouble() + ya * ya.toDouble() + za * za.toDouble())
val xb = b.x
val yb = b.y
val zb = b.z
val distB = sqrt(xb * xb.toDouble() + yb * yb.toDouble() + zb * zb.toDouble())
return@sortWith distA.compareTo(distB)
}
val immutableList = com.google.common.collect.ImmutableList.copyOf(result)
blockShapeCache[EllipsoidShapeCacheKey(x, y, z)] = SoftReference(immutableList)
return immutableList
}

View File

@ -6,6 +6,7 @@ package ru.dbotthepony.mc.otm.core
import com.google.common.collect.ImmutableList
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import net.minecraft.core.BlockPos
import net.minecraft.nbt.ByteArrayTag
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
@ -14,6 +15,8 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.Entity
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateHolder
import net.minecraft.world.level.block.state.properties.Property
import net.minecraft.world.phys.Vec3
@ -344,3 +347,17 @@ fun <T> List<T>.toImmutableList(): ImmutableList<T> {
operator fun <R> (() -> R).getValue(thisRef: Any, property: KProperty<*>): R {
return invoke()
}
fun BlockState.getExplosionResistance(level: BlockGetter, pos: BlockPos): Float {
return try {
getExplosionResistance(level, pos, null)
} catch (err: NullPointerException) {
block.explosionResistance
// Потому что возможно какой-либо мод не ожидает что Explosion == null
// особенно учитывая что интерфейс IForgeBlock не имеет @ParamsAreNonnullByDefault
// и аргумент не помечен как @Nullable
// тем самым имеет тип Explosion! который указывается как Explosion? .. Explosion!!
} catch (err: IllegalArgumentException) {
block.explosionResistance
}
}

View File

@ -13,6 +13,7 @@ import ru.dbotthepony.mc.otm.android.AndroidFeatureType
import ru.dbotthepony.mc.otm.android.AndroidResearch
import ru.dbotthepony.mc.otm.android.AndroidResearchType
import ru.dbotthepony.mc.otm.android.AndroidSwitchableFeature
import ru.dbotthepony.mc.otm.android.feature.ShockwaveFeature
import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.client.MatteryGUI
import ru.dbotthepony.mc.otm.client.minecraft
@ -353,6 +354,23 @@ class SwitchAndroidFeaturePacket(val type: AndroidFeatureType<*>, val newState:
}
}
object TriggerShockwavePacket : MatteryPacket {
override fun write(buff: FriendlyByteBuf) {
// no op
}
override fun play(context: Supplier<NetworkEvent.Context>) {
context.packetHandled = true
context.enqueueWork {
val shockwave = context.sender?.matteryPlayer?.getFeature(AndroidFeatures.SHOCKWAVE) as ShockwaveFeature? ?: return@enqueueWork
if (shockwave.cooldown <= 0 && shockwave.isActive && shockwave.airTicks > 0) {
shockwave.shockwave()
}
}
}
}
object MatteryPlayerNetworkChannel : MatteryNetworkChannel(
version = "1",
name = "player"
@ -375,5 +393,6 @@ object MatteryPlayerNetworkChannel : MatteryNetworkChannel(
add(ExoSuitMenuClose::class, { ExoSuitMenuClose }, PLAY_TO_SERVER)
add(SwitchAndroidFeaturePacket::class, SwitchAndroidFeaturePacket.Companion::read, PLAY_TO_SERVER)
add(TriggerShockwavePacket::class, { TriggerShockwavePacket }, PLAY_TO_SERVER)
}
}

View File

@ -19,6 +19,7 @@ object AndroidFeatures {
val NANOBOTS_ARMOR: AndroidFeatureType<*> by registry.register(MNames.NANOBOTS_ARMOR) { AndroidFeatureType(::NanobotsArmor) }
val EXTENDED_REACH: AndroidFeatureType<*> by registry.register(MNames.EXTENDED_REACH) { AndroidFeatureType(::ExtendedReach) }
val NIGHT_VISION: AndroidFeatureType<*> by registry.register(MNames.NIGHT_VISION) { AndroidFeatureType(::NightVisionFeature) }
val SHOCKWAVE: AndroidFeatureType<*> by registry.register(MNames.SHOCKWAVE) { AndroidFeatureType(::ShockwaveFeature) }
internal fun register(bus: IEventBus) {
registry.register(bus)

View File

@ -260,7 +260,7 @@ object AndroidResearch {
armorSpeedList.add(registry.register(MNames.NANOBOTS_ARMOR_SPEED_LIST[i]) {
AndroidResearchBuilder()
.withExperience(20 + i * 8)
.withExperience(18 + i * 6)
.withIconText(TextComponent((i + 1).toString()))
.withIcon(ICON_ARMOR)
.addPrerequisite(OverdriveThatMatters.loc(if (i > 0) MNames.NANOBOTS_ARMOR_SPEED_LIST[i - 1] else MNames.NANOBOTS_ARMOR))
@ -290,4 +290,14 @@ object AndroidResearch {
NANOBOTS_ARMOR_STRENGTH = RegistryObjectList(armorStrengthList)
ATTACK_BOOST = RegistryObjectList(attackBoostList)
}
val SHOCKWAVE: AndroidResearchType<*> by registry.register(MNames.SHOCKWAVE) {
AndroidResearchBuilder()
.withExperience(40)
.withDescription()
.withIcon(ICON_SHOCKWAVE)
.addFeatureResult(AndroidFeatures.SHOCKWAVE)
.addPrerequisite(ATTACK_BOOST[2])
.build()
}
}

View File

@ -169,6 +169,16 @@ class EMPDamageSource(entity: Entity? = null, inflictor: ItemStack? = null) : Ma
}
}
class ShockwaveDamageSource(entity: Entity? = null, inflictor: ItemStack? = null) : MatteryDamageSource(MRegistry.DAMAGE_SHOCKWAVE_NAME, entity, inflictor) {
init {
bypassArmor()
}
override fun scalesWithDifficulty(): Boolean {
return false
}
}
class PlasmaDamageSource(entity: Entity? = null, inflictor: ItemStack? = null) : MatteryDamageSource(MRegistry.DAMAGE_PLASMA_NAME, entity, inflictor) {
override fun scalesWithDifficulty(): Boolean {
return false

View File

@ -208,6 +208,7 @@ object MNames {
const val EXTENDED_REACH = "extended_reach"
const val NIGHT_VISION = "night_vision"
const val SHOCKWAVE = "shockwave"
const val IMPROVED_LIMBS = "improved_limbs"
// stats

View File

@ -211,6 +211,7 @@ object MRegistry {
val DAMAGE_EVENT_HORIZON = ImmutableDamageSource(DamageSource(DAMAGE_EVENT_HORIZON_ID).bypassMagic().bypassArmor())
val DAMAGE_HAWKING_RADIATION = ImmutableDamageSource(DamageSource(DAMAGE_HAWKING_RADIATION_ID))
const val DAMAGE_EMP_NAME = "otm_emp"
const val DAMAGE_SHOCKWAVE_NAME = "otm_shockwave"
const val DAMAGE_PLASMA_NAME = "otm_plasma"
val DAMAGE_EMP: DamageSource = ImmutableDamageSource(EMPDamageSource())