Upgrade system, conditional quickmove slots, frame panel close button

This commit is contained in:
DBotThePony 2023-07-05 17:06:23 +07:00
parent 6690ca03c4
commit 9dcf24cae7
Signed by: DBot
GPG Key ID: DCC23B5715498507
33 changed files with 840 additions and 81 deletions

View File

@ -603,6 +603,18 @@ private fun items(provider: MatteryLanguageProvider) {
add(MItems.ZPM_BATTERY, "Zero Point Module")
add(MItems.ZPM_BATTERY, "description", "Can be found in hands of those who travel between dimensions, if they ever reached different reality of origin of these constructs...")
add(MItems.CreativeUpgrades.SPEED, "Creative Speed Upgrade")
add(MItems.CreativeUpgrades.ENERGY_CONSUMPTION, "Creative Energy Consumption Upgrade")
add(MItems.CreativeUpgrades.ENERGY_STORAGE, "Creative Energy Storage Upgrade")
add(MItems.CreativeUpgrades.ENERGY_STORAGE_FLAT, "Creative Energy Storage Upgrade (Flat)")
add(MItems.CreativeUpgrades.ENERGY_STORAGE_FLAT_SMALL, "Creative Energy Storage Upgrade (Flat Small)")
add(MItems.CreativeUpgrades.ENERGY_THROUGHPUT, "Creative Energy Throughput Upgrade")
add(MItems.CreativeUpgrades.ENERGY_THROUGHPUT_FLAT, "Creative Energy Throughput Upgrade (Flat)")
add(MItems.CreativeUpgrades.ENERGY_THROUGHPUT_FLAT_SMALL, "Creative Energy Throughput Upgrade (Flat Small)")
add(MItems.CreativeUpgrades.FAILSAFE, "Creative Failsafe Upgrade")
add(MItems.CreativeUpgrades.FAILURE, "Creative Failure Upgrade")
add(MItems.CreativeUpgrades.PROCESSING_ITEMS, "Creative Item Processing Upgrade")
}
}
@ -688,6 +700,27 @@ private fun gui(provider: MatteryLanguageProvider) {
gui("side_mode.pull", "Pull")
gui("side_mode.push", "Push")
gui("upgrades", "Upgrades")
gui("upgrades.current", "Active upgrades:")
gui("upgrade.speed", "Operation speed: %s%%")
gui("upgrade.processing_items", "Items processed per cycle: +%s")
gui("upgrade.energy_storage_flat", "Energy capacity: +%s")
gui("upgrade.energy_storage", "Energy capacity: +%s%%")
gui("upgrade.energy_consumed", "Energy consumption: %s%%")
gui("upgrade.energy_throughput_flat", "Energy throughput: +%s")
gui("upgrade.energy_throughput", "Energy throughput: +%s%%")
gui("upgrade.failsafe", "Failure chance: %s%%")
gui("upgrade_type.list", "Upgrade classification:")
gui("upgrade_type.allowed", "Allowed upgrades:")
gui("upgrade_type.allowed_none", "No possible upgrades at the moment.")
gui("upgrade_type.speed", "Speed")
gui("upgrade_type.processing", "Processing")
gui("upgrade_type.energy_storage", "Energy Storage")
gui("upgrade_type.energy_consumption", "Energy Consumption")
gui("upgrade_type.energy_throughput", "Energy Throughput")
gui("upgrade_type.failsafe", "Failsafe")
gui("balance_inputs", "Balance input slots")
gui("sorting.default", "Default sorting")

View File

@ -607,6 +607,18 @@ private fun items(provider: MatteryLanguageProvider) {
add(MItems.ZPM_BATTERY, "Модуль нулевой точки")
add(MItems.ZPM_BATTERY, "description", "Может быть найден у тех, кто путешествует между измерениями, если, конечно, они смогли достигнуть вселенной, где данные устройства были созиданы...")
add(MItems.CreativeUpgrades.SPEED, "Творческое улучшение скорости")
add(MItems.CreativeUpgrades.ENERGY_CONSUMPTION, "Творческое улучшение энергоэффективности")
add(MItems.CreativeUpgrades.ENERGY_STORAGE, "Творческое улучшение энергохраналища")
add(MItems.CreativeUpgrades.ENERGY_STORAGE_FLAT, "Творческое улучшение энергохраналища (простое)")
add(MItems.CreativeUpgrades.ENERGY_STORAGE_FLAT_SMALL, "Творческое улучшение энергохраналища (малое простое)")
add(MItems.CreativeUpgrades.ENERGY_THROUGHPUT, "Творческое улучшение энергоканала")
add(MItems.CreativeUpgrades.ENERGY_THROUGHPUT_FLAT, "Творческое улучшение энергоканала (простое)")
add(MItems.CreativeUpgrades.ENERGY_THROUGHPUT_FLAT_SMALL, "Творческое улучшение энергоканала (малое простое)")
add(MItems.CreativeUpgrades.FAILSAFE, "Творческое улучшение отказоустойчивости")
add(MItems.CreativeUpgrades.FAILURE, "Творческое улучшение краха")
add(MItems.CreativeUpgrades.PROCESSING_ITEMS, "Творческое улучшение обработки")
}
}
@ -692,6 +704,27 @@ private fun gui(provider: MatteryLanguageProvider) {
gui("side_mode.pull", "Автоматическое вытягивание")
gui("side_mode.push", "Автоматическое выталкивание")
gui("upgrades", "Улучшения")
gui("upgrades.current", "Активные улучшения:")
gui("upgrade.speed", "Скорость работы: %s%%")
gui("upgrade.processing_items", "Работы за цикл: +%s")
gui("upgrade.energy_storage_flat", "Хранилище энергии: +%s")
gui("upgrade.energy_storage", "Хранилище энергии: +%s%%")
gui("upgrade.energy_consumed", "Потребление энергии: %s%%")
gui("upgrade.energy_throughput_flat", "Пропускная способность энергии: +%s")
gui("upgrade.energy_throughput", "Пропускная способность энергии: +%s%%")
gui("upgrade.failsafe", "Шанс неудачи: %s%%")
gui("upgrade_type.list", "Классификация улучшения:")
gui("upgrade_type.allowed", "Допустимые улучшения:")
gui("upgrade_type.allowed_none", "На данный момент нет допустимых улучшений.")
gui("upgrade_type.speed", "Скорость")
gui("upgrade_type.processing", "Обработка")
gui("upgrade_type.energy_storage", "Энергохранилище")
gui("upgrade_type.energy_consumption", "Энергоэффективность")
gui("upgrade_type.energy_throughput", "Энергоканал")
gui("upgrade_type.failsafe", "Отказоустойчивость")
gui("balance_inputs", "Балансировать входные слоты")
gui("sorting.default", "Сортировка по умолчанию")

View File

@ -112,6 +112,8 @@ fun addTags(tagsProvider: TagsProvider) {
tagsProvider.items.forge("shears").add(MItems.TRITANIUM_SHEARS)
tagsProvider.items.forge("shields").add(MItems.TRITANIUM_SHIELD)
tagsProvider.items.Appender(MItemTags.UPGRADES).add(MItems.CreativeUpgrades.LIST)
tagsProvider.blocks.Appender(BlockTags.STAIRS)
.add(MRegistry.FLOOR_TILES_STAIRS.blocks.values)
.add(MRegistry.TRITANIUM_STAIRS.allBlocks.values)

View File

@ -63,6 +63,10 @@ public class MatteryCapability {
@NotNull
public static final Capability<ICurio> CURIOS_ITEM = CapabilityManager.get(new CapabilityToken<>() {});
@Nonnull
@NotNull
public static final Capability<IMatteryUpgrade> UPGRADE = CapabilityManager.get(new CapabilityToken<>() {});
public static void register(RegisterCapabilitiesEvent event) {
event.register(IMatteryEnergyStorage.class);
event.register(MatteryPlayerCapability.class);
@ -72,5 +76,6 @@ public class MatteryCapability {
event.register(IReplicationTaskProvider.class);
event.register(IMatteryDrive.class);
event.register(StorageNode.class);
event.register(IMatteryUpgrade.class);
}
}

View File

@ -3,7 +3,9 @@ package ru.dbotthepony.mc.otm.block.entity
import net.minecraft.nbt.CompoundTag
import net.minecraft.world.item.ItemStack
import net.minecraftforge.common.util.INBTSerializable
import ru.dbotthepony.mc.otm.capability.IMatteryUpgrade
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.getDecimal
import ru.dbotthepony.mc.otm.core.math.set
@ -153,6 +155,7 @@ abstract class MachineJobEventLoop<JobType : IMachineJob> : INBTSerializable<Com
protected abstract val energy: IMatteryEnergyStorage?
protected abstract val isBlockedByRedstone: Boolean
protected abstract fun deserializeJob(nbt: CompoundTag): JobType?
protected abstract val upgrades: IMatteryUpgrade?
var currentJob: JobType? = null
set(value) {
@ -263,7 +266,8 @@ abstract class MachineJobEventLoop<JobType : IMachineJob> : INBTSerializable<Com
return
}
var availableTicks = ticks
val upgrades = upgrades ?: IMatteryUpgrade.Companion
var availableTicks = ticks * (1.0 + upgrades.speedBonus.coerceIn(MachinesConfig.Upgrades.MIN_SPEED, MachinesConfig.Upgrades.MAX_SPEED))
val energy = energy
while (!isIdling && weakGreaterThan(availableTicks, 0.0) && throttleTicks <= 0) {
@ -299,18 +303,23 @@ abstract class MachineJobEventLoop<JobType : IMachineJob> : INBTSerializable<Com
idleTicksAnim = 0
if (weakLessThan(workTicks, currentJob.ticks)) {
val ticksLeft = currentJob.ticks - workTicks
val ticksLeft = (currentJob.ticks - workTicks).coerceAtMost(availableTicks)
val ticksAdvanced: Double
var requiredPower: Decimal? = null
var extractedPower: Decimal? = null
if (currentJob.powerUsage.isZero) {
ticksAdvanced = availableTicks.coerceAtMost(ticksLeft)
ticksAdvanced = ticksLeft
} else {
requiredPower = currentJob.powerUsage * ticksLeft.coerceAtMost(availableTicks)
extractedPower = energy!!.extractEnergy(requiredPower, true)
ticksAdvanced = (extractedPower / requiredPower).toDouble().coerceAtMost(ticksLeft).coerceAtMost(availableTicks)
requiredPower = currentJob.powerUsage * (Decimal.ONE + upgrades.energyConsumed.coerceIn(MachinesConfig.Upgrades.MIN_ENERGY, MachinesConfig.Upgrades.MAX_ENERGY)) * ticksLeft.coerceAtMost(availableTicks)
if (requiredPower.isPositive) {
extractedPower = energy!!.extractEnergy(requiredPower, true)
ticksAdvanced = (extractedPower / requiredPower).toDouble().coerceIn(0.0, ticksLeft)
} else {
ticksAdvanced = ticksLeft
}
}
if (weakEqualDoubles(ticksAdvanced, 0.0)) {

View File

@ -11,9 +11,11 @@ import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.mc.otm.capability.IMatteryUpgrade
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matteryEnergy
import ru.dbotthepony.mc.otm.container.UpgradeContainer
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.nbt.getCompoundList
@ -30,7 +32,7 @@ abstract class MatteryWorkerBlockEntity<JobType : IMachineJob>(
blockPos: BlockPos,
blockState: BlockState,
val jobDeserializer: (tag: CompoundTag) -> JobType?,
maxJobs: Int = 1
maxJobs: Int = 1,
) : MatteryPoweredBlockEntity(type, blockPos, blockState) {
val jobEventLoops: ImmutableList<MachineJobEventLoop<JobType>> = immutableList(maxJobs) { id ->
object : MachineJobEventLoop<JobType>() {
@ -38,6 +40,8 @@ abstract class MatteryWorkerBlockEntity<JobType : IMachineJob>(
get() = matteryEnergy
override val isBlockedByRedstone: Boolean
get() = redstoneControl.isBlockedByRedstone
override val upgrades: IMatteryUpgrade?
get() = this@MatteryWorkerBlockEntity.upgrades
override fun deserializeJob(nbt: CompoundTag): JobType? {
return jobDeserializer.invoke(nbt)
@ -61,6 +65,8 @@ abstract class MatteryWorkerBlockEntity<JobType : IMachineJob>(
}
}
open val upgrades: IMatteryUpgrade? get() = null
var balanceInputs = false
init {

View File

@ -12,11 +12,14 @@ import ru.dbotthepony.mc.otm.block.entity.JobContainer
import ru.dbotthepony.mc.otm.block.entity.JobStatus
import ru.dbotthepony.mc.otm.block.entity.MachineItemJob
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.capability.IMatteryUpgrade
import ru.dbotthepony.mc.otm.capability.UpgradeType
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.UpgradeContainer
import ru.dbotthepony.mc.otm.container.balance
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.menu.tech.PlatePressMenu
@ -29,7 +32,8 @@ class PlatePressBlockEntity(
p_155230_: BlockState,
val isTwin: Boolean = false,
) : MatteryWorkerBlockEntity<MachineItemJob>(if (isTwin) MBlockEntities.TWIN_PLATE_PRESS else MBlockEntities.PLATE_PRESS, p_155229_, p_155230_, ::MachineItemJob, if (isTwin) 2 else 1) {
val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::setChangedLight, MachinesConfig.PLATE_PRESS))
override val upgrades = UpgradeContainer(this::setChangedLight, if (isTwin) 4 else 3, UpgradeType.BASIC)
val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::setChangedLight, upgrades.transform(MachinesConfig.PLATE_PRESS)))
val inputContainer = MatteryContainer(this::itemContainerUpdated, if (isTwin) 2 else 1).also(::addDroppableContainer)
val outputContainer = MatteryContainer(this::itemContainerUpdated, if (isTwin) 2 else 1).also(::addDroppableContainer)
@ -55,6 +59,7 @@ class PlatePressBlockEntity(
savetable(::inputContainer)
savetable(::outputContainer)
savetables.double(::experience)
savetables.stateful(::upgrades)
}
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {

View File

@ -0,0 +1,200 @@
package ru.dbotthepony.mc.otm.capability
import net.minecraft.ChatFormatting
import net.minecraft.network.chat.Component
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.immutableSet
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.formatPower
/**
* Upgrades merge by sum of their effects
*
* Values which are otherwise not stated to allow negatives or not, Do allow negative values.
*/
interface IMatteryUpgrade {
/**
* Type(s) this upgrade represent
*/
val upgradeTypes: Set<UpgradeType> get() = setOf()
/**
* [speedBonus] of `1.0` means that machine doubles the speed (performs twice the amount of
* work ticks per work cycle).
*/
val speedBonus: Double get() = 0.0
/**
* **CAN NOT be negative.**
*
* [processingItems] of `4` means machine will accept at most 5 (1 + 4) of items per job.
*/
val processingItems: Int get() = 0
/**
* **CAM NOT be negative.**
*
* Added directly over regular energy storage capacity.
*/
val energyStorageFlat: Decimal get() = Decimal.ZERO
/**
* **CAM NOT be negative.**
*/
val energyStorage: Decimal get() = Decimal.ZERO
/**
* Value of `1` means power consumption is doubled, `2` is tripled, `-0.5` is halved.
*/
val energyConsumed: Decimal get() = Decimal.ZERO
/**
* **CAN NOT be negative.**
*
* Added directly over regular throughput
*/
val energyThroughputFlat: Decimal get() = Decimal.ZERO
/**
* **CAN NOT be negative.**
*
* Value of `1` means power throughput is doubled, `2` is tripled, and so on.
*/
val energyThroughput: Decimal get() = Decimal.ZERO
/**
* **CAN NOT be negative.**
*
* **Merged by multiplication**
*/
val failureMultiplier: Double get() = 1.0
companion object : IMatteryUpgrade
}
fun IMatteryUpgrade.addUpgradeTooltipLines(tooltips: MutableCollection<Component>) {
if (upgradeTypes.isNotEmpty() && ShiftPressedCond.asBoolean) {
tooltips.add(TranslatableComponent("otm.gui.upgrade_type.list").withStyle(ChatFormatting.GRAY))
for (upgrade in upgradeTypes) {
tooltips.add(upgrade.component.copy().withStyle(ChatFormatting.GRAY))
}
tooltips.add(TextComponent(""))
}
if (speedBonus >= 0.01) {
tooltips.add(TranslatableComponent("otm.gui.upgrade.speed", TextComponent("+" + (speedBonus.coerceIn(MachinesConfig.Upgrades.MIN_SPEED, MachinesConfig.Upgrades.MAX_SPEED) * 100.0).toInt().toString()).withStyle(ChatFormatting.DARK_GREEN)).withStyle(ChatFormatting.GRAY))
} else if (speedBonus <= -0.01) {
tooltips.add(TranslatableComponent("otm.gui.upgrade.speed", TextComponent((speedBonus.coerceIn(MachinesConfig.Upgrades.MIN_SPEED, MachinesConfig.Upgrades.MAX_SPEED) * 100.0).toInt().toString()).withStyle(ChatFormatting.DARK_RED)).withStyle(ChatFormatting.GRAY))
}
if (processingItems != 0) {
tooltips.add(TranslatableComponent("otm.gui.upgrade.processing_items", TextComponent("+$processingItems").withStyle(ChatFormatting.DARK_GREEN)).withStyle(ChatFormatting.GRAY))
}
if (energyStorageFlat != Decimal.ZERO) {
tooltips.add(TranslatableComponent("otm.gui.upgrade.energy_storage_flat", energyStorageFlat.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.DARK_GREEN)).withStyle(ChatFormatting.GRAY))
}
if (energyStorage != Decimal.ZERO) {
tooltips.add(TranslatableComponent("otm.gui.upgrade.energy_storage", TextComponent((energyStorage * 100).toString(0)).withStyle(ChatFormatting.DARK_GREEN)).withStyle(ChatFormatting.GRAY))
}
if (energyConsumed > Decimal.ONE) {
tooltips.add(TranslatableComponent("otm.gui.upgrade.energy_consumed", TextComponent("+" + (energyConsumed.coerceIn(MachinesConfig.Upgrades.MIN_ENERGY, MachinesConfig.Upgrades.MAX_ENERGY) * 100).toString(0)).withStyle(ChatFormatting.DARK_RED)).withStyle(ChatFormatting.GRAY))
} else if (energyConsumed < Decimal.MINUS_ONE) {
tooltips.add(TranslatableComponent("otm.gui.upgrade.energy_consumed", TextComponent((energyConsumed.coerceIn(MachinesConfig.Upgrades.MIN_ENERGY, MachinesConfig.Upgrades.MAX_ENERGY) * 100).toString(0)).withStyle(ChatFormatting.DARK_GREEN)).withStyle(ChatFormatting.GRAY))
}
if (energyThroughputFlat != Decimal.ZERO) {
tooltips.add(TranslatableComponent("otm.gui.upgrade.energy_throughput_flat", energyThroughputFlat.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.DARK_GREEN)).withStyle(ChatFormatting.GRAY))
}
if (energyThroughput != Decimal.ZERO) {
tooltips.add(TranslatableComponent("otm.gui.upgrade.energy_throughput", TextComponent((energyThroughput * 100).toString(0)).withStyle(ChatFormatting.DARK_GREEN)).withStyle(ChatFormatting.GRAY))
}
if (failureMultiplier >= 1.01) {
tooltips.add(TranslatableComponent("otm.gui.upgrade.failsafe", TextComponent("+" + ((failureMultiplier - 1) * 100).toInt().toString()).withStyle(ChatFormatting.DARK_RED)).withStyle(ChatFormatting.GRAY))
} else if (failureMultiplier <= 0.99) {
tooltips.add(TranslatableComponent("otm.gui.upgrade.failsafe", TextComponent(((failureMultiplier.coerceAtLeast(0.0) - 1) * 100).toInt().toString()).withStyle(ChatFormatting.DARK_GREEN)).withStyle(ChatFormatting.GRAY))
}
}
fun IMatteryUpgrade.getUpgradeTooltipLines(): MutableList<Component> {
return ArrayList<Component>().also { addUpgradeTooltipLines(it) }
}
enum class UpgradeType {
SPEED,
PROCESSING,
ENERGY_STORAGE,
ENERGY_CONSUMPTION,
ENERGY_THROUGHPUT,
FAILSAFE;
val localeId = "otm.gui.upgrade_type.${name.lowercase()}"
val component: Component get() = TranslatableComponent(localeId)
val flag = 1 shl (ordinal + 1)
fun set() = sets[flag]
companion object {
private val cached = values()
private val sets = Array(2 shl cached.size) {
immutableSet {
for (u in values()) if (it.and(u.flag) != 0) accept(u)
}
}
@JvmField
val ALL = set(*values())
@JvmField
val BASIC = set(SPEED, ENERGY_STORAGE, ENERGY_CONSUMPTION, ENERGY_THROUGHPUT)
fun set(vararg types: UpgradeType): Set<UpgradeType> {
var flags = 0
for (v in types) flags = flags or v.flag
return sets[flags]
}
fun set(): Set<UpgradeType> {
return sets[0]
}
fun set(
type0: UpgradeType,
): Set<UpgradeType> = sets[type0.flag]
fun set(
type0: UpgradeType,
type1: UpgradeType,
): Set<UpgradeType> = sets[type0.flag or type1.flag]
fun set(
type0: UpgradeType,
type1: UpgradeType,
type2: UpgradeType,
): Set<UpgradeType> = sets[type0.flag or type1.flag or type2.flag]
fun set(
type0: UpgradeType,
type1: UpgradeType,
type2: UpgradeType,
type3: UpgradeType,
): Set<UpgradeType> = sets[type0.flag or type1.flag or type2.flag or type3.flag]
fun set(
type0: UpgradeType,
type1: UpgradeType,
type2: UpgradeType,
type3: UpgradeType,
type4: UpgradeType,
): Set<UpgradeType> = sets[type0.flag or type1.flag or type2.flag or type3.flag or type4.flag]
}
}

View File

@ -59,7 +59,7 @@ object Widgets18 {
val PATTERN_SLOT_BACKGROUND = slotBgGrid.next()
val MATTER_CAPACITOR_SLOT_BACKGROUND = slotBgGrid.next()
private val controlsGrid = WidgetLocation.SIDE_CONTROLS.grid(rows = 7, columns = 9)
private val controlsGrid = WidgetLocation.SIDE_CONTROLS.grid(rows = 8, columns = 9)
val BATTERY_ONLY = controlsGrid.next()
val ITEMS_CONFIGURATION = controlsGrid.next()
@ -160,4 +160,6 @@ object Widgets18 {
put(RelativeSide.FRONT, FRONT_CONTROLS)
put(RelativeSide.BACK, BACK_CONTROLS)
}
val UPGRADES = controlsGrid.next()
}

View File

@ -319,7 +319,10 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
* @return FramePanel created, or null
*/
protected open fun makeMainFrame(): FramePanel<MatteryScreen<*>>? {
return FramePanel(this, null, 0f, 0f, DEFAULT_FRAME_WIDTH, DEFAULT_FRAME_HEIGHT, getTitle())
return FramePanel(this, null, 0f, 0f, DEFAULT_FRAME_WIDTH, DEFAULT_FRAME_HEIGHT, getTitle()).also {
it.onClose { onClose() }
it.makeCloseButton()
}
}
public override fun recalculateQuickCraftRemaining() {

View File

@ -1,8 +1,10 @@
package ru.dbotthepony.mc.otm.client.screen.panels
import com.google.common.collect.ImmutableList
import com.mojang.blaze3d.platform.InputConstants
import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.PoseStack
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
import it.unimi.dsi.fastutil.objects.ReferenceArraySet
import net.minecraft.client.gui.ComponentPath
import net.minecraft.client.gui.Font
@ -23,6 +25,7 @@ import ru.dbotthepony.mc.otm.client.render.currentScissorRect
import ru.dbotthepony.mc.otm.client.render.popScissorRect
import ru.dbotthepony.mc.otm.client.render.pushScissorRect
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.button.AbstractButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.input.QueryUserPanel
import java.util.*
import kotlin.collections.ArrayList
@ -417,6 +420,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
return field
}
field = null
return null
}
@ -965,7 +969,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
return true
}
if (isHovered) {
if (isHovered || this is AbstractButtonPanel<*> && isPressed) {
val tooltip = tooltip
val tooltipList = tooltipList
@ -1186,10 +1190,6 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
this.height = height
}
companion object {
private val LOGGER = LogManager.getLogger()!!
}
protected open fun visibilityChanges(new: Boolean, old: Boolean) {
}
@ -1589,7 +1589,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
fun keyPressed(key: Int, scancode: Int, mods: Int): Boolean {
if (!isVisible() || !acceptKeyboardInput) return false
if (!isEverFocused()) return false
if (flashAnyBlocker(true)) return true
if (flashAnyBlocker(key !in ignoreFlashKeys)) return true
if (isFocusedThis) return keyPressedInternal(key, scancode, mods)
for (child in visibleChildrenInternal) {
@ -1737,4 +1737,21 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
fun queryUser(title: Component, text: Collection<Component>, onConfirm: Runnable, onCancel: Runnable? = null): QueryUserPanel<S> {
return QueryUserPanel(screen, title, text, onConfirm, onCancel).also { blockingWindow = it }
}
companion object {
private val LOGGER = LogManager.getLogger()!!
private val ignoreFlashKeys = IntAVLTreeSet()
init {
ignoreFlashKeys.add(InputConstants.KEY_LEFT)
ignoreFlashKeys.add(InputConstants.KEY_RIGHT)
ignoreFlashKeys.add(InputConstants.KEY_UP)
ignoreFlashKeys.add(InputConstants.KEY_DOWN)
ignoreFlashKeys.add(InputConstants.KEY_RCONTROL)
ignoreFlashKeys.add(InputConstants.KEY_LCONTROL)
ignoreFlashKeys.add(InputConstants.KEY_RSHIFT)
ignoreFlashKeys.add(InputConstants.KEY_LSHIFT)
}
}
}

View File

@ -12,6 +12,8 @@ import net.minecraft.network.chat.Component
import org.lwjgl.opengl.GL30
import ru.dbotthepony.mc.otm.client.playGuiClickSound
import ru.dbotthepony.mc.otm.client.render.*
import ru.dbotthepony.mc.otm.client.screen.panels.button.AbstractButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.ButtonPanel
open class FramePanel<out S : Screen>(
screen: S,
@ -123,6 +125,30 @@ open class FramePanel<out S : Screen>(
}
}
inner class CloseButton : AbstractButtonPanel<S>(screen, this@FramePanel, this@FramePanel.width - CLOSE_BUTTON.width, 0f, CLOSE_BUTTON.width, CLOSE_BUTTON.height) {
override fun onClick(mouseButton: Int) {
close()
}
override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
if (isPressed) {
CLOSE_BUTTON_PRESSED.render(graphics, 0f, 0f, width, height)
} else if (isHovered) {
CLOSE_BUTTON_HOVERED.render(graphics, 0f, 0f, width, height)
} else {
CLOSE_BUTTON.render(graphics, 0f, 0f, width, height)
}
}
override fun onRemoved() {
super.onRemoved()
if (closeButton == this) {
closeButton = null
}
}
}
protected val tabs: java.util.ArrayList<Tab> = ArrayList()
override fun performLayout() {
@ -131,13 +157,29 @@ open class FramePanel<out S : Screen>(
tab.initial = i == 0
}
closeButton?.setPos(width - CLOSE_BUTTON.width, 0f)
super.performLayout()
}
protected var dragging = false
protected var closeButton: CloseButton? = null
var closeOnEscape = false
fun makeCloseButton() {
if (closeButton == null)
closeButton = CloseButton()
}
/**
* Adds close button and makes panel [close] when user presses ESC with this panel focused
*/
fun behaveAsWindow() {
closeOnEscape = true
makeCloseButton()
}
init {
setDockPadding(PADDING, if (title != null) PADDING_TOP else PADDING, PADDING, PADDING)
}
@ -187,9 +229,24 @@ open class FramePanel<out S : Screen>(
}
protected val closeCallbacks = ArrayList<Runnable>()
fun onClose(callback: Runnable) {
closeCallbacks.add(callback)
}
/**
* Usually just calls [remove]
*/
open fun close() {
if (isRemoved) return
remove()
closeCallbacks.forEach { it.run() }
}
override fun keyPressedInternal(key: Int, scancode: Int, mods: Int): Boolean {
if (key == InputConstants.KEY_ESCAPE && closeOnEscape) {
remove()
close()
return true
}
@ -258,9 +315,13 @@ open class FramePanel<out S : Screen>(
padding = DockProperty(-3f, -3f, -3f, -3f)
)
val TAB_RIGHT_CONNECTION = WidgetLocation.MISC.sprite(x = 30f, y = 0f, width = 3f, height = 5f)
val TAB_LEFT_CONNECTION = WidgetLocation.MISC.sprite(x = 33f, y = 0f, width = 3f, height = 5f)
val TAB_BACKGROUND = WidgetLocation.MISC.sprite(x = 30f, y = 6f, width = 6f, height = 6f)
val TAB_RIGHT_CONNECTION = WidgetLocation.MISC.sprite(x = 30f, y = 0f, width = 3f, height = 5f)
val TAB_LEFT_CONNECTION = WidgetLocation.MISC.sprite(x = 33f, y = 0f, width = 3f, height = 5f)
val TAB_BACKGROUND = WidgetLocation.MISC.sprite(x = 30f, y = 6f, width = 6f, height = 6f)
val CLOSE_BUTTON = WidgetLocation.MISC.sprite(x = 51f, y = 0f, width = 13f, height = 11f)
val CLOSE_BUTTON_HOVERED = WidgetLocation.MISC.sprite(x = 51f, y = 11f, width = 13f, height = 11f)
val CLOSE_BUTTON_PRESSED = WidgetLocation.MISC.sprite(x = 51f, y = 22f, width = 13f, height = 11f)
const val TAB_HEIGHT = 28f
const val TAB_WIDTH = 28f

View File

@ -3,26 +3,38 @@ package ru.dbotthepony.mc.otm.client.screen.panels.button
import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.ChatFormatting
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.network.chat.Component
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.addUpgradeTooltipLines
import ru.dbotthepony.mc.otm.client.isCtrlDown
import ru.dbotthepony.mc.otm.client.isShiftDown
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite
import ru.dbotthepony.mc.otm.client.render.Widgets18
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.Dock
import ru.dbotthepony.mc.otm.client.screen.panels.DockResizeMode
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.value
import ru.dbotthepony.mc.otm.menu.UpgradeSlots
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.input.FluidConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput
import java.util.function.Predicate
import kotlin.math.ceil
import kotlin.math.pow
private fun <S : MatteryScreen<*>> makeRedstoneSettingButton(
screen: S,
@ -212,6 +224,7 @@ private fun <S : MatteryScreen<*>> makeItemHandlerControlPanel(
moveButtons(front, back, left, right, top, bottom)
screen.addPanel(frame)
frame.requestFocus()
frame.closeOnEscape = true
return frame
}
@ -240,6 +253,7 @@ private fun <S : MatteryScreen<*>> makeEnergyConfigPanel(
moveButtons(front, back, left, right, top, bottom)
screen.addPanel(frame)
frame.requestFocus()
frame.closeOnEscape = true
return frame
}
@ -268,6 +282,7 @@ private fun <S : MatteryScreen<*>> makeFluidConfigPanel(
moveButtons(front, back, left, right, top, bottom)
screen.addPanel(frame)
frame.requestFocus()
frame.closeOnEscape = true
return frame
}
@ -281,12 +296,17 @@ class DeviceControls<out S : MatteryScreen<*>>(
val energyConfig: EnergyConfigPlayerInput? = null,
val fluidConfig: FluidConfigPlayerInput? = null,
val balanceInputs: BooleanInputWithFeedback? = null,
val upgrades: UpgradeSlots? = null,
) : EditablePanel<S>(screen, parent, x = parent.width + 3f, height = 0f, width = 0f) {
val itemConfigButton: LargeRectangleButtonPanel<S>?
val energyConfigButton: LargeRectangleButtonPanel<S>?
val fluidConfigButton: LargeRectangleButtonPanel<S>?
val redstoneControlsButton: LargeEnumRectangleButtonPanel<S, RedstoneSetting>?
val balanceInputsButton: LargeBooleanRectangleButtonPanel<S>?
val upgradesButton: LargeRectangleButtonPanel<S>?
private var upgradeWindow: FramePanel<S>? = null
private var nextY = 0f
fun <P : EditablePanel<@UnsafeVariance S>> addButton(button: P): P {
@ -310,6 +330,82 @@ class DeviceControls<out S : MatteryScreen<*>>(
redstoneControlsButton = null
}
if (upgrades != null) {
upgradesButton = addButton(object : LargeRectangleButtonPanel<S>(
screen, this@DeviceControls,
skinElement = Widgets18.UPGRADES
) {
init {
tooltip = TranslatableComponent("otm.gui.upgrades")
}
override fun tickInner() {
super.tickInner()
tooltipList = ArrayList<Component>().also {
it.add(TranslatableComponent("otm.gui.upgrades"))
it.add(TextComponent(""))
if (upgrades.allowedTypes.isEmpty()) {
it.add(TranslatableComponent("otm.gui.upgrade_type.allowed_none").withStyle(ChatFormatting.DARK_GRAY))
} else {
it.add(TranslatableComponent("otm.gui.upgrade_type.allowed").withStyle(ChatFormatting.GRAY))
for (upgrade in upgrades.allowedTypes) {
it.add(upgrade.component.copy().withStyle(ChatFormatting.GRAY))
}
}
val i = it.size
upgrades.currentStats.addUpgradeTooltipLines(it)
if (it.size != i) {
it.add(i, TranslatableComponent("otm.gui.upgrades.current").withStyle(ChatFormatting.GRAY))
it.add(i, TextComponent(""))
}
}
}
override fun onClick(mouseButton: Int) {
if (mouseButton == InputConstants.MOUSE_BUTTON_LEFT) {
if (upgradeWindow != null && !upgradeWindow!!.isRemoved) {
upgradeWindow!!.toScreenCenter()
upgradeWindow!!.popup()
} else {
val square = ceil(upgrades.slots.size.toDouble().pow(0.5)).toInt()
val size = square * AbstractSlotPanel.SIZE
upgradeWindow = FramePanel(screen, (size + 40f).coerceAtLeast(120f), 30f + size, TranslatableComponent("otm.gui.upgrades")).also { frame ->
val grid = GridPanel(screen, frame, width = AbstractSlotPanel.SIZE * square, height = AbstractSlotPanel.SIZE * square, columns = square, rows = square)
for (slot in upgrades.slots) {
SlotPanel(screen, grid, slot)
}
grid.dock = Dock.FILL
grid.dockResize = DockResizeMode.NONE
screen.addPanel(frame)
val parentFrame = this@DeviceControls.parent!!
frame.behaveAsWindow()
frame.setPos(parentFrame.absoluteX + parentFrame.width / 2f - frame.width / 2f, parentFrame.absoluteY + parentFrame.height / 2f - frame.height / 2f)
frame.popup()
upgrades.openState.value = true
frame.onClose { upgrades.openState.value = false }
parentFrame.blockingWindow = frame
}
}
}
}
})
} else {
upgradesButton = null
}
if (balanceInputs != null) {
balanceInputsButton = addButton(LargeBooleanRectangleButtonPanel(
screen, this,
@ -396,8 +492,9 @@ fun <S : MatteryScreen<*>> makeDeviceControls(
energyConfig: EnergyConfigPlayerInput? = null,
fluidConfig: FluidConfigPlayerInput? = null,
balanceInputs: BooleanInputWithFeedback? = null,
upgrades: UpgradeSlots? = null,
): DeviceControls<S> {
return DeviceControls(screen, parent, extra = extra, redstoneConfig = redstoneConfig,
itemConfig = itemConfig, energyConfig = energyConfig, fluidConfig = fluidConfig,
balanceInputs = balanceInputs)
balanceInputs = balanceInputs, upgrades = upgrades)
}

View File

@ -63,6 +63,7 @@ open class QueryUserPanel<out S: Screen>(
this.width = maxWidth.coerceAtLeast(bottom.children.stream().mapToDouble { it.width.toDouble() }.sum().toFloat() + 2f) + PADDING * 2f
toScreenCenter()
behaveAsWindow()
}
override fun keyPressedInternal(key: Int, scancode: Int, mods: Int): Boolean {

View File

@ -636,7 +636,7 @@ class AndroidStationScreen constructor(p_97741_: AndroidStationMenu, p_97742_: I
researchCanvas.setDockMargin(4f, 4f, 4f, 4f)
research!!.toScreenCenter()
research!!.closeOnEscape = true
research!!.behaveAsWindow()
addPanel(research!!)
research!!.requestFocus()
}

View File

@ -8,7 +8,6 @@ import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.WidePowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.WideProfiledPowerGaugePanel
import ru.dbotthepony.mc.otm.menu.tech.PlatePressMenu
@ -24,7 +23,7 @@ class PlatePressScreen(menu: PlatePressMenu, inventory: Inventory, title: Compon
ProgressGaugePanel(this, frame, menu.progressGauge, 78f, PROGRESS_ARROW_TOP)
SlotPanel(this, frame, menu.outputSlot, 104f, PROGRESS_SLOT_TOP)
makeDeviceControls(this, frame, redstoneConfig = menu.redstoneConfig, energyConfig = menu.energyConfig, itemConfig = menu.itemConfig)
makeDeviceControls(this, frame, redstoneConfig = menu.redstoneConfig, energyConfig = menu.energyConfig, itemConfig = menu.itemConfig, upgrades = menu.upgrades)
return frame
}

View File

@ -27,7 +27,7 @@ class TwinPlatePressScreen(menu: TwinPlatePressMenu, inventory: Inventory, title
ProgressGaugePanel(this, frame, menu.progressGauge1, 78f, PROGRESS_ARROW_TOP + 10f)
SlotPanel(this, frame, menu.outputSlots[1], 104f, PROGRESS_SLOT_TOP + 10f)
makeDeviceControls(this, frame, redstoneConfig = menu.redstoneConfig, energyConfig = menu.energyConfig, itemConfig = menu.itemConfig, balanceInputs = menu.balanceInputs)
makeDeviceControls(this, frame, redstoneConfig = menu.redstoneConfig, energyConfig = menu.energyConfig, itemConfig = menu.itemConfig, balanceInputs = menu.balanceInputs, upgrades = menu.upgrades)
return frame
}

View File

@ -10,6 +10,7 @@ import ru.dbotthepony.mc.otm.block.entity.tech.AndroidStationBlockEntity
import ru.dbotthepony.mc.otm.block.entity.tech.ChemicalGeneratorBlockEntity
import ru.dbotthepony.mc.otm.capability.energy.BlockEnergyStorageImpl
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.defineDecimal
import ru.dbotthepony.mc.otm.registry.MNames
object MachinesConfig : AbstractConfig("machines") {
@ -42,4 +43,34 @@ object MachinesConfig : AbstractConfig("machines") {
}
val ANDROID_CHARGER = BlockEnergyStorageImpl.makeConfigEntry(builder, MNames.ANDROID_CHARGER, capacity = Decimal(400_000), throughput = Decimal(8192)) { AndroidCharger }
object Upgrades {
init {
builder.push("UPGRADES")
}
val MIN_SPEED: Double by builder
.comment("Minimal combined upgrade speed percentage (upgrades can't decrease machine speed below this)")
.defineInRange("MIN_SPEED", -0.8, -1.0, Double.MAX_VALUE)
val MAX_SPEED: Double by builder
.comment("Maximal combined upgrade speed percentage")
.defineInRange("MAX_SPEED", Double.MAX_VALUE, 0.0, Double.MAX_VALUE)
val MIN_ENERGY by builder
.comment("Minimal combined energy consumption percentage (upgrades can't decrease machine energy consumption below this)")
.defineDecimal("MIN_ENERGY", Decimal(-0.8), Decimal(-1.0))
val MAX_ENERGY by builder
.comment("Maximal combined energy consumption percentage")
.defineDecimal("MAX_ENERGY", Decimal.LONG_MAX_VALUE, Decimal.ZERO)
init {
builder.pop()
}
}
init {
Upgrades
}
}

View File

@ -0,0 +1,54 @@
package ru.dbotthepony.mc.otm.container
import ru.dbotthepony.mc.otm.capability.IMatteryUpgrade
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.UpgradeType
import ru.dbotthepony.mc.otm.config.ConciseBalanceValues
import ru.dbotthepony.mc.otm.config.VerboseBalanceValues
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.math.Decimal
import kotlin.math.pow
open class UpgradeContainer(slotCount: Int, open val allowedUpgrades: Set<UpgradeType> = UpgradeType.ALL, watcher: Runnable = Runnable {}) : MatteryContainer(watcher, slotCount), IMatteryUpgrade {
constructor(watcher: Runnable, slotCount: Int, allowedUpgrades: Set<UpgradeType>) : this(slotCount, allowedUpgrades, watcher)
final override val upgradeTypes: Set<UpgradeType>
get() = setOf()
override val speedBonus: Double
get() = stream().filter { it.isNotEmpty }.mapToDouble { it.getCapability(MatteryCapability.UPGRADE).map { it.speedBonus }.orElse(0.0) * it.count }.sum()
override val processingItems: Int
get() = stream().filter { it.isNotEmpty }.mapToInt { it.getCapability(MatteryCapability.UPGRADE).map { it.processingItems }.orElse(0).coerceAtLeast(0) * it.count }.sum()
override val energyStorageFlat: Decimal
get() = stream().filter { it.isNotEmpty }.map { it.getCapability(MatteryCapability.UPGRADE).map { it.energyStorageFlat }.orElse(Decimal.ZERO).moreThanZero() * it.count }.reduce(Decimal.ZERO, Decimal::plus)
override val energyStorage: Decimal
get() = stream().filter { it.isNotEmpty }.map { it.getCapability(MatteryCapability.UPGRADE).map { it.energyStorage }.orElse(Decimal.ZERO).moreThanZero() * it.count }.reduce(Decimal.ZERO, Decimal::plus)
override val energyConsumed: Decimal
get() = stream().filter { it.isNotEmpty }.map { it.getCapability(MatteryCapability.UPGRADE).map { it.energyConsumed }.orElse(Decimal.ZERO) * it.count }.reduce(Decimal.ZERO, Decimal::plus)
override val failureMultiplier: Double
get() = stream().filter { it.isNotEmpty }.mapToDouble { it.getCapability(MatteryCapability.UPGRADE).map { it.failureMultiplier }.orElse(1.0).coerceAtLeast(0.0).pow(it.count.toDouble()) }.reduce(1.0) { a, b -> a * b }
override val energyThroughputFlat: Decimal
get() = stream().filter { it.isNotEmpty }.map { it.getCapability(MatteryCapability.UPGRADE).map { it.energyThroughputFlat }.orElse(Decimal.ZERO).moreThanZero() * it.count }.reduce(Decimal.ZERO, Decimal::plus)
override val energyThroughput: Decimal
get() = stream().filter { it.isNotEmpty }.map { it.getCapability(MatteryCapability.UPGRADE).map { it.energyThroughput }.orElse(Decimal.ZERO).moreThanZero() * it.count }.reduce(Decimal.ZERO, Decimal::plus)
fun transform(values: ConciseBalanceValues): ConciseBalanceValues {
return object : ConciseBalanceValues {
override val capacity: Decimal
get() = values.capacity * (energyStorage + Decimal.ONE) + energyStorageFlat
override val throughput: Decimal
get() = values.throughput * (energyThroughput + Decimal.ONE) + energyThroughputFlat
}
}
fun transform(values: VerboseBalanceValues): VerboseBalanceValues {
return object : VerboseBalanceValues {
override val capacity: Decimal
get() = values.capacity * (energyStorage + Decimal.ONE) + energyStorageFlat
override val receive: Decimal
get() = values.receive * (energyThroughput + Decimal.ONE) + energyThroughputFlat
override val extract: Decimal
get() = values.extract * (energyThroughput + Decimal.ONE) + energyThroughputFlat
}
}
}

View File

@ -91,8 +91,8 @@ inline fun <T> LazyOptional<T>.ifPresentK(lambda: (T) -> Unit) {
val ItemStack.tagNotNull: CompoundTag get() = orCreateTag
val FluidStack.isNotEmpty get() = !isEmpty
val ItemStack.isNotEmpty get() = !isEmpty
inline val FluidStack.isNotEmpty get() = !isEmpty
inline val ItemStack.isNotEmpty get() = !isEmpty
inline var Entity.position: Vec3
get() = position()

View File

@ -0,0 +1,41 @@
package ru.dbotthepony.mc.otm.core.collect
import java.util.EnumMap
import java.util.function.BooleanSupplier
import java.util.stream.Stream
class ConditionalEnumSet<T : Enum<T>>private constructor(private val backing: EnumMap<T, BooleanSupplier>, marker: Unit) : Set<T> {
constructor(clazz: Class<T>) : this(EnumMap(clazz), Unit)
constructor(map: Map<T, BooleanSupplier>) : this(EnumMap(map), Unit)
override val size: Int
get() = backing.values.stream().filter { it.asBoolean }.count().toInt()
override fun contains(element: T): Boolean {
return backing[element]?.asBoolean ?: false
}
override fun containsAll(elements: Collection<T>): Boolean {
return elements.all { contains(it) }
}
override fun isEmpty(): Boolean {
return backing.isEmpty() || backing.values.none { it.asBoolean }
}
override fun stream(): Stream<T> {
return backing.entries.stream().filter { it.value.asBoolean }.map { it.key }
}
override fun iterator(): Iterator<T> {
return backing.entries.iterator().filter { it.value.asBoolean }.map { it.key }
}
fun add(value: T, condition: BooleanSupplier) {
backing[value] = condition
}
fun remove(value: T): Boolean {
return backing.remove(value) != null
}
}

View File

@ -1,68 +1,57 @@
package ru.dbotthepony.mc.otm.core.collect
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap
import java.util.function.BooleanSupplier
import java.util.stream.Stream
class ConditionalSet<T> : AbstractSet<T> {
// method without boxing
fun interface Condition {
fun check(): Boolean
class ConditionalSet<E : Any> : Set<E> {
private val backing = Object2ObjectLinkedOpenHashMap<E, BooleanSupplier>()
override val size: Int
get() = backing.values.stream().filter { it.asBoolean }.count().toInt()
override fun contains(element: E): Boolean {
return backing[element]?.asBoolean ?: false
}
private val getters: Array<Pair<Condition, T>>
constructor(vararg getters: Pair<Condition, T>) : super() {
this.getters = Array(getters.size) { getters[it] }
override fun containsAll(elements: Collection<E>): Boolean {
return elements.all { contains(it) }
}
constructor(getters: List<Pair<Condition, T>>) : super() {
this.getters = Array(getters.size) { getters[it] }
override fun isEmpty(): Boolean {
return backing.isEmpty() || backing.values.stream().noneMatch { it.asBoolean }
}
constructor(getters: Stream<Pair<Condition, T>>) : super() {
this.getters = getters.toArray { arrayOfNulls<Pair<Condition, T>>(it) }
override fun stream(): Stream<E> {
return backing.entries.stream().filter { it.value.asBoolean }.map { it.key }
}
override val size: Int get() {
var i = 0
for (pair in getters) {
if (pair.first.check()) {
i++
}
}
return i
override fun iterator(): Iterator<E> {
return backing.entries.iterator().filter { it.value.asBoolean }.map { it.key }
}
override fun iterator(): Iterator<T> {
return object : Iterator<T> {
val parent = getters.iterator()
private var pair: Pair<Condition, T>? = null
fun add(element: E, condition: BooleanSupplier): Boolean {
if (element in backing) return false
backing[element] = condition
return true
}
private fun search() {
for (pair in parent) {
if (pair.first.check()) {
this.pair = pair
return
}
}
fun addFirst(element: E, condition: BooleanSupplier): Boolean {
if (element in backing) return false
backing.putAndMoveToFirst(element, condition)
return true
}
this.pair = null
}
fun replace(element: E, condition: BooleanSupplier) {
backing[element] = condition
}
init {
search()
}
fun replaceFirst(element: E, condition: BooleanSupplier) {
backing.remove(element)
backing.putAndMoveToFirst(element, condition)
}
override fun hasNext(): Boolean {
return pair != null
}
override fun next(): T {
val pair = pair ?: throw NoSuchElementException()
search()
return pair.second
}
}
fun remove(element: E): Boolean {
return backing.remove(element) != null
}
}

View File

@ -0,0 +1,51 @@
package ru.dbotthepony.mc.otm.item
import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.world.item.Item
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.LazyOptional
import ru.dbotthepony.mc.otm.capability.IMatteryUpgrade
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.UpgradeType
import ru.dbotthepony.mc.otm.capability.addUpgradeTooltipLines
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.math.Decimal
class SimpleUpgrade(
properties: Properties,
override val upgradeTypes: Set<UpgradeType>,
override val speedBonus: Double = 0.0,
override val processingItems: Int = 0,
override val energyStorageFlat: Decimal = Decimal.ZERO,
override val energyStorage: Decimal = Decimal.ZERO,
override val energyConsumed: Decimal = Decimal.ZERO,
override val energyThroughputFlat: Decimal = Decimal.ZERO,
override val energyThroughput: Decimal = Decimal.ZERO,
override val failureMultiplier: Double = 1.0,
) : Item(properties), IMatteryUpgrade, ICapabilityProvider {
private val resolver = LazyOptional.of { this }
override fun <T : Any?> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
if (cap === MatteryCapability.UPGRADE) {
return resolver.cast()
}
return LazyOptional.empty()
}
override fun appendHoverText(p_41421_: ItemStack, p_41422_: Level?, p_41423_: MutableList<Component>, p_41424_: TooltipFlag) {
super.appendHoverText(p_41421_, p_41422_, p_41423_, p_41424_)
addUpgradeTooltipLines(p_41423_)
}
override fun initCapabilities(stack: ItemStack, nbt: CompoundTag?): ICapabilityProvider {
return this
}
}

View File

@ -11,6 +11,7 @@ import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.Container
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
@ -23,6 +24,9 @@ import net.minecraft.world.item.enchantment.EnchantmentHelper.hasBindingCurse
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraftforge.network.NetworkEvent
import net.minecraftforge.network.PacketDistributor
import ru.dbotthepony.mc.otm.capability.IMatteryUpgrade
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.UpgradeType
import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.compat.cos.cosmeticArmorSlots
@ -31,7 +35,12 @@ import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.container.ItemFilterNetworkSlot
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.UpgradeContainer
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.collect.ConditionalEnumSet
import ru.dbotthepony.mc.otm.core.collect.ConditionalSet
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.BigDecimalValueCodec
import ru.dbotthepony.mc.otm.core.util.BinaryStringCodec
import ru.dbotthepony.mc.otm.core.util.BooleanValueCodec
@ -49,10 +58,14 @@ import ru.dbotthepony.mc.otm.network.packetHandled
import ru.dbotthepony.mc.otm.network.sender
import ru.dbotthepony.mc.otm.network.synchronizer.FieldSynchronizer
import ru.dbotthepony.mc.otm.network.synchronizer.IField
import ru.dbotthepony.mc.otm.network.synchronizer.IMutableBooleanField
import java.io.DataInputStream
import java.io.DataOutputStream
import java.math.BigDecimal
import java.util.*
import java.util.function.BooleanSupplier
import java.util.function.DoubleSupplier
import java.util.function.IntSupplier
import java.util.function.Predicate
import java.util.function.Supplier
@ -63,6 +76,17 @@ data class EquipmentSlots(
val curiosSlots: List<PlayerSlot<Slot, Slot>>
)
/**
* [openState] **is clientside only**, attempting to use it on server will result
* in classloading exceptions.
*/
data class UpgradeSlots(
val slots: List<MatterySlot>,
val allowedTypes: Set<UpgradeType>,
val openState: GetterSetter<Boolean>,
val currentStats: IMatteryUpgrade
)
abstract class MatteryMenu @JvmOverloads protected constructor(
menuType: MenuType<*>?,
containerId: Int,
@ -382,7 +406,7 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
return player.distanceToSqr(pos.x.toDouble() + 0.5, pos.y.toDouble() + 0.5, pos.z.toDouble() + 0.5) <= 64.0
}
private val externalSlots = ArrayList<Slot>()
private val externalSlots = ConditionalSet<Slot>()
private val quickMoveMapping = Reference2ObjectOpenHashMap<Slot, ReferenceArrayList<Collection<Slot>>>()
override fun addSlot(pSlot: Slot): Slot {
@ -412,11 +436,17 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
/**
* Adds slot to "storage slots" list (utilized by quick move) and calls [addSlot]
*
* [condition] allows to specify when slot is invisible on GUI (hence being ignored by quick move)
*/
protected fun <T : Slot> addStorageSlot(slot: T, addMapping: Boolean = true): T {
protected fun <T : Slot> addStorageSlot(slot: T, addMapping: Boolean = true, prepend: Boolean = false, condition: BooleanSupplier = BooleanSupplier { true }): T {
if (slot !in externalSlots) {
addSlot(slot)
externalSlots.add(slot)
if (prepend)
externalSlots.replaceFirst(slot, condition)
else
externalSlots.replace(slot, condition)
if (addMapping) {
mapQuickMove(slot, equipmentSlots)
@ -446,8 +476,8 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
mapQuickMove(slot, externalSlots, prepend = prepend)
}
protected fun mapQuickMove(slot: Slot, target: Collection<Slot>, prepend: Boolean = false) {
val listing = quickMoveMapping.computeIfAbsent(slot, Reference2ObjectFunction { ReferenceArrayList(1) })
protected fun mapQuickMove(slot: Slot, target: Collection<Slot>, prepend: Boolean = false, condition: BooleanSupplier = BooleanSupplier { true }) {
val listing = quickMoveMapping.computeIfAbsent(slot, Reference2ObjectFunction { ReferenceArrayList(1) /* ReferenceArrayList ибо мы используем его в некотором смысле как множество */ })
listing.remove(target)
if (prepend)
@ -715,6 +745,56 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
)
}
fun makeUpgradeSlots(count: Int, container: UpgradeContainer?): UpgradeSlots {
if (container != null) {
require(count == container.containerSize) { "Upgrade container size ${container.containerSize} does not match with provided size $count" }
}
val allowedTypes = EnumMap<UpgradeType, IMutableBooleanField>(UpgradeType::class.java)
for (value in UpgradeType.ALL) {
allowedTypes[value] = mSynchronizer.bool()
if (container != null) {
allowedTypes[value]!!.boolean = value in container.allowedUpgrades
}
}
val syncContainer = container ?: SimpleContainer(count)
var isOpen = false
val input = PlayerInput(BooleanValueCodec, allowSpectators = true, handler = { isOpen = it })
return UpgradeSlots(
slots = immutableList(count) {
object : MatterySlot(syncContainer, it) {
init {
mapQuickMoveToInventory(this)
addStorageSlot(this, prepend = true, condition = { isOpen })
}
override fun mayPlace(itemStack: ItemStack): Boolean {
return super.mayPlace(itemStack) && itemStack.getCapability(MatteryCapability.UPGRADE).map { it.upgradeTypes.any { allowedTypes[it]!!.boolean } }.orElse(false)
}
}
},
allowedTypes = ConditionalEnumSet(allowedTypes),
openState = GetterSetter.of({ isOpen }, { input.input(it); isOpen = it }),
currentStats = object : IMatteryUpgrade {
override val upgradeTypes: Set<UpgradeType> = setOf()
override val speedBonus: Double by mSynchronizer.computedDouble(DoubleSupplier { container?.speedBonus ?: 0.0 }).property
override val processingItems: Int by mSynchronizer.computedInt(IntSupplier { container?.processingItems ?: 0 }).property
override val energyStorageFlat: Decimal by mSynchronizer.computedDecimal { container?.energyStorageFlat ?: Decimal.ZERO }
override val energyStorage: Decimal by mSynchronizer.computedDecimal { container?.energyStorage ?: Decimal.ZERO }
override val energyConsumed: Decimal by mSynchronizer.computedDecimal { container?.energyConsumed ?: Decimal.ZERO }
override val energyThroughputFlat: Decimal by mSynchronizer.computedDecimal { container?.energyThroughputFlat ?: Decimal.ZERO }
override val energyThroughput: Decimal by mSynchronizer.computedDecimal { container?.energyThroughput ?: Decimal.ZERO }
override val failureMultiplier: Double by mSynchronizer.computedDouble(DoubleSupplier { container?.failureMultiplier ?: 1.0 }).property
}
)
}
companion object {
val TEXTURE_EMPTY_SLOTS: List<ResourceLocation> = ImmutableList.of(
InventoryMenu.EMPTY_ARMOR_SLOT_BOOTS,

View File

@ -10,6 +10,7 @@ import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.container.UpgradeContainer
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.runOnClient

View File

@ -26,6 +26,8 @@ class PlatePressMenu @JvmOverloads constructor(
val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig, allowPull = true)
val profiledEnergy = ProfiledLevelGaugeWidget(this, tile?.energy, energyWidget)
val upgrades = makeUpgradeSlots(3, tile?.upgrades)
init {
addStorageSlot(inputSlot)
addStorageSlot(outputSlot)

View File

@ -31,6 +31,8 @@ class TwinPlatePressMenu @JvmOverloads constructor(
val balanceInputs = BooleanInputWithFeedback(this)
val upgrades = makeUpgradeSlots(4, tile?.upgrades)
init {
if (tile != null) {
balanceInputs.with(tile::balanceInputs)

View File

@ -130,6 +130,7 @@ private fun CreativeModeTab.Output.fluids(value: Item) {
private fun addMainCreativeTabItems(consumer: CreativeModeTab.Output) {
with(consumer) {
accept(MItems.MACHINES)
accept(MItems.CreativeUpgrades.LIST)
accept(MRegistry.CARGO_CRATES.item)
accept(MItems.HOLO_SIGN)

View File

@ -4,7 +4,6 @@ package ru.dbotthepony.mc.otm.registry
import net.minecraft.ChatFormatting
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.EquipmentSlot
import net.minecraft.world.food.FoodProperties
import net.minecraft.world.item.*
import net.minecraft.world.item.crafting.Ingredient
@ -15,9 +14,11 @@ import net.minecraftforge.eventbus.api.IEventBus
import net.minecraftforge.registries.DeferredRegister
import net.minecraftforge.registries.ForgeRegistries
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.capability.UpgradeType
import ru.dbotthepony.mc.otm.config.ItemsConfig
import ru.dbotthepony.mc.otm.core.collect.SupplierList
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.item.*
import ru.dbotthepony.mc.otm.item.exopack.ExoPackCraftingUpgradeItem
import ru.dbotthepony.mc.otm.item.exopack.ExoPackProbeItem
@ -160,6 +161,38 @@ object MItems {
TRITANIUM_ANVIL = SupplierList(props)
}
object CreativeUpgrades {
val SPEED: SimpleUpgrade by registry.register("creative_speed_upgrade") { SimpleUpgrade(Item.Properties().rarity(Rarity.EPIC), UpgradeType.SPEED.set(), speedBonus = 1.0) }
val ENERGY_CONSUMPTION: SimpleUpgrade by registry.register("creative_energy_consumption_upgrade") { SimpleUpgrade(Item.Properties().rarity(Rarity.EPIC), UpgradeType.ENERGY_CONSUMPTION.set(), energyConsumed = Decimal.MINUS_ONE) }
val ENERGY_STORAGE: SimpleUpgrade by registry.register("creative_energy_storage_upgrade") { SimpleUpgrade(Item.Properties().rarity(Rarity.EPIC), UpgradeType.ENERGY_STORAGE.set(), energyStorage = Decimal.ONE) }
val ENERGY_STORAGE_FLAT: SimpleUpgrade by registry.register("creative_energy_storage_flat_upgrade") { SimpleUpgrade(Item.Properties().rarity(Rarity.EPIC), UpgradeType.ENERGY_STORAGE.set(), energyStorageFlat = Decimal.LONG_MAX_VALUE) }
val ENERGY_STORAGE_FLAT_SMALL: SimpleUpgrade by registry.register("creative_energy_storage_flat2_upgrade") { SimpleUpgrade(Item.Properties().rarity(Rarity.EPIC), UpgradeType.ENERGY_STORAGE.set(), energyStorageFlat = Decimal.INT_MAX_VALUE) }
val ENERGY_THROUGHPUT: SimpleUpgrade by registry.register("creative_energy_throughput_upgrade") { SimpleUpgrade(Item.Properties().rarity(Rarity.EPIC), UpgradeType.ENERGY_THROUGHPUT.set(), energyThroughput = Decimal.ONE) }
val ENERGY_THROUGHPUT_FLAT: SimpleUpgrade by registry.register("creative_energy_throughput_flat_upgrade") { SimpleUpgrade(Item.Properties().rarity(Rarity.EPIC), UpgradeType.ENERGY_THROUGHPUT.set(), energyThroughputFlat = Decimal.LONG_MAX_VALUE) }
val ENERGY_THROUGHPUT_FLAT_SMALL: SimpleUpgrade by registry.register("creative_energy_throughput_flat2_upgrade") { SimpleUpgrade(Item.Properties().rarity(Rarity.EPIC), UpgradeType.ENERGY_THROUGHPUT.set(), energyThroughputFlat = Decimal.INT_MAX_VALUE) }
val FAILSAFE: SimpleUpgrade by registry.register("creative_failsafe_upgrade") { SimpleUpgrade(Item.Properties().rarity(Rarity.EPIC), UpgradeType.FAILSAFE.set(), failureMultiplier = 0.0) }
val FAILURE: SimpleUpgrade by registry.register("creative_failure_upgrade") { SimpleUpgrade(Item.Properties().rarity(Rarity.EPIC), UpgradeType.FAILSAFE.set(), failureMultiplier = 2.0) }
val PROCESSING_ITEMS: SimpleUpgrade by registry.register("creative_processing_upgrade") { SimpleUpgrade(Item.Properties().rarity(Rarity.EPIC), UpgradeType.PROCESSING.set(), processingItems = 1) }
val LIST = SupplierList(
::SPEED,
::ENERGY_CONSUMPTION,
::ENERGY_STORAGE,
::ENERGY_STORAGE_FLAT,
::ENERGY_STORAGE_FLAT_SMALL,
::ENERGY_THROUGHPUT,
::ENERGY_THROUGHPUT_FLAT,
::ENERGY_THROUGHPUT_FLAT_SMALL,
::FAILSAFE,
::FAILURE,
::PROCESSING_ITEMS,
)
}
init {
CreativeUpgrades
}
val MATTER_DUST: Item by registry.register(MNames.MATTER_DUST) { MatterDustItem() }
val TRITANIUM_ORE_CLUMP: Item by registry.register(MNames.TRITANIUM_ORE_CLUMP) { Item(DEFAULT_PROPERTIES) }

View File

@ -25,6 +25,7 @@ object MItemTags {
val CARGO_CRATES: TagKey<Item> = ItemTags.create(ResourceLocation(OverdriveThatMatters.MOD_ID, "cargo_crates"))
val MINECART_CARGO_CRATES: TagKey<Item> = ItemTags.create(ResourceLocation(OverdriveThatMatters.MOD_ID, "minecart_cargo_crates"))
val INDUSTRIAL_GLASS: TagKey<Item> = ItemTags.create(ResourceLocation(OverdriveThatMatters.MOD_ID, "industrial_glass"))
val UPGRADES: TagKey<Item> = ItemTags.create(ResourceLocation(OverdriveThatMatters.MOD_ID, "upgrades"))
val CRAFTING_TABLES: TagKey<Item> = ItemTags.create(ResourceLocation("forge", "crafting_tables"))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 468 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB