diff options
Diffstat (limited to 'src/main/kotlin/events')
-rw-r--r-- | src/main/kotlin/events/CustomItemModelEvent.kt | 60 | ||||
-rw-r--r-- | src/main/kotlin/events/EntityRenderTintEvent.kt | 66 | ||||
-rw-r--r-- | src/main/kotlin/events/EntityUpdateEvent.kt | 53 | ||||
-rw-r--r-- | src/main/kotlin/events/FirmamentEventBus.kt | 3 | ||||
-rw-r--r-- | src/main/kotlin/events/IsSlotProtectedEvent.kt | 81 | ||||
-rw-r--r-- | src/main/kotlin/events/PlayerInventoryUpdate.kt | 19 | ||||
-rw-r--r-- | src/main/kotlin/events/WorldKeyboardEvent.kt | 17 | ||||
-rw-r--r-- | src/main/kotlin/events/WorldMouseMoveEvent.kt | 5 |
8 files changed, 243 insertions, 61 deletions
diff --git a/src/main/kotlin/events/CustomItemModelEvent.kt b/src/main/kotlin/events/CustomItemModelEvent.kt index 11528fd..7b86980 100644 --- a/src/main/kotlin/events/CustomItemModelEvent.kt +++ b/src/main/kotlin/events/CustomItemModelEvent.kt @@ -1,23 +1,75 @@ package moe.nea.firmament.events +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull +import net.minecraft.component.DataComponentTypes import net.minecraft.item.ItemStack import net.minecraft.util.Identifier +import moe.nea.firmament.util.collections.WeakCache +import moe.nea.firmament.util.collections.WeakCache.CacheFunction +import moe.nea.firmament.util.mc.IntrospectableItemModelManager // TODO: assert an order on these events data class CustomItemModelEvent( val itemStack: ItemStack, + val itemModelManager: IntrospectableItemModelManager, var overrideModel: Identifier? = null, ) : FirmamentEvent() { companion object : FirmamentEventBus<CustomItemModelEvent>() { + val weakCache = + object : WeakCache<ItemStack, IntrospectableItemModelManager, Optional<Identifier>>("ItemModelIdentifier") { + override fun mkRef( + key: ItemStack, + extraData: IntrospectableItemModelManager + ): WeakCache<ItemStack, IntrospectableItemModelManager, Optional<Identifier>>.Ref { + return IRef(key, extraData) + } + + inner class IRef(weakInstance: ItemStack, data: IntrospectableItemModelManager) : + Ref(weakInstance, data) { + override fun shouldBeEvicted(): Boolean = false + val isSimpleStack = weakInstance.componentChanges.isEmpty || (weakInstance.componentChanges.size() == 1 && weakInstance.get( + DataComponentTypes.CUSTOM_DATA)?.isEmpty == true) + val item = weakInstance.item + override fun hashCode(): Int { + if (isSimpleStack) + return Objects.hash(item, extraData) + return super.hashCode() + } + + override fun equals(other: Any?): Boolean { + if (other is IRef && isSimpleStack) { + return other.isSimpleStack && item == other.item + } + return super.equals(other) + } + } + } + val cache = CacheFunction.WithExtraData(weakCache, ::getModelIdentifier0) + @JvmStatic - fun getModelIdentifier(itemStack: ItemStack?): Identifier? { - // TODO: Re-add memoization and add an error / warning if the model does not exist + fun getModelIdentifier(itemStack: ItemStack?, itemModelManager: IntrospectableItemModelManager): Identifier? { if (itemStack == null) return null - return publish(CustomItemModelEvent(itemStack)).overrideModel + return cache.invoke(itemStack, itemModelManager).getOrNull() + } + + fun getModelIdentifier0( + itemStack: ItemStack, + itemModelManager: IntrospectableItemModelManager + ): Optional<Identifier> { + // TODO: add an error / warning if the model does not exist + return Optional.ofNullable(publish(CustomItemModelEvent(itemStack, itemModelManager)).overrideModel) } } fun overrideIfExists(overrideModel: Identifier) { - this.overrideModel = overrideModel + if (itemModelManager.hasModel_firmament(overrideModel)) + this.overrideModel = overrideModel + } + + fun overrideIfEmpty(identifier: Identifier) { + if (overrideModel == null) + overrideModel = identifier } } diff --git a/src/main/kotlin/events/EntityRenderTintEvent.kt b/src/main/kotlin/events/EntityRenderTintEvent.kt new file mode 100644 index 0000000..29b888b --- /dev/null +++ b/src/main/kotlin/events/EntityRenderTintEvent.kt @@ -0,0 +1,66 @@ +package moe.nea.firmament.events + +import net.minecraft.client.render.GameRenderer +import net.minecraft.client.render.OverlayTexture +import net.minecraft.client.render.entity.state.EntityRenderState +import net.minecraft.entity.Entity +import net.minecraft.entity.LivingEntity +import moe.nea.firmament.util.render.TintedOverlayTexture + +/** + * Change the tint color of a [LivingEntity] + */ +class EntityRenderTintEvent( + val entity: Entity, + val renderState: HasTintRenderState +) : FirmamentEvent.Cancellable() { + init { + if (entity !is LivingEntity) { + cancel() + } + } + + companion object : FirmamentEventBus<EntityRenderTintEvent>() { + /** + * Static variable containing an override for [GameRenderer.getOverlayTexture]. Should be only set briefly. + * + * This variable only affects render layers that naturally make use of the overlay texture, have proper overlay UVs set (`overlay u != 0`), and have a shader that makes use of the overlay (does not have the `NO_OVERLAY` flag set in its json definition). + * + * Currently supported layers: [net.minecraft.client.render.entity.equipment.EquipmentRenderer], [net.minecraft.client.render.entity.model.PlayerEntityModel], as well as some others naturally. + * + * @see moe.nea.firmament.mixins.render.entitytints.ReplaceOverlayTexture + * @see TintedOverlayTexture + */ + @JvmField + var overlayOverride: OverlayTexture? = null + } + + @Suppress("PropertyName", "FunctionName") + interface HasTintRenderState { + /** + * Multiplicative tint applied before the overlay. + */ + var tint_firmament: Int + + /** + * Must be set for [tint_firmament] to have any effect. + */ + var hasTintOverride_firmament: Boolean + + // TODO: allow for more specific selection of which layers get tinted + /** + * Specify a [TintedOverlayTexture] to be used. This does not apply to render layers not using the overlay texture. + * @see overlayOverride + */ + var overlayTexture_firmament: TintedOverlayTexture? + fun reset_firmament() + + companion object { + @JvmStatic + fun cast(state: EntityRenderState): HasTintRenderState { + return state as HasTintRenderState + } + } + } + +} diff --git a/src/main/kotlin/events/EntityUpdateEvent.kt b/src/main/kotlin/events/EntityUpdateEvent.kt index d091984..fec2fa5 100644 --- a/src/main/kotlin/events/EntityUpdateEvent.kt +++ b/src/main/kotlin/events/EntityUpdateEvent.kt @@ -1,10 +1,14 @@ - package moe.nea.firmament.events +import com.mojang.datafixers.util.Pair import net.minecraft.entity.Entity +import net.minecraft.entity.EquipmentSlot import net.minecraft.entity.LivingEntity import net.minecraft.entity.data.DataTracker +import net.minecraft.item.ItemStack import net.minecraft.network.packet.s2c.play.EntityAttributesS2CPacket +import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.util.MC /** * This event is fired when some entity properties are updated. @@ -13,19 +17,44 @@ import net.minecraft.network.packet.s2c.play.EntityAttributesS2CPacket * *after* the values have been applied to the entity. */ sealed class EntityUpdateEvent : FirmamentEvent() { - companion object : FirmamentEventBus<EntityUpdateEvent>() + companion object : FirmamentEventBus<EntityUpdateEvent>() { + @Subscribe + fun onPlayerInventoryUpdate(event: PlayerInventoryUpdate) { + val p = MC.player ?: return + val updatedSlots = listOf( + EquipmentSlot.HEAD to 39, + EquipmentSlot.CHEST to 38, + EquipmentSlot.LEGS to 37, + EquipmentSlot.FEET to 36, + EquipmentSlot.OFFHAND to 40, + EquipmentSlot.MAINHAND to p.inventory.selectedSlot, // TODO: also equipment update when you swap your selected slot perhaps + ).mapNotNull { (slot, stackIndex) -> + val slotIndex = p.playerScreenHandler.getSlotIndex(p.inventory, stackIndex).asInt + event.getOrNull(slotIndex)?.let { + Pair.of(slot, it) + } + } + if (updatedSlots.isNotEmpty()) + publish(EquipmentUpdate(p, updatedSlots)) + } + } + + abstract val entity: Entity - abstract val entity: Entity + data class AttributeUpdate( + override val entity: LivingEntity, + val attributes: List<EntityAttributesS2CPacket.Entry>, + ) : EntityUpdateEvent() - data class AttributeUpdate( - override val entity: LivingEntity, - val attributes: List<EntityAttributesS2CPacket.Entry>, - ) : EntityUpdateEvent() + data class TrackedDataUpdate( + override val entity: Entity, + val trackedValues: List<DataTracker.SerializedEntry<*>>, + ) : EntityUpdateEvent() - data class TrackedDataUpdate( - override val entity: Entity, - val trackedValues: List<DataTracker.SerializedEntry<*>>, - ) : EntityUpdateEvent() + data class EquipmentUpdate( + override val entity: Entity, + val newEquipment: List<Pair<EquipmentSlot, ItemStack>>, + ) : EntityUpdateEvent() -// TODO: onEntityPassengersSet, onEntityAttach?, onEntityEquipmentUpdate, onEntityStatusEffect +// TODO: onEntityPassengersSet, onEntityAttach?, onEntityStatusEffect } diff --git a/src/main/kotlin/events/FirmamentEventBus.kt b/src/main/kotlin/events/FirmamentEventBus.kt index 71331d1..af4e16a 100644 --- a/src/main/kotlin/events/FirmamentEventBus.kt +++ b/src/main/kotlin/events/FirmamentEventBus.kt @@ -3,6 +3,7 @@ package moe.nea.firmament.events import java.util.concurrent.CopyOnWriteArrayList import org.apache.commons.lang3.reflect.TypeUtils import moe.nea.firmament.Firmament +import moe.nea.firmament.util.ErrorUtil import moe.nea.firmament.util.MC /** @@ -48,7 +49,7 @@ open class FirmamentEventBus<T : FirmamentEvent> { val klass = e.javaClass if (!function.knownErrors.contains(klass) || Firmament.DEBUG) { function.knownErrors.add(klass) - Firmament.logger.error("Caught exception during processing event $event by $function", e) + ErrorUtil.softError("Caught exception during processing event $event by $function", e) } } } diff --git a/src/main/kotlin/events/IsSlotProtectedEvent.kt b/src/main/kotlin/events/IsSlotProtectedEvent.kt index cd2b676..8fe0a96 100644 --- a/src/main/kotlin/events/IsSlotProtectedEvent.kt +++ b/src/main/kotlin/events/IsSlotProtectedEvent.kt @@ -1,5 +1,3 @@ - - package moe.nea.firmament.events import net.minecraft.item.ItemStack @@ -8,39 +6,60 @@ import net.minecraft.screen.slot.SlotActionType import net.minecraft.text.Text import moe.nea.firmament.util.CommonSoundEffects import moe.nea.firmament.util.MC +import moe.nea.firmament.util.grey +import moe.nea.firmament.util.hover +import moe.nea.firmament.util.red +import moe.nea.firmament.util.tr data class IsSlotProtectedEvent( - val slot: Slot?, - val actionType: SlotActionType, - var isProtected: Boolean, - val itemStackOverride: ItemStack?, - var silent: Boolean = false, + val slot: Slot?, + val actionType: SlotActionType, + var isProtected: Boolean, + val itemStackOverride: ItemStack?, + val origin: MoveOrigin, + var silent: Boolean = false, ) : FirmamentEvent() { - val itemStack get() = itemStackOverride ?: slot!!.stack + val itemStack get() = itemStackOverride ?: slot!!.stack + + fun protect() { + isProtected = true + silent = false + } - fun protect() { - isProtected = true - } + fun protectSilent() { + if (!isProtected) { + silent = true + } + isProtected = true + } - fun protectSilent() { - if (!isProtected) { - silent = true - } - isProtected = true - } + enum class MoveOrigin { + DROP_FROM_HOTBAR, + SALVAGE, + INVENTORY_MOVE + ; + } - companion object : FirmamentEventBus<IsSlotProtectedEvent>() { - @JvmStatic - @JvmOverloads - fun shouldBlockInteraction(slot: Slot?, action: SlotActionType, itemStackOverride: ItemStack? = null): Boolean { - if (slot == null && itemStackOverride == null) return false - val event = IsSlotProtectedEvent(slot, action, false, itemStackOverride) - publish(event) - if (event.isProtected && !event.silent) { - MC.sendChat(Text.translatable("firmament.protectitem").append(event.itemStack.name)) - CommonSoundEffects.playFailure() - } - return event.isProtected - } - } + companion object : FirmamentEventBus<IsSlotProtectedEvent>() { + @JvmStatic + @JvmOverloads + fun shouldBlockInteraction( + slot: Slot?, action: SlotActionType, + origin: MoveOrigin, + itemStackOverride: ItemStack? = null, + ): Boolean { + if (slot == null && itemStackOverride == null) return false + val event = IsSlotProtectedEvent(slot, action, false, itemStackOverride, origin) + publish(event) + if (event.isProtected && !event.silent) { + MC.sendChat(tr("firmament.protectitem", "Firmament protected your item: ${event.itemStack.name}.\n") + .red() + .append(tr("firmament.protectitem.hoverhint", "Hover for more info.").grey()) + .hover(tr("firmament.protectitem.hint", + "To unlock this item use the Lock Slot or Lock Item keybind from Firmament while hovering over this item."))) + CommonSoundEffects.playFailure() + } + return event.isProtected + } + } } diff --git a/src/main/kotlin/events/PlayerInventoryUpdate.kt b/src/main/kotlin/events/PlayerInventoryUpdate.kt index 6e8203a..88439a9 100644 --- a/src/main/kotlin/events/PlayerInventoryUpdate.kt +++ b/src/main/kotlin/events/PlayerInventoryUpdate.kt @@ -1,11 +1,22 @@ - package moe.nea.firmament.events import net.minecraft.item.ItemStack sealed class PlayerInventoryUpdate : FirmamentEvent() { - companion object : FirmamentEventBus<PlayerInventoryUpdate>() - data class Single(val slot: Int, val stack: ItemStack) : PlayerInventoryUpdate() - data class Multi(val contents: List<ItemStack>) : PlayerInventoryUpdate() + companion object : FirmamentEventBus<PlayerInventoryUpdate>() + data class Single(val slot: Int, val stack: ItemStack) : PlayerInventoryUpdate() { + override fun getOrNull(slot: Int): ItemStack? { + if (slot == this.slot) return stack + return null + } + + } + + data class Multi(val contents: List<ItemStack>) : PlayerInventoryUpdate() { + override fun getOrNull(slot: Int): ItemStack? { + return contents.getOrNull(slot) + } + } + abstract fun getOrNull(slot: Int): ItemStack? } diff --git a/src/main/kotlin/events/WorldKeyboardEvent.kt b/src/main/kotlin/events/WorldKeyboardEvent.kt index e8566fd..1d6a758 100644 --- a/src/main/kotlin/events/WorldKeyboardEvent.kt +++ b/src/main/kotlin/events/WorldKeyboardEvent.kt @@ -1,18 +1,17 @@ - - package moe.nea.firmament.events import net.minecraft.client.option.KeyBinding import moe.nea.firmament.keybindings.IKeyBinding data class WorldKeyboardEvent(val keyCode: Int, val scanCode: Int, val modifiers: Int) : FirmamentEvent.Cancellable() { - companion object : FirmamentEventBus<WorldKeyboardEvent>() + companion object : FirmamentEventBus<WorldKeyboardEvent>() - fun matches(keyBinding: KeyBinding): Boolean { - return matches(IKeyBinding.minecraft(keyBinding)) - } + fun matches(keyBinding: KeyBinding): Boolean { + return matches(IKeyBinding.minecraft(keyBinding)) + } - fun matches(keyBinding: IKeyBinding): Boolean { - return keyBinding.matches(keyCode, scanCode, modifiers) - } + fun matches(keyBinding: IKeyBinding, atLeast: Boolean = false): Boolean { + return if (atLeast) keyBinding.matchesAtLeast(keyCode, scanCode, modifiers) else + keyBinding.matches(keyCode, scanCode, modifiers) + } } diff --git a/src/main/kotlin/events/WorldMouseMoveEvent.kt b/src/main/kotlin/events/WorldMouseMoveEvent.kt new file mode 100644 index 0000000..7a17ba4 --- /dev/null +++ b/src/main/kotlin/events/WorldMouseMoveEvent.kt @@ -0,0 +1,5 @@ +package moe.nea.firmament.events + +data class WorldMouseMoveEvent(val deltaX: Double, val deltaY: Double) : FirmamentEvent.Cancellable() { + companion object : FirmamentEventBus<WorldMouseMoveEvent>() +} |