diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/ImpreciseFraction.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/ImpreciseFraction.kt index f47089dac..38ac50454 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/ImpreciseFraction.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/ImpreciseFraction.kt @@ -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)) { this.decimal = 0.0 this.whole = whole diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/item/weapon/AbstractWeaponItem.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/item/weapon/AbstractWeaponItem.kt new file mode 100644 index 000000000..761f42327 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/item/weapon/AbstractWeaponItem.kt @@ -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) { + 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) { + 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).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(val tables: KClass, 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() + + // 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).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).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).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).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).dataTable = predictedData + processClientInputs(heldItem, held) + } + + heldItem.think(held, event.player) + + if (event.side == LogicalSide.CLIENT) { + heldItem.dataTable = null + } + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/item/weapon/PlasmaRifleItem.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/item/weapon/PlasmaRifleItem.kt index bdad4d3a5..e89e1aee5 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/item/weapon/PlasmaRifleItem.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/item/weapon/PlasmaRifleItem.kt @@ -1,628 +1,9 @@ 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.projectile.Arrow -import net.minecraft.world.item.Item 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.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) { - 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) { - 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).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(val tables: KClass, 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() - - // 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).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).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).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).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).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 { - private val energyResolver = LazyOptional.of { this } - private var innerBatteryLevel = ImpreciseFraction.ZERO - - var battery: ItemStack = ItemStack.EMPTY - - override fun getCapability(cap: Capability, side: Direction?): LazyOptional { - 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(tables: KClass, private val energyCapacity: ImpreciseFraction) : AbstractWeaponItem(tables) { - override fun appendHoverText( - itemStack: ItemStack, - p_41422_: Level?, - p_41423_: MutableList, - 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::class, ImpreciseFraction(200_000)) { override val roundsPerMinute: Int = 400 @@ -631,6 +12,14 @@ class PlasmaRifleItem : PlasmaWeaponItem(WeaponDataTable::class override fun primaryFire(itemStack: ItemStack, player: Player, dt: WeaponDataTable): Boolean { 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 } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/item/weapon/PlasmaWeaponItem.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/item/weapon/PlasmaWeaponItem.kt new file mode 100644 index 000000000..2ff05a0a9 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/item/weapon/PlasmaWeaponItem.kt @@ -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 { + private val energyResolver = LazyOptional.of { this } + private var innerBatteryLevel = ImpreciseFraction.ZERO + + var battery: ItemStack = ItemStack.EMPTY + + override fun getCapability(cap: Capability, side: Direction?): LazyOptional { + 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(tables: KClass, private val energyCapacity: ImpreciseFraction) : AbstractWeaponItem(tables) { + override fun appendHoverText( + itemStack: ItemStack, + p_41422_: Level?, + p_41423_: MutableList, + 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) + } +} \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/DamageSources.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/DamageSources.kt index ff60299d2..2de599c70 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/DamageSources.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/DamageSources.kt @@ -6,8 +6,159 @@ import net.minecraft.world.damagesource.DamageSource import net.minecraft.world.entity.Entity import net.minecraft.world.entity.LivingEntity 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 { bypassArmor() bypassMagic() @@ -16,22 +167,10 @@ class EMPDamageSource(private val entity: Entity? = null) : DamageSource(MRegist override fun scalesWithDifficulty(): Boolean { return false } +} - override fun getLocalizedDeathMessage(victim: LivingEntity): Component { - val itemStack = (entity as LivingEntity?)?.mainHandItem ?: ItemStack.EMPTY - - 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 +class PlasmaDamageSource(entity: Entity? = null, inflictor: ItemStack? = null) : MatteryDamageSource(MRegistry.DAMAGE_PLASMA_NAME, entity, inflictor) { + override fun scalesWithDifficulty(): Boolean { + return false } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt index ea7fe5572..9e004f40c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt @@ -21,7 +21,7 @@ import ru.dbotthepony.mc.otm.item.weapon.PlasmaRifleItem object MItems { 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 = DeferredRegister.create(ForgeRegistries.ITEMS, OverdriveThatMatters.MOD_ID) internal fun register() { registry.register(FMLJavaModLoadingContext.get().modEventBus) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt index d06274d91..08a0b87ed 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MNames.kt @@ -171,6 +171,9 @@ object MNames { const val DAMAGE_ABSORBED = "damage_absorbed" const val HEALTH_REGENERATED = "health_regenerated" const val POWER_CONSUMED = "power_consumed" + + // entities + const val PLASMA = "plasma_projectile" } object StatNames { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt index e31cb484f..def523102 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt @@ -96,6 +96,7 @@ object MRegistry { MBlocks.register() MBlockEntities.register() + MEntityTypes.register() MMenus.register() MItems.register() AndroidFeatures.register() @@ -116,18 +117,15 @@ object MRegistry { }) } - val DAMAGE_BECOME_ANDROID = DamageSource("otm_become_android") - val DAMAGE_BECOME_HUMANE = DamageSource("otm_become_humane") - val DAMAGE_EVENT_HORIZON = DamageSource("otm_event_horizon") - val DAMAGE_HAWKING_RADIATION = DamageSource("otm_hawking_radiation") + val DAMAGE_BECOME_ANDROID = ImmutableDamageSource(DamageSource("otm_become_android").bypassArmor().bypassInvul().bypassMagic()) + val DAMAGE_BECOME_HUMANE = ImmutableDamageSource(DamageSource("otm_become_humane").bypassArmor().bypassInvul().bypassMagic()) + val DAMAGE_EVENT_HORIZON = ImmutableDamageSource(DamageSource("otm_event_horizon").bypassMagic().bypassArmor()) + val DAMAGE_HAWKING_RADIATION = ImmutableDamageSource(DamageSource("otm_hawking_radiation")) 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 { - 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(); } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/RegistryEntryExt.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/RegistryEntryExt.kt index 278e60ea0..e622d4d88 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/RegistryEntryExt.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/RegistryEntryExt.kt @@ -4,6 +4,6 @@ import net.minecraftforge.registries.IForgeRegistryEntry import net.minecraftforge.registries.RegistryObject import kotlin.reflect.KProperty -operator fun > RegistryObject.getValue(mItems: Any, property: KProperty<*>): T { +operator fun > RegistryObject.getValue(thisRef: Any, property: KProperty<*>): T { return get() }