More weapon tests

This commit is contained in:
DBotThePony 2022-03-13 21:46:11 +07:00
parent 4f1e2217fe
commit 2aed09593c
Signed by: DBot
GPG Key ID: DCC23B5715498507
9 changed files with 815 additions and 648 deletions

View File

@ -137,7 +137,7 @@ class ImpreciseFraction @JvmOverloads constructor(whole: BigInteger, decimal: Do
} }
// дробная часть равна нулю // дробная часть равна нулю
if (decimal == -0.0 || decimal == 0.0) { if (decimal == 0.0) {
if (!isZero(whole)) { if (!isZero(whole)) {
this.decimal = 0.0 this.decimal = 0.0
this.whole = whole this.whole = whole

View File

@ -0,0 +1,456 @@
package ru.dbotthepony.mc.otm.item.weapon
import com.mojang.math.Vector3f
import net.minecraft.client.Minecraft
import net.minecraft.client.model.HumanoidModel
import net.minecraft.client.renderer.block.model.ItemTransforms
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Rarity
import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.client.event.EntityViewRenderEvent
import net.minecraftforge.client.event.InputEvent
import net.minecraftforge.client.event.RenderHandEvent
import net.minecraftforge.client.event.RenderPlayerEvent
import net.minecraftforge.event.TickEvent
import net.minecraftforge.eventbus.api.SubscribeEvent
import net.minecraftforge.fml.LogicalSide
import net.minecraftforge.network.NetworkEvent
import ru.dbotthepony.kvector.util.linearInterpolation
import ru.dbotthepony.kvector.vector.Angle3f
import ru.dbotthepony.kvector.vector.ndouble.Vector3d
import ru.dbotthepony.mc.otm.*
import ru.dbotthepony.mc.otm.network.MatteryNetworking
import java.util.*
import java.util.function.Supplier
import kotlin.math.PI
import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.full.primaryConstructor
private val ItemStack.weaponDataTable get() = tag?.let(::WeaponDataTable)
enum class WeaponScopePacket(val scope: Boolean) {
SCOPE_IN(true), SCOPE_OUT(false);
fun write(buff: FriendlyByteBuf) {
buff.writeBoolean(scope)
}
fun play(supplier: Supplier<NetworkEvent.Context>) {
supplier.get().packetHandled = true
// TODO: Manual synchronization
supplier.get().enqueueWork {
val stack = supplier.get().sender!!.mainHandItem
val item = stack.item as? AbstractWeaponItem<*> ?: return@enqueueWork
item.dataTable(stack).wantsToScope = scope
}
}
companion object {
fun read(buff: FriendlyByteBuf) = if (buff.readBoolean()) SCOPE_IN else SCOPE_OUT
}
}
enum class WeaponFireInputPacket(val primary: Boolean) {
PRIMARY(true), SECONDARY(false);
fun write(buff: FriendlyByteBuf) {
buff.writeBoolean(primary)
}
fun play(supplier: Supplier<NetworkEvent.Context>) {
supplier.get().packetHandled = true
// TODO: Manual synchronization
supplier.get().enqueueWork {
val stack = supplier.get().sender!!.mainHandItem
val item = stack.item as? AbstractWeaponItem<*> ?: return@enqueueWork
// Listen server: client and server thread compete for lock
// so it is very likely item is being in predicted context
val predictedData = item.dataTable
item.dataTable = null
if (primary)
item.tryPrimaryFire(stack, supplier.get().sender!!)
else
item.trySecondaryFire(stack, supplier.get().sender!!)
(item as AbstractWeaponItem<WeaponDataTable>).dataTable = predictedData
}
}
companion object {
fun read(buff: FriendlyByteBuf) = if (buff.readBoolean()) PRIMARY else SECONDARY
}
}
open class WeaponDataTable(val tag: CompoundTag) {
var shotCooldown by tag.ints
var wantsToScope by tag.booleans
var scoped by tag.booleans
var scopeTicks by tag.ints
var nextPrimaryFire by tag.ints
var nextSecondaryFire by tag.ints
var uuid by tag.uuids
}
abstract class AbstractWeaponItem<D : WeaponDataTable>(val tables: KClass<D>, rarity: Rarity = Rarity.UNCOMMON) : Item(
Properties().tab(OverdriveThatMatters.INSTANCE.CREATIVE_TAB).stacksTo(1).rarity(rarity)) {
fun makeDataTable(tag: CompoundTag) = tables.primaryConstructor!!.call(tag)
/**
* Predicted data table, for client use only
*/
var dataTable: D? = null
set(value) {
field = if (value == null || value::class.isSubclassOf(tables)) {
value
} else {
throw ClassCastException("Reified generics: ${value::class.qualifiedName} cannot be cast to ${tables.qualifiedName}")
}
}
fun compatibleDataTable(value: WeaponDataTable?) = value == null || value::class.isSubclassOf(tables)
@Suppress("unchecked_cast")
fun dataTable(itemStack: ItemStack): D {
if (dataTable != null) {
return dataTable!!
}
return makeDataTable(itemStack.tagNotNull)
}
open fun holster(itemStack: ItemStack, player: Player, dt: D = dataTable(itemStack)) {
dt.wantsToScope = false
}
open fun deploy(itemStack: ItemStack, player: Player, dt: D = dataTable(itemStack)) {
dt.wantsToScope = false
if (dt.uuid == EMPTY_UUID) {
dt.uuid = UUID.randomUUID()
}
}
abstract val roundsPerMinute: Int
val roundsPerSecond get() = roundsPerMinute / 60
val fireCooldown get() = (1200 / roundsPerMinute).coerceAtLeast(1)
open val positionIdle get() = Vector3d(1.0, -0.5, -1.0)
open val positionIronSights get() = Vector3d(0.0, -0.23, -1.0)
open val rotIdle get() = Angle3f(PI.toFloat() / 36f, PI.toFloat() / 18f)
open val rotIronSights get() = Angle3f.ZERO
open val primaryAutomatic = true
open val secondaryAutomatic = true
open val ironSightsFOV: Double get() = 2.0
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) / scopingTime.toDouble()
}
abstract val scopingTime: Int
open fun think(itemStack: ItemStack, player: Player, dt: D = dataTable(itemStack)) {
if (dt.wantsToScope && dt.scopeTicks < scopingTime) {
dt.scopeTicks++
} else if (!dt.wantsToScope && dt.scopeTicks > 0) {
dt.scopeTicks--
}
}
open fun thinkHolstered(itemStack: ItemStack, player: Player, dt: D = dataTable(itemStack)) {
if (dt.shotCooldown > 0) {
dt.shotCooldown--
}
}
open fun think2(itemStack: ItemStack, player: Player, dt: D = dataTable(itemStack)) {
if (dt.nextPrimaryFire > 0) {
dt.nextPrimaryFire--
}
if (dt.nextSecondaryFire > 0) {
dt.nextSecondaryFire--
}
}
open fun primaryFire(itemStack: ItemStack, player: Player, dt: D = dataTable(itemStack)): Boolean {
return false
}
open fun secondaryFire(itemStack: ItemStack, player: Player, dt: D = dataTable(itemStack)): Boolean {
return false
}
fun tryPrimaryFire(itemStack: ItemStack, player: Player, dt: D = dataTable(itemStack)): Boolean {
if (dt.nextPrimaryFire <= 0 && primaryFire(itemStack, player, dt)) {
dt.nextPrimaryFire = fireCooldown
return true
}
return false
}
fun trySecondaryFire(itemStack: ItemStack, player: Player, dt: D = dataTable(itemStack)): Boolean {
if (dt.nextSecondaryFire <= 0 && secondaryFire(itemStack, player, dt)) {
dt.nextSecondaryFire = fireCooldown
return true
}
return false
}
override fun getDestroySpeed(p_41425_: ItemStack, p_41426_: BlockState): Float {
return 0f
}
override fun onLeftClickEntity(stack: ItemStack?, player: Player?, entity: Entity?): Boolean {
return true
}
companion object {
// shared
private val items = WeakHashMap<Player, ItemStack>()
// client only
private var inRender = false
var predictedData: WeaponDataTable? = null
@SubscribeEvent
@Suppress("unused")
fun fovHook(event: EntityViewRenderEvent.FieldOfView) {
val player = event.camera.entity as? Player ?: return
(player.mainHandItem.item as? AbstractWeaponItem<*>)?.let {
@Suppress("unchecked_cast")
if (it.compatibleDataTable(predictedData))
(it as AbstractWeaponItem<WeaponDataTable>).dataTable = predictedData
event.fov /= linearInterpolation(
it.ironSightsProgress(
player.mainHandItem,
event.partialTicks
) * 1.7 - 0.6, 1.0, it.ironSightsFOV
)
it.dataTable = null
}
}
@SubscribeEvent
@Suppress("unused")
fun playerRenderHook(event: RenderPlayerEvent.Pre) {
if (event.player.mainHandItem.item is AbstractWeaponItem<*> && event.player.offhandItem.isEmpty) {
event.renderer.model.rightArmPose = HumanoidModel.ArmPose.BOW_AND_ARROW
event.renderer.model.leftArmPose = HumanoidModel.ArmPose.EMPTY
}
}
@SubscribeEvent
@Suppress("unused")
fun clickHook(event: InputEvent.ClickInputEvent) {
val player = Minecraft.getInstance().player!!
if (player.mainHandItem.item is AbstractWeaponItem<*> && player.offhandItem.isEmpty && event.isAttack) {
event.isCanceled = true
event.setSwingHand(false)
}
}
@SubscribeEvent
@Suppress("unused", "unchecked_cast")
fun renderViewModel(event: RenderHandEvent) {
if (inRender) {
return
}
val stack = event.itemStack
val item = stack.item as? AbstractWeaponItem<*> ?: return
inRender = true
event.isCanceled = true
(item as AbstractWeaponItem<WeaponDataTable>).dataTable = predictedData
val player = Minecraft.getInstance().player!!
val pose = event.poseStack
val itemInHandRenderer = Minecraft.getInstance().itemInHandRenderer
pose.pushPose()
val progress = item.ironSightsProgress(stack, event.partialTicks.toDouble())
val (x, y, z) = Vector3d.bezier(
progress,
item.positionIdle,
item.positionIdle,
item.positionIdle,
Vector3d(0.0, -1.0, -1.0),
item.positionIronSights,
item.positionIronSights,
item.positionIronSights,
)
pose.translate(x, y, z)
val (pitch, yaw, roll) = Angle3f.bezier(
progress.toFloat(),
item.rotIdle,
item.rotIdle,
item.rotIdle,
item.rotIdle.copy(roll = -PI.toFloat() / 6f + item.rotIdle.roll),
item.rotIronSights,
item.rotIronSights,
item.rotIronSights,
item.rotIronSights,
item.rotIronSights,
)
pose.mulPose(Vector3f.ZP.rotation(roll))
pose.mulPose(Vector3f.YP.rotation(yaw))
pose.mulPose(Vector3f.XP.rotation(pitch))
itemInHandRenderer.renderItem(
player,
stack,
ItemTransforms.TransformType.FIRST_PERSON_RIGHT_HAND,
false,
pose,
event.multiBufferSource,
event.packedLight
)
pose.popPose()
item.dataTable = null
inRender = false
}
private val localPlayer: Player? get() {
return Minecraft.getInstance().player
}
private var firedPrimary = false
private var firedSecondary = false
private fun processClientInputs(item: AbstractWeaponItem<*>, itemStack: ItemStack) {
val minecraft = Minecraft.getInstance()
val player = minecraft.player ?: return
val dt = item.dataTable(itemStack)
if (!dt.wantsToScope && minecraft.options.keyUse.isDown) {
dt.wantsToScope = true
MatteryNetworking.CHANNEL.sendToServer(WeaponScopePacket.SCOPE_IN)
} else if (dt.wantsToScope && !minecraft.options.keyUse.isDown) {
dt.wantsToScope = false
MatteryNetworking.CHANNEL.sendToServer(WeaponScopePacket.SCOPE_OUT)
}
if (minecraft.options.keyAttack.isDown && (item.primaryAutomatic || !firedPrimary)) {
firedPrimary = true
if (item.tryPrimaryFire(itemStack, player)) {
MatteryNetworking.CHANNEL.sendToServer(WeaponFireInputPacket.PRIMARY)
}
} else if (!minecraft.options.keyAttack.isDown) {
firedPrimary = false
}
if (minecraft.options.keyAttack.isDown && (item.secondaryAutomatic || !firedSecondary)) {
firedSecondary = true
if (item.trySecondaryFire(itemStack, player)) {
MatteryNetworking.CHANNEL.sendToServer(WeaponFireInputPacket.SECONDARY)
}
} else if (!minecraft.options.keyAttack.isDown) {
firedSecondary = false
}
}
@SubscribeEvent
@Suppress("unused", "unchecked_cast")
fun tick(event: TickEvent.PlayerTickEvent) {
if (event.phase != TickEvent.Phase.START)
return
if (event.side == LogicalSide.CLIENT && event.player != localPlayer)
return
val held = event.player.mainHandItem
for (stack in event.player.inventory.items) {
if (held !== stack) {
(stack.item as? AbstractWeaponItem<*>)?.thinkHolstered(stack, event.player)
}
(stack.item as? AbstractWeaponItem<*>)?.let {
if (event.side == LogicalSide.CLIENT) {
(it as AbstractWeaponItem<WeaponDataTable>).dataTable = predictedData
}
it.think2(stack, event.player)
if (event.side == LogicalSide.CLIENT) {
it.dataTable = null
}
}
}
val prev = items[event.player]
val heldItem = held.item
if (held.weaponDataTable?.uuid != prev?.weaponDataTable?.uuid) {
(prev?.item as? AbstractWeaponItem<*>)?.let {
if (event.side == LogicalSide.CLIENT) {
(it as AbstractWeaponItem<WeaponDataTable>).dataTable = predictedData
}
it.holster(prev, event.player)
if (event.side == LogicalSide.CLIENT) {
it.dataTable = null
}
}
(held.item as? AbstractWeaponItem<*>)?.deploy(held, event.player)
items[event.player] = held
if (event.side == LogicalSide.CLIENT && heldItem is AbstractWeaponItem<*>) {
predictedData = heldItem.makeDataTable(held.tagNotNull.copy())
firedPrimary = false
firedSecondary = false
}
}
if (heldItem is AbstractWeaponItem<*>) {
// this can fail horribly, and die horribly, if weapon item changed,
// but its UUID did not (compound tag fraud)
if (event.side == LogicalSide.CLIENT) {
(heldItem as AbstractWeaponItem<WeaponDataTable>).dataTable = predictedData
processClientInputs(heldItem, held)
}
heldItem.think(held, event.player)
if (event.side == LogicalSide.CLIENT) {
heldItem.dataTable = null
}
}
}
}
}

View File

@ -1,628 +1,9 @@
package ru.dbotthepony.mc.otm.item.weapon package ru.dbotthepony.mc.otm.item.weapon
import com.mojang.math.Vector3f
import net.minecraft.ChatFormatting
import net.minecraft.client.Minecraft
import net.minecraft.client.model.HumanoidModel
import net.minecraft.client.renderer.block.model.ItemTransforms
import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.TranslatableComponent
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.entity.projectile.Arrow import net.minecraft.world.entity.projectile.Arrow
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Rarity
import net.minecraft.world.item.TooltipFlag
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.client.event.EntityViewRenderEvent
import net.minecraftforge.client.event.InputEvent
import net.minecraftforge.client.event.RenderHandEvent
import net.minecraftforge.client.event.RenderPlayerEvent
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ICapabilityProvider
import net.minecraftforge.common.util.INBTSerializable
import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.energy.CapabilityEnergy
import net.minecraftforge.event.TickEvent
import net.minecraftforge.eventbus.api.SubscribeEvent
import net.minecraftforge.fml.LogicalSide
import net.minecraftforge.network.NetworkEvent
import ru.dbotthepony.kvector.util.linearInterpolation
import ru.dbotthepony.kvector.vector.Angle3f
import ru.dbotthepony.kvector.vector.ndouble.Vector3d
import ru.dbotthepony.mc.otm.*
import ru.dbotthepony.mc.otm.capability.*
import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.menu.FormattingHelper
import ru.dbotthepony.mc.otm.network.MatteryNetworking
import java.util.*
import java.util.function.Supplier
import kotlin.math.PI
import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.full.primaryConstructor
enum class WeaponScopePacket(val scope: Boolean) {
SCOPE_IN(true), SCOPE_OUT(false);
fun write(buff: FriendlyByteBuf) {
buff.writeBoolean(scope)
}
fun play(supplier: Supplier<NetworkEvent.Context>) {
supplier.get().packetHandled = true
// TODO: Manual synchronization
supplier.get().enqueueWork {
val stack = supplier.get().sender!!.mainHandItem
val item = stack.item as? AbstractWeaponItem<*> ?: return@enqueueWork
item.dataTable(stack).wantsToScope = scope
}
}
companion object {
fun read(buff: FriendlyByteBuf) = if (buff.readBoolean()) SCOPE_IN else SCOPE_OUT
}
}
enum class WeaponFireInputPacket(val primary: Boolean) {
PRIMARY(true), SECONDARY(false);
fun write(buff: FriendlyByteBuf) {
buff.writeBoolean(primary)
}
fun play(supplier: Supplier<NetworkEvent.Context>) {
supplier.get().packetHandled = true
// TODO: Manual synchronization
supplier.get().enqueueWork {
val stack = supplier.get().sender!!.mainHandItem
val item = stack.item as? AbstractWeaponItem<*> ?: return@enqueueWork
// Listen server: client and server thread compete for lock
// so it is very likely item is being in predicted context
val predictedData = item.dataTable
item.dataTable = null
if (primary)
item.tryPrimaryFire(stack, supplier.get().sender!!)
else
item.trySecondaryFire(stack, supplier.get().sender!!)
(item as AbstractWeaponItem<WeaponDataTable>).dataTable = predictedData
}
}
companion object {
fun read(buff: FriendlyByteBuf) = if (buff.readBoolean()) PRIMARY else SECONDARY
}
}
open class WeaponDataTable(val tag: CompoundTag) {
var shotCooldown by tag.ints
var wantsToScope by tag.booleans
var scoped by tag.booleans
var scopeTicks by tag.ints
var nextPrimaryFire by tag.ints
var nextSecondaryFire by tag.ints
var uuid by tag.uuids
}
private val ItemStack.weaponDataTable get() = tag?.let(::WeaponDataTable)
abstract class AbstractWeaponItem<D : WeaponDataTable>(val tables: KClass<D>, rarity: Rarity = Rarity.UNCOMMON) : Item(Properties().tab(OverdriveThatMatters.INSTANCE.CREATIVE_TAB).stacksTo(1).rarity(rarity)) {
fun makeDataTable(tag: CompoundTag) = tables.primaryConstructor!!.call(tag)
/**
* Predicted data table, for client use only
*/
var dataTable: D? = null
set(value) {
field = if (value == null || value::class.isSubclassOf(tables)) {
value
} else {
throw ClassCastException("Reified generics: ${value::class.qualifiedName} cannot be cast to ${tables.qualifiedName}")
}
}
fun compatibleDataTable(value: WeaponDataTable?) = value == null || value::class.isSubclassOf(tables)
@Suppress("unchecked_cast")
fun dataTable(itemStack: ItemStack): D {
if (dataTable != null) {
return dataTable!!
}
return makeDataTable(itemStack.tagNotNull)
}
open fun holster(itemStack: ItemStack, player: Player, dt: D = dataTable(itemStack)) {
dt.wantsToScope = false
}
open fun deploy(itemStack: ItemStack, player: Player, dt: D = dataTable(itemStack)) {
dt.wantsToScope = false
if (dt.uuid == EMPTY_UUID) {
dt.uuid = UUID.randomUUID()
}
}
abstract val roundsPerMinute: Int
val roundsPerSecond get() = roundsPerMinute / 60
val fireCooldown get() = (1200 / roundsPerMinute).coerceAtLeast(1)
open val positionIdle get() = Vector3d(1.0, -0.5, -1.0)
open val positionIronSights get() = Vector3d(0.0, -0.55, -1.0)
open val rotIdle get() = Angle3f(PI.toFloat() / 36f, PI.toFloat() / 18f)
open val rotIronSights get() = Angle3f.ZERO
open val primaryAutomatic = true
open val secondaryAutomatic = true
open val ironSightsFOV: Double get() = 2.0
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) / scopingTime.toDouble()
}
abstract val scopingTime: Int
open fun think(itemStack: ItemStack, player: Player, dt: D = dataTable(itemStack)) {
if (dt.wantsToScope && dt.scopeTicks < scopingTime) {
dt.scopeTicks++
} else if (!dt.wantsToScope && dt.scopeTicks > 0) {
dt.scopeTicks--
}
}
open fun thinkHolstered(itemStack: ItemStack, player: Player, dt: D = dataTable(itemStack)) {
if (dt.shotCooldown > 0) {
dt.shotCooldown--
}
}
open fun think2(itemStack: ItemStack, player: Player, dt: D = dataTable(itemStack)) {
if (dt.nextPrimaryFire > 0) {
dt.nextPrimaryFire--
}
if (dt.nextSecondaryFire > 0) {
dt.nextSecondaryFire--
}
}
open fun primaryFire(itemStack: ItemStack, player: Player, dt: D = dataTable(itemStack)): Boolean {
return false
}
open fun secondaryFire(itemStack: ItemStack, player: Player, dt: D = dataTable(itemStack)): Boolean {
return false
}
fun tryPrimaryFire(itemStack: ItemStack, player: Player, dt: D = dataTable(itemStack)): Boolean {
if (dt.nextPrimaryFire <= 0 && primaryFire(itemStack, player, dt)) {
dt.nextPrimaryFire = fireCooldown
return true
}
return false
}
fun trySecondaryFire(itemStack: ItemStack, player: Player, dt: D = dataTable(itemStack)): Boolean {
if (dt.nextSecondaryFire <= 0 && secondaryFire(itemStack, player, dt)) {
dt.nextSecondaryFire = fireCooldown
return true
}
return false
}
override fun getDestroySpeed(p_41425_: ItemStack, p_41426_: BlockState): Float {
return 0f
}
override fun onLeftClickEntity(stack: ItemStack?, player: Player?, entity: Entity?): Boolean {
return true
}
companion object {
// shared
private val items = WeakHashMap<Player, ItemStack>()
// client only
private var inRender = false
var predictedData: WeaponDataTable? = null
@SubscribeEvent
@Suppress("unused")
fun fovHook(event: EntityViewRenderEvent.FieldOfView) {
val player = event.camera.entity as? Player ?: return
(player.mainHandItem.item as? AbstractWeaponItem<*>)?.let {
@Suppress("unchecked_cast")
if (it.compatibleDataTable(predictedData))
(it as AbstractWeaponItem<WeaponDataTable>).dataTable = predictedData
event.fov /= linearInterpolation(it.ironSightsProgress(player.mainHandItem, event.partialTicks), 1.0, it.ironSightsFOV)
it.dataTable = null
}
}
@SubscribeEvent
@Suppress("unused")
fun playerRenderHook(event: RenderPlayerEvent.Pre) {
if (event.player.mainHandItem.item is AbstractWeaponItem<*> && event.player.offhandItem.isEmpty) {
event.renderer.model.rightArmPose = HumanoidModel.ArmPose.BOW_AND_ARROW
event.renderer.model.leftArmPose = HumanoidModel.ArmPose.BOW_AND_ARROW
}
}
@SubscribeEvent
@Suppress("unused")
fun clickHook(event: InputEvent.ClickInputEvent) {
val player = Minecraft.getInstance().player!!
if (player.mainHandItem.item is AbstractWeaponItem<*> && player.offhandItem.isEmpty && event.isAttack) {
event.isCanceled = true
event.setSwingHand(false)
}
}
@SubscribeEvent
@Suppress("unused", "unchecked_cast")
fun renderViewModel(event: RenderHandEvent) {
if (inRender) {
return
}
val stack = event.itemStack
val item = stack.item as? AbstractWeaponItem<*> ?: return
inRender = true
event.isCanceled = true
(item as AbstractWeaponItem<WeaponDataTable>).dataTable = predictedData
val player = Minecraft.getInstance().player!!
val pose = event.poseStack
val itemInHandRenderer = Minecraft.getInstance().itemInHandRenderer
pose.pushPose()
val progress = item.ironSightsProgress(stack, event.partialTicks.toDouble())
val (x, y, z) = Vector3d.bezier(
progress,
item.positionIdle,
item.positionIdle,
item.positionIdle,
Vector3d(0.0, -1.0, -1.0),
item.positionIronSights,
item.positionIronSights,
item.positionIronSights,
)
pose.translate(x, y, z)
val (pitch, yaw, roll) = Angle3f.bezier(
progress.toFloat(),
item.rotIdle,
item.rotIdle,
item.rotIdle,
item.rotIdle.copy(roll = -PI.toFloat() / 6f + item.rotIdle.roll),
item.rotIronSights,
item.rotIronSights,
item.rotIronSights,
item.rotIronSights,
item.rotIronSights,
)
pose.mulPose(Vector3f.ZP.rotation(roll))
pose.mulPose(Vector3f.YP.rotation(yaw))
pose.mulPose(Vector3f.XP.rotation(pitch))
itemInHandRenderer.renderItem(
player,
stack,
ItemTransforms.TransformType.FIRST_PERSON_RIGHT_HAND,
false,
pose,
event.multiBufferSource,
event.packedLight
)
pose.popPose()
item.dataTable = null
inRender = false
}
private val localPlayer: Player? get() {
return Minecraft.getInstance().player
}
private var firedPrimary = false
private var firedSecondary = false
private fun processClientInputs(item: AbstractWeaponItem<*>, itemStack: ItemStack) {
val minecraft = Minecraft.getInstance()
val player = minecraft.player ?: return
val dt = item.dataTable(itemStack)
if (!dt.wantsToScope && minecraft.options.keyUse.isDown) {
dt.wantsToScope = true
MatteryNetworking.CHANNEL.sendToServer(WeaponScopePacket.SCOPE_IN)
} else if (dt.wantsToScope && !minecraft.options.keyUse.isDown) {
dt.wantsToScope = false
MatteryNetworking.CHANNEL.sendToServer(WeaponScopePacket.SCOPE_OUT)
}
if (minecraft.options.keyAttack.isDown && (item.primaryAutomatic || !firedPrimary)) {
firedPrimary = true
if (item.tryPrimaryFire(itemStack, player)) {
MatteryNetworking.CHANNEL.sendToServer(WeaponFireInputPacket.PRIMARY)
}
} else if (!minecraft.options.keyAttack.isDown) {
firedPrimary = false
}
if (minecraft.options.keyAttack.isDown && (item.secondaryAutomatic || !firedSecondary)) {
firedSecondary = true
if (item.trySecondaryFire(itemStack, player)) {
MatteryNetworking.CHANNEL.sendToServer(WeaponFireInputPacket.SECONDARY)
}
} else if (!minecraft.options.keyAttack.isDown) {
firedSecondary = false
}
}
@SubscribeEvent
@Suppress("unused", "unchecked_cast")
fun tick(event: TickEvent.PlayerTickEvent) {
if (event.phase != TickEvent.Phase.START)
return
if (event.side == LogicalSide.CLIENT && event.player != localPlayer)
return
val held = event.player.mainHandItem
for (stack in event.player.inventory.items) {
if (held !== stack) {
(stack.item as? AbstractWeaponItem<*>)?.thinkHolstered(stack, event.player)
}
(stack.item as? AbstractWeaponItem<*>)?.let {
if (event.side == LogicalSide.CLIENT) {
(it as AbstractWeaponItem<WeaponDataTable>).dataTable = predictedData
}
it.think2(stack, event.player)
if (event.side == LogicalSide.CLIENT) {
it.dataTable = null
}
}
}
val prev = items[event.player]
val heldItem = held.item
if (held.weaponDataTable?.uuid != prev?.weaponDataTable?.uuid) {
(prev?.item as? AbstractWeaponItem<*>)?.let {
if (event.side == LogicalSide.CLIENT) {
(it as AbstractWeaponItem<WeaponDataTable>).dataTable = predictedData
}
it.holster(prev, event.player)
if (event.side == LogicalSide.CLIENT) {
it.dataTable = null
}
}
(held.item as? AbstractWeaponItem<*>)?.deploy(held, event.player)
items[event.player] = held
if (event.side == LogicalSide.CLIENT && heldItem is AbstractWeaponItem<*>) {
predictedData = heldItem.makeDataTable(held.tagNotNull.copy())
firedPrimary = false
firedSecondary = false
}
}
if (heldItem is AbstractWeaponItem<*>) {
// this can fail horribly, and die horribly, if weapon item changed,
// but its UUID did not (compound tag fraud)
if (event.side == LogicalSide.CLIENT) {
(heldItem as AbstractWeaponItem<WeaponDataTable>).dataTable = predictedData
processClientInputs(heldItem, held)
}
heldItem.think(held, event.player)
if (event.side == LogicalSide.CLIENT) {
heldItem.dataTable = null
}
}
}
}
}
class PlasmaWeaponEnergy(val itemStack: ItemStack, private val innerCapacity: ImpreciseFraction) :
IMatteryEnergyStorage, ICapabilityProvider, INBTSerializable<CompoundTag> {
private val energyResolver = LazyOptional.of { this }
private var innerBatteryLevel = ImpreciseFraction.ZERO
var battery: ItemStack = ItemStack.EMPTY
override fun <T : Any> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
if (cap === MatteryCapability.ENERGY || cap === CapabilityEnergy.ENERGY) {
return energyResolver.cast()
}
return LazyOptional.empty()
}
override fun serializeNBT(): CompoundTag {
val nbt = CompoundTag()
nbt["battery_level"] = innerBatteryLevel.serializeNBT()
nbt["battery"] = battery.serializeNBT()
return nbt
}
override fun deserializeNBT(nbt: CompoundTag) {
innerBatteryLevel = ImpreciseFraction.deserializeNBT(nbt["battery_level"])
battery = ItemStack.of(nbt["battery"] as CompoundTag)
}
override fun extractEnergyOuter(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
return ImpreciseFraction.ZERO
}
override fun extractEnergyInner(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
if (!howMuch.isPositive)
return ImpreciseFraction.ZERO
@Suppress("NAME_SHADOWING")
var howMuch = howMuch
var totalExtracted = ImpreciseFraction.ZERO
if (!battery.isEmpty) {
battery.getCapability(CapabilityEnergy.ENERGY).ifPresentK {
val extracted = it.extractEnergy(howMuch, simulate)
if (extracted >= howMuch) {
return extracted
}
howMuch -= extracted
totalExtracted += extracted
}
}
val newEnergy = (innerBatteryLevel - howMuch).moreThanZero()
val diff = innerBatteryLevel - newEnergy
if (!simulate) {
innerBatteryLevel = newEnergy
}
return diff + totalExtracted
}
override fun receiveEnergyOuter(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
return receiveEnergyInner(howMuch, simulate)
}
override fun receiveEnergyInner(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
if (!howMuch.isPositive)
return ImpreciseFraction.ZERO
@Suppress("NAME_SHADOWING")
var howMuch = howMuch
var totalReceived = ImpreciseFraction.ZERO
if (!battery.isEmpty) {
battery.getCapability(CapabilityEnergy.ENERGY).ifPresentK {
val received = it.receiveEnergy(howMuch, simulate)
if (received >= howMuch) {
return received
}
howMuch -= received
totalReceived += received
}
}
if (innerBatteryLevel >= innerCapacity) {
return totalReceived
}
val newEnergy = (innerBatteryLevel - howMuch).min(innerCapacity)
val diff = newEnergy - innerBatteryLevel
if (!simulate) {
innerBatteryLevel = newEnergy
}
return diff + totalReceived
}
fun think() {
if (!battery.isEmpty && innerBatteryLevel < innerCapacity) {
battery.getCapability(CapabilityEnergy.ENERGY).ifPresentK {
innerBatteryLevel += it.extractEnergy(innerCapacity - innerBatteryLevel, false)
}
}
}
override fun canExtract(): Boolean {
return false
}
override fun canReceive(): Boolean {
return true
}
override val batteryLevel: ImpreciseFraction
get() = (battery.energy?.energyStoredMattery ?: ImpreciseFraction.ZERO) + innerBatteryLevel
override val maxBatteryLevel: ImpreciseFraction
get() = (battery.energy?.maxEnergyStoredMattery ?: ImpreciseFraction.ZERO) + innerCapacity
}
abstract class PlasmaWeaponItem<D : WeaponDataTable>(tables: KClass<D>, private val energyCapacity: ImpreciseFraction) : AbstractWeaponItem<D>(tables) {
override fun appendHoverText(
itemStack: ItemStack,
p_41422_: Level?,
p_41423_: MutableList<Component>,
p_41424_: TooltipFlag
) {
super.appendHoverText(itemStack, p_41422_, p_41423_, p_41424_)
itemStack.getCapability(MatteryCapability.ENERGY).ifPresentK {
p_41423_.add(
TranslatableComponent(
"otm.item.power.normal.storage",
FormattingHelper.formatPower(it.batteryLevel),
FormattingHelper.formatPower(it.maxBatteryLevel)
).withStyle(ChatFormatting.GRAY)
)
}
}
fun energyData(itemStack: ItemStack) = itemStack.matteryEnergy as PlasmaWeaponEnergy
override fun think2(itemStack: ItemStack, player: Player, dt: D) {
super.think2(itemStack, player, dt)
itemStack.getCapability(MatteryCapability.ENERGY).ifPresentK {
it as PlasmaWeaponEnergy
it.think()
}
}
override fun initCapabilities(stack: ItemStack, nbt: CompoundTag?): ICapabilityProvider {
return PlasmaWeaponEnergy(stack, energyCapacity)
}
}
class PlasmaRifleItem : PlasmaWeaponItem<WeaponDataTable>(WeaponDataTable::class, ImpreciseFraction(200_000)) { class PlasmaRifleItem : PlasmaWeaponItem<WeaponDataTable>(WeaponDataTable::class, ImpreciseFraction(200_000)) {
override val roundsPerMinute: Int = 400 override val roundsPerMinute: Int = 400
@ -631,6 +12,14 @@ class PlasmaRifleItem : PlasmaWeaponItem<WeaponDataTable>(WeaponDataTable::class
override fun primaryFire(itemStack: ItemStack, player: Player, dt: WeaponDataTable): Boolean { override fun primaryFire(itemStack: ItemStack, player: Player, dt: WeaponDataTable): Boolean {
println("FIER! ${Thread.currentThread()}") println("FIER! ${Thread.currentThread()}")
if (!player.level.isClientSide) {
val arrow = Arrow(player.level, player)
arrow.shootFromRotation(player, player.xRot, player.yRot, 0f, 2f, 0f)
player.level.addFreshEntity(arrow)
}
return true return true
} }

View File

@ -0,0 +1,182 @@
package ru.dbotthepony.mc.otm.item.weapon
import net.minecraft.ChatFormatting
import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.TranslatableComponent
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.TooltipFlag
import net.minecraft.world.level.Level
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ICapabilityProvider
import net.minecraftforge.common.util.INBTSerializable
import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.energy.CapabilityEnergy
import ru.dbotthepony.mc.otm.capability.*
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.ifPresentK
import ru.dbotthepony.mc.otm.menu.FormattingHelper
import ru.dbotthepony.mc.otm.set
import kotlin.reflect.KClass
class PlasmaWeaponEnergy(val itemStack: ItemStack, private val innerCapacity: ImpreciseFraction) :
IMatteryEnergyStorage, ICapabilityProvider, INBTSerializable<CompoundTag> {
private val energyResolver = LazyOptional.of { this }
private var innerBatteryLevel = ImpreciseFraction.ZERO
var battery: ItemStack = ItemStack.EMPTY
override fun <T : Any> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
if (cap === MatteryCapability.ENERGY || cap === CapabilityEnergy.ENERGY) {
return energyResolver.cast()
}
return LazyOptional.empty()
}
override fun serializeNBT(): CompoundTag {
val nbt = CompoundTag()
nbt["battery_level"] = innerBatteryLevel.serializeNBT()
nbt["battery"] = battery.serializeNBT()
return nbt
}
override fun deserializeNBT(nbt: CompoundTag) {
innerBatteryLevel = ImpreciseFraction.deserializeNBT(nbt["battery_level"])
battery = ItemStack.of(nbt["battery"] as CompoundTag)
}
override fun extractEnergyOuter(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
return ImpreciseFraction.ZERO
}
override fun extractEnergyInner(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
if (!howMuch.isPositive)
return ImpreciseFraction.ZERO
@Suppress("NAME_SHADOWING")
var howMuch = howMuch
var totalExtracted = ImpreciseFraction.ZERO
if (!battery.isEmpty) {
battery.getCapability(CapabilityEnergy.ENERGY).ifPresentK {
val extracted = it.extractEnergy(howMuch, simulate)
if (extracted >= howMuch) {
return extracted
}
howMuch -= extracted
totalExtracted += extracted
}
}
val newEnergy = (innerBatteryLevel - howMuch).moreThanZero()
val diff = innerBatteryLevel - newEnergy
if (!simulate) {
innerBatteryLevel = newEnergy
}
return diff + totalExtracted
}
override fun receiveEnergyOuter(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
return receiveEnergyInner(howMuch, simulate)
}
override fun receiveEnergyInner(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
if (!howMuch.isPositive)
return ImpreciseFraction.ZERO
@Suppress("NAME_SHADOWING")
var howMuch = howMuch
var totalReceived = ImpreciseFraction.ZERO
if (!battery.isEmpty) {
battery.getCapability(CapabilityEnergy.ENERGY).ifPresentK {
val received = it.receiveEnergy(howMuch, simulate)
if (received >= howMuch) {
return received
}
howMuch -= received
totalReceived += received
}
}
if (innerBatteryLevel >= innerCapacity) {
return totalReceived
}
val newEnergy = (innerBatteryLevel - howMuch).min(innerCapacity)
val diff = newEnergy - innerBatteryLevel
if (!simulate) {
innerBatteryLevel = newEnergy
}
return diff + totalReceived
}
fun think() {
if (!battery.isEmpty && innerBatteryLevel < innerCapacity) {
battery.getCapability(CapabilityEnergy.ENERGY).ifPresentK {
innerBatteryLevel += it.extractEnergy(innerCapacity - innerBatteryLevel, false)
}
}
}
override fun canExtract(): Boolean {
return false
}
override fun canReceive(): Boolean {
return true
}
override val batteryLevel: ImpreciseFraction
get() = (battery.energy?.energyStoredMattery ?: ImpreciseFraction.ZERO) + innerBatteryLevel
override val maxBatteryLevel: ImpreciseFraction
get() = (battery.energy?.maxEnergyStoredMattery ?: ImpreciseFraction.ZERO) + innerCapacity
}
abstract class PlasmaWeaponItem<D : WeaponDataTable>(tables: KClass<D>, private val energyCapacity: ImpreciseFraction) : AbstractWeaponItem<D>(tables) {
override fun appendHoverText(
itemStack: ItemStack,
p_41422_: Level?,
p_41423_: MutableList<Component>,
p_41424_: TooltipFlag
) {
super.appendHoverText(itemStack, p_41422_, p_41423_, p_41424_)
itemStack.getCapability(MatteryCapability.ENERGY).ifPresentK {
p_41423_.add(
TranslatableComponent(
"otm.item.power.normal.storage",
FormattingHelper.formatPower(it.batteryLevel),
FormattingHelper.formatPower(it.maxBatteryLevel)
).withStyle(ChatFormatting.GRAY)
)
}
}
fun energyData(itemStack: ItemStack) = itemStack.matteryEnergy as PlasmaWeaponEnergy
override fun think2(itemStack: ItemStack, player: Player, dt: D) {
super.think2(itemStack, player, dt)
itemStack.getCapability(MatteryCapability.ENERGY).ifPresentK {
it as PlasmaWeaponEnergy
it.think()
}
}
override fun initCapabilities(stack: ItemStack, nbt: CompoundTag?): ICapabilityProvider {
return PlasmaWeaponEnergy(stack, energyCapacity)
}
}

View File

@ -6,8 +6,159 @@ import net.minecraft.world.damagesource.DamageSource
import net.minecraft.world.entity.Entity import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.phys.Vec3
class EMPDamageSource(private val entity: Entity? = null) : DamageSource(MRegistry.DAMAGE_EMP_NAME) { class ImmutableDamageSource(private val parent: DamageSource) : DamageSource(parent.msgId) {
override fun equals(other: Any?): Boolean {
return parent == other
}
override fun hashCode(): Int {
return parent.hashCode()
}
override fun toString(): String {
return parent.toString()
}
override fun isProjectile(): Boolean {
return parent.isProjectile
}
override fun setProjectile(): DamageSource {
throw UnsupportedOperationException()
}
override fun isExplosion(): Boolean {
return parent.isExplosion
}
override fun setExplosion(): DamageSource {
throw UnsupportedOperationException()
}
override fun isBypassArmor(): Boolean {
return parent.isBypassArmor
}
override fun isDamageHelmet(): Boolean {
return parent.isDamageHelmet
}
override fun getFoodExhaustion(): Float {
return parent.foodExhaustion
}
override fun isBypassInvul(): Boolean {
return parent.isBypassInvul
}
override fun isBypassMagic(): Boolean {
return parent.isBypassMagic
}
override fun getDirectEntity(): Entity? {
return parent.directEntity
}
override fun getEntity(): Entity? {
return parent.entity
}
override fun bypassArmor(): DamageSource {
throw UnsupportedOperationException()
}
override fun damageHelmet(): DamageSource {
throw UnsupportedOperationException()
}
override fun bypassInvul(): DamageSource {
throw UnsupportedOperationException()
}
override fun bypassMagic(): DamageSource {
throw UnsupportedOperationException()
}
override fun setIsFire(): DamageSource {
throw UnsupportedOperationException()
}
override fun setNoAggro(): DamageSource {
throw UnsupportedOperationException()
}
override fun getLocalizedDeathMessage(p_19343_: LivingEntity): Component {
return super.getLocalizedDeathMessage(p_19343_)
}
override fun isFire(): Boolean {
return parent.isFire
}
override fun isNoAggro(): Boolean {
return parent.isNoAggro
}
override fun getMsgId(): String {
return parent.getMsgId()
}
override fun setScalesWithDifficulty(): DamageSource {
throw UnsupportedOperationException()
}
override fun scalesWithDifficulty(): Boolean {
return parent.scalesWithDifficulty()
}
override fun isMagic(): Boolean {
return parent.isMagic
}
override fun setMagic(): DamageSource {
throw UnsupportedOperationException()
}
override fun isFall(): Boolean {
return parent.isFall
}
override fun setIsFall(): DamageSource {
throw UnsupportedOperationException()
}
override fun isCreativePlayer(): Boolean {
return parent.isCreativePlayer
}
override fun getSourcePosition(): Vec3? {
return parent.sourcePosition
}
}
abstract class MatteryDamageSource(name: String, private val entity: Entity? = null, private val inflictor: ItemStack? = null) : DamageSource(name) {
override fun getLocalizedDeathMessage(victim: LivingEntity): Component {
val itemStack = inflictor ?: (entity as LivingEntity?)?.mainHandItem ?: ItemStack.EMPTY
if (!itemStack.isEmpty && itemStack.hasCustomHoverName()) {
return TranslatableComponent("death.attack.$msgId.player.item", victim.displayName, entity!!.displayName, itemStack.displayName)
}
if (entity != null) {
return TranslatableComponent("death.attack.$msgId.player", victim.displayName, entity.displayName)
}
return TranslatableComponent("death.attack.$msgId", victim.displayName)
}
final override fun getEntity(): Entity? {
return entity
}
}
class EMPDamageSource(entity: Entity? = null, inflictor: ItemStack? = null) : MatteryDamageSource(MRegistry.DAMAGE_EMP_NAME, entity, inflictor) {
init { init {
bypassArmor() bypassArmor()
bypassMagic() bypassMagic()
@ -16,22 +167,10 @@ class EMPDamageSource(private val entity: Entity? = null) : DamageSource(MRegist
override fun scalesWithDifficulty(): Boolean { override fun scalesWithDifficulty(): Boolean {
return false return false
} }
}
override fun getLocalizedDeathMessage(victim: LivingEntity): Component { class PlasmaDamageSource(entity: Entity? = null, inflictor: ItemStack? = null) : MatteryDamageSource(MRegistry.DAMAGE_PLASMA_NAME, entity, inflictor) {
val itemStack = (entity as LivingEntity?)?.mainHandItem ?: ItemStack.EMPTY override fun scalesWithDifficulty(): Boolean {
return false
if (!itemStack.isEmpty && itemStack.hasCustomHoverName()) {
return TranslatableComponent("death.attack.otm_emp.player.item", victim.displayName, entity!!.displayName, itemStack.displayName)
}
if (entity != null) {
return TranslatableComponent("death.attack.otm_emp.player", victim.displayName, entity.displayName)
}
return TranslatableComponent("death.attack.otm_emp", victim.displayName)
}
override fun getEntity(): Entity? {
return entity
} }
} }

View File

@ -21,7 +21,7 @@ import ru.dbotthepony.mc.otm.item.weapon.PlasmaRifleItem
object MItems { object MItems {
private val DEFAULT_PROPERTIES = Item.Properties().stacksTo(64).tab(OverdriveThatMatters.INSTANCE.CREATIVE_TAB) private val DEFAULT_PROPERTIES = Item.Properties().stacksTo(64).tab(OverdriveThatMatters.INSTANCE.CREATIVE_TAB)
private val registry = DeferredRegister.create(ForgeRegistries.ITEMS, OverdriveThatMatters.MOD_ID) private val registry: DeferredRegister<Item> = DeferredRegister.create(ForgeRegistries.ITEMS, OverdriveThatMatters.MOD_ID)
internal fun register() { internal fun register() {
registry.register(FMLJavaModLoadingContext.get().modEventBus) registry.register(FMLJavaModLoadingContext.get().modEventBus)

View File

@ -171,6 +171,9 @@ object MNames {
const val DAMAGE_ABSORBED = "damage_absorbed" const val DAMAGE_ABSORBED = "damage_absorbed"
const val HEALTH_REGENERATED = "health_regenerated" const val HEALTH_REGENERATED = "health_regenerated"
const val POWER_CONSUMED = "power_consumed" const val POWER_CONSUMED = "power_consumed"
// entities
const val PLASMA = "plasma_projectile"
} }
object StatNames { object StatNames {

View File

@ -96,6 +96,7 @@ object MRegistry {
MBlocks.register() MBlocks.register()
MBlockEntities.register() MBlockEntities.register()
MEntityTypes.register()
MMenus.register() MMenus.register()
MItems.register() MItems.register()
AndroidFeatures.register() AndroidFeatures.register()
@ -116,18 +117,15 @@ object MRegistry {
}) })
} }
val DAMAGE_BECOME_ANDROID = DamageSource("otm_become_android") val DAMAGE_BECOME_ANDROID = ImmutableDamageSource(DamageSource("otm_become_android").bypassArmor().bypassInvul().bypassMagic())
val DAMAGE_BECOME_HUMANE = DamageSource("otm_become_humane") val DAMAGE_BECOME_HUMANE = ImmutableDamageSource(DamageSource("otm_become_humane").bypassArmor().bypassInvul().bypassMagic())
val DAMAGE_EVENT_HORIZON = DamageSource("otm_event_horizon") val DAMAGE_EVENT_HORIZON = ImmutableDamageSource(DamageSource("otm_event_horizon").bypassMagic().bypassArmor())
val DAMAGE_HAWKING_RADIATION = DamageSource("otm_hawking_radiation") val DAMAGE_HAWKING_RADIATION = ImmutableDamageSource(DamageSource("otm_hawking_radiation"))
const val DAMAGE_EMP_NAME = "otm_emp" const val DAMAGE_EMP_NAME = "otm_emp"
val DAMAGE_EMP: DamageSource = EMPDamageSource() const val DAMAGE_PLASMA_NAME = "otm_plasma"
val DAMAGE_EMP: DamageSource = ImmutableDamageSource(EMPDamageSource())
init { init {
DAMAGE_BECOME_ANDROID.bypassArmor().bypassInvul().bypassMagic()
DAMAGE_BECOME_HUMANE.bypassArmor().bypassInvul().bypassMagic()
DAMAGE_EVENT_HORIZON.bypassMagic().bypassArmor()
DAMAGE_EMP.bypassMagic().bypassArmor()
// DAMAGE_HAWKING_RADIATION.bypassMagic().bypassArmor(); // DAMAGE_HAWKING_RADIATION.bypassMagic().bypassArmor();
} }

View File

@ -4,6 +4,6 @@ import net.minecraftforge.registries.IForgeRegistryEntry
import net.minecraftforge.registries.RegistryObject import net.minecraftforge.registries.RegistryObject
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
operator fun <T : IForgeRegistryEntry<T>> RegistryObject<T>.getValue(mItems: Any, property: KProperty<*>): T { operator fun <T : IForgeRegistryEntry<T>> RegistryObject<T>.getValue(thisRef: Any, property: KProperty<*>): T {
return get() return get()
} }