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, "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.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.pull", "Pull")
gui("side_mode.push", "Push") 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("balance_inputs", "Balance input slots")
gui("sorting.default", "Default sorting") gui("sorting.default", "Default sorting")

View File

@ -607,6 +607,18 @@ private fun items(provider: MatteryLanguageProvider) {
add(MItems.ZPM_BATTERY, "Модуль нулевой точки") add(MItems.ZPM_BATTERY, "Модуль нулевой точки")
add(MItems.ZPM_BATTERY, "description", "Может быть найден у тех, кто путешествует между измерениями, если, конечно, они смогли достигнуть вселенной, где данные устройства были созиданы...") 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.pull", "Автоматическое вытягивание")
gui("side_mode.push", "Автоматическое выталкивание") 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("balance_inputs", "Балансировать входные слоты")
gui("sorting.default", "Сортировка по умолчанию") gui("sorting.default", "Сортировка по умолчанию")

View File

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

View File

@ -63,6 +63,10 @@ public class MatteryCapability {
@NotNull @NotNull
public static final Capability<ICurio> CURIOS_ITEM = CapabilityManager.get(new CapabilityToken<>() {}); 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) { public static void register(RegisterCapabilitiesEvent event) {
event.register(IMatteryEnergyStorage.class); event.register(IMatteryEnergyStorage.class);
event.register(MatteryPlayerCapability.class); event.register(MatteryPlayerCapability.class);
@ -72,5 +76,6 @@ public class MatteryCapability {
event.register(IReplicationTaskProvider.class); event.register(IReplicationTaskProvider.class);
event.register(IMatteryDrive.class); event.register(IMatteryDrive.class);
event.register(StorageNode.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.nbt.CompoundTag
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraftforge.common.util.INBTSerializable 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.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.Decimal
import ru.dbotthepony.mc.otm.core.math.getDecimal import ru.dbotthepony.mc.otm.core.math.getDecimal
import ru.dbotthepony.mc.otm.core.math.set 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 energy: IMatteryEnergyStorage?
protected abstract val isBlockedByRedstone: Boolean protected abstract val isBlockedByRedstone: Boolean
protected abstract fun deserializeJob(nbt: CompoundTag): JobType? protected abstract fun deserializeJob(nbt: CompoundTag): JobType?
protected abstract val upgrades: IMatteryUpgrade?
var currentJob: JobType? = null var currentJob: JobType? = null
set(value) { set(value) {
@ -263,7 +266,8 @@ abstract class MachineJobEventLoop<JobType : IMachineJob> : INBTSerializable<Com
return 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 val energy = energy
while (!isIdling && weakGreaterThan(availableTicks, 0.0) && throttleTicks <= 0) { while (!isIdling && weakGreaterThan(availableTicks, 0.0) && throttleTicks <= 0) {
@ -299,18 +303,23 @@ abstract class MachineJobEventLoop<JobType : IMachineJob> : INBTSerializable<Com
idleTicksAnim = 0 idleTicksAnim = 0
if (weakLessThan(workTicks, currentJob.ticks)) { if (weakLessThan(workTicks, currentJob.ticks)) {
val ticksLeft = currentJob.ticks - workTicks val ticksLeft = (currentJob.ticks - workTicks).coerceAtMost(availableTicks)
val ticksAdvanced: Double val ticksAdvanced: Double
var requiredPower: Decimal? = null var requiredPower: Decimal? = null
var extractedPower: Decimal? = null var extractedPower: Decimal? = null
if (currentJob.powerUsage.isZero) { if (currentJob.powerUsage.isZero) {
ticksAdvanced = availableTicks.coerceAtMost(ticksLeft) ticksAdvanced = ticksLeft
} else { } else {
requiredPower = currentJob.powerUsage * ticksLeft.coerceAtMost(availableTicks) requiredPower = currentJob.powerUsage * (Decimal.ONE + upgrades.energyConsumed.coerceIn(MachinesConfig.Upgrades.MIN_ENERGY, MachinesConfig.Upgrades.MAX_ENERGY)) * ticksLeft.coerceAtMost(availableTicks)
extractedPower = energy!!.extractEnergy(requiredPower, true)
ticksAdvanced = (extractedPower / requiredPower).toDouble().coerceAtMost(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)) { 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.Block
import net.minecraft.world.level.block.entity.BlockEntityType import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState 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.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matteryEnergy 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.immutableList
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.nbt.getCompoundList import ru.dbotthepony.mc.otm.core.nbt.getCompoundList
@ -30,7 +32,7 @@ abstract class MatteryWorkerBlockEntity<JobType : IMachineJob>(
blockPos: BlockPos, blockPos: BlockPos,
blockState: BlockState, blockState: BlockState,
val jobDeserializer: (tag: CompoundTag) -> JobType?, val jobDeserializer: (tag: CompoundTag) -> JobType?,
maxJobs: Int = 1 maxJobs: Int = 1,
) : MatteryPoweredBlockEntity(type, blockPos, blockState) { ) : MatteryPoweredBlockEntity(type, blockPos, blockState) {
val jobEventLoops: ImmutableList<MachineJobEventLoop<JobType>> = immutableList(maxJobs) { id -> val jobEventLoops: ImmutableList<MachineJobEventLoop<JobType>> = immutableList(maxJobs) { id ->
object : MachineJobEventLoop<JobType>() { object : MachineJobEventLoop<JobType>() {
@ -38,6 +40,8 @@ abstract class MatteryWorkerBlockEntity<JobType : IMachineJob>(
get() = matteryEnergy get() = matteryEnergy
override val isBlockedByRedstone: Boolean override val isBlockedByRedstone: Boolean
get() = redstoneControl.isBlockedByRedstone get() = redstoneControl.isBlockedByRedstone
override val upgrades: IMatteryUpgrade?
get() = this@MatteryWorkerBlockEntity.upgrades
override fun deserializeJob(nbt: CompoundTag): JobType? { override fun deserializeJob(nbt: CompoundTag): JobType? {
return jobDeserializer.invoke(nbt) return jobDeserializer.invoke(nbt)
@ -61,6 +65,8 @@ abstract class MatteryWorkerBlockEntity<JobType : IMachineJob>(
} }
} }
open val upgrades: IMatteryUpgrade? get() = null
var balanceInputs = false var balanceInputs = false
init { 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.JobStatus
import ru.dbotthepony.mc.otm.block.entity.MachineItemJob import ru.dbotthepony.mc.otm.block.entity.MachineItemJob
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity 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.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter 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.container.balance
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.menu.tech.PlatePressMenu import ru.dbotthepony.mc.otm.menu.tech.PlatePressMenu
@ -29,7 +32,8 @@ class PlatePressBlockEntity(
p_155230_: BlockState, p_155230_: BlockState,
val isTwin: Boolean = false, 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) { ) : 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 inputContainer = MatteryContainer(this::itemContainerUpdated, if (isTwin) 2 else 1).also(::addDroppableContainer)
val outputContainer = 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(::inputContainer)
savetable(::outputContainer) savetable(::outputContainer)
savetables.double(::experience) savetables.double(::experience)
savetables.stateful(::upgrades)
} }
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu { 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 PATTERN_SLOT_BACKGROUND = slotBgGrid.next()
val MATTER_CAPACITOR_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 BATTERY_ONLY = controlsGrid.next()
val ITEMS_CONFIGURATION = controlsGrid.next() val ITEMS_CONFIGURATION = controlsGrid.next()
@ -160,4 +160,6 @@ object Widgets18 {
put(RelativeSide.FRONT, FRONT_CONTROLS) put(RelativeSide.FRONT, FRONT_CONTROLS)
put(RelativeSide.BACK, BACK_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 * @return FramePanel created, or null
*/ */
protected open fun makeMainFrame(): FramePanel<MatteryScreen<*>>? { 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() { public override fun recalculateQuickCraftRemaining() {

View File

@ -1,8 +1,10 @@
package ru.dbotthepony.mc.otm.client.screen.panels package ru.dbotthepony.mc.otm.client.screen.panels
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import com.mojang.blaze3d.platform.InputConstants
import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.PoseStack import com.mojang.blaze3d.vertex.PoseStack
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
import it.unimi.dsi.fastutil.objects.ReferenceArraySet import it.unimi.dsi.fastutil.objects.ReferenceArraySet
import net.minecraft.client.gui.ComponentPath import net.minecraft.client.gui.ComponentPath
import net.minecraft.client.gui.Font 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.popScissorRect
import ru.dbotthepony.mc.otm.client.render.pushScissorRect import ru.dbotthepony.mc.otm.client.render.pushScissorRect
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen 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 ru.dbotthepony.mc.otm.client.screen.panels.input.QueryUserPanel
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -417,6 +420,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
return field return field
} }
field = null
return null return null
} }
@ -965,7 +969,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
return true return true
} }
if (isHovered) { if (isHovered || this is AbstractButtonPanel<*> && isPressed) {
val tooltip = tooltip val tooltip = tooltip
val tooltipList = tooltipList val tooltipList = tooltipList
@ -1186,10 +1190,6 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
this.height = height this.height = height
} }
companion object {
private val LOGGER = LogManager.getLogger()!!
}
protected open fun visibilityChanges(new: Boolean, old: Boolean) { 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 { fun keyPressed(key: Int, scancode: Int, mods: Int): Boolean {
if (!isVisible() || !acceptKeyboardInput) return false if (!isVisible() || !acceptKeyboardInput) return false
if (!isEverFocused()) 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) if (isFocusedThis) return keyPressedInternal(key, scancode, mods)
for (child in visibleChildrenInternal) { 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> { fun queryUser(title: Component, text: Collection<Component>, onConfirm: Runnable, onCancel: Runnable? = null): QueryUserPanel<S> {
return QueryUserPanel(screen, title, text, onConfirm, onCancel).also { blockingWindow = it } 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 org.lwjgl.opengl.GL30
import ru.dbotthepony.mc.otm.client.playGuiClickSound import ru.dbotthepony.mc.otm.client.playGuiClickSound
import ru.dbotthepony.mc.otm.client.render.* 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>( open class FramePanel<out S : Screen>(
screen: S, 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() protected val tabs: java.util.ArrayList<Tab> = ArrayList()
override fun performLayout() { override fun performLayout() {
@ -131,13 +157,29 @@ open class FramePanel<out S : Screen>(
tab.initial = i == 0 tab.initial = i == 0
} }
closeButton?.setPos(width - CLOSE_BUTTON.width, 0f)
super.performLayout() super.performLayout()
} }
protected var dragging = false protected var dragging = false
protected var closeButton: CloseButton? = null
var closeOnEscape = false 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 { init {
setDockPadding(PADDING, if (title != null) PADDING_TOP else PADDING, PADDING, PADDING) 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 { override fun keyPressedInternal(key: Int, scancode: Int, mods: Int): Boolean {
if (key == InputConstants.KEY_ESCAPE && closeOnEscape) { if (key == InputConstants.KEY_ESCAPE && closeOnEscape) {
remove() close()
return true return true
} }
@ -258,9 +315,13 @@ open class FramePanel<out S : Screen>(
padding = DockProperty(-3f, -3f, -3f, -3f) padding = DockProperty(-3f, -3f, -3f, -3f)
) )
val TAB_RIGHT_CONNECTION = WidgetLocation.MISC.sprite(x = 30f, y = 0f, width = 3f, height = 5f) 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_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_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_HEIGHT = 28f
const val TAB_WIDTH = 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 com.mojang.blaze3d.platform.InputConstants
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.client.gui.GuiGraphics 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.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting
import ru.dbotthepony.mc.otm.capability.FlowDirection 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.isCtrlDown
import ru.dbotthepony.mc.otm.client.isShiftDown import ru.dbotthepony.mc.otm.client.isShiftDown
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite
import ru.dbotthepony.mc.otm.client.render.Widgets18 import ru.dbotthepony.mc.otm.client.render.Widgets18
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen 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.EditablePanel
import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel 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.GetterSetter
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.RelativeSide 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.BooleanInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.input.FluidConfigPlayerInput import ru.dbotthepony.mc.otm.menu.input.FluidConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput
import java.util.function.Predicate import java.util.function.Predicate
import kotlin.math.ceil
import kotlin.math.pow
private fun <S : MatteryScreen<*>> makeRedstoneSettingButton( private fun <S : MatteryScreen<*>> makeRedstoneSettingButton(
screen: S, screen: S,
@ -212,6 +224,7 @@ private fun <S : MatteryScreen<*>> makeItemHandlerControlPanel(
moveButtons(front, back, left, right, top, bottom) moveButtons(front, back, left, right, top, bottom)
screen.addPanel(frame) screen.addPanel(frame)
frame.requestFocus() frame.requestFocus()
frame.closeOnEscape = true
return frame return frame
} }
@ -240,6 +253,7 @@ private fun <S : MatteryScreen<*>> makeEnergyConfigPanel(
moveButtons(front, back, left, right, top, bottom) moveButtons(front, back, left, right, top, bottom)
screen.addPanel(frame) screen.addPanel(frame)
frame.requestFocus() frame.requestFocus()
frame.closeOnEscape = true
return frame return frame
} }
@ -268,6 +282,7 @@ private fun <S : MatteryScreen<*>> makeFluidConfigPanel(
moveButtons(front, back, left, right, top, bottom) moveButtons(front, back, left, right, top, bottom)
screen.addPanel(frame) screen.addPanel(frame)
frame.requestFocus() frame.requestFocus()
frame.closeOnEscape = true
return frame return frame
} }
@ -281,12 +296,17 @@ class DeviceControls<out S : MatteryScreen<*>>(
val energyConfig: EnergyConfigPlayerInput? = null, val energyConfig: EnergyConfigPlayerInput? = null,
val fluidConfig: FluidConfigPlayerInput? = null, val fluidConfig: FluidConfigPlayerInput? = null,
val balanceInputs: BooleanInputWithFeedback? = null, val balanceInputs: BooleanInputWithFeedback? = null,
val upgrades: UpgradeSlots? = null,
) : EditablePanel<S>(screen, parent, x = parent.width + 3f, height = 0f, width = 0f) { ) : EditablePanel<S>(screen, parent, x = parent.width + 3f, height = 0f, width = 0f) {
val itemConfigButton: LargeRectangleButtonPanel<S>? val itemConfigButton: LargeRectangleButtonPanel<S>?
val energyConfigButton: LargeRectangleButtonPanel<S>? val energyConfigButton: LargeRectangleButtonPanel<S>?
val fluidConfigButton: LargeRectangleButtonPanel<S>? val fluidConfigButton: LargeRectangleButtonPanel<S>?
val redstoneControlsButton: LargeEnumRectangleButtonPanel<S, RedstoneSetting>? val redstoneControlsButton: LargeEnumRectangleButtonPanel<S, RedstoneSetting>?
val balanceInputsButton: LargeBooleanRectangleButtonPanel<S>? val balanceInputsButton: LargeBooleanRectangleButtonPanel<S>?
val upgradesButton: LargeRectangleButtonPanel<S>?
private var upgradeWindow: FramePanel<S>? = null
private var nextY = 0f private var nextY = 0f
fun <P : EditablePanel<@UnsafeVariance S>> addButton(button: P): P { fun <P : EditablePanel<@UnsafeVariance S>> addButton(button: P): P {
@ -310,6 +330,82 @@ class DeviceControls<out S : MatteryScreen<*>>(
redstoneControlsButton = null 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) { if (balanceInputs != null) {
balanceInputsButton = addButton(LargeBooleanRectangleButtonPanel( balanceInputsButton = addButton(LargeBooleanRectangleButtonPanel(
screen, this, screen, this,
@ -396,8 +492,9 @@ fun <S : MatteryScreen<*>> makeDeviceControls(
energyConfig: EnergyConfigPlayerInput? = null, energyConfig: EnergyConfigPlayerInput? = null,
fluidConfig: FluidConfigPlayerInput? = null, fluidConfig: FluidConfigPlayerInput? = null,
balanceInputs: BooleanInputWithFeedback? = null, balanceInputs: BooleanInputWithFeedback? = null,
upgrades: UpgradeSlots? = null,
): DeviceControls<S> { ): DeviceControls<S> {
return DeviceControls(screen, parent, extra = extra, redstoneConfig = redstoneConfig, return DeviceControls(screen, parent, extra = extra, redstoneConfig = redstoneConfig,
itemConfig = itemConfig, energyConfig = energyConfig, fluidConfig = fluidConfig, 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 this.width = maxWidth.coerceAtLeast(bottom.children.stream().mapToDouble { it.width.toDouble() }.sum().toFloat() + 2f) + PADDING * 2f
toScreenCenter() toScreenCenter()
behaveAsWindow()
} }
override fun keyPressedInternal(key: Int, scancode: Int, mods: Int): Boolean { 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) researchCanvas.setDockMargin(4f, 4f, 4f, 4f)
research!!.toScreenCenter() research!!.toScreenCenter()
research!!.closeOnEscape = true research!!.behaveAsWindow()
addPanel(research!!) addPanel(research!!)
research!!.requestFocus() 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.button.makeDeviceControls
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.widget.ProgressGaugePanel 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.client.screen.widget.WideProfiledPowerGaugePanel
import ru.dbotthepony.mc.otm.menu.tech.PlatePressMenu 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) ProgressGaugePanel(this, frame, menu.progressGauge, 78f, PROGRESS_ARROW_TOP)
SlotPanel(this, frame, menu.outputSlot, 104f, PROGRESS_SLOT_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 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) ProgressGaugePanel(this, frame, menu.progressGauge1, 78f, PROGRESS_ARROW_TOP + 10f)
SlotPanel(this, frame, menu.outputSlots[1], 104f, PROGRESS_SLOT_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 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.block.entity.tech.ChemicalGeneratorBlockEntity
import ru.dbotthepony.mc.otm.capability.energy.BlockEnergyStorageImpl import ru.dbotthepony.mc.otm.capability.energy.BlockEnergyStorageImpl
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.defineDecimal
import ru.dbotthepony.mc.otm.registry.MNames import ru.dbotthepony.mc.otm.registry.MNames
object MachinesConfig : AbstractConfig("machines") { 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 } 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 ItemStack.tagNotNull: CompoundTag get() = orCreateTag
val FluidStack.isNotEmpty get() = !isEmpty inline val FluidStack.isNotEmpty get() = !isEmpty
val ItemStack.isNotEmpty get() = !isEmpty inline val ItemStack.isNotEmpty get() = !isEmpty
inline var Entity.position: Vec3 inline var Entity.position: Vec3
get() = position() 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 package ru.dbotthepony.mc.otm.core.collect
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap
import java.util.function.BooleanSupplier
import java.util.stream.Stream import java.util.stream.Stream
class ConditionalSet<T> : AbstractSet<T> { class ConditionalSet<E : Any> : Set<E> {
// method without boxing private val backing = Object2ObjectLinkedOpenHashMap<E, BooleanSupplier>()
fun interface Condition {
fun check(): Boolean 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>> override fun containsAll(elements: Collection<E>): Boolean {
return elements.all { contains(it) }
constructor(vararg getters: Pair<Condition, T>) : super() {
this.getters = Array(getters.size) { getters[it] }
} }
constructor(getters: List<Pair<Condition, T>>) : super() { override fun isEmpty(): Boolean {
this.getters = Array(getters.size) { getters[it] } return backing.isEmpty() || backing.values.stream().noneMatch { it.asBoolean }
} }
constructor(getters: Stream<Pair<Condition, T>>) : super() { override fun stream(): Stream<E> {
this.getters = getters.toArray { arrayOfNulls<Pair<Condition, T>>(it) } return backing.entries.stream().filter { it.value.asBoolean }.map { it.key }
} }
override val size: Int get() { override fun iterator(): Iterator<E> {
var i = 0 return backing.entries.iterator().filter { it.value.asBoolean }.map { it.key }
for (pair in getters) {
if (pair.first.check()) {
i++
}
}
return i
} }
override fun iterator(): Iterator<T> { fun add(element: E, condition: BooleanSupplier): Boolean {
return object : Iterator<T> { if (element in backing) return false
val parent = getters.iterator() backing[element] = condition
private var pair: Pair<Condition, T>? = null return true
}
private fun search() { fun addFirst(element: E, condition: BooleanSupplier): Boolean {
for (pair in parent) { if (element in backing) return false
if (pair.first.check()) { backing.putAndMoveToFirst(element, condition)
this.pair = pair return true
return }
}
}
this.pair = null fun replace(element: E, condition: BooleanSupplier) {
} backing[element] = condition
}
init { fun replaceFirst(element: E, condition: BooleanSupplier) {
search() backing.remove(element)
} backing.putAndMoveToFirst(element, condition)
}
override fun hasNext(): Boolean { fun remove(element: E): Boolean {
return pair != null return backing.remove(element) != null
}
override fun next(): T {
val pair = pair ?: throw NoSuchElementException()
search()
return pair.second
}
}
} }
} }

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.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.Container import net.minecraft.world.Container
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu 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.minecraft.world.level.block.entity.BlockEntity
import net.minecraftforge.network.NetworkEvent import net.minecraftforge.network.NetworkEvent
import net.minecraftforge.network.PacketDistributor 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.capability.matteryPlayer
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.compat.cos.cosmeticArmorSlots 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.ItemFilter
import ru.dbotthepony.mc.otm.container.ItemFilterNetworkSlot import ru.dbotthepony.mc.otm.container.ItemFilterNetworkSlot
import ru.dbotthepony.mc.otm.container.MatteryContainer 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.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.BigDecimalValueCodec
import ru.dbotthepony.mc.otm.core.util.BinaryStringCodec import ru.dbotthepony.mc.otm.core.util.BinaryStringCodec
import ru.dbotthepony.mc.otm.core.util.BooleanValueCodec 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.sender
import ru.dbotthepony.mc.otm.network.synchronizer.FieldSynchronizer import ru.dbotthepony.mc.otm.network.synchronizer.FieldSynchronizer
import ru.dbotthepony.mc.otm.network.synchronizer.IField import ru.dbotthepony.mc.otm.network.synchronizer.IField
import ru.dbotthepony.mc.otm.network.synchronizer.IMutableBooleanField
import java.io.DataInputStream import java.io.DataInputStream
import java.io.DataOutputStream import java.io.DataOutputStream
import java.math.BigDecimal import java.math.BigDecimal
import java.util.* 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.Predicate
import java.util.function.Supplier import java.util.function.Supplier
@ -63,6 +76,17 @@ data class EquipmentSlots(
val curiosSlots: List<PlayerSlot<Slot, Slot>> 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( abstract class MatteryMenu @JvmOverloads protected constructor(
menuType: MenuType<*>?, menuType: MenuType<*>?,
containerId: Int, 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 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>>>() private val quickMoveMapping = Reference2ObjectOpenHashMap<Slot, ReferenceArrayList<Collection<Slot>>>()
override fun addSlot(pSlot: Slot): 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] * 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) { if (slot !in externalSlots) {
addSlot(slot) addSlot(slot)
externalSlots.add(slot)
if (prepend)
externalSlots.replaceFirst(slot, condition)
else
externalSlots.replace(slot, condition)
if (addMapping) { if (addMapping) {
mapQuickMove(slot, equipmentSlots) mapQuickMove(slot, equipmentSlots)
@ -446,8 +476,8 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
mapQuickMove(slot, externalSlots, prepend = prepend) mapQuickMove(slot, externalSlots, prepend = prepend)
} }
protected fun mapQuickMove(slot: Slot, target: Collection<Slot>, prepend: Boolean = false) { protected fun mapQuickMove(slot: Slot, target: Collection<Slot>, prepend: Boolean = false, condition: BooleanSupplier = BooleanSupplier { true }) {
val listing = quickMoveMapping.computeIfAbsent(slot, Reference2ObjectFunction { ReferenceArrayList(1) }) val listing = quickMoveMapping.computeIfAbsent(slot, Reference2ObjectFunction { ReferenceArrayList(1) /* ReferenceArrayList ибо мы используем его в некотором смысле как множество */ })
listing.remove(target) listing.remove(target)
if (prepend) 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 { companion object {
val TEXTURE_EMPTY_SLOTS: List<ResourceLocation> = ImmutableList.of( val TEXTURE_EMPTY_SLOTS: List<ResourceLocation> = ImmutableList.of(
InventoryMenu.EMPTY_ARMOR_SLOT_BOOTS, 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.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy import ru.dbotthepony.mc.otm.capability.energy
import ru.dbotthepony.mc.otm.client.minecraft 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.GetterSetter
import ru.dbotthepony.mc.otm.core.immutableList import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.runOnClient 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 energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig, allowPull = true)
val profiledEnergy = ProfiledLevelGaugeWidget(this, tile?.energy, energyWidget) val profiledEnergy = ProfiledLevelGaugeWidget(this, tile?.energy, energyWidget)
val upgrades = makeUpgradeSlots(3, tile?.upgrades)
init { init {
addStorageSlot(inputSlot) addStorageSlot(inputSlot)
addStorageSlot(outputSlot) addStorageSlot(outputSlot)

View File

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

View File

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

View File

@ -4,7 +4,6 @@ package ru.dbotthepony.mc.otm.registry
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.EquipmentSlot
import net.minecraft.world.food.FoodProperties import net.minecraft.world.food.FoodProperties
import net.minecraft.world.item.* import net.minecraft.world.item.*
import net.minecraft.world.item.crafting.Ingredient 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.DeferredRegister
import net.minecraftforge.registries.ForgeRegistries import net.minecraftforge.registries.ForgeRegistries
import ru.dbotthepony.mc.otm.OverdriveThatMatters 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.config.ItemsConfig
import ru.dbotthepony.mc.otm.core.collect.SupplierList import ru.dbotthepony.mc.otm.core.collect.SupplierList
import ru.dbotthepony.mc.otm.core.TranslatableComponent 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.*
import ru.dbotthepony.mc.otm.item.exopack.ExoPackCraftingUpgradeItem import ru.dbotthepony.mc.otm.item.exopack.ExoPackCraftingUpgradeItem
import ru.dbotthepony.mc.otm.item.exopack.ExoPackProbeItem import ru.dbotthepony.mc.otm.item.exopack.ExoPackProbeItem
@ -160,6 +161,38 @@ object MItems {
TRITANIUM_ANVIL = SupplierList(props) 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 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) } 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 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 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 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")) 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