Captain Redstone Keyes
This commit is contained in:
parent
6f67912041
commit
9621356682
@ -633,6 +633,11 @@ private fun blocks(provider: MatteryLanguageProvider) {
|
||||
|
||||
private fun items(provider: MatteryLanguageProvider) {
|
||||
with(provider.english) {
|
||||
add(MItems.REDSTONE_INTERACTOR, "Redstone Key")
|
||||
add(MItems.REDSTONE_INTERACTOR, "desc", "Allows to 'send' redstone signal out of any block")
|
||||
add(MItems.REDSTONE_INTERACTOR, "desc1", "In other words, allows to temporarily activate redstone mechanisms, such as opening Iron Doors")
|
||||
add(MItems.REDSTONE_INTERACTOR, "desc2", "Use while sneaking to set redstone timer")
|
||||
|
||||
add(MItems.PROCEDURAL_BATTERY, "Mythical Battery")
|
||||
add(MItems.PROCEDURAL_BATTERY, "desc", "These batteries are found in dungeons with randomized stats")
|
||||
|
||||
|
@ -634,6 +634,11 @@ private fun blocks(provider: MatteryLanguageProvider) {
|
||||
|
||||
private fun items(provider: MatteryLanguageProvider) {
|
||||
with(provider.russian) {
|
||||
add(MItems.REDSTONE_INTERACTOR, "Ключ красного камня")
|
||||
add(MItems.REDSTONE_INTERACTOR, "desc", "'Посылает' сигнал красного камня из блока, с которым взаимодействует")
|
||||
add(MItems.REDSTONE_INTERACTOR, "desc1", "Другими словами, позволяет активировать механизмы красного камня, такие как железные двери")
|
||||
add(MItems.REDSTONE_INTERACTOR, "desc2", "Для настройки таймера используйте будучи крадясь")
|
||||
|
||||
add(MItems.PROCEDURAL_BATTERY, "Загадочный аккумулятор")
|
||||
add(MItems.PROCEDURAL_BATTERY, "desc", "Данные аккумуляторы можно найти в подземельях, со случайными характеристиками")
|
||||
|
||||
|
@ -522,4 +522,9 @@ fun addCraftingTableRecipes(consumer: RecipeOutput) {
|
||||
.rowB(MItemTags.TRITANIUM_PLATES)
|
||||
.build(consumer, "grill_alt_c/${color?.name?.lowercase() ?: "default"}")
|
||||
}
|
||||
|
||||
MatteryRecipe(MItems.REDSTONE_INTERACTOR, category = RecipeCategory.TOOLS)
|
||||
.rowAB(Items.LEVER, Tags.Items.NUGGETS_IRON)
|
||||
.rowB(Tags.Items.DUSTS_REDSTONE)
|
||||
.build(consumer)
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
package ru.dbotthepony.mc.otm.mixin;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import ru.dbotthepony.mc.otm.item.tool.RedstoneInteractorItem;
|
||||
|
||||
@Mixin(BlockBehaviour.BlockStateBase.class)
|
||||
public abstract class BlockStateBaseMixin {
|
||||
@Shadow
|
||||
protected abstract BlockState asState();
|
||||
|
||||
@Inject(
|
||||
method = "getSignal(Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;Lnet/minecraft/core/Direction;)I",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true,
|
||||
remap = false
|
||||
)
|
||||
public void getSignal(BlockGetter p_60747_, BlockPos p_60748_, Direction p_60749_, CallbackInfoReturnable<Integer> info) {
|
||||
if (p_60747_ instanceof Level level) {
|
||||
int hookResult = RedstoneInteractorItem.Companion.getSignalHook(level, this.asState(), p_60748_, p_60749_);
|
||||
|
||||
if (hookResult != -1) {
|
||||
info.setReturnValue(hookResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -9,9 +9,6 @@ import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.world.level.Level
|
||||
import net.neoforged.api.distmarker.Dist
|
||||
import net.neoforged.bus.api.EventPriority
|
||||
import net.neoforged.bus.api.SubscribeEvent
|
||||
import net.neoforged.fml.common.EventBusSubscriber
|
||||
import net.neoforged.fml.loading.FMLLoader
|
||||
import net.neoforged.neoforge.event.server.ServerAboutToStartEvent
|
||||
import net.neoforged.neoforge.event.server.ServerStoppedEvent
|
||||
@ -19,7 +16,6 @@ import net.neoforged.neoforge.event.server.ServerStoppingEvent
|
||||
import net.neoforged.neoforge.event.tick.LevelTickEvent
|
||||
import net.neoforged.neoforge.event.tick.ServerTickEvent
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
|
||||
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
|
||||
import ru.dbotthepony.mc.otm.client.minecraft
|
||||
import ru.dbotthepony.mc.otm.core.collect.WeakHashSet
|
||||
@ -226,6 +222,17 @@ fun tickServer(ticker: IConditionalTickable) {
|
||||
postServerTick.add(ticker, SERVER_IS_LIVE, "Server is stopping")
|
||||
}
|
||||
|
||||
fun Level.timer(time: Int, ticker: Runnable): TickList.Timer? {
|
||||
if (this.isClientSide) return null
|
||||
|
||||
if (!SERVER_IS_LIVE) {
|
||||
LOGGER.error("Refusing to add ticker $ticker while server is dying", IllegalStateException("Server is stopping"))
|
||||
return null
|
||||
}
|
||||
|
||||
return postWorldTick.computeIfAbsent(this) { TickList() }.timer(time, ticker)
|
||||
}
|
||||
|
||||
fun Level.once(ticker: ITickable) {
|
||||
if (this.isClientSide) return
|
||||
|
||||
|
@ -56,6 +56,7 @@ import ru.dbotthepony.mc.otm.item.tool.ExplosiveHammerItem
|
||||
import ru.dbotthepony.mc.otm.item.armor.TritaniumArmorItem
|
||||
import ru.dbotthepony.mc.otm.item.QuantumBatteryItem
|
||||
import ru.dbotthepony.mc.otm.item.PortableCondensationDriveItem
|
||||
import ru.dbotthepony.mc.otm.item.tool.RedstoneInteractorItem
|
||||
import ru.dbotthepony.mc.otm.matter.AbstractRegistryAction
|
||||
import ru.dbotthepony.mc.otm.matter.IMatterFunction
|
||||
import ru.dbotthepony.mc.otm.matter.MatterManager
|
||||
@ -200,6 +201,8 @@ object OverdriveThatMatters {
|
||||
|
||||
FORGE_BUS.addListener(EventPriority.NORMAL, MCommands::register)
|
||||
|
||||
FORGE_BUS.addListener(EventPriority.NORMAL, RedstoneInteractorItem::onUse)
|
||||
|
||||
if (isCuriosLoaded) {
|
||||
FORGE_BUS.addListener(EventPriority.NORMAL, ::onCuriosSlotModifiersUpdated)
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||
import ru.dbotthepony.mc.otm.core.get
|
||||
import ru.dbotthepony.mc.otm.core.math.plus
|
||||
import ru.dbotthepony.mc.otm.core.set
|
||||
import ru.dbotthepony.mc.otm.item.tool.RedstoneInteractorItem
|
||||
import ru.dbotthepony.mc.otm.shapes.BlockShapes
|
||||
|
||||
class ComputerTerminalBlock(val color: DyeColor?) : RotatableMatteryBlock(
|
||||
@ -44,7 +45,7 @@ class ComputerTerminalBlock(val color: DyeColor?) : RotatableMatteryBlock(
|
||||
private val shapes = getShapeForEachState(rotationProperty) { BlockShapes.COMPUTER_TERMINAL.rotateFromNorth(it[rotationProperty]).computeShape() }
|
||||
|
||||
init {
|
||||
registerDefaultState(defaultBlockState().set(BlockStateProperties.POWERED, false).set(TICKS, TickTimer.TICK_20))
|
||||
registerDefaultState(defaultBlockState().set(BlockStateProperties.POWERED, false).set(TICKS, RedstoneInteractorItem.TickTimer.TICK_20))
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
@ -86,9 +87,9 @@ class ComputerTerminalBlock(val color: DyeColor?) : RotatableMatteryBlock(
|
||||
): InteractionResult {
|
||||
if (ply.isShiftKeyDown) {
|
||||
val current = blockState[TICKS]
|
||||
val next = TickTimer.entries.getOrElse(current.ordinal + 1) { TickTimer.TICK_2 }
|
||||
val next = RedstoneInteractorItem.TickTimer.entries.getOrElse(current.ordinal + 1) { RedstoneInteractorItem.TickTimer.TICK_2 }
|
||||
|
||||
level.playSound(ply, blockPos, SoundEvents.DISPENSER_FAIL, SoundSource.BLOCKS, 1f, 1f)
|
||||
level.playSound(ply, blockPos, SoundEvents.DISPENSER_FAIL, SoundSource.BLOCKS, 0.3f, 1.1f)
|
||||
level.gameEvent(ply, GameEvent.BLOCK_ACTIVATE, blockPos)
|
||||
|
||||
if (!level.isClientSide) {
|
||||
@ -141,21 +142,7 @@ class ComputerTerminalBlock(val color: DyeColor?) : RotatableMatteryBlock(
|
||||
return shapes[state]!!
|
||||
}
|
||||
|
||||
enum class TickTimer(val ticks: Int) : StringRepresentable {
|
||||
TICK_2(2),
|
||||
TICK_10(10),
|
||||
TICK_20(20),
|
||||
TICK_30(30),
|
||||
TICK_40(40);
|
||||
|
||||
private val str = ticks.toString()
|
||||
|
||||
override fun getSerializedName(): String {
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TICKS: EnumProperty<TickTimer> = EnumProperty.create("ticks", TickTimer::class.java)
|
||||
val TICKS: EnumProperty<RedstoneInteractorItem.TickTimer> = EnumProperty.create("ticks", RedstoneInteractorItem.TickTimer::class.java)
|
||||
}
|
||||
}
|
||||
|
@ -19,12 +19,9 @@ class TickList : ITickable {
|
||||
|
||||
private var nextSometime = 0
|
||||
|
||||
inner class Timer(val timerTicks: Int, val runnable: Runnable) : Comparable<Timer> {
|
||||
inner class Timer(timerTicks: Int, private var runnable: Runnable?) : Comparable<Timer> {
|
||||
val ringAt = ticks + timerTicks
|
||||
|
||||
var finished = false
|
||||
private set
|
||||
|
||||
init {
|
||||
timers.add(this)
|
||||
}
|
||||
@ -33,10 +30,14 @@ class TickList : ITickable {
|
||||
return ringAt.compareTo(other.ringAt)
|
||||
}
|
||||
|
||||
fun cancel() {
|
||||
runnable = null
|
||||
timers.remove(this)
|
||||
}
|
||||
|
||||
fun execute() {
|
||||
if (finished) return
|
||||
runnable.run()
|
||||
finished = true
|
||||
runnable?.run()
|
||||
runnable = null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,132 @@
|
||||
package ru.dbotthepony.mc.otm.item.tool
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import net.minecraft.ChatFormatting
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.util.StringRepresentable
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.InteractionResultHolder
|
||||
import net.minecraft.world.ItemInteractionResult
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.context.UseOnContext
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
import net.neoforged.neoforge.event.entity.player.UseItemOnBlockEvent
|
||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||
import ru.dbotthepony.mc.otm.core.math.component1
|
||||
import ru.dbotthepony.mc.otm.core.math.component2
|
||||
import ru.dbotthepony.mc.otm.core.math.component3
|
||||
import ru.dbotthepony.mc.otm.core.math.plus
|
||||
import ru.dbotthepony.mc.otm.core.util.TickList
|
||||
import ru.dbotthepony.mc.otm.item.MatteryItem
|
||||
import ru.dbotthepony.mc.otm.registry.MDataComponentTypes
|
||||
import ru.dbotthepony.mc.otm.timer
|
||||
import java.util.*
|
||||
|
||||
class RedstoneInteractorItem : MatteryItem(Properties().stacksTo(1)) {
|
||||
init {
|
||||
tooltips.add { TranslatableComponent("$descriptionId.desc").withStyle(ChatFormatting.GRAY) }
|
||||
tooltips.add { TranslatableComponent("$descriptionId.desc1").withStyle(ChatFormatting.GRAY) }
|
||||
tooltips.add { TranslatableComponent("$descriptionId.desc2").withStyle(ChatFormatting.GRAY) }
|
||||
}
|
||||
|
||||
private fun updateTickTimer(level: Level, player: Player, itemStack: ItemStack) {
|
||||
val current = itemStack[MDataComponentTypes.TICK_TIMER] ?: TickTimer.TICK_30
|
||||
val next = TickTimer.entries.getOrElse(current.ordinal + 1) { RedstoneInteractorItem.TickTimer.TICK_2 }
|
||||
|
||||
if (level.isClientSide) {
|
||||
player.sendSystemMessage(TranslatableComponent("otm.gui.tick_timer_set", next.ticks.toString()))
|
||||
// play sound in front of player so it sounds properly with HRTF enabled
|
||||
val (x, y, z) = player.getEyePosition(1f) + player.getViewVector(1f)
|
||||
level.playSound(player, x, y, z, SoundEvents.DISPENSER_FAIL, SoundSource.PLAYERS, 0.1f, 1.1f)
|
||||
}
|
||||
|
||||
itemStack[MDataComponentTypes.TICK_TIMER] = next
|
||||
}
|
||||
|
||||
override fun use(level: Level, player: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
|
||||
if (player.isShiftKeyDown) {
|
||||
val itemStack = player.getItemInHand(hand)
|
||||
updateTickTimer(level, player, itemStack)
|
||||
return InteractionResultHolder.success(itemStack)
|
||||
}
|
||||
|
||||
return super.use(level, player, hand)
|
||||
}
|
||||
|
||||
override fun useOn(context: UseOnContext): InteractionResult {
|
||||
if (context.player?.isShiftKeyDown == true) {
|
||||
updateTickTimer(context.level, context.player!!, context.itemInHand)
|
||||
return InteractionResult.sidedSuccess(context.level.isClientSide())
|
||||
}
|
||||
|
||||
context.level.playSound(context.player, context.clickedPos, SoundEvents.STONE_BUTTON_CLICK_ON, SoundSource.BLOCKS, 1f, 1f)
|
||||
context.level.gameEvent(context.player, GameEvent.BLOCK_ACTIVATE, context.clickedPos)
|
||||
|
||||
if (context.level.isClientSide)
|
||||
return InteractionResult.SUCCESS
|
||||
|
||||
val map = signals.computeIfAbsent(context.level) { HashMap() }
|
||||
val positions = Direction.entries.map { context.clickedPos + it.normal }.toMutableList()
|
||||
positions.add(context.clickedPos)
|
||||
|
||||
for (pos in positions) {
|
||||
val shouldSendUpdate = pos !in map
|
||||
|
||||
val timer = context.level.timer(context.itemInHand.getOrDefault(MDataComponentTypes.TICK_TIMER, TickTimer.TICK_30).ticks) {
|
||||
map.remove(pos)
|
||||
context.level.updateNeighborsAt(pos, context.level.getBlockState(pos).block)
|
||||
} ?: throw RuntimeException("Timer unexpectedly ended up being null")
|
||||
|
||||
map.put(pos, timer)?.cancel()
|
||||
|
||||
if (shouldSendUpdate) {
|
||||
context.level.updateNeighborsAt(pos, context.level.getBlockState(pos).block)
|
||||
}
|
||||
}
|
||||
|
||||
return InteractionResult.CONSUME
|
||||
}
|
||||
|
||||
enum class TickTimer(val ticks: Int) : StringRepresentable {
|
||||
TICK_2(2),
|
||||
TICK_10(10),
|
||||
TICK_20(20),
|
||||
TICK_30(30),
|
||||
TICK_40(40);
|
||||
|
||||
private val str = ticks.toString()
|
||||
|
||||
override fun getSerializedName(): String {
|
||||
return str
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CODEC: Codec<TickTimer> = StringRepresentable.fromEnum(TickTimer::values)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val signals = WeakHashMap<Level, HashMap<BlockPos, TickList.Timer>>()
|
||||
|
||||
fun getSignalHook(level: Level, blockState: BlockState, blockPos: BlockPos, direction: Direction): Int {
|
||||
if (signals[level]?.containsKey(blockPos) == true)
|
||||
return 15
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
fun onUse(event: UseItemOnBlockEvent) {
|
||||
if (event.itemStack.item is RedstoneInteractorItem) {
|
||||
if ((event.itemStack.item as RedstoneInteractorItem).useOn(event.useOnContext).consumesAction())
|
||||
event.cancelWithResult(ItemInteractionResult.sidedSuccess(event.level.isClientSide()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -167,6 +167,7 @@ private fun addMainCreativeTabItems(consumer: CreativeModeTab.Output) {
|
||||
|
||||
energized(MItems.ENERGY_SWORD)
|
||||
|
||||
accept(MItems.REDSTONE_INTERACTOR)
|
||||
accept(MItems.EXPLOSIVE_HAMMER)
|
||||
accept(ItemStack(MItems.EXPLOSIVE_HAMMER).also { MItems.EXPLOSIVE_HAMMER.prime(it) })
|
||||
|
||||
|
@ -14,6 +14,7 @@ import ru.dbotthepony.mc.otm.capability.matter.PatternState
|
||||
import ru.dbotthepony.mc.otm.container.ItemFilter
|
||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||
import ru.dbotthepony.mc.otm.data.DecimalCodec
|
||||
import ru.dbotthepony.mc.otm.item.tool.RedstoneInteractorItem
|
||||
import ru.dbotthepony.mc.otm.network.StreamCodecs
|
||||
import java.util.UUID
|
||||
|
||||
@ -60,6 +61,7 @@ object MDataComponentTypes {
|
||||
}
|
||||
|
||||
val ITEM_FILTER: DataComponentType<ItemFilter> by registry.register("item_filter") { DataComponentType.builder<ItemFilter>().persistent(ItemFilter.CODEC).build() }
|
||||
val TICK_TIMER: DataComponentType<RedstoneInteractorItem.TickTimer> by registry.register("tick_timer") { DataComponentType.builder<RedstoneInteractorItem.TickTimer>().persistent(RedstoneInteractorItem.TickTimer.CODEC).build() }
|
||||
|
||||
val EXPERIENCE: DataComponentType<Long> by registry.register("experience") { DataComponentType.builder<Long>().persistent(Codec.LONG).networkSynchronized(StreamCodecs.LONG).build() }
|
||||
|
||||
|
@ -68,6 +68,7 @@ import ru.dbotthepony.mc.otm.item.matter.MatterDustItem
|
||||
import ru.dbotthepony.mc.otm.item.matter.PatternStorageItem
|
||||
import ru.dbotthepony.mc.otm.item.tool.ExplosiveHammerItem
|
||||
import ru.dbotthepony.mc.otm.item.tool.MatteryAxeItem
|
||||
import ru.dbotthepony.mc.otm.item.tool.RedstoneInteractorItem
|
||||
import ru.dbotthepony.mc.otm.item.weapon.EnergySwordItem
|
||||
import java.util.function.Supplier
|
||||
|
||||
@ -320,6 +321,8 @@ object MItems {
|
||||
val TRITANIUM_INGOT_BLOCK: BlockItem by registry.register(MNames.TRITANIUM_INGOT_BLOCK) { BlockItem(MBlocks.TRITANIUM_INGOT_BLOCK, DEFAULT_PROPERTIES) }
|
||||
val TRITANIUM_BARS: BlockItem by registry.register(MNames.TRITANIUM_BARS) { BlockItem(MBlocks.TRITANIUM_BARS, DEFAULT_PROPERTIES) }
|
||||
|
||||
val REDSTONE_INTERACTOR: RedstoneInteractorItem by registry.register("redstone_interactor") { RedstoneInteractorItem() }
|
||||
|
||||
val ESSENCE_SERVO: EssenceServoItem by registry.register("essence_servo") { EssenceServoItem() }
|
||||
val ESSENCE_CAPSULE: EssenceCapsuleItem by registry.register("essence_capsule") { EssenceCapsuleItem(false) }
|
||||
val ESSENCE_DRIVE: EssenceCapsuleItem by registry.register("essence_drive") { EssenceCapsuleItem(true) }
|
||||
|
@ -17,7 +17,8 @@
|
||||
"MixinPlayer",
|
||||
"HopperBlockEntityMixin",
|
||||
"DispenserBlockEntityMixin",
|
||||
"GuiGraphicsMixin"
|
||||
"GuiGraphicsMixin",
|
||||
"BlockStateBaseMixin"
|
||||
],
|
||||
"client": [
|
||||
"MixinGameRenderer",
|
||||
|
Loading…
Reference in New Issue
Block a user