diff --git a/README.md b/README.md new file mode 100644 index 0000000..fbeed1b --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# ⛏Create: Ore Deposits + +**An addon for the [Create](https://modrinth.com/mod/create) mod** + +This mod aims to add drills and deposits to allow for a more fair way to get large quantities of materials. + +> **This mod is a work in progress.** Many features are uncompleted, unbalanced, placeholders, or entirely subject to change/removal. +> +> **THIS IS NOT READY FOR SURVIVAL PLAY** + +--- + +## TODO + +### Drill System +- [ ] Heat tooltip +- [ ] Tip type tooltip +- [ ] Lubricant tooltip +- [ ] Coolant tooltip +- [ ] Temperature affects tip durability +- [ ] Deposits require matching tip type +- [ ] Crafting recipes for drills and tips + +### Fluids +- [ ] Tiered lubricants +- [ ] Tiered coolants + +### Refinement +- [ ] Unprocessed ore items +- [ ] Refining recipe tree + +### World & Polish +- [ ] Worldgen for ore deposits +- [ ] Textures & models +- [ ] Ponder scenes + +--- + +## Contributing + +This project is still "early" in development. Any contributions like textures, features, or even just balancing is GREATLY appreciated. \ No newline at end of file diff --git a/src/main/java/com/createcivilization/create_ore_deposits/CreateOreDeposits.kt b/src/main/java/com/createcivilization/create_ore_deposits/CreateOreDeposits.kt index c1b0c1a..0fc9874 100644 --- a/src/main/java/com/createcivilization/create_ore_deposits/CreateOreDeposits.kt +++ b/src/main/java/com/createcivilization/create_ore_deposits/CreateOreDeposits.kt @@ -63,8 +63,9 @@ data object CreateOreDeposits { } private fun registerDataMapTypes(event: RegisterDataMapTypesEvent) { - event.register(CreateOreDepositsDataMaps.HARDNESS_DATA) + event.register(CreateOreDepositsDataMaps.DEPOSIT_DATA) event.register(CreateOreDepositsDataMaps.COOLING_FACTOR_DATA) + event.register(CreateOreDepositsDataMaps.LUBRICANT_FACTOR_DATA) } @SubscribeEvent diff --git a/src/main/java/com/createcivilization/create_ore_deposits/config/Config.kt b/src/main/java/com/createcivilization/create_ore_deposits/config/Config.kt index 7046764..35a4ace 100644 --- a/src/main/java/com/createcivilization/create_ore_deposits/config/Config.kt +++ b/src/main/java/com/createcivilization/create_ore_deposits/config/Config.kt @@ -23,14 +23,6 @@ data object Config { internal val _baseTemperature: ModConfigSpec.DoubleValue = builder.defineInRange("baseTemperature", 293.0, -Double.MAX_VALUE, Double.MAX_VALUE) inline val baseTemperature: Float get() = _baseTemperature.get().toFloat() - - @PublishedApi - internal val _dampening: ModConfigSpec.DoubleValue = builder.defineInRange("dampening", 0.008, 0.0, Double.MAX_VALUE) - inline val dampening: Float get() = _dampening.get().toFloat() - - @PublishedApi - internal val _scale: ModConfigSpec.DoubleValue = builder.defineInRange("scale", 1.0, 0.0, Double.MAX_VALUE) - inline val scale: Float get() = _scale.get().toFloat() } } diff --git a/src/main/java/com/createcivilization/create_ore_deposits/registry/block/CreateOreDepositsBlocks.kt b/src/main/java/com/createcivilization/create_ore_deposits/registry/block/CreateOreDepositsBlocks.kt index 305b919..de87f7e 100644 --- a/src/main/java/com/createcivilization/create_ore_deposits/registry/block/CreateOreDepositsBlocks.kt +++ b/src/main/java/com/createcivilization/create_ore_deposits/registry/block/CreateOreDepositsBlocks.kt @@ -29,24 +29,19 @@ import net.minecraft.world.level.storage.loot.providers.number.ConstantValue object CreateOreDepositsBlocks { - val EXAMPLE_DEPOSIT: BlockEntry = REGISTRATE.block("example_deposit", ::Block) - .initialProperties(SharedProperties::stone) // Specify your custom tab - .simpleItem() - .register() - - val DRILL_BLOCK: BlockEntry = REGISTRATE.block("deposit_drill", ::DepositDrillBlock) .initialProperties(SharedProperties::stone) .properties { it.mapColor(MapColor.PODZOL).noOcclusion() } .transform(axeOrPickaxe()) .onRegister(movementBehaviour(DrillMovementBehaviour())) - .onRegister { b -> BlockStressValues.IMPACTS.register(b) { 4.0 } } + .onRegister { b -> BlockStressValues.IMPACTS.register(b) {100.0} } .item() .transform(customItemModel()) .register() // TEMP LOOT VALUES, CHANGE LATER. // Deposits + val EXAMPLE_DEPOSIT: BlockEntry = registerDepositGuaranteed("example_deposit", Blocks.STONE, Items.NETHERITE_BLOCK) val COAL_ORE_DEPOSIT: BlockEntry = registerDeposit("coal_ore_deposit", Blocks.COAL_ORE, Items.COAL, 3f, 0.8f) val IRON_ORE_DEPOSIT: BlockEntry = registerDepositSingleRoll("iron_ore_deposit", Blocks.IRON_ORE, Items.RAW_IRON, 0.8f) val GOLD_ORE_DEPOSIT: BlockEntry = registerDepositSingleRoll("gold_ore_deposit", Blocks.GOLD_ORE, Items.RAW_GOLD, 0.6f) diff --git a/src/main/java/com/createcivilization/create_ore_deposits/registry/block/entries/deposit_drill/DepositDrillBlockEntity.kt b/src/main/java/com/createcivilization/create_ore_deposits/registry/block/entries/deposit_drill/DepositDrillBlockEntity.kt index 59992e8..6007dd1 100644 --- a/src/main/java/com/createcivilization/create_ore_deposits/registry/block/entries/deposit_drill/DepositDrillBlockEntity.kt +++ b/src/main/java/com/createcivilization/create_ore_deposits/registry/block/entries/deposit_drill/DepositDrillBlockEntity.kt @@ -1,15 +1,14 @@ package com.createcivilization.create_ore_deposits.registry.block.entries.deposit_drill import com.createcivilization.create_ore_deposits.config.Config -import com.createcivilization.create_ore_deposits.registry.datamap.CreateOreDepositsDataMaps -import com.createcivilization.create_ore_deposits.registry.datamap.CreateOreDepositsDataMaps.COOLING_FACTOR_DATA -import com.createcivilization.create_ore_deposits.registry.datamap.CreateOreDepositsDataMaps.HARDNESS_DATA +import com.createcivilization.create_ore_deposits.registry.datamap.CreateOreDepositsDataMaps.DEPOSIT_DATA import com.createcivilization.create_ore_deposits.registry.datamap.CreateOreDepositsDataMaps.LUBRICANT_FACTOR_DATA import com.createcivilization.create_ore_deposits.registry.fluid.CreateOreDepositsFluids import com.createcivilization.create_ore_deposits.registry.fluid.FluidHandler import com.createcivilization.create_ore_deposits.registry.tag.CreateOreDepositsTags import com.createcivilization.create_ore_deposits.util.translate import com.simibubi.create.content.kinetics.base.BlockBreakingKineticBlockEntity +import com.simibubi.create.foundation.item.TooltipHelper import com.simibubi.create.foundation.utility.BlockHelper import com.simibubi.create.foundation.utility.ServerSpeedProvider import net.createmod.catnip.animation.LerpedFloat @@ -20,6 +19,7 @@ import net.minecraft.core.Direction import net.minecraft.core.HolderLookup import net.minecraft.core.registries.BuiltInRegistries import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.NbtUtils import net.minecraft.network.chat.Component import net.minecraft.server.level.ServerLevel import net.minecraft.world.item.ItemStack @@ -30,44 +30,40 @@ import net.minecraft.world.level.block.entity.BlockEntityType import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.properties.BlockStateProperties import net.minecraft.world.level.material.Fluids -import net.neoforged.neoforge.fluids.FluidStack import net.neoforged.neoforge.fluids.capability.IFluidHandler.FluidAction import net.neoforged.neoforge.items.IItemHandler import net.neoforged.neoforge.items.ItemStackHandler -import java.util.function.Predicate -import kotlin.math.pow import kotlin.math.roundToInt -// Minimum value for LerpedFloat to not get jumpy -private const val min = 0.5 +private const val MIN_LERP = 0.5 class DepositDrillBlockEntity( - type : BlockEntityType<*>, + type: BlockEntityType<*>, pos: BlockPos, blockState: BlockState ) : BlockBreakingKineticBlockEntity(type, pos, blockState) { - /** - * Allows array access syntax on the [itemHandler] property. - */ - operator fun ItemStackHandler.get(index: Int): ItemStack = this.getStackInSlot(index) - - // The Float value distance from the bottom of the drill, should always be positive private var drillOffset: Float = 0f - private var lerpedOffset: LerpedFloat = LerpedFloat.linear().startWithValue(min) + private var lerpedOffset: LerpedFloat = LerpedFloat.linear().startWithValue(MIN_LERP) private var lastBlock: Block? = null - private var temperature: Float = 0f + private var temperature: Float = Config.SERVER.DEPOSIT_DRILL.baseTemperature + private var maxAttempts: Int = 0 + private var remainingAttempts: Int = 0 + private var drillTickCounter: Int = 0 + private var currentDepositPos: BlockPos? = null - private val itemHandler: ItemStackHandler = ItemStackHandler() - private val drillTipHandler: ItemStackHandler = ItemStackHandler() - private val lubricantHandler: FluidHandler = FluidHandler( + private val itemHandler = ItemStackHandler() + private val drillTipHandler = ItemStackHandler() + + private val lubricantHandler = FluidHandler( 1000, mutableSetOf( CreateOreDepositsFluids.LUBRICANT.get(), CreateOreDepositsFluids.LUBRICANT.get().source ) ) - private val coolantHandler: FluidHandler = FluidHandler( + + private val coolantHandler = FluidHandler( 1000, mutableSetOf( Fluids.WATER, @@ -78,178 +74,169 @@ class DepositDrillBlockEntity( override fun tick() { super.tick() - val movementSpeed: Float = getMovementSpeed() - val canMove: Boolean = getTargetBlock() == Blocks.AIR || movementSpeed < 0 + val movementSpeed = getMovementSpeed() + val canMove = getTargetBlock() == Blocks.AIR || movementSpeed < 0 + lerpedOffset.forceNextSync() if (canMove) { drillOffset = (movementSpeed + drillOffset).coerceAtLeast(0f) - lerpedOffset.forceNextSync() setLerpedOffset(drillOffset) } else { - lerpedOffset.forceNextSync() setLerpedOffset(drillOffset.roundToInt()) } updateTemperature() + onBreakTick() damageTip() } - // Due to this always being called IMMEDIATELY before adding to the break tick, this allows us to do something every break tick. - override fun getBreakSpeed(): Float { - onBreakTick() + override fun lazyTick() { + super.lazyTick() + setChanged() + sendData() + if (getMovementSpeed() != 0f) lubricantHandler.drain(1, FluidAction.EXECUTE) + } - return super.getBreakSpeed() + override fun canBreak(stateToBreak: BlockState, blockHardness: Float): Boolean { + if (isDeposit(stateToBreak)) return false + return super.canBreak(stateToBreak, blockHardness) } + override fun getBreakSpeed(): Float = super.getBreakSpeed() + + override fun getBreakingPos(): BlockPos = if (canMine()) getTargetPos() else BlockPos.ZERO + /** - * Simulates block drops for deposits, allowing deposits to return drops every break tick + * Handles per-tick extraction from deposit blocks. Unlike normal blocks + * (which break all at once), deposits yield drops over many ticks based + * on [maxAttempts] and the current extraction interval. */ fun onBreakTick() { - if (!canMine()) - return + if (level?.isClientSide != false) return + if (!canMine()) return + + val targetPos = getTargetPos() val blockState = getTargetBlockState() ?: return - if (!isBlockStateADeposit(blockState)) + + if (!isDeposit(blockState)) { + currentDepositPos = null return - val serverLevel = level as? ServerLevel ?: return + } - for (stack in getSimulatedDrops(blockState, serverLevel, getTargetPos())) { - itemHandler.insertItem(0, stack, false) + if (currentDepositPos != targetPos) { + clearDestroyProgress() + currentDepositPos = targetPos + maxAttempts = blockState.blockHolder.getData(DEPOSIT_DATA)?.maxAttempts ?: 0 + remainingAttempts = maxAttempts + drillTickCounter = 0 } - } - fun getSimulatedDrops(state: BlockState, serverLevel: ServerLevel, pos: BlockPos) : List { - return Block.getDrops(state, serverLevel, pos, null, null, ItemStack.EMPTY) + if (remainingAttempts <= 0) return + + drillTickCounter++ + val extractInterval = calculateExtractionInterval() + + if (drillTickCounter >= extractInterval) { + drillTickCounter = 0 + remainingAttempts-- + + val serverLevel = level as? ServerLevel ?: return + for (stack in getSimulatedDrops(blockState, serverLevel, targetPos)) { + itemHandler.insertItem(0, stack, false) + } + + updateDestroyProgress(targetPos) + + if (remainingAttempts <= 0) { + level?.destroyBlockProgress(blockPos.hashCode(), targetPos, -1) + level?.setBlock(targetPos, Blocks.AIR.defaultBlockState(), 3) + currentDepositPos = null + } + } } fun canMine(): Boolean { - val inventory = itemHandler[0] - val drillTipInventory = drillTipHandler[0] - val inventoryNotFull = inventory.count != itemHandler.getSlotLimit(0) - val targetBlockIsTheSameAsLastBlock = getTargetBlock() == lastBlock + val tip = drillTipHandler.getStackInSlot(0) + if (tip.isEmpty) return false - if(drillTipInventory.isEmpty){ - return false - } + val inventory = itemHandler.getStackInSlot(0) + if (inventory.isEmpty) return true - return inventory.isEmpty || (inventoryNotFull && targetBlockIsTheSameAsLastBlock) + val notFull = inventory.count != itemHandler.getSlotLimit(0) + return notFull && getTargetBlock() == lastBlock } - override fun lazyTick() { - super.lazyTick() - setChanged() - sendData() - if (getMovementSpeed() != 0f) lubricantHandler.drain(1, FluidAction.EXECUTE) + fun calculateExtractionInterval(): Int = 1025 - (speed * 4).roundToInt() + + fun getSimulatedDrops(state: BlockState, serverLevel: ServerLevel, pos: BlockPos): List { + return Block.getDrops(state, serverLevel, pos, null, null, ItemStack.EMPTY) } override fun onBlockBroken(stateToBreak: BlockState) { lastBlock = getTargetBlock() BlockHelper.destroyBlock(level, breakingPos, 1f) { drops: ItemStack -> - // Since onBreakTick() already inserts deposit drops into the inventory, we skip them to not add twice on break - if (!isBlockStateADeposit(stateToBreak)) + if (!isDeposit(stateToBreak)) { itemHandler.insertItem(0, drops, false) + } } } - override fun getBreakingPos(): BlockPos = if (canMine()) getTargetPos() else BlockPos.ZERO - - override fun calculateStressApplied(): Float { - val lubeFactor = getLubricantFactor().coerceIn(0f, 1f) - val baseStress = 512f * getBlockHardness(getTargetBlockState()) - - val stressMultiplier = 0.5f + 0.5f * (1f - lubeFactor).pow(0.5f) - - return baseStress * stressMultiplier + fun clearDestroyProgress() { + currentDepositPos?.let { pos -> + level?.destroyBlockProgress(blockPos.hashCode(), pos, -1) + } } - - override fun read(compound: CompoundTag, registries: HolderLookup.Provider, clientPacket: Boolean) { - val nbt: CompoundTag = compound.getCompound("DepositDrill") - - drillOffset = nbt.getFloat("DrillOffset") - temperature = nbt.getFloat("Temperature") - if (nbt.contains("LastBlock")) lastBlock = BuiltInRegistries.BLOCK.get(NBTHelper.readResourceLocation(nbt, "LastBlock")) - itemHandler.deserializeNBT(registries, nbt.getCompound("ItemHandler")) - drillTipHandler.deserializeNBT(registries, nbt.getCompound("DrillTipHandler")) - lubricantHandler.deserializeNBT(registries, nbt.getCompound("Lubricant")) - coolantHandler.deserializeNBT(registries, nbt.getCompound("Coolant")) - - super.read(compound, registries, clientPacket) + fun updateDestroyProgress(targetPos: BlockPos) { + if (maxAttempts <= 0) return + val attemptsUsed = maxAttempts - remainingAttempts + val stage = ((attemptsUsed.toFloat() / maxAttempts) * 10f).toInt().coerceIn(0, 9) + level?.destroyBlockProgress(blockPos.hashCode(), targetPos, stage) } - override fun write(compound: CompoundTag, registries: HolderLookup.Provider, clientPacket: Boolean) { - val nbt = CompoundTag() - nbt.putFloat("DrillOffset", drillOffset) - nbt.putFloat("Temperature", temperature) - if (lastBlock != null) NBTHelper.writeResourceLocation(nbt, "LastBlock", BuiltInRegistries.BLOCK.getKey(lastBlock!!)) - nbt.put("ItemHandler", itemHandler.serializeNBT(registries)) - nbt.put("DrillTipHandler", drillTipHandler.serializeNBT(registries)) - nbt.put("Lubricant", lubricantHandler.serializeNBT(registries)) - nbt.put("Coolant", coolantHandler.serializeNBT(registries)) - - compound.put("DepositDrill", nbt) - super.write(compound, registries, clientPacket) - } + fun updateTemperature() { + val baseCooling = Config.SERVER.DEPOSIT_DRILL.baseCooling + val baseTemperature = Config.SERVER.DEPOSIT_DRILL.baseTemperature - override fun addToGoggleTooltip(tooltip: MutableList, isPlayerSneaking: Boolean): Boolean { - translate("tooltip.drill.header").forGoggles(tooltip) + val targetState = getTargetBlockState() + val isMining = targetState != null && isDeposit(targetState) && canMine() && speed > 0 - val targetBlock: Block? = level?.getBlockState(getDrillTipPos())?.block - if (targetBlock != null && targetBlock != Blocks.AIR) - translate("tooltip.drill.drilling", Component.translatable(targetBlock.descriptionId)) - .style(ChatFormatting.GRAY) - .forGoggles(tooltip) + val heatGen = if (isMining) speed * getBlockHardness(targetState) else 0.0f - if (!itemHandler[0].isEmpty) - translate("tooltip.drill.contains", Component.translatable(itemHandler[0].descriptionId), itemHandler[0].count) - .style(ChatFormatting.GREEN) - .forGoggles(tooltip) + val dissipation = (baseCooling + getLubricantFactor() + getCoolingFactor()).coerceAtLeast(0.1f) + val equilibriumTemp = baseTemperature + (heatGen / dissipation) + val approachRate = (0.02f * dissipation).coerceIn(0.01f, 1.0f) - val fluidInLubricantTank: FluidStack = lubricantHandler.getFluidInTank(0) - if (!fluidInLubricantTank.isEmpty) - translate("tooltip.drill.contains.lube", Component.translatable(fluidInLubricantTank.descriptionId), fluidInLubricantTank.amount) - .style(ChatFormatting.GOLD) - .forGoggles(tooltip) - - val fluidInCoolantTank: FluidStack = coolantHandler.getFluidInTank(0) - if (!fluidInCoolantTank.isEmpty) - translate("tooltip.drill.contains.coolant", Component.translatable(fluidInCoolantTank.descriptionId), fluidInCoolantTank.amount) - .style(ChatFormatting.BLUE) - .forGoggles(tooltip) - - //Temp Prob + temperature += (equilibriumTemp - temperature) * approachRate + } - if (!drillTipHandler[0].isEmpty) { - val tipStack = drillTipHandler[0] - translate("tooltip.drill.tip.contains", Component.translatable(tipStack.descriptionId)) - .style(ChatFormatting.GREEN) - .forGoggles(tooltip) + private fun damageTip() { + val itemStack = drillTipHandler.getStackInSlot(0) + if (itemStack.isEmpty || !itemStack.tags.anyMatch(CreateOreDepositsTags.DRILL_TIP::equals)) return - // Show durability if item is damageable - if (tipStack.isDamageableItem) { - val maxDurability = tipStack.maxDamage - val currentDamage = tipStack.damageValue - val remainingDurability = maxDurability - currentDamage - val durabilityPercent = (remainingDurability.toFloat() / maxDurability * 100).roundToInt() - - val durabilityColor = when { - durabilityPercent > 66 -> ChatFormatting.GREEN - durabilityPercent > 33 -> ChatFormatting.YELLOW - durabilityPercent > 10 -> ChatFormatting.GOLD - else -> ChatFormatting.RED - } + val excessTemp = (temperature - Config.SERVER.DEPOSIT_DRILL.baseTemperature).coerceAtLeast(0f) - translate("tooltip.drill.tip.durability", remainingDurability, maxDurability, durabilityPercent) - .style(durabilityColor) - .forGoggles(tooltip) - } + val damage = when { + excessTemp < 20f -> 0 + excessTemp < 50f -> 1 + excessTemp < 100f -> ((excessTemp - 50f) / 25f).toInt() + 1 + else -> ((excessTemp - 100f) / 20f + 3f).toInt().coerceAtMost(8) } + if (damage < 1) return - translate("tooltip.drill.heat", String.format("%.2f", temperature)) - .style(ChatFormatting.RED) - .forGoggles(tooltip) + val world = level as? ServerLevel ?: return + itemStack.hurtAndBreak(damage, world, null) { + drillTipHandler.setStackInSlot(0, ItemStack.EMPTY) + notifyUpdate() + } + } - return super.addToGoggleTooltip(tooltip, isPlayerSneaking) + override fun calculateStressApplied(): Float { + // SU = 128 * (4 - lubeFactor) * hardness + val lubricantFactor = getLubricantFactor() + val hardness = getBlockHardness(getTargetBlockState()) + return 128 * (4 - lubricantFactor) * hardness } fun getBlockHardness(blockState: BlockState?): Float { @@ -259,158 +246,161 @@ class DepositDrillBlockEntity( } fun getLubricantFactor(): Float { - val lubricantFactorData: CreateOreDepositsDataMaps.LubricantFactorData = - lubricantHandler.getFluidInTank(1).fluidHolder.getData(LUBRICANT_FACTOR_DATA) ?: return 0.0f - return lubricantFactorData.lubeFactor + return lubricantHandler.getFluidInTank(1) + .fluidHolder + .getData(LUBRICANT_FACTOR_DATA) + ?.lubeFactor ?: 0.0f } fun getCoolingFactor(): Float { - val coolingFactorData: CreateOreDepositsDataMaps.CoolingFactorData = - coolantHandler.getFluidInTank(1).fluidHolder.getData(COOLING_FACTOR_DATA) ?: return 0.0f - return coolingFactorData.coolingFactor + return coolantHandler.getFluidInTank(0) + .fluidHolder + .getData(LUBRICANT_FACTOR_DATA) + ?.lubeFactor ?: 0.0f } - fun updateTemperature() { - val blockHardness = getBlockHardness(getTargetBlockState()) - val coolingFactor = getCoolingFactor() - - val baseCooling = Config.SERVER.DEPOSIT_DRILL.baseCooling - val baseTemperature: Float = Config.SERVER.DEPOSIT_DRILL.baseTemperature - val dampening = Config.SERVER.DEPOSIT_DRILL.dampening - val scale = Config.SERVER.DEPOSIT_DRILL.scale - val rpm: Float = if (this.speed < 0f) 0f else this.speed - - val heating = blockHardness * rpm.pow(scale) * dampening - val effectiveCooling = baseCooling + (coolingFactor * 50f) - val cooling = effectiveCooling * (temperature - baseTemperature) * dampening + private fun isDeposit(state: BlockState?): Boolean = state?.`is`(CreateOreDepositsTags.DEPOSIT) ?: false - temperature = (temperature + heating - cooling).coerceAtLeast(baseTemperature) - } + fun getTargetBlock(): Block? = getTargetBlockState()?.block - fun setLerpedOffset(value: Number) { - lerpedOffset.setValue(value.toDouble().coerceAtLeast(min)) - } + fun getTargetBlockState(): BlockState? = level?.getBlockState(getTargetPos()) - fun getTargetBlock(): Block = getTargetBlockState()?.block!! + fun getTargetPos(): BlockPos { + val tip = getDrillTipPos() + val stateAtTip = level?.getBlockState(tip) - fun getTargetBlockState(): BlockState? { - // This now simply returns the block state at the resolved target position. - return level?.getBlockState(getTargetPos()) + return if (stateAtTip?.let(::isDeposit) == true) { + getFurthestConnectedDeposit(level!!, tip) + } else { + tip + } } - fun getTargetPos(): BlockPos { - val tip: BlockPos = getDrillTipPos() - val stateAtTip: BlockState? = level?.getBlockState(tip) - - val blockAtDrillTipIsADepositBlock: Boolean = stateAtTip?.let(::isBlockStateADeposit) == true + fun getDrillTipPos(): BlockPos = blockPos.offset(0, -lerpedOffset.value.toInt() - 1, 0) - return if (blockAtDrillTipIsADepositBlock) getFurthestDepositConnectedToDeposit(level!!, tip) else tip + private fun getFurthestConnectedDeposit(level: Level, start: BlockPos): BlockPos { + val startState = level.getBlockState(start) + return getFurthestConnectedBlock(level, start) { it == startState } } - private fun damageTip() { - val itemStack = drillTipHandler[0] - if (itemStack.isEmpty || !itemStack.tags.anyMatch(CreateOreDepositsTags.DRILL_TIP::equals)) return + private fun getFurthestConnectedBlock( + level: Level, + start: BlockPos, + filter: (BlockState) -> Boolean + ): BlockPos { + val visited = mutableSetOf(start) + val queue = ArrayDeque() + queue += start + var last = start - val baseTemp = Config.SERVER.DEPOSIT_DRILL.baseTemperature // ~293K - val excessTemp = (temperature - baseTemp).coerceAtLeast(0f) + while (queue.isNotEmpty()) { + val current = queue.removeFirst() + last = current - val damage = when { - excessTemp < 20f -> 0 // Safe: < 313K - excessTemp < 50f -> 1 // Caution: 313-343K - excessTemp < 100f -> ((excessTemp - 50f) / 25f).toInt() + 1 // Danger: 343-393K - else -> ((excessTemp - 100f) / 20f + 3f).toInt().coerceAtMost(8) // Critical: > 393K + for (dir in Direction.entries) { + val neighbor = current.relative(dir) + if (neighbor !in visited && filter(level.getBlockState(neighbor))) { + visited += neighbor + queue += neighbor + } + } } - if (damage < 1) return - - val world = level - if (world !is ServerLevel) return - itemStack.hurtAndBreak(damage, world, null) { - drillTipHandler.setStackInSlot(0, ItemStack.EMPTY) - notifyUpdate() - } + return last } + fun getInterpolatedOffset(partialTicks: Float): Float = + lerpedOffset.getValue(partialTicks).coerceAtLeast(3 / 16f) - private fun isBlockStateADeposit(state: BlockState): Boolean = state.`is`(CreateOreDepositsTags.DEPOSIT) - - private fun getFurthestDepositConnectedToDeposit( - level: Level, - startingDepositPos: BlockPos - ): BlockPos { - val startingDepositState = level.getBlockState(startingDepositPos) - val connectedBlocks = getConnectedBlocksWithFilter(level, startingDepositPos, startingDepositState::equals) + fun getMovementSpeed(): Float { + var movementSpeed = convertToLinear(getSpeed()) + if (level!!.isClientSide) movementSpeed *= ServerSpeedProvider.get() + return movementSpeed + } - return connectedBlocks.maxByOrNull { blockPos -> - startingDepositPos.distSqr(blockPos) - } ?: startingDepositPos // fallback if list is empty + private fun setLerpedOffset(value: Number) { + lerpedOffset.setValue(value.toDouble().coerceAtLeast(MIN_LERP)) } - private fun getConnectedBlocksWithFilter( - level: Level, - startingDepositPos: BlockPos, - filter: Predicate - ): List { - val connected = mutableListOf() - val visited = mutableListOf() - val queue = ArrayDeque() + override fun read(compound: CompoundTag, registries: HolderLookup.Provider, clientPacket: Boolean) { + val nbt = compound.getCompound("DepositDrill") - queue += startingDepositPos - visited += startingDepositPos + maxAttempts = nbt.getInt("MaxAttempts") + remainingAttempts = nbt.getInt("RemainingAttempts") + drillTickCounter = nbt.getInt("DrillTickCount") + drillOffset = nbt.getFloat("DrillOffset") + temperature = nbt.getFloat("Temperature") - while (queue.isNotEmpty()) { - val current = queue.removeFirst() + if (nbt.contains("LastBlock")) + lastBlock = BuiltInRegistries.BLOCK.get(NBTHelper.readResourceLocation(nbt, "LastBlock")) + if (nbt.contains("CurrentDepositPos")) + currentDepositPos = NBTHelper.readBlockPos(nbt, "CurrentDepositPos") + + itemHandler.deserializeNBT(registries, nbt.getCompound("ItemHandler")) + drillTipHandler.deserializeNBT(registries, nbt.getCompound("DrillTipHandler")) + lubricantHandler.deserializeNBT(registries, nbt.getCompound("Lubricant")) + coolantHandler.deserializeNBT(registries, nbt.getCompound("Coolant")) - if (!filter.test(level.getBlockState(current))) continue + super.read(compound, registries, clientPacket) + } - connected.add(current) + override fun write(compound: CompoundTag, registries: HolderLookup.Provider, clientPacket: Boolean) { + val nbt = CompoundTag() - val offsets = setOf( - BlockPos(1, 0, 0), - BlockPos(-1, 0, 0), - BlockPos(0, 1, 0), - BlockPos(0, -1, 0), - BlockPos(0, 0, 1), - BlockPos(0, 0, -1) - ) - val neighbors = offsets.map { pos -> current.offset(pos) } + nbt.putInt("MaxAttempts", maxAttempts) + nbt.putInt("RemainingAttempts", remainingAttempts) + nbt.putInt("DrillTickCount", drillTickCounter) + nbt.putFloat("DrillOffset", drillOffset) + nbt.putFloat("Temperature", temperature) - for (neighbor in neighbors) { - if (neighbor !in visited && filter.test(level.getBlockState(neighbor))) { - visited.add(neighbor) - queue.add(neighbor) - } - } + lastBlock?.let { + NBTHelper.writeResourceLocation(nbt, "LastBlock", BuiltInRegistries.BLOCK.getKey(it)) + } + currentDepositPos?.let { + nbt.put("CurrentDepositPos", NbtUtils.writeBlockPos(it)) } - return connected + nbt.put("ItemHandler", itemHandler.serializeNBT(registries)) + nbt.put("DrillTipHandler", drillTipHandler.serializeNBT(registries)) + nbt.put("Lubricant", lubricantHandler.serializeNBT(registries)) + nbt.put("Coolant", coolantHandler.serializeNBT(registries)) + + compound.put("DepositDrill", nbt) + super.write(compound, registries, clientPacket) } - fun getDrillTipPos(): BlockPos = blockPos.offset(0, (-lerpedOffset.value.toInt() - 1), 0) + override fun addToGoggleTooltip(tooltip: MutableList, isPlayerSneaking: Boolean): Boolean { + translate("tooltip.drill.header").forGoggles(tooltip) - fun getInterpolatedOffset(partialTicks: Float): Float = lerpedOffset.getValue(partialTicks).coerceAtLeast(3 / 16f) + val targetBlock = getTargetBlockState()?.block + if (targetBlock != null && targetBlock != Blocks.AIR) { + translate("tooltip.drill.drilling", Component.translatable(targetBlock.descriptionId)) + .style(ChatFormatting.GRAY) + .forGoggles(tooltip) + } - fun getMovementSpeed(): Float { - var movementSpeed = convertToLinear(getSpeed()) - if (level!!.isClientSide) movementSpeed *= ServerSpeedProvider.get() - return movementSpeed - } + if (isDeposit(getTargetBlockState())) { + val attemptsUsed = maxAttempts - remainingAttempts + val stage = ((attemptsUsed.toFloat() / maxAttempts) * 10f).toInt().coerceIn(0, 10) + val bar = TooltipHelper.makeProgressBar(10, stage) + translate("tooltip.drill.progress") + .add(Component.literal(bar)) + .forGoggles(tooltip) + } - // FIXME: Why is this unused? What was it meant for? - Mavity - private val facingAxis: Direction.Axis = blockState.getValue(BlockStateProperties.HORIZONTAL_FACING).axis + return super.addToGoggleTooltip(tooltip, isPlayerSneaking) + } fun getItemHandler(direction: Direction): IItemHandler? { - //Grabs block state and checks if the right side of the block. If it is we can slap a funnel on it. - return if (direction == blockState.getValue(BlockStateProperties.HORIZONTAL_FACING).counterClockWise) itemHandler else null + val outputSide = blockState.getValue(BlockStateProperties.HORIZONTAL_FACING).counterClockWise + return if (direction == outputSide) itemHandler else null } - fun getDrillTipItemHandler(): IItemHandler { - return drillTipHandler - } + fun getDrillTipItemHandler(): IItemHandler = drillTipHandler - // Checks if the axis is the Y axis (up and down) and if its positive (just up) thus from the top fun getFluidHandler(direction: Direction): FluidHandler? = when { - direction.axis == Direction.Axis.Y && direction.axisDirection == Direction.AxisDirection.POSITIVE -> lubricantHandler + direction.axis == Direction.Axis.Y + && direction.axisDirection == Direction.AxisDirection.POSITIVE -> lubricantHandler direction == blockState.getValue(BlockStateProperties.HORIZONTAL_FACING).opposite -> coolantHandler else -> null } diff --git a/src/main/java/com/createcivilization/create_ore_deposits/registry/datagen/DataMapProvider.kt b/src/main/java/com/createcivilization/create_ore_deposits/registry/datagen/DataMapProvider.kt index 76c8116..72a8d52 100644 --- a/src/main/java/com/createcivilization/create_ore_deposits/registry/datagen/DataMapProvider.kt +++ b/src/main/java/com/createcivilization/create_ore_deposits/registry/datagen/DataMapProvider.kt @@ -1,10 +1,10 @@ package com.createcivilization.create_ore_deposits.registry.datagen +import com.createcivilization.create_ore_deposits.registry.block.CreateOreDepositsBlocks import com.createcivilization.create_ore_deposits.registry.datamap.CreateOreDepositsDataMaps import com.createcivilization.create_ore_deposits.registry.fluid.CreateOreDepositsFluids import net.minecraft.core.HolderLookup import net.minecraft.data.PackOutput -import net.minecraft.tags.BlockTags import net.minecraft.tags.FluidTags import net.neoforged.neoforge.common.data.DataMapProvider import java.util.concurrent.CompletableFuture @@ -15,14 +15,15 @@ class DataMapProvider( ) : DataMapProvider(packOutput, lookupProvider) { override fun gather(provider: HolderLookup.Provider) { - builder(CreateOreDepositsDataMaps.HARDNESS_DATA) - .add(BlockTags.DIRT, CreateOreDepositsDataMaps.HardnessData(0.1f), false) + builder(CreateOreDepositsDataMaps.DEPOSIT_DATA) + .add(CreateOreDepositsBlocks.EXAMPLE_DEPOSIT, CreateOreDepositsDataMaps.DepositData(10, 1f), false) + .add(CreateOreDepositsBlocks.DIAMOND_ORE_DEPOSIT, CreateOreDepositsDataMaps.DepositData(50, 5f), false) builder(CreateOreDepositsDataMaps.COOLING_FACTOR_DATA) - .add(FluidTags.WATER, CreateOreDepositsDataMaps.CoolingFactorData(5f), false) + .add(FluidTags.WATER, CreateOreDepositsDataMaps.CoolingFactorData(1f), false) builder(CreateOreDepositsDataMaps.LUBRICANT_FACTOR_DATA) - .add(CreateOreDepositsFluids.LUBRICANT.key, CreateOreDepositsDataMaps.LubricantFactorData(1f), false) + .add(CreateOreDepositsFluids.LUBRICANT.get().source.builtInRegistryHolder(), CreateOreDepositsDataMaps.LubricantFactorData(1f), false) } } \ No newline at end of file diff --git a/src/main/java/com/createcivilization/create_ore_deposits/registry/datamap/CreateOreDepositsDataMaps.kt b/src/main/java/com/createcivilization/create_ore_deposits/registry/datamap/CreateOreDepositsDataMaps.kt index 94258ef..d287563 100644 --- a/src/main/java/com/createcivilization/create_ore_deposits/registry/datamap/CreateOreDepositsDataMaps.kt +++ b/src/main/java/com/createcivilization/create_ore_deposits/registry/datamap/CreateOreDepositsDataMaps.kt @@ -1,7 +1,6 @@ package com.createcivilization.create_ore_deposits.registry.datamap import com.createcivilization.create_ore_deposits.CreateOreDeposits -import com.createcivilization.create_ore_deposits.registry.datamap.CreateOreDepositsDataMaps.LubricantFactorData import com.mojang.serialization.Codec import com.mojang.serialization.codecs.RecordCodecBuilder import net.minecraft.core.registries.Registries @@ -12,12 +11,13 @@ import net.neoforged.neoforge.registries.datamaps.DataMapType object CreateOreDepositsDataMaps { - data class HardnessData(val hardness: Float) { + data class DepositData(val maxAttempts: Int, val hardness: Float) { companion object { - val CODEC: Codec = RecordCodecBuilder.create { instance -> + val CODEC: Codec = RecordCodecBuilder.create { instance -> instance.group( - Codec.FLOAT.fieldOf("hardness").forGetter(HardnessData::hardness) - ).apply(instance, ::HardnessData) + Codec.INT.fieldOf("maxAttempts").forGetter(DepositData::maxAttempts), + Codec.FLOAT.fieldOf("hardness").forGetter(DepositData::hardness) + ).apply(instance, ::DepositData) } } } @@ -42,11 +42,11 @@ object CreateOreDepositsDataMaps { } } - val HARDNESS_DATA: DataMapType = + val DEPOSIT_DATA: DataMapType = DataMapType.builder( - ResourceLocation.fromNamespaceAndPath(CreateOreDeposits.MOD_ID, "hardness_data"), + ResourceLocation.fromNamespaceAndPath(CreateOreDeposits.MOD_ID, "deposit_data"), Registries.BLOCK, - HardnessData.CODEC + DepositData.CODEC ).build() val COOLING_FACTOR_DATA: DataMapType = diff --git a/src/main/java/com/createcivilization/create_ore_deposits/registry/fluid/CreateOreDepositsFluids.kt b/src/main/java/com/createcivilization/create_ore_deposits/registry/fluid/CreateOreDepositsFluids.kt index 0b5c313..ea39466 100644 --- a/src/main/java/com/createcivilization/create_ore_deposits/registry/fluid/CreateOreDepositsFluids.kt +++ b/src/main/java/com/createcivilization/create_ore_deposits/registry/fluid/CreateOreDepositsFluids.kt @@ -13,8 +13,10 @@ object CreateOreDepositsFluids { .fluid("lubricant", "block/fluid/lubricant_still".asResource(), "block/fluid/lubricant_flow".asResource()) .properties { it.viscosity(1500).density(500) } .fluidProperties { it.levelDecreasePerBlock(2).tickRate(25).slopeFindDistance(3).explosionResistance(100f) } + .source { BaseFlowingFluid.Source(it) } .register() + val SLAG: FluidEntry = CreateOreDeposits.REGISTRATE .fluid("slag", "block/fluid/slag_still".asResource(), "block/fluid/slag_flow".asResource()) .properties { it.viscosity(1500).density(500) } diff --git a/src/main/java/com/createcivilization/create_ore_deposits/registry/item/CreateOreDepositsItems.kt b/src/main/java/com/createcivilization/create_ore_deposits/registry/item/CreateOreDepositsItems.kt index 14ed5fb..b7e7afa 100644 --- a/src/main/java/com/createcivilization/create_ore_deposits/registry/item/CreateOreDepositsItems.kt +++ b/src/main/java/com/createcivilization/create_ore_deposits/registry/item/CreateOreDepositsItems.kt @@ -8,7 +8,7 @@ import net.minecraft.world.item.Item object CreateOreDepositsItems { val DIAMOND_DRILL_TIP: ItemEntry = REGISTRATE.item("diamond_drill_tip", ::Item) - .properties {it.durability(2000).stacksTo(1).setNoRepair()} + .properties {it.durability(20000).stacksTo(1).setNoRepair()} .tag(CreateOreDepositsTags.DRILL_TIP) .defaultModel() .register() diff --git a/src/main/resources/assets/create_ore_deposits/lang/en_us.json b/src/main/resources/assets/create_ore_deposits/lang/en_us.json index 001b5a5..3d4c14e 100644 --- a/src/main/resources/assets/create_ore_deposits/lang/en_us.json +++ b/src/main/resources/assets/create_ore_deposits/lang/en_us.json @@ -14,12 +14,13 @@ "fluid.create_ore_deposits.lubricant": "Lubricant", "create_ore_deposits.tooltip.drill.header": "Drill Information", - "create_ore_deposits.tooltip.drill.drilling": "Block: %1$s", + "create_ore_deposits.tooltip.drill.drilling": "Mining: %1$s", "create_ore_deposits.tooltip.drill.contains": "Item: %1$s x%2$s", - "create_ore_deposits.tooltip.drill.tip.contains": "Drill Tip: %1$s", + "create_ore_deposits.tooltip.drill.tip.contains": "Current Tip: %1$s", "create_ore_deposits.tooltip.drill.tip.durability": "Durability: %s / %s (%s%%)", "create_ore_deposits.tooltip.drill.contains.lube": "Lube: %1$s x%2$s", "create_ore_deposits.tooltip.drill.contains.coolant": "Coolant: %1$s x%2$s", + "create_ore_deposits.tooltip.drill.progress": "Current Progress: ", "create_ore_deposits.tooltip.drill.heat": "Heat: %s K", "create_ore_deposits.ponder.drill.intro.text_1": "This is a Drill. It Drills.",