More weapon tests
This commit is contained in:
parent
4f1e2217fe
commit
2aed09593c
@ -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
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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 {
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user