Exopack arbitrary inventory slots charging

This commit is contained in:
DBotThePony 2023-07-09 14:45:13 +07:00
parent 344364520d
commit 37f4b77994
Signed by: DBot
GPG Key ID: DCC23B5715498507
13 changed files with 510 additions and 55 deletions

View File

@ -118,6 +118,7 @@ private fun sounds(provider: MatteryLanguageProvider) {
private fun misc(provider: MatteryLanguageProvider) { private fun misc(provider: MatteryLanguageProvider) {
with(provider.english) { with(provider.english) {
gui("help.slot_filters", "Hold CTRL to setup slot filters") gui("help.slot_filters", "Hold CTRL to setup slot filters")
gui("help.slot_charging", "Hold ALT to switch slot charging")
misc("needs_no_power", "Requires no power to operate") misc("needs_no_power", "Requires no power to operate")

View File

@ -126,6 +126,7 @@ private fun sounds(provider: MatteryLanguageProvider) {
private fun misc(provider: MatteryLanguageProvider) { private fun misc(provider: MatteryLanguageProvider) {
with(provider.russian) { with(provider.russian) {
gui("help.slot_filters", "Удерживайте CTRL для настройки фильтрации слотов") gui("help.slot_filters", "Удерживайте CTRL для настройки фильтрации слотов")
gui("help.slot_charging", "Удерживайте ALT для переключения зарядки слотов")
misc("needs_no_power", "Не требует энергии для работы") misc("needs_no_power", "Не требует энергии для работы")

View File

@ -1,11 +1,14 @@
package ru.dbotthepony.mc.otm.capability package ru.dbotthepony.mc.otm.capability
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.commands.Commands import net.minecraft.commands.Commands
import net.minecraft.commands.arguments.EntityArgument import net.minecraft.commands.arguments.EntityArgument
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.nbt.ByteTag
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.IntTag
import net.minecraft.nbt.ListTag import net.minecraft.nbt.ListTag
import net.minecraft.nbt.StringTag import net.minecraft.nbt.StringTag
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
@ -72,6 +75,7 @@ import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.config.AndroidConfig import ru.dbotthepony.mc.otm.config.AndroidConfig
import ru.dbotthepony.mc.otm.config.ExopackConfig import ru.dbotthepony.mc.otm.config.ExopackConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.get
import ru.dbotthepony.mc.otm.container.iterator import ru.dbotthepony.mc.otm.container.iterator
import ru.dbotthepony.mc.otm.container.stream import ru.dbotthepony.mc.otm.container.stream
import ru.dbotthepony.mc.otm.container.vanishCursedItems import ru.dbotthepony.mc.otm.container.vanishCursedItems
@ -80,14 +84,18 @@ import ru.dbotthepony.mc.otm.core.collect.UUIDIntModifiersMap
import ru.dbotthepony.mc.otm.core.collect.filter import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.minus import ru.dbotthepony.mc.otm.core.math.minus
import ru.dbotthepony.mc.otm.core.nbt.getByteList
import ru.dbotthepony.mc.otm.core.nbt.getCompoundList import ru.dbotthepony.mc.otm.core.nbt.getCompoundList
import ru.dbotthepony.mc.otm.core.nbt.getIntList
import ru.dbotthepony.mc.otm.core.nbt.getStringList import ru.dbotthepony.mc.otm.core.nbt.getStringList
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.util.BooleanValueCodec
import ru.dbotthepony.mc.otm.core.util.IntValueCodec import ru.dbotthepony.mc.otm.core.util.IntValueCodec
import ru.dbotthepony.mc.otm.core.util.ItemValueCodec import ru.dbotthepony.mc.otm.core.util.ItemValueCodec
import ru.dbotthepony.mc.otm.core.util.Savetables import ru.dbotthepony.mc.otm.core.util.Savetables
import ru.dbotthepony.mc.otm.core.util.TickList import ru.dbotthepony.mc.otm.core.util.TickList
import ru.dbotthepony.mc.otm.core.util.UUIDValueCodec import ru.dbotthepony.mc.otm.core.util.UUIDValueCodec
import ru.dbotthepony.mc.otm.core.util.VarIntValueCodec
import ru.dbotthepony.mc.otm.menu.ExoPackInventoryMenu import ru.dbotthepony.mc.otm.menu.ExoPackInventoryMenu
import ru.dbotthepony.mc.otm.network.* import ru.dbotthepony.mc.otm.network.*
import ru.dbotthepony.mc.otm.network.synchronizer.FieldSynchronizer import ru.dbotthepony.mc.otm.network.synchronizer.FieldSynchronizer
@ -224,6 +232,27 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
synchronizer.Field(null, ItemValueCodec.nullable) synchronizer.Field(null, ItemValueCodec.nullable)
} }
val regularSlotChargeFlag = immutableList(Inventory.INVENTORY_SIZE + 4) {
synchronizer.bool()
}
private fun slotChargeToDefault() {
// броня
regularSlotChargeFlag[36].boolean = true
regularSlotChargeFlag[37].boolean = true
regularSlotChargeFlag[38].boolean = true
regularSlotChargeFlag[39].boolean = true
}
init {
slotChargeToDefault()
}
val exoPackSlotsChargeFlag by synchronizer.Set(
codec = VarIntValueCodec,
backingSet = IntAVLTreeSet(),
)
/** /**
* Exopack container, which actually store items inside Exopack * Exopack container, which actually store items inside Exopack
*/ */
@ -808,6 +837,18 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
} }
} }
tag["regularSlotChargeFlag"] = ListTag().also {
for (flag in regularSlotChargeFlag) {
it.add(ByteTag.valueOf(flag.boolean))
}
}
tag["exoPackSlotsChargeFlag"] = ListTag().also {
for (value in exoPackSlotsChargeFlag) {
it.add(IntTag.valueOf(value))
}
}
return tag return tag
} }
@ -818,6 +859,14 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
filter.value = null filter.value = null
} }
for (flag in regularSlotChargeFlag) {
flag.boolean = false
}
exoPackSlotsChargeFlag.clear()
slotChargeToDefault()
val regularSlotFilters = tag.getStringList("regularSlotFilters") val regularSlotFilters = tag.getStringList("regularSlotFilters")
for (i in 0 until regularSlotFilters.size.coerceAtMost(this.regularSlotFilters.size)) { for (i in 0 until regularSlotFilters.size.coerceAtMost(this.regularSlotFilters.size)) {
@ -826,6 +875,16 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
this.regularSlotFilters[i].value = ForgeRegistries.ITEMS.getValue(ResourceLocation.tryParse(path) ?: continue) ?: Items.AIR this.regularSlotFilters[i].value = ForgeRegistries.ITEMS.getValue(ResourceLocation.tryParse(path) ?: continue) ?: Items.AIR
} }
val regularSlotChargeFlag = tag.getByteList("regularSlotChargeFlag")
for (i in 0 until regularSlotChargeFlag.size.coerceAtMost(this.regularSlotChargeFlag.size)) {
this.regularSlotChargeFlag[i].boolean = regularSlotChargeFlag[i].asInt > 0
}
for (v in tag.getIntList("exoPackSlotsChargeFlag")) {
this.exoPackSlotsChargeFlag.add(v.asInt)
}
// iterations // iterations
deathLog.clear() deathLog.clear()
@ -965,9 +1024,10 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
if (isExoPackSmeltingInstalled) if (isExoPackSmeltingInstalled)
smelters.forEach { it.think() } smelters.forEach { it.think() }
if (!exoPackChargeSlots.isEmpty && exoPackEnergy.batteryLevel.isPositive) { if (exoPackEnergy.batteryLevel.isPositive) {
var available = exoPackEnergy.extractEnergy(exoPackEnergy.batteryLevel, true) var available = exoPackEnergy.extractEnergy(exoPackEnergy.batteryLevel, true)
if (!exoPackChargeSlots.isEmpty) {
for (item in exoPackChargeSlots) { for (item in exoPackChargeSlots) {
if (item.isNotEmpty) { if (item.isNotEmpty) {
item.energy?.let { item.energy?.let {
@ -978,6 +1038,39 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
} }
} }
} }
if (available.isPositive) {
for ((i, flag) in regularSlotChargeFlag.withIndex()) {
if (flag.boolean) {
val item = ply.inventory[i]
if (item.isNotEmpty) {
item.energy?.let {
available -= exoPackEnergy.extractEnergy(it.receiveEnergy(available, false), false)
}
if (!available.isPositive) break
}
}
}
}
if (available.isPositive) {
for (slot in exoPackSlotsChargeFlag) {
if (slot in 0 until exoPackContainer.containerSize) {
val item = exoPackContainer[slot]
if (item.isNotEmpty) {
item.energy?.let {
available -= exoPackEnergy.extractEnergy(it.receiveEnergy(available, false), false)
}
if (!available.isPositive) break
}
}
}
}
}
} }
} }

View File

@ -21,6 +21,7 @@ fun Window.isKeyDown(key: Int) = InputConstants.isKeyDown(window, key)
val Window.isShiftDown get() = isKeyDown(InputConstants.KEY_LSHIFT) || isKeyDown(InputConstants.KEY_RSHIFT) val Window.isShiftDown get() = isKeyDown(InputConstants.KEY_LSHIFT) || isKeyDown(InputConstants.KEY_RSHIFT)
val Window.isCtrlDown get() = isKeyDown(InputConstants.KEY_RCONTROL) || isKeyDown(InputConstants.KEY_LCONTROL) val Window.isCtrlDown get() = isKeyDown(InputConstants.KEY_RCONTROL) || isKeyDown(InputConstants.KEY_LCONTROL)
val Window.isAltDown get() = isKeyDown(InputConstants.KEY_RALT) || isKeyDown(InputConstants.KEY_LALT)
object ShiftPressedCond : BooleanSupplier { object ShiftPressedCond : BooleanSupplier {
override fun getAsBoolean(): Boolean { override fun getAsBoolean(): Boolean {

View File

@ -13,7 +13,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.*
import ru.dbotthepony.mc.otm.client.screen.panels.button.LargeRectangleButtonPanel import ru.dbotthepony.mc.otm.client.screen.panels.button.LargeRectangleButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.InventorySlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.BackgroundPanel import ru.dbotthepony.mc.otm.client.screen.panels.util.BackgroundPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel
@ -55,7 +55,7 @@ class ExoPackInventoryScreen(menu: ExoPackInventoryMenu) : MatteryScreen<ExoPack
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> { override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
val frame = FramePanel(this, width = 200f, height = FRAME_BASE_HEIGHT + inventoryRows * AbstractSlotPanel.SIZE, title = this.title) val frame = FramePanel(this, width = 200f, height = FRAME_BASE_HEIGHT + inventoryRows * AbstractSlotPanel.SIZE, title = this.title)
frame.makeHelpButton().addSlotFiltersHelp() frame.makeHelpButton().addSlotFiltersHelp().tooltips.add(TranslatableComponent("otm.gui.help.slot_charging"))
val hotbarStrip = EditablePanel(this, frame, height = 18f) val hotbarStrip = EditablePanel(this, frame, height = 18f)
hotbarStrip.dock = Dock.BOTTOM hotbarStrip.dock = Dock.BOTTOM
@ -92,7 +92,7 @@ class ExoPackInventoryScreen(menu: ExoPackInventoryMenu) : MatteryScreen<ExoPack
mainInventoryLine.dock = Dock.BOTTOM mainInventoryLine.dock = Dock.BOTTOM
for (slot in menu.playerHotbarSlots) { for (slot in menu.playerHotbarSlots) {
UserFilteredSlotPanel.of(this, hotbarStrip, slot).also { InventorySlotPanel(this, hotbarStrip, slot).also {
it.dock = Dock.LEFT it.dock = Dock.LEFT
} }
} }

View File

@ -1,41 +1,42 @@
package ru.dbotthepony.mc.otm.client.screen package ru.dbotthepony.mc.otm.client.screen
import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.PoseStack
import it.unimi.dsi.fastutil.ints.Int2ObjectFunction import it.unimi.dsi.fastutil.ints.Int2ObjectFunction
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.client.gui.Font import net.minecraft.client.gui.Font
import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
import net.minecraft.client.renderer.entity.ItemRenderer
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.inventory.Slot import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.Item import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraftforge.client.ForgeHooksClient
import net.minecraftforge.client.event.ContainerScreenEvent.Render.Background import net.minecraftforge.client.event.ContainerScreenEvent.Render.Background
import net.minecraftforge.client.event.ContainerScreenEvent.Render.Foreground import net.minecraftforge.client.event.ContainerScreenEvent.Render.Foreground
import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.MinecraftForge
import org.lwjgl.opengl.GL11 import org.lwjgl.opengl.GL11
import org.lwjgl.opengl.GL13
import ru.dbotthepony.mc.otm.config.ClientConfig
import ru.dbotthepony.mc.otm.client.moveMousePosScaled import ru.dbotthepony.mc.otm.client.moveMousePosScaled
import ru.dbotthepony.mc.otm.client.render.clearDepth
import ru.dbotthepony.mc.otm.client.render.translation import ru.dbotthepony.mc.otm.client.render.translation
import ru.dbotthepony.mc.otm.client.screen.panels.* import ru.dbotthepony.mc.otm.client.screen.panels.*
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.InventorySlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.HeightControls import ru.dbotthepony.mc.otm.client.screen.panels.util.HeightControls
import ru.dbotthepony.mc.otm.client.screen.panels.util.ScrollBarConstants import ru.dbotthepony.mc.otm.client.screen.panels.util.ScrollBarConstants
import ru.dbotthepony.mc.otm.config.ClientConfig
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.component1 import ru.dbotthepony.mc.otm.core.math.component1
import ru.dbotthepony.mc.otm.core.math.component2 import ru.dbotthepony.mc.otm.core.math.component2
import ru.dbotthepony.mc.otm.core.math.component3
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.menu.MatteryMenu
import java.util.Collections import java.util.*
import kotlin.collections.ArrayDeque
import kotlin.collections.List
import kotlin.collections.MutableSet
import kotlin.collections.indices
import kotlin.collections.isNotEmpty
import kotlin.collections.withIndex
/** /**
* This class encapsulate most of logic for handling EditablePanel and it's children. * This class encapsulate most of logic for handling EditablePanel and it's children.
@ -151,12 +152,13 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
if (menu.playerInventorySlots.isNotEmpty() && menu.autoCreateInventoryFrame) { if (menu.playerInventorySlots.isNotEmpty() && menu.autoCreateInventoryFrame) {
if (menu.playerExoSuitSlots.isEmpty()) { if (menu.playerExoSuitSlots.isEmpty()) {
inventoryFrame = FramePanel<MatteryScreen<*>>(this, null, 0f, 0f, INVENTORY_FRAME_WIDTH, INVENTORY_FRAME_HEIGHT, inventory.displayName).also(this::addPanel) inventoryFrame = FramePanel<MatteryScreen<*>>(this, null, 0f, 0f, INVENTORY_FRAME_WIDTH, INVENTORY_FRAME_HEIGHT, inventory.displayName).also(this::addPanel)
inventoryFrame!!.makeHelpButton().addSlotFiltersHelp().tooltips.add(TranslatableComponent("otm.gui.help.slot_charging"))
val hotbarStrip = EditablePanel(this, inventoryFrame, height = AbstractSlotPanel.SIZE) val hotbarStrip = EditablePanel(this, inventoryFrame, height = AbstractSlotPanel.SIZE)
hotbarStrip.dock = Dock.BOTTOM hotbarStrip.dock = Dock.BOTTOM
for (slot in menu.playerHotbarSlots) { for (slot in menu.playerHotbarSlots) {
UserFilteredSlotPanel.of(this, hotbarStrip, slot).also { InventorySlotPanel(this, hotbarStrip, slot).also {
it.dock = Dock.LEFT it.dock = Dock.LEFT
} }
} }
@ -174,6 +176,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
} }
} else { } else {
inventoryFrame = FramePanel<MatteryScreen<*>>(this, null, 0f, 0f, INVENTORY_FRAME_WIDTH_EXTENDED, BASE_INVENTORY_FRAME_HEIGHT + AbstractSlotPanel.SIZE * inventoryRows, inventory.displayName).also(this::addPanel) inventoryFrame = FramePanel<MatteryScreen<*>>(this, null, 0f, 0f, INVENTORY_FRAME_WIDTH_EXTENDED, BASE_INVENTORY_FRAME_HEIGHT + AbstractSlotPanel.SIZE * inventoryRows, inventory.displayName).also(this::addPanel)
inventoryFrame!!.makeHelpButton().addSlotFiltersHelp().tooltips.add(TranslatableComponent("otm.gui.help.slot_charging"))
inventoryScrollbar = DiscreteScrollBarPanel(this, inventoryFrame, { integerDivisionDown(menu.playerCombinedInventorySlots.size, 9) }, { inventoryScrollbar = DiscreteScrollBarPanel(this, inventoryFrame, { integerDivisionDown(menu.playerCombinedInventorySlots.size, 9) }, {
_, old, new -> _, old, new ->
@ -206,7 +209,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
hotbarStrip.dock = Dock.BOTTOM hotbarStrip.dock = Dock.BOTTOM
for (slot in menu.playerHotbarSlots) { for (slot in menu.playerHotbarSlots) {
UserFilteredSlotPanel.of(this, hotbarStrip, slot).also { InventorySlotPanel(this, hotbarStrip, slot).also {
it.dock = Dock.LEFT it.dock = Dock.LEFT
} }
} }
@ -249,7 +252,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
for (i in 0 .. (8).coerceAtMost(menu.playerCombinedInventorySlots.size - offset - 1)) { for (i in 0 .. (8).coerceAtMost(menu.playerCombinedInventorySlots.size - offset - 1)) {
val slot = menu.playerCombinedInventorySlots[offset + i] val slot = menu.playerCombinedInventorySlots[offset + i]
object : UserFilteredSlotPanel<MatteryScreen<*>, Slot>(this@MatteryScreen, canvas, slot) { object : InventorySlotPanel<MatteryScreen<*>, MatteryMenu.InventorySlot>(this@MatteryScreen, canvas, slot) {
init { init {
dock = Dock.LEFT dock = Dock.LEFT
} }

View File

@ -5,6 +5,7 @@ import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.FoldableSlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.FoldableSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.InventorySlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.BackgroundPanel import ru.dbotthepony.mc.otm.client.screen.panels.util.BackgroundPanel
import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleButton import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleButton
@ -64,10 +65,10 @@ open class PlayerEquipmentPanel<S : MatteryScreen<*>>(
for ((slot, cosmeticSlot) in armorSlots) { for ((slot, cosmeticSlot) in armorSlots) {
if (cosmeticSlot == null) { if (cosmeticSlot == null) {
SlotPanel(screen, armorSlotsStrip, slot).dock = Dock.TOP InventorySlotPanel(screen, armorSlotsStrip, slot).dock = Dock.TOP
} else { } else {
FoldableSlotPanel(screen, armorSlotsStrip, FoldableSlotPanel(screen, armorSlotsStrip,
SlotPanel(screen, null, slot).also { CosmeticToggleButton(screen, it, slot.type) }, InventorySlotPanel(screen, null, slot).also { CosmeticToggleButton(screen, it, slot.type) },
listOf(SlotPanel(screen, null, cosmeticSlot))).dock = Dock.TOP listOf(SlotPanel(screen, null, cosmeticSlot))).dock = Dock.TOP
} }

View File

@ -0,0 +1,50 @@
package ru.dbotthepony.mc.otm.client.screen.panels.slot
import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.world.item.Item
import ru.dbotthepony.mc.otm.client.isAltDown
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.playGuiClickSound
import ru.dbotthepony.mc.otm.client.render.MatterySprite
import ru.dbotthepony.mc.otm.client.render.Widgets18
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.menu.MatteryMenu
open class InventorySlotPanel<out S : MatteryScreen<*>, out T : MatteryMenu.InventorySlot>(
screen: S,
parent: EditablePanel<*>?,
slot: T,
x: Float = 0f,
y: Float = 0f,
noItemIcon: MatterySprite? = null
) : UserFilteredSlotPanel<S, T>(screen, parent, slot, x, y, SIZE, SIZE, noItemIcon) {
override var slotFilter: Item?
get() = slot.filter?.get()
set(value) { slot.filter?.accept(value) }
override fun renderSlotBackground(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
super.renderSlotBackground(graphics, mouseX, mouseY, partialTick)
if (slot.chargeFlag?.get() == true) {
Widgets18.BATTERY_SLOT_BACKGROUND.render(graphics, 0f, 0f, width, height)
}
}
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
if (button == InputConstants.MOUSE_BUTTON_LEFT && minecraft.window.isAltDown) {
val chargeFlag = slot.chargeFlag
if (chargeFlag == null) {
return super.mouseClickedInner(x, y, button)
} else {
chargeFlag.accept(!chargeFlag.get())
playGuiClickSound()
return true
}
}
return super.mouseClickedInner(x, y, button)
}
}

View File

@ -262,20 +262,40 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
return super.isSameInventory(other) return super.isSameInventory(other)
} }
var chargeFlag: GetterSetter<Boolean>? = null
private set
init { init {
val mattery = ply.matteryPlayer val mattery = ply.matteryPlayer
if (mattery != null) { if (mattery != null) {
if (container === inventory && slotIndex in mattery.regularSlotFilters.indices) { if (container === inventory) {
if (slotIndex in mattery.regularSlotFilters.indices)
filter = GetterSetter.of( filter = GetterSetter.of(
getter = { mattery.regularSlotFilters[slotIndex].value }, getter = { mattery.regularSlotFilters[slotIndex].value },
setter = { MatteryPlayerNetworkChannel.sendToServer(SetInventoryFilterPacket(SetInventoryFilterPacket.Type.INVENTORY, slotIndex, it)) } setter = { MatteryPlayerNetworkChannel.sendToServer(SetInventoryFilterPacket(SetInventoryFilterPacket.Type.INVENTORY, slotIndex, it)) }
) )
if (slotIndex in mattery.regularSlotChargeFlag.indices) {
val input = booleanInput(true) { mattery.regularSlotChargeFlag[slotIndex].boolean = it }
chargeFlag = GetterSetter.of(
getter = { mattery.regularSlotChargeFlag[slotIndex].boolean },
setter = input::input
)
}
} else if (container === mattery.exoPackContainer) { } else if (container === mattery.exoPackContainer) {
filter = GetterSetter.of( filter = GetterSetter.of(
getter = { mattery.exoPackContainer.getSlotFilter(slotIndex) }, getter = { mattery.exoPackContainer.getSlotFilter(slotIndex) },
setter = { MatteryPlayerNetworkChannel.sendToServer(SetInventoryFilterPacket(SetInventoryFilterPacket.Type.EXOPACK, slotIndex, it)) } setter = { MatteryPlayerNetworkChannel.sendToServer(SetInventoryFilterPacket(SetInventoryFilterPacket.Type.EXOPACK, slotIndex, it)) }
) )
val input = booleanInput(true) { if (it) mattery.exoPackSlotsChargeFlag.add(slotIndex) else mattery.exoPackSlotsChargeFlag.remove(slotIndex) }
chargeFlag = GetterSetter.of(
getter = { slotIndex in mattery.exoPackSlotsChargeFlag },
setter = input::input
)
} else { } else {
filter = null filter = null
} }

View File

@ -1,5 +1,5 @@
package ru.dbotthepony.mc.otm.network.synchronizer package ru.dbotthepony.mc.otm.network.synchronizer
enum class MapAction { enum class ChangesetAction {
CLEAR, ADD, REMOVE CLEAR, ADD, REMOVE
} }

View File

@ -349,6 +349,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
// use LinkedList because it is ensured memory is freed on LinkedList#clear // use LinkedList because it is ensured memory is freed on LinkedList#clear
private val mapBacklogs = Reference2ObjectOpenHashMap<Map<*, *>, LinkedList<Pair<Any?, (DataOutputStream) -> Unit>>>() private val mapBacklogs = Reference2ObjectOpenHashMap<Map<*, *>, LinkedList<Pair<Any?, (DataOutputStream) -> Unit>>>()
private val setBacklogs = Reference2ObjectOpenHashMap<Set<*>, LinkedList<Pair<Any?, (DataOutputStream) -> Unit>>>()
var unused: Boolean = false var unused: Boolean = false
private set private set
@ -401,6 +402,24 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
}) })
} }
internal fun <K, V> removeMapBacklog(map: Map<K, V>) {
mapBacklogs.remove(map)
}
internal fun <V> getSetBacklog(set: Set<V>): LinkedList<Pair<Any?, (DataOutputStream) -> Unit>> {
if (unused) {
return LinkedList()
}
return setBacklogs.computeIfAbsent(set, Reference2ObjectFunction {
LinkedList()
})
}
internal fun <V> removeSetBacklog(set: Set<V>) {
setBacklogs.remove(set)
}
fun collectNetworkPayload(): FastByteArrayOutputStream? { fun collectNetworkPayload(): FastByteArrayOutputStream? {
if (unused || dirtyFields.isEmpty()) { if (unused || dirtyFields.isEmpty()) {
return null return null
@ -1527,6 +1546,246 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
} }
} }
inner class Set<E>(
private val codec: IStreamCodec<E>,
private val backingSet: MutableSet<E>,
private val callback: ((changes: Collection<SetChangeset<E>>) -> Unit)? = null,
) : AbstractField<MutableSet<E>>() {
private var isRemote = false
private var isDirty = false
private fun pushBacklog(element: E, action: (DataOutputStream) -> Unit) {
check(!isRemote) { "Field marked as remote" }
val pair = element to action
forEachEndpoint {
val list = it.getSetBacklog(this)
val iterator = list.listIterator()
for (value in iterator) {
if (value.first == element) {
iterator.remove()
}
}
list.addLast(pair)
}
}
override fun observe(): Boolean {
return false
}
override fun remove() {
if (!isRemoved) {
forEachEndpoint { it.removeSetBacklog(this) }
}
super.remove()
}
override fun markDirty() {
check(!isRemoved) { "Field was removed" }
notifyEndpoints(this)
isDirty = true
}
override fun markDirty(endpoint: Endpoint) {
super.markDirty(endpoint)
endpoint.getSetBacklog(this).let {
it.clear()
it.add(null to ClearBacklogEntry)
for (value in backingSet) {
it.add(value to { it.write(ChangesetAction.ADD.ordinal + 1); codec.write(it, value) })
}
}
}
override val value: MutableSet<E> = object : MutableSet<E> {
override fun add(element: E): Boolean {
if (backingSet.add(element)) {
if (!isRemote) {
markDirty()
pushBacklog(element) {
it.write(ChangesetAction.ADD.ordinal + 1)
codec.write(it, element)
}
}
return true
}
return false
}
override fun addAll(elements: Collection<E>): Boolean {
var any = false
elements.forEach { any = add(it) || any }
return any
}
override fun clear() {
if (backingSet.isNotEmpty()) {
backingSet.clear()
if (!isRemote) {
markDirty()
forEachEndpoint {
it.getSetBacklog(this@Set).let {
it.clear()
it.add(null to ClearBacklogEntry)
}
}
}
}
}
override fun iterator(): MutableIterator<E> {
return object : MutableIterator<E> {
private val parent = backingSet.iterator()
private var lastElement: Any? = Mark
override fun hasNext(): Boolean {
return parent.hasNext()
}
override fun next(): E {
return parent.next().also { lastElement = it }
}
override fun remove() {
parent.remove()
val lastElement = lastElement
if (lastElement !== Mark) {
this.lastElement = Mark
if (!isRemote) {
markDirty()
pushBacklog(lastElement as E) {
it.write(ChangesetAction.REMOVE.ordinal + 1)
codec.write(it, lastElement as E)
}
}
}
}
}
}
override fun remove(element: E): Boolean {
if (backingSet.remove(element)) {
if (!isRemote) {
markDirty()
pushBacklog(element) {
it.write(ChangesetAction.REMOVE.ordinal + 1)
codec.write(it, element)
}
}
return true
}
return false
}
override fun removeAll(elements: Collection<E>): Boolean {
var any = false
elements.forEach { any = remove(it) || any }
return any
}
override fun retainAll(elements: Collection<E>): Boolean {
var any = false
val iterator = iterator()
for (value in iterator) {
if (value !in elements) {
any = true
iterator.remove()
}
}
return any
}
override val size: Int
get() = backingSet.size
override fun contains(element: E): Boolean {
return element in backingSet
}
override fun containsAll(elements: Collection<E>): Boolean {
return backingSet.containsAll(elements)
}
override fun isEmpty(): Boolean {
return backingSet.isEmpty()
}
}
override fun write(stream: DataOutputStream, endpoint: Endpoint) {
val list = endpoint.getSetBacklog(this)
for (value in list) {
value.second.invoke(stream)
}
stream.write(0)
list.clear()
isDirty = false
}
override fun read(stream: DataInputStream) {
if (!isRemote) {
isRemote = true
forEachEndpoint { it.removeSetBacklog(this) }
}
isDirty = false
var action = stream.read()
val changeset = LinkedList<SetChangeset<E>>()
while (action != 0) {
if ((action - 1) !in ChangesetActionList.indices) {
throw IllegalArgumentException("Unknown changeset action with index ${action - 1}")
}
when (ChangesetActionList[action - 1]) {
ChangesetAction.CLEAR -> {
changeset.add(SetChangeset(ChangesetAction.CLEAR, null))
backingSet.clear()
}
ChangesetAction.ADD -> {
val read = codec.read(stream)
changeset.add(SetChangeset(ChangesetAction.ADD, read))
backingSet.add(read)
}
ChangesetAction.REMOVE -> {
val read = codec.read(stream)
changeset.add(SetChangeset(ChangesetAction.REMOVE, read))
backingSet.remove(read)
}
}
action = stream.read()
}
callback?.invoke(changeset)
}
}
inner class Map<K, V>( inner class Map<K, V>(
private val keyCodec: IStreamCodec<K>, private val keyCodec: IStreamCodec<K>,
private val valueCodec: IStreamCodec<V>, private val valueCodec: IStreamCodec<V>,
@ -1545,17 +1804,19 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
} }
private fun pushBacklog(key: Any?, value: (DataOutputStream) -> Unit) { private fun pushBacklog(key: Any?, value: (DataOutputStream) -> Unit) {
val pair = key to value
forEachEndpoint { forEachEndpoint {
val list = it.getMapBacklog(this) val list = it.getMapBacklog(this)
val iterator = list.listIterator() val iterator = list.listIterator()
for (pair in iterator) { for (e in iterator) {
if (pair.first == key) { if (e.first == key) {
iterator.remove() iterator.remove()
} }
} }
list.addLast(key to value) list.addLast(pair)
} }
} }
@ -1582,7 +1843,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
val valueCopy = valueCodec.copy(value) val valueCopy = valueCodec.copy(value)
pushBacklog(key) { pushBacklog(key) {
it.write(MapAction.ADD.ordinal + 1) it.write(ChangesetAction.ADD.ordinal + 1)
keyCodec.write(it, key) keyCodec.write(it, key)
valueCodec.write(it, valueCopy) valueCodec.write(it, valueCopy)
} }
@ -1631,7 +1892,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
val valueCopy = valueCodec.copy(value) val valueCopy = valueCodec.copy(value)
val action = { it: DataOutputStream -> val action = { it: DataOutputStream ->
it.write(MapAction.ADD.ordinal + 1) it.write(ChangesetAction.ADD.ordinal + 1)
keyCodec.write(it, key) keyCodec.write(it, key)
valueCodec.write(it, valueCopy) valueCodec.write(it, valueCopy)
} }
@ -1660,7 +1921,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
val valueCopy = valueCodec.copy(value) val valueCopy = valueCodec.copy(value)
backlog.add(key to { backlog.add(key to {
it.write(MapAction.ADD.ordinal + 1) it.write(ChangesetAction.ADD.ordinal + 1)
keyCodec.write(it, key) keyCodec.write(it, key)
valueCodec.write(it, valueCopy) valueCodec.write(it, valueCopy)
}) })
@ -1707,7 +1968,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
pushBacklog(key) { pushBacklog(key) {
@Suppress("BlockingMethodInNonBlockingContext") // false positive @Suppress("BlockingMethodInNonBlockingContext") // false positive
it.write(MapAction.ADD.ordinal + 1) it.write(ChangesetAction.ADD.ordinal + 1)
keyCodec.write(it, key) keyCodec.write(it, key)
valueCodec.write(it, valueCopy) valueCodec.write(it, valueCopy)
} }
@ -1735,7 +1996,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
pushBacklog(key) { pushBacklog(key) {
@Suppress("BlockingMethodInNonBlockingContext") // false positive @Suppress("BlockingMethodInNonBlockingContext") // false positive
it.write(MapAction.REMOVE.ordinal + 1) it.write(ChangesetAction.REMOVE.ordinal + 1)
keyCodec.write(it, keyCopy) keyCodec.write(it, keyCopy)
} }
@ -1767,7 +2028,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
override fun read(stream: DataInputStream) { override fun read(stream: DataInputStream) {
if (!isRemote) { if (!isRemote) {
isRemote = true isRemote = true
clearBacklog() forEachEndpoint { it.removeMapBacklog(this) }
observingBackingMap?.clear() observingBackingMap?.clear()
} }
@ -1777,27 +2038,27 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
var readAction = stream.read() - 1 var readAction = stream.read() - 1
while (readAction != -1) { while (readAction != -1) {
if (readAction >= MapActionList.size) { if (readAction >= ChangesetActionList.size) {
throw IndexOutOfBoundsException("Unknown map action with ID $readAction") throw IndexOutOfBoundsException("Unknown map action with ID $readAction")
} }
when (MapActionList[readAction]) { when (ChangesetActionList[readAction]) {
MapAction.CLEAR -> { ChangesetAction.CLEAR -> {
backingMap.clear() backingMap.clear()
changeset.add(ClearMapChangeset) changeset.add(ClearMapChangeset)
} }
MapAction.ADD -> { ChangesetAction.ADD -> {
val key = keyCodec.read(stream) val key = keyCodec.read(stream)
val value = valueCodec.read(stream) val value = valueCodec.read(stream)
backingMap[key] = value backingMap[key] = value
changeset.add(MapChangeset(MapAction.ADD, key, value)) changeset.add(MapChangeset(ChangesetAction.ADD, key, value))
} }
MapAction.REMOVE -> { ChangesetAction.REMOVE -> {
val key = keyCodec.read(stream) val key = keyCodec.read(stream)
backingMap.remove(key) backingMap.remove(key)
changeset.add(MapChangeset(MapAction.REMOVE, key, null)) changeset.add(MapChangeset(ChangesetAction.REMOVE, key, null))
} }
} }
@ -1860,9 +2121,11 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
return i return i
} }
private object Mark
companion object { companion object {
private val ClearBacklogEntry = { stream: DataOutputStream -> stream.write(MapAction.CLEAR.ordinal + 1) } private val ClearBacklogEntry = { stream: DataOutputStream -> stream.write(ChangesetAction.CLEAR.ordinal + 1) }
private val MapActionList = MapAction.values() private val ChangesetActionList = ChangesetAction.values()
private val ClearMapChangeset = MapChangeset(MapAction.CLEAR, null, null) private val ClearMapChangeset = MapChangeset(ChangesetAction.CLEAR, null, null)
} }
} }

View File

@ -1,23 +1,23 @@
package ru.dbotthepony.mc.otm.network.synchronizer package ru.dbotthepony.mc.otm.network.synchronizer
data class MapChangeset<out K, out V>( data class MapChangeset<out K, out V>(
val action: MapAction, val action: ChangesetAction,
val key: K?, val key: K?,
val value: V? val value: V?
) { ) {
inline fun map(add: (K, V) -> Unit, remove: (K) -> Unit) { inline fun map(add: (K, V) -> Unit, remove: (K) -> Unit) {
when (action) { when (action) {
MapAction.ADD -> add.invoke(key!!, value!!) ChangesetAction.ADD -> add.invoke(key!!, value!!)
MapAction.REMOVE -> remove.invoke(key!!) ChangesetAction.REMOVE -> remove.invoke(key!!)
else -> {} else -> {}
} }
} }
inline fun map(add: (K, V) -> Unit, remove: (K) -> Unit, clear: () -> Unit) { inline fun map(add: (K, V) -> Unit, remove: (K) -> Unit, clear: () -> Unit) {
when (action) { when (action) {
MapAction.CLEAR -> clear.invoke() ChangesetAction.CLEAR -> clear.invoke()
MapAction.ADD -> add.invoke(key!!, value!!) ChangesetAction.ADD -> add.invoke(key!!, value!!)
MapAction.REMOVE -> remove.invoke(key!!) ChangesetAction.REMOVE -> remove.invoke(key!!)
} }
} }
} }

View File

@ -0,0 +1,22 @@
package ru.dbotthepony.mc.otm.network.synchronizer
data class SetChangeset<out V>(
val action: ChangesetAction,
val value: V?
) {
inline fun map(add: (V) -> Unit, remove: (V) -> Unit) {
when (action) {
ChangesetAction.ADD -> add.invoke(value!!)
ChangesetAction.REMOVE -> remove.invoke(value!!)
else -> {}
}
}
inline fun map(add: (V) -> Unit, remove: (V) -> Unit, clear: () -> Unit) {
when (action) {
ChangesetAction.CLEAR -> clear.invoke()
ChangesetAction.ADD -> add.invoke(value!!)
ChangesetAction.REMOVE -> remove.invoke(value!!)
}
}
}