Shockwave feature test, some refactoring
This commit is contained in:
parent
f8965e583c
commit
0713b7bb09
@ -40,3 +40,11 @@ fun ForgeConfigSpec.Builder.appendComment(vararg comments: String): ForgeConfigS
|
|||||||
builderContextField.set(context, reconstruct)
|
builderContextField.set(context, reconstruct)
|
||||||
return this
|
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)
|
||||||
|
}
|
||||||
|
@ -141,7 +141,28 @@ object ServerConfig {
|
|||||||
|
|
||||||
val NIGHT_VISION_POWER_DRAW by specBuilder.defineImpreciseFraction("nightVisionPowerDraw", ImpreciseFraction(8), ImpreciseFraction.ZERO)
|
val NIGHT_VISION_POWER_DRAW by specBuilder.defineImpreciseFraction("nightVisionPowerDraw", ImpreciseFraction(8), ImpreciseFraction.ZERO)
|
||||||
|
|
||||||
|
object Shockwave {
|
||||||
init {
|
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.pop()
|
||||||
|
|
||||||
specBuilder.comment("Tweaking of exosuits").push("exosuitPlayer")
|
specBuilder.comment("Tweaking of exosuits").push("exosuitPlayer")
|
||||||
|
@ -11,7 +11,7 @@ import java.io.DataInputStream
|
|||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
abstract class AndroidFeature(val type: AndroidFeatureType<*>, val android: MatteryPlayerCapability) : INBTSerializable<CompoundTag> {
|
abstract class AndroidFeature(val type: AndroidFeatureType<*>, val android: MatteryPlayerCapability) : INBTSerializable<CompoundTag> {
|
||||||
val entity get() = android.ply
|
val ply get() = android.ply
|
||||||
val synchronizer = FieldSynchronizer()
|
val synchronizer = FieldSynchronizer()
|
||||||
|
|
||||||
open var level by synchronizer.int(setter = setter@{ value, field, setByRemote ->
|
open var level by synchronizer.int(setter = setter@{ value, field, setByRemote ->
|
||||||
|
@ -22,6 +22,7 @@ abstract class AndroidSwitchableFeature(type: AndroidFeatureType<*>, android: Ma
|
|||||||
|
|
||||||
open val allowToSwitchByPlayer: Boolean get() = true
|
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)
|
abstract fun renderIcon(stack: PoseStack, x: Float, y: Float, width: Float, height: Float)
|
||||||
|
|
||||||
override fun serializeNBT(): CompoundTag {
|
override fun serializeNBT(): CompoundTag {
|
||||||
|
@ -9,7 +9,7 @@ import java.util.*
|
|||||||
|
|
||||||
class AttackBoost(android: MatteryPlayerCapability) : AndroidFeature(AndroidFeatures.LIMB_OVERCLOCKING, android) {
|
class AttackBoost(android: MatteryPlayerCapability) : AndroidFeature(AndroidFeatures.LIMB_OVERCLOCKING, android) {
|
||||||
override fun applyModifiers() {
|
override fun applyModifiers() {
|
||||||
val modifier = entity.getAttribute(Attributes.ATTACK_DAMAGE)
|
val modifier = ply.getAttribute(Attributes.ATTACK_DAMAGE)
|
||||||
|
|
||||||
if (modifier != null) {
|
if (modifier != null) {
|
||||||
modifier.removePermanentModifier(MODIFIER_ID)
|
modifier.removePermanentModifier(MODIFIER_ID)
|
||||||
@ -18,7 +18,7 @@ class AttackBoost(android: MatteryPlayerCapability) : AndroidFeature(AndroidFeat
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun removeModifiers() {
|
override fun removeModifiers() {
|
||||||
entity.getAttribute(Attributes.ATTACK_DAMAGE)?.removePermanentModifier(MODIFIER_ID)
|
ply.getAttribute(Attributes.ATTACK_DAMAGE)?.removePermanentModifier(MODIFIER_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -12,7 +12,7 @@ class ExtendedReach(android: MatteryPlayerCapability) : AndroidFeature(AndroidFe
|
|||||||
if (!ForgeMod.REACH_DISTANCE.isPresent)
|
if (!ForgeMod.REACH_DISTANCE.isPresent)
|
||||||
return
|
return
|
||||||
|
|
||||||
val reach = entity.getAttribute(ForgeMod.REACH_DISTANCE.get()) ?: return
|
val reach = ply.getAttribute(ForgeMod.REACH_DISTANCE.get()) ?: return
|
||||||
|
|
||||||
reach.removePermanentModifier(MODIFIER_ID)
|
reach.removePermanentModifier(MODIFIER_ID)
|
||||||
reach.addPermanentModifier(AttributeModifier(MODIFIER_ID, type.displayName.toString(), level + 1.0, AttributeModifier.Operation.ADDITION))
|
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)
|
if (!ForgeMod.REACH_DISTANCE.isPresent)
|
||||||
return
|
return
|
||||||
|
|
||||||
entity.getAttribute(ForgeMod.REACH_DISTANCE.get())?.removePermanentModifier(MODIFIER_ID)
|
ply.getAttribute(ForgeMod.REACH_DISTANCE.get())?.removePermanentModifier(MODIFIER_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -9,14 +9,14 @@ import java.util.*
|
|||||||
|
|
||||||
class LimbOverclocking(android: MatteryPlayerCapability) : AndroidFeature(AndroidFeatures.LIMB_OVERCLOCKING, android) {
|
class LimbOverclocking(android: MatteryPlayerCapability) : AndroidFeature(AndroidFeatures.LIMB_OVERCLOCKING, android) {
|
||||||
override fun applyModifiers() {
|
override fun applyModifiers() {
|
||||||
val speed = entity.getAttribute(Attributes.MOVEMENT_SPEED)
|
val speed = ply.getAttribute(Attributes.MOVEMENT_SPEED)
|
||||||
|
|
||||||
if (speed != null) {
|
if (speed != null) {
|
||||||
speed.removePermanentModifier(MODIFIER_ID)
|
speed.removePermanentModifier(MODIFIER_ID)
|
||||||
speed.addPermanentModifier(AttributeModifier(MODIFIER_ID, type.displayName.toString(), (level + 1) * 0.08, AttributeModifier.Operation.MULTIPLY_TOTAL))
|
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) {
|
if (attackSpeed != null) {
|
||||||
attackSpeed.removePermanentModifier(MODIFIER_ID)
|
attackSpeed.removePermanentModifier(MODIFIER_ID)
|
||||||
@ -25,8 +25,8 @@ class LimbOverclocking(android: MatteryPlayerCapability) : AndroidFeature(Androi
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun removeModifiers() {
|
override fun removeModifiers() {
|
||||||
entity.getAttribute(Attributes.MOVEMENT_SPEED)?.removePermanentModifier(MODIFIER_ID)
|
ply.getAttribute(Attributes.MOVEMENT_SPEED)?.removePermanentModifier(MODIFIER_ID)
|
||||||
entity.getAttribute(Attributes.ATTACK_SPEED)?.removePermanentModifier(MODIFIER_ID)
|
ply.getAttribute(Attributes.ATTACK_SPEED)?.removePermanentModifier(MODIFIER_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -9,7 +9,6 @@ import ru.dbotthepony.mc.otm.capability.extractEnergyInnerExact
|
|||||||
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
|
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
|
||||||
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
|
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
|
||||||
import ru.dbotthepony.mc.otm.registry.StatNames
|
import ru.dbotthepony.mc.otm.registry.StatNames
|
||||||
import ru.dbotthepony.mc.otm.container.set
|
|
||||||
import ru.dbotthepony.mc.otm.core.set
|
import ru.dbotthepony.mc.otm.core.set
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@ -47,7 +46,7 @@ class NanobotsArmor(android: MatteryPlayerCapability) : AndroidFeature(AndroidFe
|
|||||||
val powerExtracted = android.androidEnergy.extractEnergyInner(powerRequired, false)
|
val powerExtracted = android.androidEnergy.extractEnergyInner(powerRequired, false)
|
||||||
val realAbsorbed = (powerExtracted / ENERGY_PER_HITPOINT).toFloat()
|
val realAbsorbed = (powerExtracted / ENERGY_PER_HITPOINT).toFloat()
|
||||||
event.amount = event.amount - realAbsorbed
|
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--
|
layers--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
|
|||||||
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
|
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
|
||||||
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
|
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
|
||||||
import ru.dbotthepony.mc.otm.registry.StatNames
|
import ru.dbotthepony.mc.otm.registry.StatNames
|
||||||
import ru.dbotthepony.mc.otm.container.set
|
|
||||||
import ru.dbotthepony.mc.otm.core.set
|
import ru.dbotthepony.mc.otm.core.set
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@ -17,21 +16,21 @@ class NanobotsRegeneration(android: MatteryPlayerCapability) : AndroidFeature(An
|
|||||||
private var healTicks = 0
|
private var healTicks = 0
|
||||||
|
|
||||||
override fun tickServer() {
|
override fun tickServer() {
|
||||||
if (entity.health > 0f && entity.health < entity.maxHealth) {
|
if (ply.health > 0f && ply.health < ply.maxHealth) {
|
||||||
ticksPassed++
|
ticksPassed++
|
||||||
|
|
||||||
val waitTime = TICKS_BETWEEN_HEAL.getOrElse(healTicks) { TICKS_BETWEEN_HEAL.last() }
|
val waitTime = TICKS_BETWEEN_HEAL.getOrElse(healTicks) { TICKS_BETWEEN_HEAL.last() }
|
||||||
|
|
||||||
if (ticksPassed > waitTime) {
|
if (ticksPassed > waitTime) {
|
||||||
val missingHealth = entity.maxHealth - entity.health
|
val missingHealth = ply.maxHealth - ply.health
|
||||||
val power = ENERGY_PER_HITPOINT * missingHealth
|
val power = ENERGY_PER_HITPOINT * missingHealth
|
||||||
val extracted = android.androidEnergy.extractEnergyInner(power, false)
|
val extracted = android.androidEnergy.extractEnergyInner(power, false)
|
||||||
|
|
||||||
if (extracted.isPositive) {
|
if (extracted.isPositive) {
|
||||||
healTicks = (healTicks + 1).coerceAtMost(level)
|
healTicks = (healTicks + 1).coerceAtMost(level)
|
||||||
val healed = (extracted / ENERGY_PER_HITPOINT).toFloat()
|
val healed = (extracted / ENERGY_PER_HITPOINT).toFloat()
|
||||||
entity.heal(healed)
|
ply.heal(healed)
|
||||||
(entity as ServerPlayer?)?.awardStat(StatNames.HEALTH_REGENERATED, (healed * 10f).roundToInt())
|
(ply as ServerPlayer?)?.awardStat(StatNames.HEALTH_REGENERATED, (healed * 10f).roundToInt())
|
||||||
ticksPassed = 0
|
ticksPassed = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ package ru.dbotthepony.mc.otm.android.feature
|
|||||||
import com.mojang.blaze3d.vertex.PoseStack
|
import com.mojang.blaze3d.vertex.PoseStack
|
||||||
import net.minecraft.world.effect.MobEffectInstance
|
import net.minecraft.world.effect.MobEffectInstance
|
||||||
import net.minecraft.world.effect.MobEffects
|
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.ServerConfig
|
||||||
import ru.dbotthepony.mc.otm.android.AndroidSwitchableFeature
|
import ru.dbotthepony.mc.otm.android.AndroidSwitchableFeature
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
|
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,7 @@ class StepAssistFeature(android: MatteryPlayerCapability) : AndroidFeature(Andro
|
|||||||
if (!ForgeMod.STEP_HEIGHT_ADDITION.isPresent)
|
if (!ForgeMod.STEP_HEIGHT_ADDITION.isPresent)
|
||||||
return
|
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.removePermanentModifier(MODIFIER_ID)
|
||||||
reach.addPermanentModifier(AttributeModifier(MODIFIER_ID, type.displayName.toString(), level + 1.0, AttributeModifier.Operation.ADDITION))
|
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)
|
if (!ForgeMod.STEP_HEIGHT_ADDITION.isPresent)
|
||||||
return
|
return
|
||||||
|
|
||||||
entity.getAttribute(ForgeMod.STEP_HEIGHT_ADDITION.get())?.removePermanentModifier(MODIFIER_ID)
|
ply.getAttribute(ForgeMod.STEP_HEIGHT_ADDITION.get())?.removePermanentModifier(MODIFIER_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -31,7 +31,7 @@ import ru.dbotthepony.mc.otm.matter.getMatterValue
|
|||||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||||
import ru.dbotthepony.mc.otm.registry.MItems
|
import ru.dbotthepony.mc.otm.registry.MItems
|
||||||
import ru.dbotthepony.mc.otm.registry.MRegistry
|
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 ru.dbotthepony.mc.otm.core.set
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
@ -302,7 +302,7 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : BlockEn
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (gravitationStrength > 0.4) {
|
if (gravitationStrength > 0.4) {
|
||||||
val sphere = getSphericalShape((gravitationStrength * 6.0).roundToInt())
|
val sphere = getSphericalBlockPositions((gravitationStrength * 6.0).roundToInt())
|
||||||
|
|
||||||
if (sphere.size != lastSphereSizeOuter) {
|
if (sphere.size != lastSphereSizeOuter) {
|
||||||
lastSphereSizeOuter = sphere.size
|
lastSphereSizeOuter = sphere.size
|
||||||
@ -327,12 +327,14 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : BlockEn
|
|||||||
|
|
||||||
val eResist = try {
|
val eResist = try {
|
||||||
getBlock.getExplosionResistance(level, pos, null)
|
getBlock.getExplosionResistance(level, pos, null)
|
||||||
} catch (err: Throwable) {
|
} catch (err: NullPointerException) {
|
||||||
getBlock.block.getExplosionResistance()
|
getBlock.block.explosionResistance
|
||||||
// Потому что возможно какой-либо мод не ожидает что Explosion == null
|
// Потому что возможно какой-либо мод не ожидает что Explosion == null
|
||||||
// особенно учитывая что интерфейс IForgeBlock не имеет @ParamsAreNonnullByDefault
|
// особенно учитывая что интерфейс IForgeBlock не имеет @ParamsAreNonnullByDefault
|
||||||
// и аргумент не помечен как @Nullable
|
// и аргумент не помечен как @Nullable
|
||||||
// тем самым имеет тип Explosion! который указывается как Explosion? .. Explosion!!
|
// тем самым имеет тип Explosion! который указывается как Explosion? .. Explosion!!
|
||||||
|
} catch (err: IllegalArgumentException) {
|
||||||
|
getBlock.block.explosionResistance
|
||||||
}
|
}
|
||||||
|
|
||||||
var strengthLinear = sqrt(blockPos.distSqr(pos))
|
var strengthLinear = sqrt(blockPos.distSqr(pos))
|
||||||
@ -362,49 +364,5 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : BlockEn
|
|||||||
const val ITERATIONS = 30_000
|
const val ITERATIONS = 30_000
|
||||||
val BASELINE_MASS = ImpreciseFraction(1_000)
|
val BASELINE_MASS = ImpreciseFraction(1_000)
|
||||||
val HAWKING_MASS_LOSE_STEP = ImpreciseFraction("-0.1")
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,10 +2,14 @@ package ru.dbotthepony.mc.otm.core
|
|||||||
|
|
||||||
import com.mojang.math.Quaternion
|
import com.mojang.math.Quaternion
|
||||||
import com.mojang.math.Vector3f
|
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.BlockPos
|
||||||
import net.minecraft.core.Direction
|
import net.minecraft.core.Direction
|
||||||
import net.minecraft.core.Vec3i
|
import net.minecraft.core.Vec3i
|
||||||
import net.minecraft.world.phys.Vec3
|
import net.minecraft.world.phys.Vec3
|
||||||
|
import java.lang.ref.SoftReference
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
|
|
||||||
typealias Vector = Vec3
|
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 Direction.times(int: Int): Vec3i = this.normal.multiply(int)
|
||||||
operator fun Vec3i.times(double: Double): Vector = Vector(x * double, y * double, z * double)
|
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 {
|
fun BlockPos.asVector(): Vector {
|
||||||
return Vector(x + 0.5, y + 0.5, z + 0.5)
|
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 Direction.unaryMinus(): Direction = this.opposite
|
||||||
operator fun Vec3i.unaryMinus(): Vec3i = Vec3i(-x, -y, -z)
|
operator fun Vec3i.unaryMinus(): Vec3i = Vec3i(-x, -y, -z)
|
||||||
operator fun BlockPos.unaryMinus(): BlockPos = BlockPos(-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
|
||||||
|
}
|
||||||
|
@ -6,6 +6,7 @@ package ru.dbotthepony.mc.otm.core
|
|||||||
import com.google.common.collect.ImmutableList
|
import com.google.common.collect.ImmutableList
|
||||||
import com.google.gson.JsonElement
|
import com.google.gson.JsonElement
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.nbt.ByteArrayTag
|
import net.minecraft.nbt.ByteArrayTag
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.nbt.Tag
|
import net.minecraft.nbt.Tag
|
||||||
@ -14,6 +15,8 @@ import net.minecraft.resources.ResourceLocation
|
|||||||
import net.minecraft.world.entity.Entity
|
import net.minecraft.world.entity.Entity
|
||||||
import net.minecraft.world.item.Item
|
import net.minecraft.world.item.Item
|
||||||
import net.minecraft.world.item.ItemStack
|
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.StateHolder
|
||||||
import net.minecraft.world.level.block.state.properties.Property
|
import net.minecraft.world.level.block.state.properties.Property
|
||||||
import net.minecraft.world.phys.Vec3
|
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 {
|
operator fun <R> (() -> R).getValue(thisRef: Any, property: KProperty<*>): R {
|
||||||
return invoke()
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -13,6 +13,7 @@ import ru.dbotthepony.mc.otm.android.AndroidFeatureType
|
|||||||
import ru.dbotthepony.mc.otm.android.AndroidResearch
|
import ru.dbotthepony.mc.otm.android.AndroidResearch
|
||||||
import ru.dbotthepony.mc.otm.android.AndroidResearchType
|
import ru.dbotthepony.mc.otm.android.AndroidResearchType
|
||||||
import ru.dbotthepony.mc.otm.android.AndroidSwitchableFeature
|
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.capability.matteryPlayer
|
||||||
import ru.dbotthepony.mc.otm.client.MatteryGUI
|
import ru.dbotthepony.mc.otm.client.MatteryGUI
|
||||||
import ru.dbotthepony.mc.otm.client.minecraft
|
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(
|
object MatteryPlayerNetworkChannel : MatteryNetworkChannel(
|
||||||
version = "1",
|
version = "1",
|
||||||
name = "player"
|
name = "player"
|
||||||
@ -375,5 +393,6 @@ object MatteryPlayerNetworkChannel : MatteryNetworkChannel(
|
|||||||
add(ExoSuitMenuClose::class, { ExoSuitMenuClose }, PLAY_TO_SERVER)
|
add(ExoSuitMenuClose::class, { ExoSuitMenuClose }, PLAY_TO_SERVER)
|
||||||
|
|
||||||
add(SwitchAndroidFeaturePacket::class, SwitchAndroidFeaturePacket.Companion::read, PLAY_TO_SERVER)
|
add(SwitchAndroidFeaturePacket::class, SwitchAndroidFeaturePacket.Companion::read, PLAY_TO_SERVER)
|
||||||
|
add(TriggerShockwavePacket::class, { TriggerShockwavePacket }, PLAY_TO_SERVER)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ object AndroidFeatures {
|
|||||||
val NANOBOTS_ARMOR: AndroidFeatureType<*> by registry.register(MNames.NANOBOTS_ARMOR) { AndroidFeatureType(::NanobotsArmor) }
|
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 EXTENDED_REACH: AndroidFeatureType<*> by registry.register(MNames.EXTENDED_REACH) { AndroidFeatureType(::ExtendedReach) }
|
||||||
val NIGHT_VISION: AndroidFeatureType<*> by registry.register(MNames.NIGHT_VISION) { AndroidFeatureType(::NightVisionFeature) }
|
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) {
|
internal fun register(bus: IEventBus) {
|
||||||
registry.register(bus)
|
registry.register(bus)
|
||||||
|
@ -260,7 +260,7 @@ object AndroidResearch {
|
|||||||
|
|
||||||
armorSpeedList.add(registry.register(MNames.NANOBOTS_ARMOR_SPEED_LIST[i]) {
|
armorSpeedList.add(registry.register(MNames.NANOBOTS_ARMOR_SPEED_LIST[i]) {
|
||||||
AndroidResearchBuilder()
|
AndroidResearchBuilder()
|
||||||
.withExperience(20 + i * 8)
|
.withExperience(18 + i * 6)
|
||||||
.withIconText(TextComponent((i + 1).toString()))
|
.withIconText(TextComponent((i + 1).toString()))
|
||||||
.withIcon(ICON_ARMOR)
|
.withIcon(ICON_ARMOR)
|
||||||
.addPrerequisite(OverdriveThatMatters.loc(if (i > 0) MNames.NANOBOTS_ARMOR_SPEED_LIST[i - 1] else MNames.NANOBOTS_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)
|
NANOBOTS_ARMOR_STRENGTH = RegistryObjectList(armorStrengthList)
|
||||||
ATTACK_BOOST = RegistryObjectList(attackBoostList)
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
class PlasmaDamageSource(entity: Entity? = null, inflictor: ItemStack? = null) : MatteryDamageSource(MRegistry.DAMAGE_PLASMA_NAME, entity, inflictor) {
|
||||||
override fun scalesWithDifficulty(): Boolean {
|
override fun scalesWithDifficulty(): Boolean {
|
||||||
return false
|
return false
|
||||||
|
@ -208,6 +208,7 @@ object MNames {
|
|||||||
|
|
||||||
const val EXTENDED_REACH = "extended_reach"
|
const val EXTENDED_REACH = "extended_reach"
|
||||||
const val NIGHT_VISION = "night_vision"
|
const val NIGHT_VISION = "night_vision"
|
||||||
|
const val SHOCKWAVE = "shockwave"
|
||||||
const val IMPROVED_LIMBS = "improved_limbs"
|
const val IMPROVED_LIMBS = "improved_limbs"
|
||||||
|
|
||||||
// stats
|
// stats
|
||||||
|
@ -211,6 +211,7 @@ object MRegistry {
|
|||||||
val DAMAGE_EVENT_HORIZON = ImmutableDamageSource(DamageSource(DAMAGE_EVENT_HORIZON_ID).bypassMagic().bypassArmor())
|
val DAMAGE_EVENT_HORIZON = ImmutableDamageSource(DamageSource(DAMAGE_EVENT_HORIZON_ID).bypassMagic().bypassArmor())
|
||||||
val DAMAGE_HAWKING_RADIATION = ImmutableDamageSource(DamageSource(DAMAGE_HAWKING_RADIATION_ID))
|
val DAMAGE_HAWKING_RADIATION = ImmutableDamageSource(DamageSource(DAMAGE_HAWKING_RADIATION_ID))
|
||||||
const val DAMAGE_EMP_NAME = "otm_emp"
|
const val DAMAGE_EMP_NAME = "otm_emp"
|
||||||
|
const val DAMAGE_SHOCKWAVE_NAME = "otm_shockwave"
|
||||||
const val DAMAGE_PLASMA_NAME = "otm_plasma"
|
const val DAMAGE_PLASMA_NAME = "otm_plasma"
|
||||||
val DAMAGE_EMP: DamageSource = ImmutableDamageSource(EMPDamageSource())
|
val DAMAGE_EMP: DamageSource = ImmutableDamageSource(EMPDamageSource())
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user