Exosuit probe

This commit is contained in:
DBotThePony 2022-08-31 00:48:12 +07:00
parent f0ba88776d
commit 52e6971c9d
Signed by: DBot
GPG Key ID: DCC23B5715498507
8 changed files with 281 additions and 6 deletions

View File

@ -45,7 +45,22 @@ private fun sounds(provider: MatteryLanguageProvider) {
private fun misc(provider: MatteryLanguageProvider) { private fun misc(provider: MatteryLanguageProvider) {
with(provider.english) { with(provider.english) {
misc("gui.exosuit", "Exosuit Inventory") gui("exosuit", "Exosuit Inventory")
gui("exosuit.probe1", "This little device feels unnatural to touch, it is almost certainly resilient to any possible attempt to break it open.")
gui("exosuit.probe2", "There is fingerprint reader built into one of sides which gently glow when touched.")
gui("exosuit.probe3", "It seems this box will unlock once you strongly press fingerprint reader, and you feel what's inside will affect you without any way back!")
gui("exosuit.already_activated", "You already have exosuit following you")
misc("exosuit.granted1", "As you keep pressing fingerprint reader, you are getting hurt in finger.")
misc("exosuit.granted2", "After you raise your finger, fingerprint reader glows very bright.")
misc("exosuit.granted3", "Then, fingerprint reader fades, leaving faint trace not of your finger, but of your very soul.")
misc("exosuit.granted4", "The device opens... And whatever was inside it shroud you, yet you feel nothing, as it wasn't even there.")
misc("exosuit.granted5", "As whatever shrouded you takes final form, you feel it binds to your soul.")
misc("exosuit.granted6", "INITIALIZATION SEQUENCE COMPLETE. WELCOME, USER %s")
misc("exosuit.granted7", "You are not permanently equipped with four dimensional omni-present Exosuit.")
misc("exosuit.granted8", "As of now, this exosuit is not much, but it's built-in AI hints there are upgrade modules out there somewhere...")
misc("iteration", "Iteration %s") misc("iteration", "Iteration %s")
misc("death_reason", "Decommissioned!") misc("death_reason", "Decommissioned!")
@ -213,6 +228,9 @@ private fun death(provider: MatteryLanguageProvider) {
death("otm_hawking_radiation.player", "%1\$s disintegrated whilst fighting %2\$s") death("otm_hawking_radiation.player", "%1\$s disintegrated whilst fighting %2\$s")
death("otm_emp.player", "%1\$s blew fuzes of %2\$s") death("otm_emp.player", "%1\$s blew fuzes of %2\$s")
death("otm_emp.player.item", "%1\$s blew fuzes of %2\$s using %3\$s") death("otm_emp.player.item", "%1\$s blew fuzes of %2\$s using %3\$s")
death(MRegistry.DAMAGE_EXOSUIT_PROBE_ID, "%1\$s hit little finger against cupboard")
death("${MRegistry.DAMAGE_EXOSUIT_PROBE_ID}.player", "%1\$s hit little finger against cupboard whilst %2\$s tried to finish them off")
} }
} }
@ -275,6 +293,7 @@ private fun blocks(provider: MatteryLanguageProvider) {
private fun items(provider: MatteryLanguageProvider) { private fun items(provider: MatteryLanguageProvider) {
with(provider.english) { with(provider.english) {
add(MItems.EXOSUIT_PROBE, "Exosuit Probe")
add(MItems.NUTRIENT_PASTE, "Nutrient paste") add(MItems.NUTRIENT_PASTE, "Nutrient paste")
add(MItems.BLACK_HOLE_SCANNER, "Singularity Scanner") add(MItems.BLACK_HOLE_SCANNER, "Singularity Scanner")

View File

@ -82,7 +82,7 @@ public final class OverdriveThatMatters {
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setupClient); FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setupClient);
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
MinecraftForge.EVENT_BUS.register(TickerKt.class); MinecraftForge.EVENT_BUS.register(GlobalEventHandlerKt.class);
MinecraftForge.EVENT_BUS.register(MatteryPlayerCapability.Companion); MinecraftForge.EVENT_BUS.register(MatteryPlayerCapability.Companion);
MinecraftForge.EVENT_BUS.register(MatterRegistryKt.class); MinecraftForge.EVENT_BUS.register(MatterRegistryKt.class);
MinecraftForge.EVENT_BUS.register(MatterDataKt.class); MinecraftForge.EVENT_BUS.register(MatterDataKt.class);
@ -120,6 +120,8 @@ public final class OverdriveThatMatters {
MinecraftForge.EVENT_BUS.register(MatteryGUI.INSTANCE); MinecraftForge.EVENT_BUS.register(MatteryGUI.INSTANCE);
MinecraftForge.EVENT_BUS.register(EventHandlerKt.class); MinecraftForge.EVENT_BUS.register(EventHandlerKt.class);
event.enqueueWork(GlobalEventHandlerKt::recordClientThread);
TritaniumArmorModel.register(); TritaniumArmorModel.register();
GravitationStabilizerModel.register(); GravitationStabilizerModel.register();
} }

View File

@ -27,13 +27,127 @@ private val postWorldTick = WeakHashMap<Level, ArrayList<IConditionalTickable>>(
private val preWorldTickOnce = WeakHashMap<Level, ArrayList<ITickable>>() private val preWorldTickOnce = WeakHashMap<Level, ArrayList<ITickable>>()
private val postWorldTickOnce = WeakHashMap<Level, ArrayList<ITickable>>() private val postWorldTickOnce = WeakHashMap<Level, ArrayList<ITickable>>()
private val preServerTickTimers = TimerQueue()
private val postServerTickTimers = TimerQueue()
class TimerQueue {
private var ticks = 0
private var head: Timer? = null
inner class Timer(val timerTicks: Int, val runnable: Runnable) {
val ringAt = ticks + timerTicks
var finished = false
private set
var next: Timer? = null
var prev: Timer? = null
init {
if (head == null) {
head = this
} else {
var next = head
while (next != null) {
if (next.ringAt >= this.ringAt) {
next.prev?.next = this
this.prev = next.prev
next.prev = this
this.next = next
break
} else if (next.next == null) {
next.next = this
this.prev = next
break
} else {
next = next.next
}
}
}
}
fun execute() {
check(!finished) { "Already finished" }
runnable.run()
finished = true
}
}
fun tick() {
ticks++
var head = head
while (head != null) {
if (head.ringAt <= ticks) {
head.execute()
head = head.next
head?.prev = null
this.head = head
} else {
break
}
}
}
fun clear() {
ticks = 0
head = null
}
}
fun addPreTickTimer(inTicks: Int, callback: Runnable): TimerQueue.Timer? {
if (SERVER_IS_DYING) {
LOGGER.error("Refusing to add timer $callback in ticks $inTicks while server is dying", IllegalStateException("Server is stopping"))
return null
}
return preServerTickTimers.Timer(inTicks, callback)
}
fun addPostTickTimer(inTicks: Int, callback: Runnable): TimerQueue.Timer? {
if (SERVER_IS_DYING) {
LOGGER.error("Refusing to add ticker $callback in ticks $inTicks while server is dying", IllegalStateException("Server is stopping"))
return null
}
return postServerTickTimers.Timer(inTicks, callback)
}
private var _server: MinecraftServer? = null private var _server: MinecraftServer? = null
private var _serverThread: Thread? = null private var _serverThread: Thread? = null
private var _clientThread: Thread? = null
fun recordClientThread() {
if (_clientThread != null) {
throw IllegalStateException("Already have client channel")
}
_clientThread = Thread.currentThread()
}
fun runIfClient(lambda: () -> Unit) {
if (_clientThread !== null) {
lambda.invoke()
}
}
fun <V> runIfClient(value: V, lambda: () -> V): V {
if (_clientThread !== null) {
return lambda.invoke()
}
return value
}
fun isServerThread(): Boolean { fun isServerThread(): Boolean {
return Thread.currentThread() === _serverThread return Thread.currentThread() === _serverThread
} }
fun isClientThread(): Boolean {
return Thread.currentThread() === _clientThread
}
val MINECRAFT_SERVER: MinecraftServer val MINECRAFT_SERVER: MinecraftServer
get() { get() {
return _server ?: throw NullPointerException("Not running a server!") return _server ?: throw NullPointerException("Not running a server!")
@ -67,6 +181,8 @@ interface IConditionalTickable : ITickable {
@SubscribeEvent(priority = EventPriority.LOWEST) @SubscribeEvent(priority = EventPriority.LOWEST)
fun onServerTick(event: ServerTickEvent) { fun onServerTick(event: ServerTickEvent) {
if (event.phase === TickEvent.Phase.START) { if (event.phase === TickEvent.Phase.START) {
preServerTickTimers.tick()
for (i in preServerTick.size - 1 downTo 0) { for (i in preServerTick.size - 1 downTo 0) {
val ticker = preServerTick[i] val ticker = preServerTick[i]
@ -82,6 +198,8 @@ fun onServerTick(event: ServerTickEvent) {
preServerTickOnce.removeAt(i) preServerTickOnce.removeAt(i)
} }
} else { } else {
postServerTickTimers.tick()
for (i in postServerTick.size - 1 downTo 0) { for (i in postServerTick.size - 1 downTo 0) {
val ticker = postServerTick[i] val ticker = postServerTick[i]
@ -223,6 +341,8 @@ fun addPostWorldTickerOnce(level: Level, ticker: ITickable) {
} }
private fun clear() { private fun clear() {
preServerTickTimers.clear()
postServerTickTimers.clear()
preServerTick.clear() preServerTick.clear()
postServerTick.clear() postServerTick.clear()
preWorldTick.clear() preWorldTick.clear()

View File

@ -0,0 +1,82 @@
package ru.dbotthepony.mc.otm.item
import net.minecraft.ChatFormatting
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResultHolder
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.*
import net.minecraft.world.level.Level
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.TranslatableComponent
import ru.dbotthepony.mc.otm.addPostTickTimer
import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.registry.MRegistry
import ru.dbotthepony.mc.otm.runIfClient
class ExoSuitProbeItem : Item(Properties().tab(OverdriveThatMatters.INSTANCE.CREATIVE_TAB).stacksTo(1).rarity(Rarity.EPIC)) {
override fun getUseDuration(p_41454_: ItemStack): Int {
return 24
}
override fun appendHoverText(p_41421_: ItemStack, p_41422_: Level?, tooltip: MutableList<Component>, p_41424_: TooltipFlag) {
super.appendHoverText(p_41421_, p_41422_, tooltip, p_41424_)
val alreadyHas = runIfClient(false) {
return@runIfClient minecraft.player?.matteryPlayer?.hasExoSuit ?: false
}
if (alreadyHas) {
tooltip.add(TranslatableComponent("otm.gui.exosuit.already_activated").withStyle(ChatFormatting.DARK_RED))
} else {
tooltip.add(TranslatableComponent("otm.gui.exosuit.probe1").withStyle(ChatFormatting.GRAY))
tooltip.add(TranslatableComponent("otm.gui.exosuit.probe2").withStyle(ChatFormatting.GRAY))
tooltip.add(TranslatableComponent("otm.gui.exosuit.probe3").withStyle(ChatFormatting.GRAY))
}
}
override fun use(p_41432_: Level, player: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
if (player.matteryPlayer?.hasExoSuit == false) {
player.startUsingItem(hand)
return InteractionResultHolder.consume(player.getItemInHand(hand))
}
return super.use(p_41432_, player, hand)
}
override fun finishUsingItem(itemStack: ItemStack, level: Level, player: LivingEntity): ItemStack {
val mattery = player.matteryPlayer ?: return super.finishUsingItem(itemStack, level, player)
if (mattery.hasExoSuit) {
return super.finishUsingItem(itemStack, level, player)
}
itemStack.shrink(1)
if (player is ServerPlayer) {
mattery.hasExoSuit = true
player.displayClientMessage(TranslatableComponent("otm.exosuit.granted1").withStyle(ChatFormatting.GRAY), false)
player.hurt(MRegistry.DAMAGE_EXOSUIT_PROBE, 1f)
for (i in 2 .. 8) {
addPostTickTimer((i - 1) * 100) {
if (!player.hasDisconnected()) {
if (i == 6) {
player.displayClientMessage(TranslatableComponent("otm.exosuit.granted$i", player.displayName.copy().withStyle(ChatFormatting.DARK_PURPLE)).withStyle(ChatFormatting.DARK_AQUA), false)
} else {
player.displayClientMessage(TranslatableComponent("otm.exosuit.granted$i").withStyle(ChatFormatting.GRAY), false)
}
}
}
}
}
return itemStack
}
override fun getUseAnimation(p_41452_: ItemStack): UseAnim = UseAnim.BOW
}

View File

@ -74,6 +74,8 @@ object MItems {
} }
} }
val EXOSUIT_PROBE: Item by registry.register(MNames.EXOSUIT_PROBE, ::ExoSuitProbeItem)
val DEBUG_EXPLOSION_SMALL: Item by registry.register(MNames.DEBUG_EXPLOSION_SMALL) { BlockItem(MBlocks.DEBUG_EXPLOSION_SMALL, DEFAULT_PROPERTIES) } val DEBUG_EXPLOSION_SMALL: Item by registry.register(MNames.DEBUG_EXPLOSION_SMALL) { BlockItem(MBlocks.DEBUG_EXPLOSION_SMALL, DEFAULT_PROPERTIES) }
val DEBUG_SPHERE_POINTS: Item by registry.register(MNames.DEBUG_SPHERE_POINTS) { BlockItem(MBlocks.DEBUG_SPHERE_POINTS, DEFAULT_PROPERTIES) } val DEBUG_SPHERE_POINTS: Item by registry.register(MNames.DEBUG_SPHERE_POINTS) { BlockItem(MBlocks.DEBUG_SPHERE_POINTS, DEFAULT_PROPERTIES) }

View File

@ -92,6 +92,8 @@ object MNames {
const val GRAVITATION_FIELD_SENSOR = "gravitation_field_sensor" const val GRAVITATION_FIELD_SENSOR = "gravitation_field_sensor"
const val PORTABLE_GRAVITATION_STABILIZER = "portable_gravitation_stabilizer" const val PORTABLE_GRAVITATION_STABILIZER = "portable_gravitation_stabilizer"
const val EXOSUIT_PROBE = "exosuit_probe"
// armor // armor
const val TRITANIUM_HELMET = "tritanium_helmet" const val TRITANIUM_HELMET = "tritanium_helmet"
const val TRITANIUM_CHESTPLATE = "tritanium_chestplate" const val TRITANIUM_CHESTPLATE = "tritanium_chestplate"

View File

@ -181,10 +181,18 @@ object MRegistry {
CRATE_PURPLE, CRATE_PURPLE,
) )
val DAMAGE_BECOME_ANDROID = ImmutableDamageSource(DamageSource("otm_become_android").bypassArmor().bypassInvul().bypassMagic()) const val DAMAGE_BECOME_ANDROID_ID = "otm_become_android"
val DAMAGE_BECOME_HUMANE = ImmutableDamageSource(DamageSource("otm_become_humane").bypassArmor().bypassInvul().bypassMagic()) const val DAMAGE_BECOME_HUMANE_ID = "otm_become_humane"
val DAMAGE_EVENT_HORIZON = ImmutableDamageSource(DamageSource("otm_event_horizon").bypassMagic().bypassArmor()) const val DAMAGE_EVENT_HORIZON_ID = "otm_event_horizon"
val DAMAGE_HAWKING_RADIATION = ImmutableDamageSource(DamageSource("otm_hawking_radiation")) const val DAMAGE_HAWKING_RADIATION_ID = "otm_hawking_radiation"
const val DAMAGE_EXOSUIT_PROBE_ID = "otm_exosuit_probe"
val DAMAGE_EXOSUIT_PROBE = ImmutableDamageSource(DamageSource(DAMAGE_EXOSUIT_PROBE_ID).bypassArmor().bypassMagic())
val DAMAGE_BECOME_ANDROID = ImmutableDamageSource(DamageSource(DAMAGE_BECOME_ANDROID_ID).bypassArmor().bypassInvul().bypassMagic())
val DAMAGE_BECOME_HUMANE = ImmutableDamageSource(DamageSource(DAMAGE_BECOME_HUMANE_ID).bypassArmor().bypassInvul().bypassMagic())
val DAMAGE_EVENT_HORIZON = ImmutableDamageSource(DamageSource(DAMAGE_EVENT_HORIZON_ID).bypassMagic().bypassArmor())
val DAMAGE_HAWKING_RADIATION = ImmutableDamageSource(DamageSource(DAMAGE_HAWKING_RADIATION_ID))
const val DAMAGE_EMP_NAME = "otm_emp" const val DAMAGE_EMP_NAME = "otm_emp"
const val DAMAGE_PLASMA_NAME = "otm_plasma" const val DAMAGE_PLASMA_NAME = "otm_plasma"
val DAMAGE_EMP: DamageSource = ImmutableDamageSource(EMPDamageSource()) val DAMAGE_EMP: DamageSource = ImmutableDamageSource(EMPDamageSource())

View File

@ -0,0 +1,40 @@
package ru.dbotthepony.mc.otm.tests
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import ru.dbotthepony.mc.otm.TimerQueue
object TimerQueueTests {
@Test
@DisplayName("TimerQueue test")
fun test() {
val queue = TimerQueue()
var state = 0
queue.Timer(1) { state = 1 }
queue.Timer(2) { state = 2 }
queue.Timer(3) { state = 3 }
queue.Timer(7) { state = 7 }
queue.Timer(6) { state = 6 }
queue.Timer(2) { state = 2 }
queue.tick() // 1
assertEquals(1, state)
queue.tick() // 2 + 2
assertEquals(2, state)
queue.tick() // 3
assertEquals(3, state)
queue.tick() // 4
assertEquals(3, state)
queue.tick() // 5
assertEquals(3, state)
queue.tick() // 6
assertEquals(6, state)
queue.tick() // 7
assertEquals(7, state)
queue.tick() // 8
assertEquals(7, state)
}
}