1.21.3 WIP
This commit is contained in:
@@ -1,21 +1,24 @@
|
||||
|
||||
package moe.nea.firmament.events
|
||||
|
||||
import java.util.function.Consumer
|
||||
import java.util.function.BiConsumer
|
||||
import net.minecraft.client.render.model.ReferencedModelsCollector
|
||||
import net.minecraft.client.util.ModelIdentifier
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
// TODO: Rename this event, since it is not really directly baking models anymore
|
||||
class BakeExtraModelsEvent(
|
||||
private val addItemModel: Consumer<ModelIdentifier>,
|
||||
private val addAnyModel: Consumer<ModelIdentifier>,
|
||||
private val addAnyModel: BiConsumer<ModelIdentifier, Identifier>,
|
||||
) : FirmamentEvent() {
|
||||
|
||||
fun addNonItemModel(modelIdentifier: ModelIdentifier) {
|
||||
this.addAnyModel.accept(modelIdentifier)
|
||||
}
|
||||
fun addNonItemModel(modelIdentifier: ModelIdentifier, identifier: Identifier) {
|
||||
this.addAnyModel.accept(modelIdentifier, identifier)
|
||||
}
|
||||
|
||||
fun addItemModel(modelIdentifier: ModelIdentifier) {
|
||||
this.addItemModel.accept(modelIdentifier)
|
||||
}
|
||||
fun addItemModel(modelIdentifier: ModelIdentifier) {
|
||||
addNonItemModel(
|
||||
modelIdentifier,
|
||||
modelIdentifier.id.withPrefixedPath(ReferencedModelsCollector.ITEM_DIRECTORY))
|
||||
}
|
||||
|
||||
companion object : FirmamentEventBus<BakeExtraModelsEvent>()
|
||||
companion object : FirmamentEventBus<BakeExtraModelsEvent>()
|
||||
}
|
||||
|
||||
@@ -2,35 +2,37 @@ package moe.nea.firmament.events
|
||||
|
||||
import java.util.Optional
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.client.render.item.ItemModels
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.render.model.BakedModelManager
|
||||
import net.minecraft.client.util.ModelIdentifier
|
||||
import net.minecraft.item.ItemStack
|
||||
import moe.nea.firmament.util.ErrorUtil
|
||||
import moe.nea.firmament.util.collections.WeakCache
|
||||
|
||||
data class CustomItemModelEvent(
|
||||
val itemStack: ItemStack,
|
||||
var overrideModel: ModelIdentifier? = null,
|
||||
val itemStack: ItemStack,
|
||||
var overrideModel: ModelIdentifier? = null,
|
||||
) : FirmamentEvent() {
|
||||
companion object : FirmamentEventBus<CustomItemModelEvent>() {
|
||||
val cache =
|
||||
WeakCache.memoize<ItemStack, BakedModelManager, Optional<BakedModel>>("CustomItemModels") { stack, models ->
|
||||
val modelId = getModelIdentifier(stack) ?: return@memoize Optional.empty()
|
||||
val bakedModel = models.getModel(modelId)
|
||||
if (bakedModel === models.missingModel) return@memoize Optional.empty()
|
||||
Optional.of(bakedModel)
|
||||
}
|
||||
companion object : FirmamentEventBus<CustomItemModelEvent>() {
|
||||
val cache =
|
||||
WeakCache.memoize<ItemStack, ItemModels, Optional<BakedModel>>("CustomItemModels") { stack, models ->
|
||||
val modelId = getModelIdentifier(stack) ?: return@memoize Optional.empty()
|
||||
ErrorUtil.softCheck("Model Id needs to have an inventory variant", modelId.variant() == "inventory")
|
||||
val bakedModel = models.getModel(modelId.id)
|
||||
if (bakedModel == null || bakedModel === models.missingModelSupplier.get()) return@memoize Optional.empty()
|
||||
Optional.of(bakedModel)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getModelIdentifier(itemStack: ItemStack?): ModelIdentifier? {
|
||||
if (itemStack == null) return null
|
||||
return publish(CustomItemModelEvent(itemStack)).overrideModel
|
||||
}
|
||||
@JvmStatic
|
||||
fun getModelIdentifier(itemStack: ItemStack?): ModelIdentifier? {
|
||||
if (itemStack == null) return null
|
||||
return publish(CustomItemModelEvent(itemStack)).overrideModel
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getModel(itemStack: ItemStack?, thing: BakedModelManager): BakedModel? {
|
||||
if (itemStack == null) return null
|
||||
return cache.invoke(itemStack, thing).getOrNull()
|
||||
}
|
||||
}
|
||||
@JvmStatic
|
||||
fun getModel(itemStack: ItemStack?, thing: ItemModels): BakedModel? {
|
||||
if (itemStack == null) return null
|
||||
return cache.invoke(itemStack, thing).getOrNull()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
9
src/main/kotlin/events/DebugInstantiateEvent.kt
Normal file
9
src/main/kotlin/events/DebugInstantiateEvent.kt
Normal file
@@ -0,0 +1,9 @@
|
||||
package moe.nea.firmament.events
|
||||
|
||||
/**
|
||||
* Called in a devenv after minecraft has been initialized. This event should be used to force instantiation of lazy
|
||||
* variables (and similar late init) to cause any possible issues to materialize.
|
||||
*/
|
||||
class DebugInstantiateEvent : FirmamentEvent() {
|
||||
companion object : FirmamentEventBus<DebugInstantiateEvent>()
|
||||
}
|
||||
@@ -5,31 +5,28 @@ import java.util.concurrent.Executor
|
||||
import net.minecraft.resource.ReloadableResourceManagerImpl
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.resource.ResourceReloader
|
||||
import net.minecraft.util.profiler.Profiler
|
||||
|
||||
data class FinalizeResourceManagerEvent(
|
||||
val resourceManager: ReloadableResourceManagerImpl,
|
||||
val resourceManager: ReloadableResourceManagerImpl,
|
||||
) : FirmamentEvent() {
|
||||
companion object : FirmamentEventBus<FinalizeResourceManagerEvent>()
|
||||
companion object : FirmamentEventBus<FinalizeResourceManagerEvent>()
|
||||
|
||||
inline fun registerOnApply(name: String, crossinline function: () -> Unit) {
|
||||
resourceManager.registerReloader(object : ResourceReloader {
|
||||
override fun reload(
|
||||
synchronizer: ResourceReloader.Synchronizer,
|
||||
manager: ResourceManager?,
|
||||
prepareProfiler: Profiler?,
|
||||
applyProfiler: Profiler?,
|
||||
prepareExecutor: Executor?,
|
||||
applyExecutor: Executor
|
||||
): CompletableFuture<Void> {
|
||||
return CompletableFuture.completedFuture(Unit)
|
||||
.thenCompose(synchronizer::whenPrepared)
|
||||
.thenAcceptAsync({ function() }, applyExecutor)
|
||||
}
|
||||
inline fun registerOnApply(name: String, crossinline function: () -> Unit) {
|
||||
resourceManager.registerReloader(object : ResourceReloader {
|
||||
override fun reload(
|
||||
synchronizer: ResourceReloader.Synchronizer,
|
||||
manager: ResourceManager,
|
||||
prepareExecutor: Executor,
|
||||
applyExecutor: Executor
|
||||
): CompletableFuture<Void> {
|
||||
return CompletableFuture.completedFuture(Unit)
|
||||
.thenCompose(synchronizer::whenPrepared)
|
||||
.thenAcceptAsync({ function() }, applyExecutor)
|
||||
}
|
||||
|
||||
override fun getName(): String {
|
||||
return name
|
||||
}
|
||||
})
|
||||
}
|
||||
override fun getName(): String {
|
||||
return name
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ data class IsSlotProtectedEvent(
|
||||
val event = IsSlotProtectedEvent(slot, action, false, itemStackOverride)
|
||||
publish(event)
|
||||
if (event.isProtected && !event.silent) {
|
||||
MC.player?.sendMessage(Text.translatable("firmament.protectitem").append(event.itemStack.name))
|
||||
MC.sendChat(Text.translatable("firmament.protectitem").append(event.itemStack.name))
|
||||
CommonSoundEffects.playFailure()
|
||||
}
|
||||
return event.isProtected
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
package moe.nea.firmament.events
|
||||
|
||||
import com.mojang.datafixers.util.Pair
|
||||
import java.util.function.Consumer
|
||||
import net.minecraft.client.gl.ShaderProgram
|
||||
import net.minecraft.client.render.VertexFormat
|
||||
import net.minecraft.resource.ResourceFactory
|
||||
import moe.nea.firmament.Firmament
|
||||
|
||||
data class RegisterCustomShadersEvent(
|
||||
val list: MutableList<Pair<ShaderProgram, Consumer<ShaderProgram>>>,
|
||||
val resourceFactory: ResourceFactory,
|
||||
) : FirmamentEvent() {
|
||||
companion object : FirmamentEventBus<RegisterCustomShadersEvent>()
|
||||
|
||||
fun register(name: String, vertexFormat: VertexFormat, saver: Consumer<ShaderProgram>) {
|
||||
require(name.startsWith("firmament_"))
|
||||
try {
|
||||
list.add(Pair.of(ShaderProgram(resourceFactory, name, vertexFormat), saver))
|
||||
} catch (ex: Exception) {
|
||||
Firmament.logger.fatal("Could not load firmament shader $name", ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,20 +3,19 @@
|
||||
package moe.nea.firmament.events
|
||||
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.texture.Sprite
|
||||
import net.minecraft.screen.slot.Slot
|
||||
import net.minecraft.util.Identifier
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.render.drawGuiTexture
|
||||
|
||||
interface SlotRenderEvents {
|
||||
val context: DrawContext
|
||||
val slot: Slot
|
||||
val mouseX: Int
|
||||
val mouseY: Int
|
||||
val delta: Float
|
||||
|
||||
fun highlight(sprite: Sprite) {
|
||||
context.drawSprite(
|
||||
fun highlight(sprite: Identifier) {
|
||||
context.drawGuiTexture(
|
||||
slot.x, slot.y, 0, 16, 16,
|
||||
sprite
|
||||
)
|
||||
@@ -24,9 +23,6 @@ interface SlotRenderEvents {
|
||||
|
||||
data class Before(
|
||||
override val context: DrawContext, override val slot: Slot,
|
||||
override val mouseX: Int,
|
||||
override val mouseY: Int,
|
||||
override val delta: Float
|
||||
) : FirmamentEvent(),
|
||||
SlotRenderEvents {
|
||||
companion object : FirmamentEventBus<Before>()
|
||||
@@ -34,9 +30,6 @@ interface SlotRenderEvents {
|
||||
|
||||
data class After(
|
||||
override val context: DrawContext, override val slot: Slot,
|
||||
override val mouseX: Int,
|
||||
override val mouseY: Int,
|
||||
override val delta: Float
|
||||
) : FirmamentEvent(),
|
||||
SlotRenderEvents {
|
||||
companion object : FirmamentEventBus<After>()
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.events
|
||||
|
||||
class WorldReadyEvent : FirmamentEvent() {
|
||||
companion object : FirmamentEventBus<WorldReadyEvent>()
|
||||
companion object : FirmamentEventBus<WorldReadyEvent>()
|
||||
// class FullyLoaded : FirmamentEvent() {
|
||||
// companion object : FirmamentEventBus<FullyLoaded>() {
|
||||
// TODO: check WorldLoadingState
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -17,10 +17,7 @@ import net.minecraft.util.math.Vec3d
|
||||
data class WorldRenderLastEvent(
|
||||
val matrices: MatrixStack,
|
||||
val tickCounter: RenderTickCounter,
|
||||
val renderBlockOutline: Boolean,
|
||||
val camera: Camera,
|
||||
val gameRenderer: GameRenderer,
|
||||
val lightmapTextureManager: LightmapTextureManager,
|
||||
val vertexConsumers: VertexConsumerProvider.Immediate,
|
||||
) : FirmamentEvent() {
|
||||
companion object : FirmamentEventBus<WorldRenderLastEvent>()
|
||||
|
||||
@@ -13,8 +13,10 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.async
|
||||
import kotlin.math.min
|
||||
import net.minecraft.client.gui.screen.ChatScreen
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.texture.NativeImage
|
||||
import net.minecraft.client.texture.NativeImageBackedTexture
|
||||
import net.minecraft.scoreboard.ScoreboardCriterion.RenderType
|
||||
import net.minecraft.text.ClickEvent
|
||||
import net.minecraft.text.HoverEvent
|
||||
import net.minecraft.text.Style
|
||||
@@ -28,6 +30,7 @@ import moe.nea.firmament.events.ScreenRenderPostEvent
|
||||
import moe.nea.firmament.features.FirmamentFeature
|
||||
import moe.nea.firmament.gui.config.ManagedConfig
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.render.drawTexture
|
||||
import moe.nea.firmament.util.transformEachRecursively
|
||||
import moe.nea.firmament.util.unformattedString
|
||||
|
||||
|
||||
@@ -37,10 +37,10 @@ object DeveloperFeatures : FirmamentFeature {
|
||||
builder.directory(gradleDir.toFile())
|
||||
builder.inheritIO()
|
||||
val process = builder.start()
|
||||
MC.player?.sendMessage(Text.translatable("firmament.dev.resourcerebuild.start"))
|
||||
MC.sendChat(Text.translatable("firmament.dev.resourcerebuild.start"))
|
||||
val startTime = TimeMark.now()
|
||||
process.toHandle().onExit().thenApply {
|
||||
MC.player?.sendMessage(Text.stringifiedTranslatable(
|
||||
MC.sendChat(Text.stringifiedTranslatable(
|
||||
"firmament.dev.resourcerebuild.done",
|
||||
startTime.passedTime()))
|
||||
Unit
|
||||
|
||||
@@ -9,6 +9,7 @@ import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.nbt.NbtOps
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.text.TextCodecs
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
@@ -54,15 +55,13 @@ object PowerUserTools : FirmamentFeature {
|
||||
var lastCopiedStack: Pair<ItemStack, Text>? = null
|
||||
set(value) {
|
||||
field = value
|
||||
if (value != null)
|
||||
lastCopiedStackViewTime = true
|
||||
if (value != null) lastCopiedStackViewTime = true
|
||||
}
|
||||
var lastCopiedStackViewTime = false
|
||||
|
||||
@Subscribe
|
||||
fun resetLastCopiedStack(event: TickEvent) {
|
||||
if (!lastCopiedStackViewTime)
|
||||
lastCopiedStack = null
|
||||
if (!lastCopiedStackViewTime) lastCopiedStack = null
|
||||
lastCopiedStackViewTime = false
|
||||
}
|
||||
|
||||
@@ -154,13 +153,13 @@ object PowerUserTools : FirmamentFeature {
|
||||
}
|
||||
ClipboardUtils.setTextContent(skullTexture.toString())
|
||||
lastCopiedStack =
|
||||
Pair(
|
||||
item,
|
||||
Text.stringifiedTranslatable("firmament.tooltip.copied.skull-id", skullTexture.toString())
|
||||
)
|
||||
Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.skull-id", skullTexture.toString()))
|
||||
println("Copied skull id: $skullTexture")
|
||||
} else if (it.matches(TConfig.copyItemStack)) {
|
||||
ClipboardUtils.setTextContent(item.encode(MC.currentOrDefaultRegistries).toPrettyString())
|
||||
ClipboardUtils.setTextContent(
|
||||
ItemStack.CODEC
|
||||
.encodeStart(MC.currentOrDefaultRegistries.getOps(NbtOps.INSTANCE), item)
|
||||
.orThrow.toPrettyString())
|
||||
lastCopiedStack = Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.stack"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,12 +104,13 @@ object AncestralSpadeSolver : SubscriptionOwner {
|
||||
if (!isEnabled()) return
|
||||
RenderInWorldContext.renderInWorld(event) {
|
||||
nextGuess?.let {
|
||||
color(1f, 1f, 0f, 0.5f)
|
||||
tinyBlock(it, 1f)
|
||||
tinyBlock(it, 1f, 0x80FFFFFF.toInt())
|
||||
// TODO: replace this
|
||||
color(1f, 1f, 0f, 1f)
|
||||
tracer(it, lineWidth = 3f)
|
||||
}
|
||||
if (particlePositions.size > 2 && lastDing.passedTime() < 10.seconds && nextGuess != null) {
|
||||
// TODO: replace this // TODO: add toggle
|
||||
color(0f, 1f, 0f, 0.7f)
|
||||
line(particlePositions)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
package moe.nea.firmament.features.diana
|
||||
|
||||
import me.shedaniel.math.Color
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import net.minecraft.particle.ParticleTypes
|
||||
import net.minecraft.util.math.BlockPos
|
||||
@@ -20,125 +20,125 @@ import moe.nea.firmament.util.render.RenderInWorldContext.Companion.renderInWorl
|
||||
object NearbyBurrowsSolver : SubscriptionOwner {
|
||||
|
||||
|
||||
private val recentlyDugBurrows: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(20)
|
||||
private val recentEnchantParticles: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(500)
|
||||
private var lastBlockClick: BlockPos? = null
|
||||
private val recentlyDugBurrows: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(20)
|
||||
private val recentEnchantParticles: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(500)
|
||||
private var lastBlockClick: BlockPos? = null
|
||||
|
||||
enum class BurrowType {
|
||||
START, MOB, TREASURE
|
||||
}
|
||||
enum class BurrowType {
|
||||
START, MOB, TREASURE
|
||||
}
|
||||
|
||||
val burrows = mutableMapOf<BlockPos, BurrowType>()
|
||||
val burrows = mutableMapOf<BlockPos, BurrowType>()
|
||||
|
||||
@Subscribe
|
||||
fun onChatEvent(event: ProcessChatEvent) {
|
||||
val lastClickedBurrow = lastBlockClick ?: return
|
||||
if (event.unformattedString.startsWith("You dug out a Griffin Burrow!") ||
|
||||
event.unformattedString.startsWith(" ☠ You were killed by") ||
|
||||
event.unformattedString.startsWith("You finished the Griffin burrow chain!")
|
||||
) {
|
||||
markAsDug(lastClickedBurrow)
|
||||
burrows.remove(lastClickedBurrow)
|
||||
}
|
||||
}
|
||||
@Subscribe
|
||||
fun onChatEvent(event: ProcessChatEvent) {
|
||||
val lastClickedBurrow = lastBlockClick ?: return
|
||||
if (event.unformattedString.startsWith("You dug out a Griffin Burrow!") ||
|
||||
event.unformattedString.startsWith(" ☠ You were killed by") ||
|
||||
event.unformattedString.startsWith("You finished the Griffin burrow chain!")
|
||||
) {
|
||||
markAsDug(lastClickedBurrow)
|
||||
burrows.remove(lastClickedBurrow)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun wasRecentlyDug(blockPos: BlockPos): Boolean {
|
||||
val lastDigTime = recentlyDugBurrows[blockPos] ?: TimeMark.farPast()
|
||||
return lastDigTime.passedTime() < 10.seconds
|
||||
}
|
||||
fun wasRecentlyDug(blockPos: BlockPos): Boolean {
|
||||
val lastDigTime = recentlyDugBurrows[blockPos] ?: TimeMark.farPast()
|
||||
return lastDigTime.passedTime() < 10.seconds
|
||||
}
|
||||
|
||||
fun markAsDug(blockPos: BlockPos) {
|
||||
recentlyDugBurrows[blockPos] = TimeMark.now()
|
||||
}
|
||||
fun markAsDug(blockPos: BlockPos) {
|
||||
recentlyDugBurrows[blockPos] = TimeMark.now()
|
||||
}
|
||||
|
||||
fun wasRecentlyEnchanted(blockPos: BlockPos): Boolean {
|
||||
val lastEnchantTime = recentEnchantParticles[blockPos] ?: TimeMark.farPast()
|
||||
return lastEnchantTime.passedTime() < 4.seconds
|
||||
}
|
||||
fun wasRecentlyEnchanted(blockPos: BlockPos): Boolean {
|
||||
val lastEnchantTime = recentEnchantParticles[blockPos] ?: TimeMark.farPast()
|
||||
return lastEnchantTime.passedTime() < 4.seconds
|
||||
}
|
||||
|
||||
fun markAsEnchanted(blockPos: BlockPos) {
|
||||
recentEnchantParticles[blockPos] = TimeMark.now()
|
||||
}
|
||||
fun markAsEnchanted(blockPos: BlockPos) {
|
||||
recentEnchantParticles[blockPos] = TimeMark.now()
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onParticles(event: ParticleSpawnEvent) {
|
||||
if (!DianaWaypoints.TConfig.nearbyWaypoints) return
|
||||
@Subscribe
|
||||
fun onParticles(event: ParticleSpawnEvent) {
|
||||
if (!DianaWaypoints.TConfig.nearbyWaypoints) return
|
||||
|
||||
val position: BlockPos = event.position.toBlockPos().down()
|
||||
val position: BlockPos = event.position.toBlockPos().down()
|
||||
|
||||
if (wasRecentlyDug(position)) return
|
||||
if (wasRecentlyDug(position)) return
|
||||
|
||||
val isEven50Spread = (event.offset.x == 0.5f && event.offset.z == 0.5f)
|
||||
val isEven50Spread = (event.offset.x == 0.5f && event.offset.z == 0.5f)
|
||||
|
||||
if (event.particleEffect.type == ParticleTypes.ENCHANT) {
|
||||
if (event.count == 5 && event.speed == 0.05F && event.offset.y == 0.4F && isEven50Spread) {
|
||||
markAsEnchanted(position)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (event.particleEffect.type == ParticleTypes.ENCHANT) {
|
||||
if (event.count == 5 && event.speed == 0.05F && event.offset.y == 0.4F && isEven50Spread) {
|
||||
markAsEnchanted(position)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (!wasRecentlyEnchanted(position)) return
|
||||
if (!wasRecentlyEnchanted(position)) return
|
||||
|
||||
if (event.particleEffect.type == ParticleTypes.ENCHANTED_HIT
|
||||
&& event.count == 4
|
||||
&& event.speed == 0.01F
|
||||
&& event.offset.y == 0.1f
|
||||
&& isEven50Spread
|
||||
) {
|
||||
burrows[position] = BurrowType.START
|
||||
}
|
||||
if (event.particleEffect.type == ParticleTypes.CRIT
|
||||
&& event.count == 3
|
||||
&& event.speed == 0.01F
|
||||
&& event.offset.y == 0.1F
|
||||
&& isEven50Spread
|
||||
) {
|
||||
burrows[position] = BurrowType.MOB
|
||||
}
|
||||
if (event.particleEffect.type == ParticleTypes.DRIPPING_LAVA
|
||||
&& event.count == 2
|
||||
&& event.speed == 0.01F
|
||||
&& event.offset.y == 0.1F
|
||||
&& event.offset.x == 0.35F && event.offset.z == 0.35f
|
||||
) {
|
||||
burrows[position] = BurrowType.TREASURE
|
||||
}
|
||||
}
|
||||
if (event.particleEffect.type == ParticleTypes.ENCHANTED_HIT
|
||||
&& event.count == 4
|
||||
&& event.speed == 0.01F
|
||||
&& event.offset.y == 0.1f
|
||||
&& isEven50Spread
|
||||
) {
|
||||
burrows[position] = BurrowType.START
|
||||
}
|
||||
if (event.particleEffect.type == ParticleTypes.CRIT
|
||||
&& event.count == 3
|
||||
&& event.speed == 0.01F
|
||||
&& event.offset.y == 0.1F
|
||||
&& isEven50Spread
|
||||
) {
|
||||
burrows[position] = BurrowType.MOB
|
||||
}
|
||||
if (event.particleEffect.type == ParticleTypes.DRIPPING_LAVA
|
||||
&& event.count == 2
|
||||
&& event.speed == 0.01F
|
||||
&& event.offset.y == 0.1F
|
||||
&& event.offset.x == 0.35F && event.offset.z == 0.35f
|
||||
) {
|
||||
burrows[position] = BurrowType.TREASURE
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onRender(event: WorldRenderLastEvent) {
|
||||
if (!DianaWaypoints.TConfig.nearbyWaypoints) return
|
||||
renderInWorld(event) {
|
||||
for ((location, burrow) in burrows) {
|
||||
when (burrow) {
|
||||
BurrowType.START -> color(.2f, .8f, .2f, 0.4f)
|
||||
BurrowType.MOB -> color(0.3f, 0.4f, 0.9f, 0.4f)
|
||||
BurrowType.TREASURE -> color(1f, 0.7f, 0.2f, 0.4f)
|
||||
}
|
||||
block(location)
|
||||
}
|
||||
}
|
||||
}
|
||||
@Subscribe
|
||||
fun onRender(event: WorldRenderLastEvent) {
|
||||
if (!DianaWaypoints.TConfig.nearbyWaypoints) return
|
||||
renderInWorld(event) {
|
||||
for ((location, burrow) in burrows) {
|
||||
val color = when (burrow) {
|
||||
BurrowType.START -> Color.ofRGBA(.2f, .8f, .2f, 0.4f)
|
||||
BurrowType.MOB -> Color.ofRGBA(0.3f, 0.4f, 0.9f, 0.4f)
|
||||
BurrowType.TREASURE -> Color.ofRGBA(1f, 0.7f, 0.2f, 0.4f)
|
||||
}
|
||||
block(location, color.color)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onSwapWorld(worldReadyEvent: WorldReadyEvent) {
|
||||
burrows.clear()
|
||||
recentEnchantParticles.clear()
|
||||
recentlyDugBurrows.clear()
|
||||
lastBlockClick = null
|
||||
}
|
||||
@Subscribe
|
||||
fun onSwapWorld(worldReadyEvent: WorldReadyEvent) {
|
||||
burrows.clear()
|
||||
recentEnchantParticles.clear()
|
||||
recentlyDugBurrows.clear()
|
||||
lastBlockClick = null
|
||||
}
|
||||
|
||||
fun onBlockClick(blockPos: BlockPos) {
|
||||
if (!DianaWaypoints.TConfig.nearbyWaypoints) return
|
||||
burrows.remove(blockPos)
|
||||
lastBlockClick = blockPos
|
||||
}
|
||||
fun onBlockClick(blockPos: BlockPos) {
|
||||
if (!DianaWaypoints.TConfig.nearbyWaypoints) return
|
||||
burrows.remove(blockPos)
|
||||
lastBlockClick = blockPos
|
||||
}
|
||||
|
||||
override val delegateFeature: FirmamentFeature
|
||||
get() = DianaWaypoints
|
||||
override val delegateFeature: FirmamentFeature
|
||||
get() = DianaWaypoints
|
||||
}
|
||||
|
||||
fun Position.toBlockPos(): BlockPos {
|
||||
return BlockPos(MathHelper.floor(x), MathHelper.floor(y), MathHelper.floor(z))
|
||||
return BlockPos(MathHelper.floor(x), MathHelper.floor(y), MathHelper.floor(z))
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ object CraftingOverlay : FirmamentFeature {
|
||||
if (!slot.hasStack()) {
|
||||
val itemStack = SBItemStack(expectedItem)?.asImmutableItemStack() ?: return
|
||||
event.context.drawItem(itemStack, event.slot.x, event.slot.y)
|
||||
event.context.drawItemInSlot(
|
||||
event.context.drawStackOverlay(
|
||||
MC.font,
|
||||
itemStack,
|
||||
event.slot.x,
|
||||
|
||||
@@ -4,6 +4,7 @@ package moe.nea.firmament.features.inventory
|
||||
|
||||
import java.awt.Color
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.util.Formatting
|
||||
import net.minecraft.util.Identifier
|
||||
@@ -16,6 +17,7 @@ import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.mc.loreAccordingToNbt
|
||||
import moe.nea.firmament.util.collections.lastNotNullOfOrNull
|
||||
import moe.nea.firmament.util.collections.memoizeIdentity
|
||||
import moe.nea.firmament.util.render.drawGuiTexture
|
||||
import moe.nea.firmament.util.unformattedString
|
||||
|
||||
object ItemRarityCosmetics : FirmamentFeature {
|
||||
@@ -43,10 +45,10 @@ object ItemRarityCosmetics : FirmamentFeature {
|
||||
"SUPREME" to Formatting.DARK_RED,
|
||||
).mapValues {
|
||||
val c = Color(it.value.colorValue!!)
|
||||
Triple(c.red / 255F, c.green / 255F, c.blue / 255F)
|
||||
c.rgb
|
||||
}
|
||||
|
||||
private fun getSkyblockRarity0(itemStack: ItemStack): Triple<Float, Float, Float>? {
|
||||
private fun getSkyblockRarity0(itemStack: ItemStack): Int? {
|
||||
return itemStack.loreAccordingToNbt.lastNotNullOfOrNull {
|
||||
val entry = it.unformattedString
|
||||
rarityToColor.entries.find { (k, v) -> k in entry }?.value
|
||||
@@ -57,13 +59,13 @@ object ItemRarityCosmetics : FirmamentFeature {
|
||||
|
||||
|
||||
fun drawItemStackRarity(drawContext: DrawContext, x: Int, y: Int, item: ItemStack) {
|
||||
val (r, g, b) = getSkyblockRarity(item) ?: return
|
||||
drawContext.drawSprite(
|
||||
val rgb = getSkyblockRarity(item) ?: return
|
||||
drawContext.drawGuiTexture(
|
||||
RenderLayer::getGuiTextured,
|
||||
Identifier.of("firmament:item_rarity_background"),
|
||||
x, y,
|
||||
0,
|
||||
16, 16,
|
||||
MC.guiAtlasManager.getSprite(Identifier.of("firmament:item_rarity_background")),
|
||||
r, g, b, 1F
|
||||
rgb
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import moe.nea.firmament.features.FirmamentFeature
|
||||
import moe.nea.firmament.gui.config.ManagedConfig
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.petData
|
||||
import moe.nea.firmament.util.render.drawGuiTexture
|
||||
import moe.nea.firmament.util.useMatch
|
||||
|
||||
object PetFeatures : FirmamentFeature {
|
||||
@@ -28,9 +29,9 @@ object PetFeatures : FirmamentFeature {
|
||||
val stack = event.slot.stack
|
||||
if (stack.petData?.active == true)
|
||||
petMenuTitle.useMatch(MC.screenName ?: return) {
|
||||
event.context.drawSprite(
|
||||
event.context.drawGuiTexture(
|
||||
event.slot.x, event.slot.y, 0, 16, 16,
|
||||
MC.guiAtlasManager.getSprite(Identifier.of("firmament:selected_pet_background"))
|
||||
Identifier.of("firmament:selected_pet_background")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import moe.nea.firmament.util.mc.ScreenUtil.getSlotByIndex
|
||||
import moe.nea.firmament.util.mc.SlotUtils.swapWithHotBar
|
||||
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
|
||||
import moe.nea.firmament.util.mc.loreAccordingToNbt
|
||||
import moe.nea.firmament.util.render.GuiRenderLayers
|
||||
import moe.nea.firmament.util.render.drawLine
|
||||
import moe.nea.firmament.util.skyblockUUID
|
||||
import moe.nea.firmament.util.unformattedString
|
||||
@@ -211,6 +212,11 @@ object SlotLocking : FirmamentFeature {
|
||||
}
|
||||
if (it.matches(TConfig.slotBind)) {
|
||||
storedLockingSlot = null
|
||||
val boundSlots = DConfig.data?.boundSlots ?: return
|
||||
if (slot != null)
|
||||
boundSlots.entries.removeIf {
|
||||
it.value == slot.index || it.key == slot.index
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,24 +337,22 @@ object SlotLocking : FirmamentFeature {
|
||||
val isSlotLocked = it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())
|
||||
val isUUIDLocked = (it.slot.stack?.skyblockUUID) in (lockedUUIDs ?: setOf())
|
||||
if (isSlotLocked || isUUIDLocked) {
|
||||
RenderSystem.disableDepthTest()
|
||||
it.context.drawSprite(
|
||||
it.slot.x, it.slot.y, 0,
|
||||
it.context.drawGuiTexture(
|
||||
GuiRenderLayers.GUI_TEXTURED_NO_DEPTH,
|
||||
when {
|
||||
isSlotLocked ->
|
||||
(Identifier.of("firmament:slot_locked"))
|
||||
|
||||
isUUIDLocked ->
|
||||
(Identifier.of("firmament:uuid_locked"))
|
||||
|
||||
else ->
|
||||
error("unreachable")
|
||||
},
|
||||
it.slot.x, it.slot.y,
|
||||
16, 16,
|
||||
MC.guiAtlasManager.getSprite(
|
||||
when {
|
||||
isSlotLocked ->
|
||||
(Identifier.of("firmament:slot_locked"))
|
||||
|
||||
isUUIDLocked ->
|
||||
(Identifier.of("firmament:uuid_locked"))
|
||||
|
||||
else ->
|
||||
error("unreachable")
|
||||
}
|
||||
)
|
||||
-1
|
||||
)
|
||||
RenderSystem.enableDepthTest()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import moe.nea.firmament.repo.RepoManager
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.SkyblockId
|
||||
import moe.nea.firmament.util.collections.memoize
|
||||
import moe.nea.firmament.util.render.drawGuiTexture
|
||||
|
||||
@Serializable
|
||||
data class InventoryButton(
|
||||
@@ -54,13 +55,13 @@ data class InventoryButton(
|
||||
}
|
||||
|
||||
fun render(context: DrawContext) {
|
||||
context.drawSprite(
|
||||
context.drawGuiTexture(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
dimensions.width,
|
||||
dimensions.height,
|
||||
MC.guiAtlasManager.getSprite(Identifier.of("firmament:inventory_button_background"))
|
||||
Identifier.of("firmament:inventory_button_background")
|
||||
)
|
||||
context.drawItem(getItem(), 1, 1)
|
||||
}
|
||||
|
||||
@@ -84,7 +84,6 @@ class InventoryButtonEditor(
|
||||
context.matrices.push()
|
||||
context.matrices.translate(0f, 0f, -10f)
|
||||
context.fill(lastGuiRect.minX, lastGuiRect.minY, lastGuiRect.maxX, lastGuiRect.maxY, -1)
|
||||
context.setShaderColor(1f, 1f, 1f, 1f)
|
||||
context.matrices.pop()
|
||||
for (button in buttons) {
|
||||
val buttonPosition = button.getBounds(lastGuiRect)
|
||||
|
||||
@@ -40,6 +40,7 @@ sealed interface StorageBackingHandle {
|
||||
* representable as a [StorageBackingHandle], meaning another screen is open, for example the enderchest icon
|
||||
* selection screen.
|
||||
*/
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
fun fromScreen(screen: Screen?): StorageBackingHandle? {
|
||||
contract {
|
||||
returnsNotNull() implies (screen != null)
|
||||
|
||||
@@ -21,6 +21,7 @@ import moe.nea.firmament.util.MoulConfigUtils.clickMCComponentInPlace
|
||||
import moe.nea.firmament.util.MoulConfigUtils.drawMCComponentInPlace
|
||||
import moe.nea.firmament.util.assertTrueOr
|
||||
import moe.nea.firmament.util.customgui.customGui
|
||||
import moe.nea.firmament.util.render.drawGuiTexture
|
||||
|
||||
class StorageOverlayScreen : Screen(Text.literal("")) {
|
||||
|
||||
@@ -162,13 +163,11 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
|
||||
context.drawGuiTexture(upperBackgroundSprite,
|
||||
measurements.x,
|
||||
measurements.y,
|
||||
0,
|
||||
measurements.overviewWidth,
|
||||
measurements.overviewHeight)
|
||||
context.drawGuiTexture(playerInventorySprite,
|
||||
measurements.playerX,
|
||||
measurements.playerY,
|
||||
0,
|
||||
PLAYER_WIDTH,
|
||||
PLAYER_HEIGHT)
|
||||
}
|
||||
@@ -188,7 +187,7 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
|
||||
items.withIndex().forEach { (index, item) ->
|
||||
val (x, y) = getPlayerInventorySlotPosition(index)
|
||||
context.drawItem(item, x, y, 0)
|
||||
context.drawItemInSlot(textRenderer, item, x, y)
|
||||
context.drawStackOverlay(textRenderer, item, x, y)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,7 +356,7 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
|
||||
val slotY = (index / 9) * SLOT_SIZE + y + 4 + textRenderer.fontHeight + 1
|
||||
if (slots == null) {
|
||||
context.drawItem(stack, slotX, slotY)
|
||||
context.drawItemInSlot(textRenderer, stack, slotX, slotY)
|
||||
context.drawStackOverlay(textRenderer, stack, slotX, slotY)
|
||||
} else {
|
||||
val slot = slots[index]
|
||||
slot.x = slotX - slotOffset.x
|
||||
|
||||
@@ -111,7 +111,7 @@ class StorageOverviewScreen() : Screen(Text.empty()) {
|
||||
context.fill(x, y, x + 18, y + 18, 0x40808080.toInt())
|
||||
}
|
||||
context.drawItem(stack, x + 1, y + 1)
|
||||
context.drawItemInSlot(MC.font, stack, x + 1, y + 1)
|
||||
context.drawStackOverlay(MC.font, stack, x + 1, y + 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package moe.nea.firmament.features.mining
|
||||
|
||||
import net.minecraft.util.Identifier
|
||||
import moe.nea.firmament.Firmament
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.events.SlotRenderEvents
|
||||
import moe.nea.firmament.gui.config.ManagedConfig
|
||||
@@ -19,10 +19,8 @@ object CommissionFeatures {
|
||||
if (!Config.highlightCompletedCommissions) return
|
||||
if (MC.screenName != "Commissions") return
|
||||
val stack = event.slot.stack
|
||||
if(stack.loreAccordingToNbt.any { it.unformattedString == "COMPLETED" }) {
|
||||
event.highlight(
|
||||
MC.guiAtlasManager.getSprite(Identifier.of("firmament:completed_commission_background"))
|
||||
)
|
||||
if (stack.loreAccordingToNbt.any { it.unformattedString == "COMPLETED" }) {
|
||||
event.highlight(Firmament.identifier("completed_commission_background"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,7 @@ import net.minecraft.client.gui.screen.ingame.HandledScreen
|
||||
import net.minecraft.entity.player.PlayerInventory
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.screen.GenericContainerScreenHandler
|
||||
import net.minecraft.screen.ScreenHandler
|
||||
import net.minecraft.screen.slot.Slot
|
||||
import net.minecraft.screen.slot.SlotActionType
|
||||
import net.minecraft.text.Text
|
||||
import moe.nea.firmament.Firmament
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
@@ -31,6 +29,7 @@ import moe.nea.firmament.util.customgui.customGui
|
||||
import moe.nea.firmament.util.mc.CommonTextures
|
||||
import moe.nea.firmament.util.mc.SlotUtils.clickRightMouseButton
|
||||
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
|
||||
import moe.nea.firmament.util.render.drawGuiTexture
|
||||
import moe.nea.firmament.util.unformattedString
|
||||
import moe.nea.firmament.util.useMatch
|
||||
|
||||
@@ -81,7 +80,7 @@ object HotmPresets {
|
||||
override fun render(drawContext: DrawContext, delta: Float, mouseX: Int, mouseY: Int) {
|
||||
drawContext.drawGuiTexture(
|
||||
CommonTextures.genericWidget(),
|
||||
bounds.x, bounds.y, 0,
|
||||
bounds.x, bounds.y,
|
||||
bounds.width,
|
||||
bounds.height,
|
||||
)
|
||||
@@ -191,7 +190,7 @@ object HotmPresets {
|
||||
if (hotmInventoryName == MC.screenName
|
||||
&& event.slot.stack.displayNameAccordingToNbt.unformattedString in highlightedPerks
|
||||
) {
|
||||
event.highlight(MC.guiAtlasManager.getSprite(Firmament.identifier("hotm_perk_preset")))
|
||||
event.highlight((Firmament.identifier("hotm_perk_preset")))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,30 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.WrapperBakedModel as WrapperBakedModelFabric
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.client.render.model.WrapperBakedModel
|
||||
import moe.nea.firmament.util.ErrorUtil
|
||||
|
||||
interface BakedModelExtra {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun cast(originalModel: BakedModel): BakedModelExtra? {
|
||||
var p = originalModel
|
||||
for (i in 0..256) {
|
||||
p = when (p) {
|
||||
is BakedModelExtra -> return p
|
||||
is WrapperBakedModel -> p.wrapped
|
||||
is WrapperBakedModelFabric -> WrapperBakedModelFabric.unwrap(p)
|
||||
else -> break
|
||||
}
|
||||
}
|
||||
ErrorUtil.softError("Could not find a baked model for $originalModel")
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
var tintOverrides_firmament: TintOverrides?
|
||||
|
||||
fun getHeadModel_firmament(): BakedModel?
|
||||
fun setHeadModel_firmament(headModel: BakedModel?)
|
||||
fun setHeadModel_firmament(headModel: BakedModel?)
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ object CustomBlockTextures {
|
||||
.flatMap { it.lookup.values }
|
||||
.flatten()
|
||||
.mapTo(mutableSetOf()) { it.replacement.blockModelIdentifier }
|
||||
.forEach { event.addNonItemModel(it) }
|
||||
.forEach { event.addNonItemModel(it, it.id) }
|
||||
}
|
||||
|
||||
private fun prepare(manager: ResourceManager): BakedReplacements {
|
||||
@@ -263,7 +263,7 @@ object CustomBlockTextures {
|
||||
val island = SkyBlockIsland.forMode(mode)
|
||||
val islandMpa = map.getOrPut(island, ::mutableMapOf)
|
||||
for ((blockId, replacement) in json.replacements) {
|
||||
val block = MC.defaultRegistries.getWrapperOrThrow(RegistryKeys.BLOCK)
|
||||
val block = MC.defaultRegistries.getOrThrow(RegistryKeys.BLOCK)
|
||||
.getOptional(RegistryKey.of(RegistryKeys.BLOCK, blockId))
|
||||
.getOrNull()
|
||||
if (block == null) {
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import java.util.Optional
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import kotlinx.serialization.UseSerializers
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.item.ArmorMaterial
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.equipment.EquipmentModel
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.resource.SinglePreparationResourceReloader
|
||||
import net.minecraft.util.Identifier
|
||||
@@ -17,90 +17,139 @@ import net.minecraft.util.profiler.Profiler
|
||||
import moe.nea.firmament.Firmament
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.events.FinalizeResourceManagerEvent
|
||||
import moe.nea.firmament.events.subscription.SubscriptionOwner
|
||||
import moe.nea.firmament.features.FirmamentFeature
|
||||
import moe.nea.firmament.features.texturepack.CustomGlobalTextures.logger
|
||||
import moe.nea.firmament.util.IdentifierSerializer
|
||||
import moe.nea.firmament.util.collections.WeakCache
|
||||
import moe.nea.firmament.util.skyBlockId
|
||||
|
||||
object CustomGlobalArmorOverrides : SubscriptionOwner {
|
||||
@Serializable
|
||||
data class ArmorOverride(
|
||||
@SerialName("item_ids")
|
||||
val itemIds: List<String>,
|
||||
val layers: List<ArmorOverrideLayer>,
|
||||
val overrides: List<ArmorOverrideOverride> = listOf(),
|
||||
) {
|
||||
@Transient
|
||||
val bakedLayers = bakeLayers(layers)
|
||||
}
|
||||
object CustomGlobalArmorOverrides {
|
||||
@Serializable
|
||||
data class ArmorOverride(
|
||||
@SerialName("item_ids")
|
||||
val itemIds: List<String>,
|
||||
val layers: List<ArmorOverrideLayer>? = null,
|
||||
val model: Identifier? = null,
|
||||
val overrides: List<ArmorOverrideOverride> = listOf(),
|
||||
) {
|
||||
@Transient
|
||||
lateinit var modelIdentifier: Identifier
|
||||
fun bake() {
|
||||
modelIdentifier = bakeModel(model, layers)
|
||||
overrides.forEach { it.bake() }
|
||||
}
|
||||
|
||||
fun bakeLayers(layers: List<ArmorOverrideLayer>): List<ArmorMaterial.Layer> {
|
||||
return layers.map { ArmorMaterial.Layer(it.identifier, it.suffix, it.tint) }
|
||||
}
|
||||
init {
|
||||
require(layers != null || model != null) { "Either model or layers must be specified for armor override" }
|
||||
require(layers == null || model == null) { "Can't specify both model and layers for armor override" }
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ArmorOverrideLayer(
|
||||
val tint: Boolean = false,
|
||||
val identifier: Identifier,
|
||||
val suffix: String = "",
|
||||
)
|
||||
@Serializable
|
||||
data class ArmorOverrideLayer(
|
||||
val tint: Boolean = false,
|
||||
val identifier: Identifier,
|
||||
val suffix: String = "",
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ArmorOverrideOverride(
|
||||
val predicate: FirmamentModelPredicate,
|
||||
val layers: List<ArmorOverrideLayer>,
|
||||
) {
|
||||
@Transient
|
||||
val bakedLayers = bakeLayers(layers)
|
||||
}
|
||||
@Serializable
|
||||
data class ArmorOverrideOverride(
|
||||
val predicate: FirmamentModelPredicate,
|
||||
val layers: List<ArmorOverrideLayer>? = null,
|
||||
val model: Identifier? = null,
|
||||
) {
|
||||
init {
|
||||
require(layers != null || model != null) { "Either model or layers must be specified for armor override override" }
|
||||
require(layers == null || model == null) { "Can't specify both model and layers for armor override override" }
|
||||
}
|
||||
|
||||
override val delegateFeature: FirmamentFeature
|
||||
get() = CustomSkyBlockTextures
|
||||
@Transient
|
||||
lateinit var modelIdentifier: Identifier
|
||||
fun bake() {
|
||||
modelIdentifier = bakeModel(model, layers)
|
||||
}
|
||||
}
|
||||
|
||||
val overrideCache = WeakCache.memoize<ItemStack, Optional<List<ArmorMaterial.Layer>>>("ArmorOverrides") { stack ->
|
||||
val id = stack.skyBlockId ?: return@memoize Optional.empty()
|
||||
val override = overrides[id.neuItem] ?: return@memoize Optional.empty()
|
||||
for (suboverride in override.overrides) {
|
||||
if (suboverride.predicate.test(stack)) {
|
||||
return@memoize Optional.of(suboverride.bakedLayers)
|
||||
}
|
||||
}
|
||||
return@memoize Optional.of(override.bakedLayers)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun overrideArmor(stack: ItemStack): List<ArmorMaterial.Layer>? {
|
||||
if (!CustomSkyBlockTextures.TConfig.enableArmorOverrides) return null
|
||||
return overrideCache.invoke(stack).getOrNull()
|
||||
}
|
||||
val overrideCache = WeakCache.memoize<ItemStack, Optional<Identifier>>("ArmorOverrides") { stack ->
|
||||
val id = stack.skyBlockId ?: return@memoize Optional.empty()
|
||||
val override = overrides[id.neuItem] ?: return@memoize Optional.empty()
|
||||
for (suboverride in override.overrides) {
|
||||
if (suboverride.predicate.test(stack)) {
|
||||
return@memoize Optional.of(suboverride.modelIdentifier)
|
||||
}
|
||||
}
|
||||
return@memoize Optional.of(override.modelIdentifier)
|
||||
}
|
||||
|
||||
var overrides: Map<String, ArmorOverride> = mapOf()
|
||||
var overrides: Map<String, ArmorOverride> = mapOf()
|
||||
private var bakedOverrides: MutableMap<Identifier, EquipmentModel> = mutableMapOf()
|
||||
private val sentinelFirmRunning = AtomicInteger()
|
||||
|
||||
@Subscribe
|
||||
fun onStart(event: FinalizeResourceManagerEvent) {
|
||||
event.resourceManager.registerReloader(object :
|
||||
SinglePreparationResourceReloader<Map<String, ArmorOverride>>() {
|
||||
override fun prepare(manager: ResourceManager, profiler: Profiler): Map<String, ArmorOverride> {
|
||||
val overrideFiles = manager.findResources("overrides/armor_models") {
|
||||
it.namespace == "firmskyblock" && it.path.endsWith(".json")
|
||||
}
|
||||
val overrides = overrideFiles.mapNotNull {
|
||||
Firmament.tryDecodeJsonFromStream<ArmorOverride>(it.value.inputStream).getOrElse { ex ->
|
||||
logger.error("Failed to load armor texture override at ${it.key}", ex)
|
||||
null
|
||||
}
|
||||
}
|
||||
val associatedMap = overrides.flatMap { obj -> obj.itemIds.map { it to obj } }
|
||||
.toMap()
|
||||
return associatedMap
|
||||
}
|
||||
private fun bakeModel(model: Identifier?, layers: List<ArmorOverrideLayer>?): Identifier {
|
||||
require(model == null || layers == null)
|
||||
if (model != null) {
|
||||
return model
|
||||
} else if (layers != null) {
|
||||
val idNumber = sentinelFirmRunning.incrementAndGet()
|
||||
val identifier = Identifier.of("firmament:sentinel/$idNumber")
|
||||
val equipmentLayers = layers.map {
|
||||
EquipmentModel.Layer(
|
||||
it.identifier, if (it.tint) {
|
||||
Optional.of(EquipmentModel.Dyeable(Optional.empty()))
|
||||
} else {
|
||||
Optional.empty()
|
||||
},
|
||||
false
|
||||
)
|
||||
}
|
||||
bakedOverrides[identifier] = EquipmentModel(
|
||||
mapOf(
|
||||
EquipmentModel.LayerType.HUMANOID to equipmentLayers,
|
||||
EquipmentModel.LayerType.HUMANOID_LEGGINGS to equipmentLayers,
|
||||
)
|
||||
)
|
||||
return identifier
|
||||
} else {
|
||||
error("Either model or layers must be non null")
|
||||
}
|
||||
}
|
||||
|
||||
override fun apply(prepared: Map<String, ArmorOverride>, manager: ResourceManager, profiler: Profiler) {
|
||||
overrides = prepared
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onStart(event: FinalizeResourceManagerEvent) {
|
||||
event.resourceManager.registerReloader(object :
|
||||
SinglePreparationResourceReloader<Map<String, ArmorOverride>>() {
|
||||
override fun prepare(manager: ResourceManager, profiler: Profiler): Map<String, ArmorOverride> {
|
||||
val overrideFiles = manager.findResources("overrides/armor_models") {
|
||||
it.namespace == "firmskyblock" && it.path.endsWith(".json")
|
||||
}
|
||||
val overrides = overrideFiles.mapNotNull {
|
||||
Firmament.tryDecodeJsonFromStream<ArmorOverride>(it.value.inputStream).getOrElse { ex ->
|
||||
logger.error("Failed to load armor texture override at ${it.key}", ex)
|
||||
null
|
||||
}
|
||||
}
|
||||
val associatedMap = overrides.flatMap { obj -> obj.itemIds.map { it to obj } }
|
||||
.toMap()
|
||||
return associatedMap
|
||||
}
|
||||
|
||||
override fun apply(prepared: Map<String, ArmorOverride>, manager: ResourceManager, profiler: Profiler) {
|
||||
bakedOverrides.clear()
|
||||
prepared.forEach { it.value.bake() }
|
||||
overrides = prepared
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun overrideArmor(itemStack: ItemStack): Optional<Identifier> {
|
||||
return overrideCache.invoke(itemStack)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun overrideArmorLayer(id: Identifier): EquipmentModel? {
|
||||
return bakedOverrides[id]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ object CustomGlobalTextures : SinglePreparationResourceReloader<CustomGlobalText
|
||||
it.overrides
|
||||
.asSequence()
|
||||
.filter { it.predicate.test(stack) }
|
||||
.map { models.modelManager.getModel(ModelIdentifier(it.model, "inventory")) }
|
||||
.map { models.getModel(it.model) }
|
||||
.firstOrNull()
|
||||
}
|
||||
.intoOptional()
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import net.minecraft.client.render.model.Baker
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
interface JsonUnbakedModelFirmExtra {
|
||||
fun storeExtraBaker_firmament(baker: Baker)
|
||||
|
||||
fun setHeadModel_firmament(identifier: Identifier?)
|
||||
fun getHeadModel_firmament(): Identifier?
|
||||
|
||||
@@ -3,7 +3,6 @@ package moe.nea.firmament.features.texturepack
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
import moe.nea.firmament.util.ErrorUtil
|
||||
import moe.nea.firmament.util.assertNotNullOr
|
||||
|
||||
data class TintOverrides(
|
||||
val layerMap: Map<Int, TintOverride> = mapOf()
|
||||
@@ -14,21 +13,22 @@ data class TintOverrides(
|
||||
val EMPTY = TintOverrides()
|
||||
private val threadLocal = object : ThreadLocal<TintOverrides>() {}
|
||||
fun enter(overrides: TintOverrides?) {
|
||||
ErrorUtil.softCheck("Double entered tintOverrides") {
|
||||
threadLocal.get() == null
|
||||
}
|
||||
ErrorUtil.softCheck("Double entered tintOverrides",
|
||||
threadLocal.get() == null)
|
||||
threadLocal.set(overrides ?: EMPTY)
|
||||
}
|
||||
|
||||
fun exit(overrides: TintOverrides?) {
|
||||
ErrorUtil.softCheck("Exited with non matching enter tintOverrides") {
|
||||
threadLocal.get() == (overrides ?: EMPTY)
|
||||
}
|
||||
ErrorUtil.softCheck("Exited with non matching enter tintOverrides",
|
||||
threadLocal.get() == (overrides ?: EMPTY))
|
||||
threadLocal.remove()
|
||||
}
|
||||
|
||||
fun getCurrentOverrides() =
|
||||
assertNotNullOr(threadLocal.get(), "Got current tintOverrides without entering") { EMPTY }
|
||||
fun getCurrentOverrides(): TintOverrides {
|
||||
return ErrorUtil.notNullOr(threadLocal.get(), "Got current tintOverrides without entering") {
|
||||
EMPTY
|
||||
}
|
||||
}
|
||||
|
||||
fun parse(jsonObject: JsonObject): TintOverrides {
|
||||
val map = mutableMapOf<Int, TintOverride>()
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.github.moulberry.repo.data.Coordinate
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.serializer
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.events.ProcessChatEvent
|
||||
@@ -98,9 +99,8 @@ object FairySouls : FirmamentFeature {
|
||||
fun onWorldRender(it: WorldRenderLastEvent) {
|
||||
if (!TConfig.displaySouls) return
|
||||
renderInWorld(it) {
|
||||
color(1F, 1F, 0F, 0.8F)
|
||||
currentMissingSouls.forEach {
|
||||
block(it.blockPos)
|
||||
block(it.blockPos, 0x80FFFF00.toInt())
|
||||
}
|
||||
color(1f, 0f, 1f, 1f)
|
||||
currentLocationSouls.forEach {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.features.world
|
||||
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType
|
||||
@@ -12,6 +10,7 @@ import kotlin.collections.set
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import net.minecraft.command.argument.BlockPosArgumentType
|
||||
import net.minecraft.server.command.CommandOutput
|
||||
import net.minecraft.server.command.ServerCommandSource
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.math.BlockPos
|
||||
@@ -35,263 +34,273 @@ import moe.nea.firmament.util.TimeMark
|
||||
import moe.nea.firmament.util.render.RenderInWorldContext
|
||||
|
||||
object Waypoints : FirmamentFeature {
|
||||
override val identifier: String
|
||||
get() = "waypoints"
|
||||
override val identifier: String
|
||||
get() = "waypoints"
|
||||
|
||||
object TConfig : ManagedConfig(identifier, Category.MINING) { // TODO: add to misc
|
||||
val tempWaypointDuration by duration("temp-waypoint-duration", 0.seconds, 1.hours) { 30.seconds }
|
||||
val showIndex by toggle("show-index") { true }
|
||||
val skipToNearest by toggle("skip-to-nearest") { false }
|
||||
// TODO: look ahead size
|
||||
}
|
||||
object TConfig : ManagedConfig(identifier, Category.MINING) { // TODO: add to misc
|
||||
val tempWaypointDuration by duration("temp-waypoint-duration", 0.seconds, 1.hours) { 30.seconds }
|
||||
val showIndex by toggle("show-index") { true }
|
||||
val skipToNearest by toggle("skip-to-nearest") { false }
|
||||
// TODO: look ahead size
|
||||
}
|
||||
|
||||
data class TemporaryWaypoint(
|
||||
val pos: BlockPos,
|
||||
val postedAt: TimeMark,
|
||||
)
|
||||
data class TemporaryWaypoint(
|
||||
val pos: BlockPos,
|
||||
val postedAt: TimeMark,
|
||||
)
|
||||
|
||||
override val config get() = TConfig
|
||||
override val config get() = TConfig
|
||||
|
||||
val temporaryPlayerWaypointList = mutableMapOf<String, TemporaryWaypoint>()
|
||||
val temporaryPlayerWaypointMatcher = "(?i)x: (-?[0-9]+),? y: (-?[0-9]+),? z: (-?[0-9]+)".toPattern()
|
||||
val temporaryPlayerWaypointList = mutableMapOf<String, TemporaryWaypoint>()
|
||||
val temporaryPlayerWaypointMatcher = "(?i)x: (-?[0-9]+),? y: (-?[0-9]+),? z: (-?[0-9]+)".toPattern()
|
||||
|
||||
val waypoints = mutableListOf<BlockPos>()
|
||||
var ordered = false
|
||||
var orderedIndex = 0
|
||||
val waypoints = mutableListOf<BlockPos>()
|
||||
var ordered = false
|
||||
var orderedIndex = 0
|
||||
|
||||
@Serializable
|
||||
data class ColeWeightWaypoint(
|
||||
val x: Int,
|
||||
val y: Int,
|
||||
val z: Int,
|
||||
val r: Int = 0,
|
||||
val g: Int = 0,
|
||||
val b: Int = 0,
|
||||
)
|
||||
@Serializable
|
||||
data class ColeWeightWaypoint(
|
||||
val x: Int,
|
||||
val y: Int,
|
||||
val z: Int,
|
||||
val r: Int = 0,
|
||||
val g: Int = 0,
|
||||
val b: Int = 0,
|
||||
)
|
||||
|
||||
@Subscribe
|
||||
fun onRenderOrderedWaypoints(event: WorldRenderLastEvent) {
|
||||
if (waypoints.isEmpty()) return
|
||||
RenderInWorldContext.renderInWorld(event) {
|
||||
if (!ordered) {
|
||||
waypoints.withIndex().forEach {
|
||||
color(0f, 0.3f, 0.7f, 0.5f)
|
||||
block(it.value)
|
||||
color(1f, 1f, 1f, 1f)
|
||||
if (TConfig.showIndex)
|
||||
withFacingThePlayer(it.value.toCenterPos()) {
|
||||
text(Text.literal(it.index.toString()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
orderedIndex %= waypoints.size
|
||||
val firstColor = Color.ofRGBA(0, 200, 40, 180)
|
||||
color(firstColor)
|
||||
tracer(waypoints[orderedIndex].toCenterPos(), lineWidth = 3f)
|
||||
waypoints.withIndex().toList()
|
||||
.wrappingWindow(orderedIndex, 3)
|
||||
.zip(
|
||||
listOf(
|
||||
firstColor,
|
||||
Color.ofRGBA(180, 200, 40, 150),
|
||||
Color.ofRGBA(180, 80, 20, 140),
|
||||
)
|
||||
)
|
||||
.reversed()
|
||||
.forEach { (waypoint, col) ->
|
||||
val (index, pos) = waypoint
|
||||
color(col)
|
||||
block(pos)
|
||||
color(1f, 1f, 1f, 1f)
|
||||
if (TConfig.showIndex)
|
||||
withFacingThePlayer(pos.toCenterPos()) {
|
||||
text(Text.literal(index.toString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Subscribe
|
||||
fun onRenderOrderedWaypoints(event: WorldRenderLastEvent) {
|
||||
if (waypoints.isEmpty()) return
|
||||
RenderInWorldContext.renderInWorld(event) {
|
||||
if (!ordered) {
|
||||
waypoints.withIndex().forEach {
|
||||
block(it.value, 0x800050A0.toInt())
|
||||
if (TConfig.showIndex)
|
||||
withFacingThePlayer(it.value.toCenterPos()) {
|
||||
text(Text.literal(it.index.toString()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
orderedIndex %= waypoints.size
|
||||
val firstColor = Color.ofRGBA(0, 200, 40, 180)
|
||||
color(firstColor)
|
||||
tracer(waypoints[orderedIndex].toCenterPos(), lineWidth = 3f)
|
||||
waypoints.withIndex().toList()
|
||||
.wrappingWindow(orderedIndex, 3)
|
||||
.zip(
|
||||
listOf(
|
||||
firstColor,
|
||||
Color.ofRGBA(180, 200, 40, 150),
|
||||
Color.ofRGBA(180, 80, 20, 140),
|
||||
)
|
||||
)
|
||||
.reversed()
|
||||
.forEach { (waypoint, col) ->
|
||||
val (index, pos) = waypoint
|
||||
block(pos, col.color)
|
||||
if (TConfig.showIndex)
|
||||
withFacingThePlayer(pos.toCenterPos()) {
|
||||
text(Text.literal(index.toString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onTick(event: TickEvent) {
|
||||
if (waypoints.isEmpty() || !ordered) return
|
||||
orderedIndex %= waypoints.size
|
||||
val p = MC.player?.pos ?: return
|
||||
if (TConfig.skipToNearest) {
|
||||
orderedIndex =
|
||||
(waypoints.withIndex().minBy { it.value.getSquaredDistance(p) }.index + 1) % waypoints.size
|
||||
} else {
|
||||
if (waypoints[orderedIndex].isWithinDistance(p, 3.0)) {
|
||||
orderedIndex = (orderedIndex + 1) % waypoints.size
|
||||
}
|
||||
}
|
||||
}
|
||||
@Subscribe
|
||||
fun onTick(event: TickEvent) {
|
||||
if (waypoints.isEmpty() || !ordered) return
|
||||
orderedIndex %= waypoints.size
|
||||
val p = MC.player?.pos ?: return
|
||||
if (TConfig.skipToNearest) {
|
||||
orderedIndex =
|
||||
(waypoints.withIndex().minBy { it.value.getSquaredDistance(p) }.index + 1) % waypoints.size
|
||||
} else {
|
||||
if (waypoints[orderedIndex].isWithinDistance(p, 3.0)) {
|
||||
orderedIndex = (orderedIndex + 1) % waypoints.size
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onProcessChat(it: ProcessChatEvent) {
|
||||
val matcher = temporaryPlayerWaypointMatcher.matcher(it.unformattedString)
|
||||
if (it.nameHeuristic != null && TConfig.tempWaypointDuration > 0.seconds && matcher.find()) {
|
||||
temporaryPlayerWaypointList[it.nameHeuristic] = TemporaryWaypoint(
|
||||
BlockPos(
|
||||
matcher.group(1).toInt(),
|
||||
matcher.group(2).toInt(),
|
||||
matcher.group(3).toInt(),
|
||||
),
|
||||
TimeMark.now()
|
||||
)
|
||||
}
|
||||
}
|
||||
@Subscribe
|
||||
fun onProcessChat(it: ProcessChatEvent) {
|
||||
val matcher = temporaryPlayerWaypointMatcher.matcher(it.unformattedString)
|
||||
if (it.nameHeuristic != null && TConfig.tempWaypointDuration > 0.seconds && matcher.find()) {
|
||||
temporaryPlayerWaypointList[it.nameHeuristic] = TemporaryWaypoint(
|
||||
BlockPos(
|
||||
matcher.group(1).toInt(),
|
||||
matcher.group(2).toInt(),
|
||||
matcher.group(3).toInt(),
|
||||
),
|
||||
TimeMark.now()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onCommand(event: CommandEvent.SubCommand) {
|
||||
event.subcommand("waypoint") {
|
||||
thenArgument("pos", BlockPosArgumentType.blockPos()) { pos ->
|
||||
thenExecute {
|
||||
val position = pos.get(this).toAbsoluteBlockPos(source.asFakeServer())
|
||||
waypoints.add(position)
|
||||
source.sendFeedback(
|
||||
Text.stringifiedTranslatable(
|
||||
"firmament.command.waypoint.added",
|
||||
position.x,
|
||||
position.y,
|
||||
position.z
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
event.subcommand("waypoints") {
|
||||
thenLiteral("clear") {
|
||||
thenExecute {
|
||||
waypoints.clear()
|
||||
source.sendFeedback(Text.translatable("firmament.command.waypoint.clear"))
|
||||
}
|
||||
}
|
||||
thenLiteral("toggleordered") {
|
||||
thenExecute {
|
||||
ordered = !ordered
|
||||
if (ordered) {
|
||||
val p = MC.player?.pos ?: Vec3d.ZERO
|
||||
orderedIndex =
|
||||
waypoints.withIndex().minByOrNull { it.value.getSquaredDistance(p) }?.index ?: 0
|
||||
}
|
||||
source.sendFeedback(Text.translatable("firmament.command.waypoint.ordered.toggle.$ordered"))
|
||||
}
|
||||
}
|
||||
thenLiteral("skip") {
|
||||
thenExecute {
|
||||
if (ordered && waypoints.isNotEmpty()) {
|
||||
orderedIndex = (orderedIndex + 1) % waypoints.size
|
||||
source.sendFeedback(Text.translatable("firmament.command.waypoint.skip"))
|
||||
} else {
|
||||
source.sendError(Text.translatable("firmament.command.waypoint.skip.error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
thenLiteral("remove") {
|
||||
thenArgument("index", IntegerArgumentType.integer(0)) { indexArg ->
|
||||
thenExecute {
|
||||
val index = get(indexArg)
|
||||
if (index in waypoints.indices) {
|
||||
waypoints.removeAt(index)
|
||||
source.sendFeedback(Text.stringifiedTranslatable(
|
||||
"firmament.command.waypoint.remove",
|
||||
index))
|
||||
} else {
|
||||
source.sendError(Text.stringifiedTranslatable("firmament.command.waypoint.remove.error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
thenLiteral("import") {
|
||||
thenExecute {
|
||||
val contents = ClipboardUtils.getTextContents()
|
||||
val data = try {
|
||||
Firmament.json.decodeFromString<List<ColeWeightWaypoint>>(contents)
|
||||
} catch (ex: Exception) {
|
||||
Firmament.logger.error("Could not load waypoints from clipboard", ex)
|
||||
source.sendError(Text.translatable("firmament.command.waypoint.import.error"))
|
||||
return@thenExecute
|
||||
}
|
||||
waypoints.clear()
|
||||
data.mapTo(waypoints) { BlockPos(it.x, it.y, it.z) }
|
||||
source.sendFeedback(
|
||||
Text.stringifiedTranslatable(
|
||||
"firmament.command.waypoint.import",
|
||||
data.size
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Subscribe
|
||||
fun onCommand(event: CommandEvent.SubCommand) {
|
||||
event.subcommand("waypoint") {
|
||||
thenArgument("pos", BlockPosArgumentType.blockPos()) { pos ->
|
||||
thenExecute {
|
||||
val position = pos.get(this).toAbsoluteBlockPos(source.asFakeServer())
|
||||
waypoints.add(position)
|
||||
source.sendFeedback(
|
||||
Text.stringifiedTranslatable(
|
||||
"firmament.command.waypoint.added",
|
||||
position.x,
|
||||
position.y,
|
||||
position.z
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
event.subcommand("waypoints") {
|
||||
thenLiteral("clear") {
|
||||
thenExecute {
|
||||
waypoints.clear()
|
||||
source.sendFeedback(Text.translatable("firmament.command.waypoint.clear"))
|
||||
}
|
||||
}
|
||||
thenLiteral("toggleordered") {
|
||||
thenExecute {
|
||||
ordered = !ordered
|
||||
if (ordered) {
|
||||
val p = MC.player?.pos ?: Vec3d.ZERO
|
||||
orderedIndex =
|
||||
waypoints.withIndex().minByOrNull { it.value.getSquaredDistance(p) }?.index ?: 0
|
||||
}
|
||||
source.sendFeedback(Text.translatable("firmament.command.waypoint.ordered.toggle.$ordered"))
|
||||
}
|
||||
}
|
||||
thenLiteral("skip") {
|
||||
thenExecute {
|
||||
if (ordered && waypoints.isNotEmpty()) {
|
||||
orderedIndex = (orderedIndex + 1) % waypoints.size
|
||||
source.sendFeedback(Text.translatable("firmament.command.waypoint.skip"))
|
||||
} else {
|
||||
source.sendError(Text.translatable("firmament.command.waypoint.skip.error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
thenLiteral("remove") {
|
||||
thenArgument("index", IntegerArgumentType.integer(0)) { indexArg ->
|
||||
thenExecute {
|
||||
val index = get(indexArg)
|
||||
if (index in waypoints.indices) {
|
||||
waypoints.removeAt(index)
|
||||
source.sendFeedback(Text.stringifiedTranslatable(
|
||||
"firmament.command.waypoint.remove",
|
||||
index))
|
||||
} else {
|
||||
source.sendError(Text.stringifiedTranslatable("firmament.command.waypoint.remove.error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
thenLiteral("import") {
|
||||
thenExecute {
|
||||
val contents = ClipboardUtils.getTextContents()
|
||||
val data = try {
|
||||
Firmament.json.decodeFromString<List<ColeWeightWaypoint>>(contents)
|
||||
} catch (ex: Exception) {
|
||||
Firmament.logger.error("Could not load waypoints from clipboard", ex)
|
||||
source.sendError(Text.translatable("firmament.command.waypoint.import.error"))
|
||||
return@thenExecute
|
||||
}
|
||||
waypoints.clear()
|
||||
data.mapTo(waypoints) { BlockPos(it.x, it.y, it.z) }
|
||||
source.sendFeedback(
|
||||
Text.stringifiedTranslatable(
|
||||
"firmament.command.waypoint.import",
|
||||
data.size
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onRenderTemporaryWaypoints(event: WorldRenderLastEvent) {
|
||||
temporaryPlayerWaypointList.entries.removeIf { it.value.postedAt.passedTime() > TConfig.tempWaypointDuration }
|
||||
if (temporaryPlayerWaypointList.isEmpty()) return
|
||||
RenderInWorldContext.renderInWorld(event) {
|
||||
color(1f, 1f, 0f, 1f)
|
||||
temporaryPlayerWaypointList.forEach { (player, waypoint) ->
|
||||
block(waypoint.pos)
|
||||
}
|
||||
color(1f, 1f, 1f, 1f)
|
||||
temporaryPlayerWaypointList.forEach { (player, waypoint) ->
|
||||
val skin =
|
||||
MC.networkHandler?.listedPlayerListEntries?.find { it.profile.name == player }
|
||||
?.skinTextures
|
||||
?.texture
|
||||
withFacingThePlayer(waypoint.pos.toCenterPos()) {
|
||||
waypoint(waypoint.pos, Text.stringifiedTranslatable("firmament.waypoint.temporary", player))
|
||||
if (skin != null) {
|
||||
matrixStack.translate(0F, -20F, 0F)
|
||||
// Head front
|
||||
texture(
|
||||
skin, 16, 16,
|
||||
1 / 8f, 1 / 8f,
|
||||
2 / 8f, 2 / 8f,
|
||||
)
|
||||
// Head overlay
|
||||
texture(
|
||||
skin, 16, 16,
|
||||
5 / 8f, 1 / 8f,
|
||||
6 / 8f, 2 / 8f,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Subscribe
|
||||
fun onRenderTemporaryWaypoints(event: WorldRenderLastEvent) {
|
||||
temporaryPlayerWaypointList.entries.removeIf { it.value.postedAt.passedTime() > TConfig.tempWaypointDuration }
|
||||
if (temporaryPlayerWaypointList.isEmpty()) return
|
||||
RenderInWorldContext.renderInWorld(event) {
|
||||
temporaryPlayerWaypointList.forEach { (player, waypoint) ->
|
||||
block(waypoint.pos, 0xFFFFFF00.toInt())
|
||||
}
|
||||
temporaryPlayerWaypointList.forEach { (player, waypoint) ->
|
||||
val skin =
|
||||
MC.networkHandler?.listedPlayerListEntries?.find { it.profile.name == player }
|
||||
?.skinTextures
|
||||
?.texture
|
||||
withFacingThePlayer(waypoint.pos.toCenterPos()) {
|
||||
waypoint(waypoint.pos, Text.stringifiedTranslatable("firmament.waypoint.temporary", player))
|
||||
if (skin != null) {
|
||||
matrixStack.translate(0F, -20F, 0F)
|
||||
// Head front
|
||||
texture(
|
||||
skin, 16, 16,
|
||||
1 / 8f, 1 / 8f,
|
||||
2 / 8f, 2 / 8f,
|
||||
)
|
||||
// Head overlay
|
||||
texture(
|
||||
skin, 16, 16,
|
||||
5 / 8f, 1 / 8f,
|
||||
6 / 8f, 2 / 8f,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onWorldReady(event: WorldReadyEvent) {
|
||||
temporaryPlayerWaypointList.clear()
|
||||
}
|
||||
@Subscribe
|
||||
fun onWorldReady(event: WorldReadyEvent) {
|
||||
temporaryPlayerWaypointList.clear()
|
||||
}
|
||||
}
|
||||
|
||||
fun <E> List<E>.wrappingWindow(startIndex: Int, windowSize: Int): List<E> {
|
||||
val result = ArrayList<E>(windowSize)
|
||||
if (startIndex + windowSize < size) {
|
||||
result.addAll(subList(startIndex, startIndex + windowSize))
|
||||
} else {
|
||||
result.addAll(subList(startIndex, size))
|
||||
result.addAll(subList(0, minOf(windowSize - (size - startIndex), startIndex)))
|
||||
}
|
||||
return result
|
||||
val result = ArrayList<E>(windowSize)
|
||||
if (startIndex + windowSize < size) {
|
||||
result.addAll(subList(startIndex, startIndex + windowSize))
|
||||
} else {
|
||||
result.addAll(subList(startIndex, size))
|
||||
result.addAll(subList(0, minOf(windowSize - (size - startIndex), startIndex)))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
fun FabricClientCommandSource.asFakeServer(): ServerCommandSource {
|
||||
val source = this
|
||||
return ServerCommandSource(
|
||||
source.player,
|
||||
source.position,
|
||||
source.rotation,
|
||||
null,
|
||||
0,
|
||||
"FakeServerCommandSource",
|
||||
Text.literal("FakeServerCommandSource"),
|
||||
null,
|
||||
source.player
|
||||
)
|
||||
val source = this
|
||||
return ServerCommandSource(
|
||||
object : CommandOutput {
|
||||
override fun sendMessage(message: Text?) {
|
||||
source.player.sendMessage(message, false)
|
||||
}
|
||||
|
||||
override fun shouldReceiveFeedback(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun shouldTrackOutput(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun shouldBroadcastConsoleToOps(): Boolean {
|
||||
return true
|
||||
}
|
||||
},
|
||||
source.position,
|
||||
source.rotation,
|
||||
null,
|
||||
0,
|
||||
"FakeServerCommandSource",
|
||||
Text.literal("FakeServerCommandSource"),
|
||||
null,
|
||||
source.player
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
package moe.nea.firmament.gui
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
@@ -10,116 +9,115 @@ import io.github.notenoughupdates.moulconfig.observer.GetSetter
|
||||
import io.github.notenoughupdates.moulconfig.platform.ModernRenderContext
|
||||
import me.shedaniel.math.Color
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.util.Identifier
|
||||
import moe.nea.firmament.Firmament
|
||||
|
||||
class BarComponent(
|
||||
val progress: GetSetter<Double>, val total: GetSetter<Double>,
|
||||
val fillColor: Color,
|
||||
val emptyColor: Color,
|
||||
val progress: GetSetter<Double>, val total: GetSetter<Double>,
|
||||
val fillColor: Color,
|
||||
val emptyColor: Color,
|
||||
) : GuiComponent() {
|
||||
override fun getWidth(): Int {
|
||||
return 80
|
||||
}
|
||||
override fun getWidth(): Int {
|
||||
return 80
|
||||
}
|
||||
|
||||
override fun getHeight(): Int {
|
||||
return 8
|
||||
}
|
||||
override fun getHeight(): Int {
|
||||
return 8
|
||||
}
|
||||
|
||||
data class Texture(
|
||||
val identifier: Identifier,
|
||||
val u1: Float, val v1: Float,
|
||||
val u2: Float, val v2: Float,
|
||||
) {
|
||||
fun draw(context: DrawContext, x: Int, y: Int, width: Int, height: Int, color: Color) {
|
||||
context.drawTexturedQuad(
|
||||
identifier,
|
||||
x, y, x + width, x + height, 0,
|
||||
u1, u2, v1, v2,
|
||||
color.red / 255F,
|
||||
color.green / 255F,
|
||||
color.blue / 255F,
|
||||
color.alpha / 255F,
|
||||
)
|
||||
}
|
||||
}
|
||||
data class Texture(
|
||||
val identifier: Identifier,
|
||||
val u1: Float, val v1: Float,
|
||||
val u2: Float, val v2: Float,
|
||||
) {
|
||||
fun draw(context: DrawContext, x: Int, y: Int, width: Int, height: Int, color: Color) {
|
||||
context.drawTexturedQuad(
|
||||
RenderLayer::getGuiTextured,
|
||||
identifier,
|
||||
x, y, x + width, x + height,
|
||||
u1, u2, v1, v2,
|
||||
color.color
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val resource = Firmament.identifier("textures/gui/bar.png")
|
||||
val left = Texture(resource, 0 / 64F, 0 / 64F, 4 / 64F, 8 / 64F)
|
||||
val middle = Texture(resource, 4 / 64F, 0 / 64F, 8 / 64F, 8 / 64F)
|
||||
val right = Texture(resource, 8 / 64F, 0 / 64F, 12 / 64F, 8 / 64F)
|
||||
val segmentOverlay = Texture(resource, 12 / 64F, 0 / 64F, 15 / 64F, 8 / 64F)
|
||||
}
|
||||
companion object {
|
||||
val resource = Firmament.identifier("textures/gui/bar.png")
|
||||
val left = Texture(resource, 0 / 64F, 0 / 64F, 4 / 64F, 8 / 64F)
|
||||
val middle = Texture(resource, 4 / 64F, 0 / 64F, 8 / 64F, 8 / 64F)
|
||||
val right = Texture(resource, 8 / 64F, 0 / 64F, 12 / 64F, 8 / 64F)
|
||||
val segmentOverlay = Texture(resource, 12 / 64F, 0 / 64F, 15 / 64F, 8 / 64F)
|
||||
}
|
||||
|
||||
private fun drawSection(
|
||||
context: DrawContext,
|
||||
texture: Texture,
|
||||
x: Int,
|
||||
y: Int,
|
||||
width: Int,
|
||||
sectionStart: Double,
|
||||
sectionEnd: Double
|
||||
) {
|
||||
if (sectionEnd < progress.get() && width == 4) {
|
||||
texture.draw(context, x, y, 4, 8, fillColor)
|
||||
return
|
||||
}
|
||||
if (sectionStart > progress.get() && width == 4) {
|
||||
texture.draw(context, x, y, 4, 8, emptyColor)
|
||||
return
|
||||
}
|
||||
val increasePerPixel = (sectionEnd - sectionStart) / width
|
||||
var valueAtPixel = sectionStart
|
||||
for (i in (0 until width)) {
|
||||
val newTex =
|
||||
Texture(texture.identifier, texture.u1 + i / 64F, texture.v1, texture.u1 + (i + 1) / 64F, texture.v2)
|
||||
newTex.draw(
|
||||
context, x + i, y, 1, 8,
|
||||
if (valueAtPixel < progress.get()) fillColor else emptyColor
|
||||
)
|
||||
valueAtPixel += increasePerPixel
|
||||
}
|
||||
}
|
||||
private fun drawSection(
|
||||
context: DrawContext,
|
||||
texture: Texture,
|
||||
x: Int,
|
||||
y: Int,
|
||||
width: Int,
|
||||
sectionStart: Double,
|
||||
sectionEnd: Double
|
||||
) {
|
||||
if (sectionEnd < progress.get() && width == 4) {
|
||||
texture.draw(context, x, y, 4, 8, fillColor)
|
||||
return
|
||||
}
|
||||
if (sectionStart > progress.get() && width == 4) {
|
||||
texture.draw(context, x, y, 4, 8, emptyColor)
|
||||
return
|
||||
}
|
||||
val increasePerPixel = (sectionEnd - sectionStart) / width
|
||||
var valueAtPixel = sectionStart
|
||||
for (i in (0 until width)) {
|
||||
val newTex =
|
||||
Texture(texture.identifier, texture.u1 + i / 64F, texture.v1, texture.u1 + (i + 1) / 64F, texture.v2)
|
||||
newTex.draw(
|
||||
context, x + i, y, 1, 8,
|
||||
if (valueAtPixel < progress.get()) fillColor else emptyColor
|
||||
)
|
||||
valueAtPixel += increasePerPixel
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(context: GuiImmediateContext) {
|
||||
val renderContext = (context.renderContext as ModernRenderContext).drawContext
|
||||
var i = 0
|
||||
val x = 0
|
||||
val y = 0
|
||||
while (i < context.width - 4) {
|
||||
drawSection(
|
||||
renderContext,
|
||||
if (i == 0) left else middle,
|
||||
x + i, y,
|
||||
(context.width - (i + 4)).coerceAtMost(4),
|
||||
i * total.get() / context.width, (i + 4) * total.get() / context.width
|
||||
)
|
||||
i += 4
|
||||
}
|
||||
drawSection(
|
||||
renderContext,
|
||||
right,
|
||||
x + context.width - 4,
|
||||
y,
|
||||
4,
|
||||
(context.width - 4) * total.get() / context.width,
|
||||
total.get()
|
||||
)
|
||||
RenderSystem.setShaderColor(1F, 1F, 1F, 1F)
|
||||
override fun render(context: GuiImmediateContext) {
|
||||
val renderContext = (context.renderContext as ModernRenderContext).drawContext
|
||||
var i = 0
|
||||
val x = 0
|
||||
val y = 0
|
||||
while (i < context.width - 4) {
|
||||
drawSection(
|
||||
renderContext,
|
||||
if (i == 0) left else middle,
|
||||
x + i, y,
|
||||
(context.width - (i + 4)).coerceAtMost(4),
|
||||
i * total.get() / context.width, (i + 4) * total.get() / context.width
|
||||
)
|
||||
i += 4
|
||||
}
|
||||
drawSection(
|
||||
renderContext,
|
||||
right,
|
||||
x + context.width - 4,
|
||||
y,
|
||||
4,
|
||||
(context.width - 4) * total.get() / context.width,
|
||||
total.get()
|
||||
)
|
||||
RenderSystem.setShaderColor(1F, 1F, 1F, 1F)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun Identifier.toMoulConfig(): MyResourceLocation {
|
||||
return MyResourceLocation(this.namespace, this.path)
|
||||
return MyResourceLocation(this.namespace, this.path)
|
||||
}
|
||||
|
||||
fun RenderContext.color(color: Color) {
|
||||
color(color.red, color.green, color.blue, color.alpha)
|
||||
color(color.red, color.green, color.blue, color.alpha)
|
||||
}
|
||||
|
||||
fun RenderContext.color(red: Int, green: Int, blue: Int, alpha: Int) {
|
||||
color(red / 255f, green / 255f, blue / 255f, alpha / 255f)
|
||||
color(red / 255f, green / 255f, blue / 255f, alpha / 255f)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
package moe.nea.firmament.gui.entity
|
||||
|
||||
import com.google.gson.Gson
|
||||
@@ -13,7 +12,9 @@ import net.minecraft.client.gui.screen.ingame.InventoryScreen
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.EntityType
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.SpawnReason
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.world.World
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.assertNotNullOr
|
||||
import moe.nea.firmament.util.iterate
|
||||
@@ -21,177 +22,177 @@ import moe.nea.firmament.util.openFirmamentResource
|
||||
import moe.nea.firmament.util.render.enableScissorWithTranslation
|
||||
|
||||
object EntityRenderer {
|
||||
val fakeWorld = FakeWorld()
|
||||
private fun <T : Entity> t(entityType: EntityType<T>): () -> T {
|
||||
return { entityType.create(fakeWorld)!! }
|
||||
}
|
||||
val fakeWorld: World get() = MC.lastWorld!!
|
||||
private fun <T : Entity> t(entityType: EntityType<T>): () -> T {
|
||||
return { entityType.create(fakeWorld, SpawnReason.LOAD)!! }
|
||||
}
|
||||
|
||||
val entityIds: Map<String, () -> LivingEntity> = mapOf(
|
||||
"Zombie" to t(EntityType.ZOMBIE),
|
||||
"Chicken" to t(EntityType.CHICKEN),
|
||||
"Slime" to t(EntityType.SLIME),
|
||||
"Wolf" to t(EntityType.WOLF),
|
||||
"Skeleton" to t(EntityType.SKELETON),
|
||||
"Creeper" to t(EntityType.CREEPER),
|
||||
"Ocelot" to t(EntityType.OCELOT),
|
||||
"Blaze" to t(EntityType.BLAZE),
|
||||
"Rabbit" to t(EntityType.RABBIT),
|
||||
"Sheep" to t(EntityType.SHEEP),
|
||||
"Horse" to t(EntityType.HORSE),
|
||||
"Eisengolem" to t(EntityType.IRON_GOLEM),
|
||||
"Silverfish" to t(EntityType.SILVERFISH),
|
||||
"Witch" to t(EntityType.WITCH),
|
||||
"Endermite" to t(EntityType.ENDERMITE),
|
||||
"Snowman" to t(EntityType.SNOW_GOLEM),
|
||||
"Villager" to t(EntityType.VILLAGER),
|
||||
"Guardian" to t(EntityType.GUARDIAN),
|
||||
"ArmorStand" to t(EntityType.ARMOR_STAND),
|
||||
"Squid" to t(EntityType.SQUID),
|
||||
"Bat" to t(EntityType.BAT),
|
||||
"Spider" to t(EntityType.SPIDER),
|
||||
"CaveSpider" to t(EntityType.CAVE_SPIDER),
|
||||
"Pigman" to t(EntityType.ZOMBIFIED_PIGLIN),
|
||||
"Ghast" to t(EntityType.GHAST),
|
||||
"MagmaCube" to t(EntityType.MAGMA_CUBE),
|
||||
"Wither" to t(EntityType.WITHER),
|
||||
"Enderman" to t(EntityType.ENDERMAN),
|
||||
"Mooshroom" to t(EntityType.MOOSHROOM),
|
||||
"WitherSkeleton" to t(EntityType.WITHER_SKELETON),
|
||||
"Cow" to t(EntityType.COW),
|
||||
"Dragon" to t(EntityType.ENDER_DRAGON),
|
||||
"Player" to { makeGuiPlayer(fakeWorld) },
|
||||
"Pig" to t(EntityType.PIG),
|
||||
"Giant" to t(EntityType.GIANT),
|
||||
)
|
||||
val entityModifiers: Map<String, EntityModifier> = mapOf(
|
||||
"playerdata" to ModifyPlayerSkin,
|
||||
"equipment" to ModifyEquipment,
|
||||
"riding" to ModifyRiding,
|
||||
"charged" to ModifyCharged,
|
||||
"witherdata" to ModifyWither,
|
||||
"invisible" to ModifyInvisible,
|
||||
"age" to ModifyAge,
|
||||
"horse" to ModifyHorse,
|
||||
"name" to ModifyName,
|
||||
)
|
||||
val entityIds: Map<String, () -> LivingEntity> = mapOf(
|
||||
"Zombie" to t(EntityType.ZOMBIE),
|
||||
"Chicken" to t(EntityType.CHICKEN),
|
||||
"Slime" to t(EntityType.SLIME),
|
||||
"Wolf" to t(EntityType.WOLF),
|
||||
"Skeleton" to t(EntityType.SKELETON),
|
||||
"Creeper" to t(EntityType.CREEPER),
|
||||
"Ocelot" to t(EntityType.OCELOT),
|
||||
"Blaze" to t(EntityType.BLAZE),
|
||||
"Rabbit" to t(EntityType.RABBIT),
|
||||
"Sheep" to t(EntityType.SHEEP),
|
||||
"Horse" to t(EntityType.HORSE),
|
||||
"Eisengolem" to t(EntityType.IRON_GOLEM),
|
||||
"Silverfish" to t(EntityType.SILVERFISH),
|
||||
"Witch" to t(EntityType.WITCH),
|
||||
"Endermite" to t(EntityType.ENDERMITE),
|
||||
"Snowman" to t(EntityType.SNOW_GOLEM),
|
||||
"Villager" to t(EntityType.VILLAGER),
|
||||
"Guardian" to t(EntityType.GUARDIAN),
|
||||
"ArmorStand" to t(EntityType.ARMOR_STAND),
|
||||
"Squid" to t(EntityType.SQUID),
|
||||
"Bat" to t(EntityType.BAT),
|
||||
"Spider" to t(EntityType.SPIDER),
|
||||
"CaveSpider" to t(EntityType.CAVE_SPIDER),
|
||||
"Pigman" to t(EntityType.ZOMBIFIED_PIGLIN),
|
||||
"Ghast" to t(EntityType.GHAST),
|
||||
"MagmaCube" to t(EntityType.MAGMA_CUBE),
|
||||
"Wither" to t(EntityType.WITHER),
|
||||
"Enderman" to t(EntityType.ENDERMAN),
|
||||
"Mooshroom" to t(EntityType.MOOSHROOM),
|
||||
"WitherSkeleton" to t(EntityType.WITHER_SKELETON),
|
||||
"Cow" to t(EntityType.COW),
|
||||
"Dragon" to t(EntityType.ENDER_DRAGON),
|
||||
"Player" to { makeGuiPlayer(fakeWorld) },
|
||||
"Pig" to t(EntityType.PIG),
|
||||
"Giant" to t(EntityType.GIANT),
|
||||
)
|
||||
val entityModifiers: Map<String, EntityModifier> = mapOf(
|
||||
"playerdata" to ModifyPlayerSkin,
|
||||
"equipment" to ModifyEquipment,
|
||||
"riding" to ModifyRiding,
|
||||
"charged" to ModifyCharged,
|
||||
"witherdata" to ModifyWither,
|
||||
"invisible" to ModifyInvisible,
|
||||
"age" to ModifyAge,
|
||||
"horse" to ModifyHorse,
|
||||
"name" to ModifyName,
|
||||
)
|
||||
|
||||
val logger = LogManager.getLogger("Firmament.Entity")
|
||||
fun applyModifiers(entityId: String, modifiers: List<JsonObject>): LivingEntity? {
|
||||
val entityType = assertNotNullOr(entityIds[entityId]) {
|
||||
logger.error("Could not create entity with id $entityId")
|
||||
return null
|
||||
}
|
||||
var entity = entityType()
|
||||
for (modifierJson in modifiers) {
|
||||
val modifier = assertNotNullOr(modifierJson["type"]?.asString?.let(entityModifiers::get)) {
|
||||
logger.error("Unknown modifier $modifierJson")
|
||||
return null
|
||||
}
|
||||
entity = modifier.apply(entity, modifierJson)
|
||||
}
|
||||
return entity
|
||||
}
|
||||
val logger = LogManager.getLogger("Firmament.Entity")
|
||||
fun applyModifiers(entityId: String, modifiers: List<JsonObject>): LivingEntity? {
|
||||
val entityType = assertNotNullOr(entityIds[entityId]) {
|
||||
logger.error("Could not create entity with id $entityId")
|
||||
return null
|
||||
}
|
||||
var entity = entityType()
|
||||
for (modifierJson in modifiers) {
|
||||
val modifier = assertNotNullOr(modifierJson["type"]?.asString?.let(entityModifiers::get)) {
|
||||
logger.error("Unknown modifier $modifierJson")
|
||||
return null
|
||||
}
|
||||
entity = modifier.apply(entity, modifierJson)
|
||||
}
|
||||
return entity
|
||||
}
|
||||
|
||||
fun constructEntity(info: JsonObject): LivingEntity? {
|
||||
val modifiers = (info["modifiers"] as JsonArray?)?.map { it.asJsonObject } ?: emptyList()
|
||||
val entityType = assertNotNullOr(info["entity"]?.asString) {
|
||||
logger.error("Missing entity type on entity object")
|
||||
return null
|
||||
}
|
||||
return applyModifiers(entityType, modifiers)
|
||||
}
|
||||
fun constructEntity(info: JsonObject): LivingEntity? {
|
||||
val modifiers = (info["modifiers"] as JsonArray?)?.map { it.asJsonObject } ?: emptyList()
|
||||
val entityType = assertNotNullOr(info["entity"]?.asString) {
|
||||
logger.error("Missing entity type on entity object")
|
||||
return null
|
||||
}
|
||||
return applyModifiers(entityType, modifiers)
|
||||
}
|
||||
|
||||
private val gson = Gson()
|
||||
fun constructEntity(location: Identifier): LivingEntity? {
|
||||
return constructEntity(
|
||||
gson.fromJson(
|
||||
location.openFirmamentResource().bufferedReader(), JsonObject::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
private val gson = Gson()
|
||||
fun constructEntity(location: Identifier): LivingEntity? {
|
||||
return constructEntity(
|
||||
gson.fromJson(
|
||||
location.openFirmamentResource().bufferedReader(), JsonObject::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun renderEntity(
|
||||
entity: LivingEntity,
|
||||
renderContext: DrawContext,
|
||||
posX: Int,
|
||||
posY: Int,
|
||||
mouseX: Float,
|
||||
mouseY: Float
|
||||
) {
|
||||
var bottomOffset = 0.0F
|
||||
var currentEntity = entity
|
||||
val maxSize = entity.iterate { it.firstPassenger as? LivingEntity }
|
||||
.map { it.height }
|
||||
.sum()
|
||||
while (true) {
|
||||
currentEntity.age = MC.player?.age ?: 0
|
||||
drawEntity(
|
||||
renderContext,
|
||||
posX,
|
||||
posY,
|
||||
posX + 50,
|
||||
posY + 80,
|
||||
minOf(2F / maxSize, 1F) * 30,
|
||||
-bottomOffset,
|
||||
mouseX,
|
||||
mouseY,
|
||||
currentEntity
|
||||
)
|
||||
val next = currentEntity.firstPassenger as? LivingEntity ?: break
|
||||
bottomOffset += currentEntity.getPassengerRidingPos(next).y.toFloat() * 0.75F
|
||||
currentEntity = next
|
||||
}
|
||||
}
|
||||
fun renderEntity(
|
||||
entity: LivingEntity,
|
||||
renderContext: DrawContext,
|
||||
posX: Int,
|
||||
posY: Int,
|
||||
mouseX: Float,
|
||||
mouseY: Float
|
||||
) {
|
||||
var bottomOffset = 0.0F
|
||||
var currentEntity = entity
|
||||
val maxSize = entity.iterate { it.firstPassenger as? LivingEntity }
|
||||
.map { it.height }
|
||||
.sum()
|
||||
while (true) {
|
||||
currentEntity.age = MC.player?.age ?: 0
|
||||
drawEntity(
|
||||
renderContext,
|
||||
posX,
|
||||
posY,
|
||||
posX + 50,
|
||||
posY + 80,
|
||||
minOf(2F / maxSize, 1F) * 30,
|
||||
-bottomOffset,
|
||||
mouseX,
|
||||
mouseY,
|
||||
currentEntity
|
||||
)
|
||||
val next = currentEntity.firstPassenger as? LivingEntity ?: break
|
||||
bottomOffset += currentEntity.getPassengerRidingPos(next).y.toFloat() * 0.75F
|
||||
currentEntity = next
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun drawEntity(
|
||||
context: DrawContext,
|
||||
x1: Int,
|
||||
y1: Int,
|
||||
x2: Int,
|
||||
y2: Int,
|
||||
size: Float,
|
||||
bottomOffset: Float,
|
||||
mouseX: Float,
|
||||
mouseY: Float,
|
||||
entity: LivingEntity
|
||||
) {
|
||||
context.enableScissorWithTranslation(x1.toFloat(), y1.toFloat(), x2.toFloat(), y2.toFloat())
|
||||
val centerX = (x1 + x2) / 2f
|
||||
val centerY = (y1 + y2) / 2f
|
||||
val targetYaw = atan(((centerX - mouseX) / 40.0f).toDouble()).toFloat()
|
||||
val targetPitch = atan(((centerY - mouseY) / 40.0f).toDouble()).toFloat()
|
||||
val rotateToFaceTheFront = Quaternionf().rotateZ(Math.PI.toFloat())
|
||||
val rotateToFaceTheCamera = Quaternionf().rotateX(targetPitch * 20.0f * (Math.PI.toFloat() / 180))
|
||||
rotateToFaceTheFront.mul(rotateToFaceTheCamera)
|
||||
val oldBodyYaw = entity.bodyYaw
|
||||
val oldYaw = entity.yaw
|
||||
val oldPitch = entity.pitch
|
||||
val oldPrevHeadYaw = entity.prevHeadYaw
|
||||
val oldHeadYaw = entity.headYaw
|
||||
entity.bodyYaw = 180.0f + targetYaw * 20.0f
|
||||
entity.yaw = 180.0f + targetYaw * 40.0f
|
||||
entity.pitch = -targetPitch * 20.0f
|
||||
entity.headYaw = entity.yaw
|
||||
entity.prevHeadYaw = entity.yaw
|
||||
val vector3f = Vector3f(0.0f, entity.height / 2.0f + bottomOffset, 0.0f)
|
||||
InventoryScreen.drawEntity(
|
||||
context,
|
||||
centerX,
|
||||
centerY,
|
||||
size,
|
||||
vector3f,
|
||||
rotateToFaceTheFront,
|
||||
rotateToFaceTheCamera,
|
||||
entity
|
||||
)
|
||||
entity.bodyYaw = oldBodyYaw
|
||||
entity.yaw = oldYaw
|
||||
entity.pitch = oldPitch
|
||||
entity.prevHeadYaw = oldPrevHeadYaw
|
||||
entity.headYaw = oldHeadYaw
|
||||
context.disableScissor()
|
||||
}
|
||||
fun drawEntity(
|
||||
context: DrawContext,
|
||||
x1: Int,
|
||||
y1: Int,
|
||||
x2: Int,
|
||||
y2: Int,
|
||||
size: Float,
|
||||
bottomOffset: Float,
|
||||
mouseX: Float,
|
||||
mouseY: Float,
|
||||
entity: LivingEntity
|
||||
) {
|
||||
context.enableScissorWithTranslation(x1.toFloat(), y1.toFloat(), x2.toFloat(), y2.toFloat())
|
||||
val centerX = (x1 + x2) / 2f
|
||||
val centerY = (y1 + y2) / 2f
|
||||
val targetYaw = atan(((centerX - mouseX) / 40.0f).toDouble()).toFloat()
|
||||
val targetPitch = atan(((centerY - mouseY) / 40.0f).toDouble()).toFloat()
|
||||
val rotateToFaceTheFront = Quaternionf().rotateZ(Math.PI.toFloat())
|
||||
val rotateToFaceTheCamera = Quaternionf().rotateX(targetPitch * 20.0f * (Math.PI.toFloat() / 180))
|
||||
rotateToFaceTheFront.mul(rotateToFaceTheCamera)
|
||||
val oldBodyYaw = entity.bodyYaw
|
||||
val oldYaw = entity.yaw
|
||||
val oldPitch = entity.pitch
|
||||
val oldPrevHeadYaw = entity.prevHeadYaw
|
||||
val oldHeadYaw = entity.headYaw
|
||||
entity.bodyYaw = 180.0f + targetYaw * 20.0f
|
||||
entity.yaw = 180.0f + targetYaw * 40.0f
|
||||
entity.pitch = -targetPitch * 20.0f
|
||||
entity.headYaw = entity.yaw
|
||||
entity.prevHeadYaw = entity.yaw
|
||||
val vector3f = Vector3f(0.0f, entity.height / 2.0f + bottomOffset, 0.0f)
|
||||
InventoryScreen.drawEntity(
|
||||
context,
|
||||
centerX,
|
||||
centerY,
|
||||
size,
|
||||
vector3f,
|
||||
rotateToFaceTheFront,
|
||||
rotateToFaceTheCamera,
|
||||
entity
|
||||
)
|
||||
entity.bodyYaw = oldBodyYaw
|
||||
entity.yaw = oldYaw
|
||||
entity.pitch = oldPitch
|
||||
entity.prevHeadYaw = oldPrevHeadYaw
|
||||
entity.headYaw = oldHeadYaw
|
||||
context.disableScissor()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,38 +1,37 @@
|
||||
|
||||
package moe.nea.firmament.gui.entity
|
||||
|
||||
import com.mojang.datafixers.util.Pair
|
||||
import com.mojang.serialization.Lifecycle
|
||||
import java.util.*
|
||||
import java.util.UUID
|
||||
import java.util.function.BooleanSupplier
|
||||
import java.util.function.Consumer
|
||||
import java.util.stream.Stream
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import kotlin.streams.asSequence
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.gui.screen.world.SelectWorldScreen
|
||||
import net.minecraft.component.type.MapIdComponent
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.damage.DamageSource
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.fluid.Fluid
|
||||
import net.minecraft.item.FuelRegistry
|
||||
import net.minecraft.item.map.MapState
|
||||
import net.minecraft.particle.ParticleEffect
|
||||
import net.minecraft.recipe.BrewingRecipeRegistry
|
||||
import net.minecraft.recipe.Ingredient
|
||||
import net.minecraft.recipe.RecipeManager
|
||||
import net.minecraft.registry.BuiltinRegistries
|
||||
import net.minecraft.recipe.RecipePropertySet
|
||||
import net.minecraft.recipe.StonecuttingRecipe
|
||||
import net.minecraft.recipe.display.CuttingRecipeDisplay
|
||||
import net.minecraft.registry.DynamicRegistryManager
|
||||
import net.minecraft.registry.Registry
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.registry.RegistryKey
|
||||
import net.minecraft.registry.RegistryKeys
|
||||
import net.minecraft.registry.RegistryWrapper
|
||||
import net.minecraft.registry.ServerDynamicRegistryType
|
||||
import net.minecraft.registry.entry.RegistryEntry
|
||||
import net.minecraft.registry.entry.RegistryEntryInfo
|
||||
import net.minecraft.registry.entry.RegistryEntryList
|
||||
import net.minecraft.registry.entry.RegistryEntryOwner
|
||||
import net.minecraft.registry.tag.TagKey
|
||||
import net.minecraft.resource.DataConfiguration
|
||||
import net.minecraft.resource.ResourcePackManager
|
||||
import net.minecraft.resource.featuretoggle.FeatureFlags
|
||||
import net.minecraft.resource.featuretoggle.FeatureSet
|
||||
import net.minecraft.scoreboard.Scoreboard
|
||||
import net.minecraft.server.SaveLoading
|
||||
import net.minecraft.server.command.CommandManager
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.sound.SoundEvent
|
||||
import net.minecraft.util.Identifier
|
||||
@@ -43,11 +42,8 @@ import net.minecraft.util.math.Box
|
||||
import net.minecraft.util.math.ChunkPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.minecraft.util.math.random.Random
|
||||
import net.minecraft.util.profiler.DummyProfiler
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.Difficulty
|
||||
import net.minecraft.world.GameRules
|
||||
import net.minecraft.world.MutableWorldProperties
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.biome.Biome
|
||||
@@ -59,430 +55,284 @@ import net.minecraft.world.chunk.EmptyChunk
|
||||
import net.minecraft.world.chunk.light.LightingProvider
|
||||
import net.minecraft.world.entity.EntityLookup
|
||||
import net.minecraft.world.event.GameEvent
|
||||
import net.minecraft.world.explosion.ExplosionBehavior
|
||||
import net.minecraft.world.tick.OrderedTick
|
||||
import net.minecraft.world.tick.QueryableTickScheduler
|
||||
import net.minecraft.world.tick.TickManager
|
||||
|
||||
fun <T> makeRegistry(registryWrapper: RegistryWrapper.Impl<T>, key: RegistryKey<out Registry<T>>): Registry<T> {
|
||||
val inverseLookup = registryWrapper.streamEntries()
|
||||
.asSequence().map { it.value() to it.registryKey() }
|
||||
.toMap()
|
||||
val idLookup = registryWrapper.streamEntries()
|
||||
.asSequence()
|
||||
.map { it.registryKey() }
|
||||
.withIndex()
|
||||
.associate { it.value to it.index }
|
||||
val map = registryWrapper.streamEntries().asSequence().map { it.registryKey() to it.value() }.toMap(mutableMapOf())
|
||||
val inverseIdLookup = idLookup.asIterable().associate { (k, v) -> v to k }
|
||||
return object : Registry<T> {
|
||||
override fun get(key: RegistryKey<T>?): T? {
|
||||
return registryWrapper.getOptional(key).getOrNull()?.value()
|
||||
}
|
||||
|
||||
override fun iterator(): MutableIterator<T> {
|
||||
return object : MutableIterator<T> {
|
||||
val iterator = registryWrapper.streamEntries().iterator()
|
||||
override fun hasNext(): Boolean {
|
||||
return iterator.hasNext()
|
||||
}
|
||||
|
||||
override fun next(): T {
|
||||
return iterator.next().value()
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRawId(value: T?): Int {
|
||||
return idLookup[inverseLookup[value ?: return -1] ?: return -1] ?: return -1
|
||||
}
|
||||
|
||||
override fun get(id: Identifier?): T? {
|
||||
return get(RegistryKey.of(key, id))
|
||||
}
|
||||
|
||||
override fun get(index: Int): T? {
|
||||
return get(inverseIdLookup[index] ?: return null)
|
||||
}
|
||||
|
||||
override fun size(): Int {
|
||||
return idLookup.size
|
||||
}
|
||||
|
||||
override fun getKey(): RegistryKey<out Registry<T>> {
|
||||
return key
|
||||
}
|
||||
|
||||
override fun getEntryInfo(key: RegistryKey<T>?): Optional<RegistryEntryInfo> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getLifecycle(): Lifecycle {
|
||||
return Lifecycle.stable()
|
||||
}
|
||||
|
||||
override fun getDefaultEntry(): Optional<RegistryEntry.Reference<T>> {
|
||||
return Optional.empty()
|
||||
}
|
||||
|
||||
override fun getIds(): MutableSet<Identifier> {
|
||||
return idLookup.keys.mapTo(mutableSetOf()) { it.value }
|
||||
}
|
||||
|
||||
override fun getEntrySet(): MutableSet<MutableMap.MutableEntry<RegistryKey<T>, T>> {
|
||||
return map.entries
|
||||
}
|
||||
|
||||
override fun getKeys(): MutableSet<RegistryKey<T>> {
|
||||
return map.keys
|
||||
}
|
||||
|
||||
override fun getRandom(random: Random?): Optional<RegistryEntry.Reference<T>> {
|
||||
return registryWrapper.streamEntries().findFirst()
|
||||
}
|
||||
|
||||
override fun containsId(id: Identifier?): Boolean {
|
||||
return idLookup.containsKey(RegistryKey.of(key, id ?: return false))
|
||||
}
|
||||
|
||||
override fun freeze(): Registry<T> {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun getEntry(rawId: Int): Optional<RegistryEntry.Reference<T>> {
|
||||
val x = inverseIdLookup[rawId] ?: return Optional.empty()
|
||||
return Optional.of(RegistryEntry.Reference.standAlone(registryWrapper, x))
|
||||
}
|
||||
|
||||
override fun streamEntries(): Stream<RegistryEntry.Reference<T>> {
|
||||
return registryWrapper.streamEntries()
|
||||
}
|
||||
|
||||
override fun streamTagsAndEntries(): Stream<Pair<TagKey<T>, RegistryEntryList.Named<T>>> {
|
||||
return streamTags().map { Pair(it, getOrCreateEntryList(it)) }
|
||||
}
|
||||
|
||||
override fun streamTags(): Stream<TagKey<T>> {
|
||||
return registryWrapper.streamTagKeys()
|
||||
}
|
||||
|
||||
override fun clearTags() {
|
||||
}
|
||||
|
||||
override fun getEntryOwner(): RegistryEntryOwner<T> {
|
||||
return registryWrapper
|
||||
}
|
||||
|
||||
override fun getReadOnlyWrapper(): RegistryWrapper.Impl<T> {
|
||||
return registryWrapper
|
||||
}
|
||||
|
||||
override fun populateTags(tagEntries: MutableMap<TagKey<T>, MutableList<RegistryEntry<T>>>?) {
|
||||
}
|
||||
|
||||
override fun getOrCreateEntryList(tag: TagKey<T>?): RegistryEntryList.Named<T> {
|
||||
return getEntryList(tag).orElseGet { RegistryEntryList.of(registryWrapper, tag) }
|
||||
}
|
||||
|
||||
override fun getEntryList(tag: TagKey<T>?): Optional<RegistryEntryList.Named<T>> {
|
||||
return registryWrapper.getOptional(tag ?: return Optional.empty())
|
||||
}
|
||||
|
||||
override fun getEntry(value: T): RegistryEntry<T> {
|
||||
return registryWrapper.getOptional(inverseLookup[value]!!).get()
|
||||
}
|
||||
|
||||
override fun getEntry(key: RegistryKey<T>?): Optional<RegistryEntry.Reference<T>> {
|
||||
return registryWrapper.getOptional(key ?: return Optional.empty())
|
||||
}
|
||||
|
||||
override fun getEntry(id: Identifier?): Optional<RegistryEntry.Reference<T>> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun createEntry(value: T): RegistryEntry.Reference<T> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun contains(key: RegistryKey<T>?): Boolean {
|
||||
return getEntry(key).isPresent
|
||||
}
|
||||
|
||||
override fun getId(value: T): Identifier? {
|
||||
return (inverseLookup[value] ?: return null).value
|
||||
}
|
||||
|
||||
override fun getKey(entry: T): Optional<RegistryKey<T>> {
|
||||
return Optional.ofNullable(inverseLookup[entry ?: return Optional.empty()])
|
||||
}
|
||||
}
|
||||
}
|
||||
import moe.nea.firmament.util.MC
|
||||
|
||||
fun createDynamicRegistry(): DynamicRegistryManager.Immutable {
|
||||
val wrapperLookup = BuiltinRegistries.createWrapperLookup()
|
||||
return object : DynamicRegistryManager.Immutable {
|
||||
override fun <E : Any?> getOptional(key: RegistryKey<out Registry<out E>>): Optional<Registry<E>> {
|
||||
val lookup = wrapperLookup.getOptionalWrapper(key).getOrNull() ?: return Optional.empty()
|
||||
val registry = makeRegistry(lookup, key as RegistryKey<out Registry<E>>)
|
||||
return Optional.of(registry)
|
||||
}
|
||||
|
||||
fun <T> entry(reg: RegistryKey<out Registry<T>>): DynamicRegistryManager.Entry<T> {
|
||||
return DynamicRegistryManager.Entry(reg, getOptional(reg).get())
|
||||
}
|
||||
|
||||
override fun streamAllRegistries(): Stream<DynamicRegistryManager.Entry<*>> {
|
||||
return wrapperLookup.streamAllRegistryKeys()
|
||||
.map { entry(it as RegistryKey<out Registry<Any>>) }
|
||||
}
|
||||
}
|
||||
// TODO: use SaveLoading.load() to properly load a full registry
|
||||
return DynamicRegistryManager.of(Registries.REGISTRIES)
|
||||
}
|
||||
|
||||
class FakeWorld(
|
||||
registries: DynamicRegistryManager.Immutable = createDynamicRegistry(),
|
||||
registries: DynamicRegistryManager.Immutable = createDynamicRegistry(),
|
||||
) : World(
|
||||
Properties,
|
||||
RegistryKey.of(RegistryKeys.WORLD, Identifier.of("firmament", "fakeworld")),
|
||||
registries,
|
||||
registries[RegistryKeys.DIMENSION_TYPE].entryOf(
|
||||
RegistryKey.of(
|
||||
RegistryKeys.DIMENSION_TYPE,
|
||||
Identifier.of("minecraft", "overworld")
|
||||
)
|
||||
),
|
||||
{ DummyProfiler.INSTANCE },
|
||||
true,
|
||||
false,
|
||||
0, 0
|
||||
Properties,
|
||||
RegistryKey.of(RegistryKeys.WORLD, Identifier.of("firmament", "fakeworld")),
|
||||
registries,
|
||||
MC.defaultRegistries.getOrThrow(RegistryKeys.DIMENSION_TYPE)
|
||||
.getOrThrow(RegistryKey.of(RegistryKeys.DIMENSION_TYPE, Identifier.of("minecraft", "overworld"))),
|
||||
true,
|
||||
false,
|
||||
0L,
|
||||
0
|
||||
) {
|
||||
object Properties : MutableWorldProperties {
|
||||
override fun getSpawnPos(): BlockPos {
|
||||
return BlockPos.ORIGIN
|
||||
}
|
||||
object Properties : MutableWorldProperties {
|
||||
override fun getSpawnPos(): BlockPos {
|
||||
return BlockPos.ORIGIN
|
||||
}
|
||||
|
||||
override fun getSpawnAngle(): Float {
|
||||
return 0F
|
||||
}
|
||||
override fun getSpawnAngle(): Float {
|
||||
return 0F
|
||||
}
|
||||
|
||||
override fun getTime(): Long {
|
||||
return 0
|
||||
}
|
||||
override fun getTime(): Long {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun getTimeOfDay(): Long {
|
||||
return 0
|
||||
}
|
||||
override fun getTimeOfDay(): Long {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun isThundering(): Boolean {
|
||||
return false
|
||||
}
|
||||
override fun isThundering(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isRaining(): Boolean {
|
||||
return false
|
||||
}
|
||||
override fun isRaining(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setRaining(raining: Boolean) {
|
||||
}
|
||||
override fun setRaining(raining: Boolean) {
|
||||
}
|
||||
|
||||
override fun isHardcore(): Boolean {
|
||||
return false
|
||||
}
|
||||
override fun isHardcore(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getGameRules(): GameRules {
|
||||
return GameRules()
|
||||
}
|
||||
override fun getDifficulty(): Difficulty {
|
||||
return Difficulty.HARD
|
||||
}
|
||||
|
||||
override fun getDifficulty(): Difficulty {
|
||||
return Difficulty.HARD
|
||||
}
|
||||
override fun isDifficultyLocked(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isDifficultyLocked(): Boolean {
|
||||
return false
|
||||
}
|
||||
override fun setSpawnPos(pos: BlockPos?, angle: Float) {}
|
||||
}
|
||||
|
||||
override fun setSpawnPos(pos: BlockPos?, angle: Float) {}
|
||||
}
|
||||
override fun getPlayers(): List<PlayerEntity> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun getPlayers(): List<PlayerEntity> {
|
||||
return emptyList()
|
||||
}
|
||||
override fun getBrightness(direction: Direction?, shaded: Boolean): Float {
|
||||
return 1f
|
||||
}
|
||||
|
||||
override fun getBrightness(direction: Direction?, shaded: Boolean): Float {
|
||||
return 1f
|
||||
}
|
||||
override fun getGeneratorStoredBiome(biomeX: Int, biomeY: Int, biomeZ: Int): RegistryEntry<Biome> {
|
||||
return registryManager.getOptionalEntry(BiomeKeys.PLAINS).get()
|
||||
}
|
||||
|
||||
override fun getGeneratorStoredBiome(biomeX: Int, biomeY: Int, biomeZ: Int): RegistryEntry<Biome> {
|
||||
return registryManager.get(RegistryKeys.BIOME).entryOf(BiomeKeys.PLAINS)
|
||||
}
|
||||
override fun getSeaLevel(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun getEnabledFeatures(): FeatureSet {
|
||||
return FeatureFlags.VANILLA_FEATURES
|
||||
}
|
||||
override fun getEnabledFeatures(): FeatureSet {
|
||||
return FeatureFlags.VANILLA_FEATURES
|
||||
}
|
||||
|
||||
class FakeTickScheduler<T> : QueryableTickScheduler<T> {
|
||||
override fun scheduleTick(orderedTick: OrderedTick<T>?) {
|
||||
}
|
||||
class FakeTickScheduler<T> : QueryableTickScheduler<T> {
|
||||
override fun scheduleTick(orderedTick: OrderedTick<T>?) {
|
||||
}
|
||||
|
||||
override fun isQueued(pos: BlockPos?, type: T): Boolean {
|
||||
return true
|
||||
}
|
||||
override fun isQueued(pos: BlockPos?, type: T): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getTickCount(): Int {
|
||||
return 0
|
||||
}
|
||||
override fun getTickCount(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun isTicking(pos: BlockPos?, type: T): Boolean {
|
||||
return true
|
||||
}
|
||||
override fun isTicking(pos: BlockPos?, type: T): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override fun getBlockTickScheduler(): QueryableTickScheduler<Block> {
|
||||
return FakeTickScheduler()
|
||||
}
|
||||
override fun getBlockTickScheduler(): QueryableTickScheduler<Block> {
|
||||
return FakeTickScheduler()
|
||||
}
|
||||
|
||||
override fun getFluidTickScheduler(): QueryableTickScheduler<Fluid> {
|
||||
return FakeTickScheduler()
|
||||
}
|
||||
override fun getFluidTickScheduler(): QueryableTickScheduler<Fluid> {
|
||||
return FakeTickScheduler()
|
||||
}
|
||||
|
||||
|
||||
class FakeChunkManager(val world: FakeWorld) : ChunkManager() {
|
||||
override fun getChunk(x: Int, z: Int, leastStatus: ChunkStatus?, create: Boolean): Chunk {
|
||||
return EmptyChunk(
|
||||
world,
|
||||
ChunkPos(x, z),
|
||||
world.registryManager.get(RegistryKeys.BIOME).entryOf(BiomeKeys.PLAINS)
|
||||
)
|
||||
}
|
||||
class FakeChunkManager(val world: FakeWorld) : ChunkManager() {
|
||||
override fun getChunk(x: Int, z: Int, leastStatus: ChunkStatus?, create: Boolean): Chunk {
|
||||
return EmptyChunk(
|
||||
world,
|
||||
ChunkPos(x, z),
|
||||
world.registryManager.getOptionalEntry(BiomeKeys.PLAINS).get()
|
||||
)
|
||||
}
|
||||
|
||||
override fun getWorld(): BlockView {
|
||||
return world
|
||||
}
|
||||
override fun getWorld(): BlockView {
|
||||
return world
|
||||
}
|
||||
|
||||
override fun tick(shouldKeepTicking: BooleanSupplier?, tickChunks: Boolean) {
|
||||
}
|
||||
override fun tick(shouldKeepTicking: BooleanSupplier?, tickChunks: Boolean) {
|
||||
}
|
||||
|
||||
override fun getDebugString(): String {
|
||||
return "FakeChunkManager"
|
||||
}
|
||||
override fun getDebugString(): String {
|
||||
return "FakeChunkManager"
|
||||
}
|
||||
|
||||
override fun getLoadedChunkCount(): Int {
|
||||
return 0
|
||||
}
|
||||
override fun getLoadedChunkCount(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun getLightingProvider(): LightingProvider {
|
||||
return FakeLightingProvider(this)
|
||||
}
|
||||
}
|
||||
override fun getLightingProvider(): LightingProvider {
|
||||
return FakeLightingProvider(this)
|
||||
}
|
||||
}
|
||||
|
||||
class FakeLightingProvider(chunkManager: FakeChunkManager) : LightingProvider(chunkManager, false, false)
|
||||
class FakeLightingProvider(chunkManager: FakeChunkManager) : LightingProvider(chunkManager, false, false)
|
||||
|
||||
override fun getChunkManager(): ChunkManager {
|
||||
return FakeChunkManager(this)
|
||||
}
|
||||
override fun getChunkManager(): ChunkManager {
|
||||
return FakeChunkManager(this)
|
||||
}
|
||||
|
||||
override fun playSound(
|
||||
source: PlayerEntity?,
|
||||
x: Double,
|
||||
y: Double,
|
||||
z: Double,
|
||||
sound: RegistryEntry<SoundEvent>?,
|
||||
category: SoundCategory?,
|
||||
volume: Float,
|
||||
pitch: Float,
|
||||
seed: Long
|
||||
) {
|
||||
}
|
||||
override fun playSound(
|
||||
source: PlayerEntity?,
|
||||
x: Double,
|
||||
y: Double,
|
||||
z: Double,
|
||||
sound: RegistryEntry<SoundEvent>?,
|
||||
category: SoundCategory?,
|
||||
volume: Float,
|
||||
pitch: Float,
|
||||
seed: Long
|
||||
) {
|
||||
}
|
||||
|
||||
override fun syncWorldEvent(player: PlayerEntity?, eventId: Int, pos: BlockPos?, data: Int) {
|
||||
}
|
||||
override fun syncWorldEvent(player: PlayerEntity?, eventId: Int, pos: BlockPos?, data: Int) {
|
||||
}
|
||||
|
||||
override fun emitGameEvent(event: RegistryEntry<GameEvent>?, emitterPos: Vec3d?, emitter: GameEvent.Emitter?) {
|
||||
}
|
||||
override fun emitGameEvent(event: RegistryEntry<GameEvent>?, emitterPos: Vec3d?, emitter: GameEvent.Emitter?) {
|
||||
}
|
||||
|
||||
override fun updateListeners(pos: BlockPos?, oldState: BlockState?, newState: BlockState?, flags: Int) {
|
||||
}
|
||||
override fun updateListeners(pos: BlockPos?, oldState: BlockState?, newState: BlockState?, flags: Int) {
|
||||
}
|
||||
|
||||
override fun playSoundFromEntity(
|
||||
source: PlayerEntity?,
|
||||
entity: Entity?,
|
||||
sound: RegistryEntry<SoundEvent>?,
|
||||
category: SoundCategory?,
|
||||
volume: Float,
|
||||
pitch: Float,
|
||||
seed: Long
|
||||
) {
|
||||
}
|
||||
override fun playSoundFromEntity(
|
||||
source: PlayerEntity?,
|
||||
entity: Entity?,
|
||||
sound: RegistryEntry<SoundEvent>?,
|
||||
category: SoundCategory?,
|
||||
volume: Float,
|
||||
pitch: Float,
|
||||
seed: Long
|
||||
) {
|
||||
}
|
||||
|
||||
override fun asString(): String {
|
||||
return "FakeWorld"
|
||||
}
|
||||
override fun createExplosion(
|
||||
entity: Entity?,
|
||||
damageSource: DamageSource?,
|
||||
behavior: ExplosionBehavior?,
|
||||
x: Double,
|
||||
y: Double,
|
||||
z: Double,
|
||||
power: Float,
|
||||
createFire: Boolean,
|
||||
explosionSourceType: ExplosionSourceType?,
|
||||
smallParticle: ParticleEffect?,
|
||||
largeParticle: ParticleEffect?,
|
||||
soundEvent: RegistryEntry<SoundEvent>?
|
||||
) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getEntityById(id: Int): Entity? {
|
||||
return null
|
||||
}
|
||||
override fun asString(): String {
|
||||
return "FakeWorld"
|
||||
}
|
||||
|
||||
override fun getTickManager(): TickManager {
|
||||
return TickManager()
|
||||
}
|
||||
override fun getEntityById(id: Int): Entity? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getMapState(id: MapIdComponent?): MapState? {
|
||||
return null
|
||||
}
|
||||
override fun getTickManager(): TickManager {
|
||||
return TickManager()
|
||||
}
|
||||
|
||||
override fun putMapState(id: MapIdComponent?, state: MapState?) {
|
||||
}
|
||||
override fun getMapState(id: MapIdComponent?): MapState? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun increaseAndGetMapId(): MapIdComponent {
|
||||
return MapIdComponent(0)
|
||||
}
|
||||
override fun putMapState(id: MapIdComponent?, state: MapState?) {
|
||||
}
|
||||
|
||||
override fun setBlockBreakingInfo(entityId: Int, pos: BlockPos?, progress: Int) {
|
||||
}
|
||||
override fun increaseAndGetMapId(): MapIdComponent {
|
||||
return MapIdComponent(0)
|
||||
}
|
||||
|
||||
override fun getScoreboard(): Scoreboard {
|
||||
return Scoreboard()
|
||||
}
|
||||
override fun setBlockBreakingInfo(entityId: Int, pos: BlockPos?, progress: Int) {
|
||||
}
|
||||
|
||||
override fun getRecipeManager(): RecipeManager {
|
||||
return RecipeManager(registryManager)
|
||||
}
|
||||
override fun getScoreboard(): Scoreboard {
|
||||
return Scoreboard()
|
||||
}
|
||||
|
||||
object FakeEntityLookup : EntityLookup<Entity> {
|
||||
override fun get(id: Int): Entity? {
|
||||
return null
|
||||
}
|
||||
override fun getRecipeManager(): RecipeManager {
|
||||
return object : RecipeManager {
|
||||
override fun getPropertySet(key: RegistryKey<RecipePropertySet>?): RecipePropertySet {
|
||||
return RecipePropertySet.EMPTY
|
||||
}
|
||||
|
||||
override fun get(uuid: UUID?): Entity? {
|
||||
return null
|
||||
}
|
||||
override fun getStonecutterRecipes(): CuttingRecipeDisplay.Grouping<StonecuttingRecipe> {
|
||||
return CuttingRecipeDisplay.Grouping.empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun iterate(): MutableIterable<Entity> {
|
||||
return mutableListOf()
|
||||
}
|
||||
object FakeEntityLookup : EntityLookup<Entity> {
|
||||
override fun get(id: Int): Entity? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun <U : Entity?> forEachIntersects(
|
||||
filter: TypeFilter<Entity, U>?,
|
||||
box: Box?,
|
||||
consumer: LazyIterationConsumer<U>?
|
||||
) {
|
||||
}
|
||||
override fun get(uuid: UUID?): Entity? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun forEachIntersects(box: Box?, action: Consumer<Entity>?) {
|
||||
}
|
||||
override fun iterate(): MutableIterable<Entity> {
|
||||
return mutableListOf()
|
||||
}
|
||||
|
||||
override fun <U : Entity?> forEach(filter: TypeFilter<Entity, U>?, consumer: LazyIterationConsumer<U>?) {
|
||||
}
|
||||
override fun <U : Entity?> forEachIntersects(
|
||||
filter: TypeFilter<Entity, U>?,
|
||||
box: Box?,
|
||||
consumer: LazyIterationConsumer<U>?
|
||||
) {
|
||||
}
|
||||
|
||||
}
|
||||
override fun forEachIntersects(box: Box?, action: Consumer<Entity>?) {
|
||||
}
|
||||
|
||||
override fun getEntityLookup(): EntityLookup<Entity> {
|
||||
return FakeEntityLookup
|
||||
}
|
||||
override fun <U : Entity?> forEach(filter: TypeFilter<Entity, U>?, consumer: LazyIterationConsumer<U>?) {
|
||||
}
|
||||
|
||||
override fun getBrewingRecipeRegistry(): BrewingRecipeRegistry {
|
||||
return BrewingRecipeRegistry.EMPTY
|
||||
}
|
||||
}
|
||||
|
||||
override fun getEntityLookup(): EntityLookup<Entity> {
|
||||
return FakeEntityLookup
|
||||
}
|
||||
|
||||
override fun getBrewingRecipeRegistry(): BrewingRecipeRegistry {
|
||||
return BrewingRecipeRegistry.EMPTY
|
||||
}
|
||||
|
||||
override fun getFuelRegistry(): FuelRegistry {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
|
||||
package moe.nea.firmament.gui.entity
|
||||
|
||||
import com.mojang.authlib.GameProfile
|
||||
import java.util.*
|
||||
import java.util.UUID
|
||||
import net.minecraft.client.network.AbstractClientPlayerEntity
|
||||
import net.minecraft.client.util.DefaultSkinHelper
|
||||
import net.minecraft.client.util.SkinTextures
|
||||
@@ -15,40 +14,40 @@ import net.minecraft.world.World
|
||||
/**
|
||||
* @see moe.nea.firmament.init.EarlyRiser
|
||||
*/
|
||||
fun makeGuiPlayer(world: FakeWorld): GuiPlayer {
|
||||
val constructor = GuiPlayer::class.java.getDeclaredConstructor(
|
||||
World::class.java,
|
||||
BlockPos::class.java,
|
||||
Float::class.javaPrimitiveType,
|
||||
GameProfile::class.java
|
||||
)
|
||||
return constructor.newInstance(world, BlockPos.ORIGIN, 0F, GameProfile(UUID.randomUUID(), "Linnea"))
|
||||
fun makeGuiPlayer(world: World): GuiPlayer {
|
||||
val constructor = GuiPlayer::class.java.getDeclaredConstructor(
|
||||
World::class.java,
|
||||
BlockPos::class.java,
|
||||
Float::class.javaPrimitiveType,
|
||||
GameProfile::class.java
|
||||
)
|
||||
return constructor.newInstance(world, BlockPos.ORIGIN, 0F, GameProfile(UUID.randomUUID(), "Linnea"))
|
||||
}
|
||||
|
||||
class GuiPlayer(world: ClientWorld?, profile: GameProfile?) : AbstractClientPlayerEntity(world, profile) {
|
||||
override fun isSpectator(): Boolean {
|
||||
return false
|
||||
}
|
||||
override fun isSpectator(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isCreative(): Boolean {
|
||||
return false
|
||||
}
|
||||
override fun isCreative(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRenderName(): Boolean {
|
||||
return false
|
||||
}
|
||||
override fun shouldRenderName(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
var skinTexture: Identifier = DefaultSkinHelper.getSkinTextures(this.getUuid()).texture
|
||||
var capeTexture: Identifier? = null
|
||||
var model: Model = Model.WIDE
|
||||
override fun getSkinTextures(): SkinTextures {
|
||||
return SkinTextures(
|
||||
skinTexture,
|
||||
null,
|
||||
capeTexture,
|
||||
null,
|
||||
model,
|
||||
true
|
||||
)
|
||||
}
|
||||
var skinTexture: Identifier = DefaultSkinHelper.getSkinTextures(this.getUuid()).texture
|
||||
var capeTexture: Identifier? = null
|
||||
var model: Model = Model.WIDE
|
||||
override fun getSkinTextures(): SkinTextures {
|
||||
return SkinTextures(
|
||||
skinTexture,
|
||||
null,
|
||||
capeTexture,
|
||||
null,
|
||||
model,
|
||||
true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import kotlin.experimental.inv
|
||||
import kotlin.experimental.or
|
||||
import net.minecraft.entity.EntityType
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.SpawnReason
|
||||
import net.minecraft.entity.passive.AbstractHorseEntity
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
@@ -19,11 +20,11 @@ object ModifyHorse : EntityModifier {
|
||||
var entity: AbstractHorseEntity = entity
|
||||
info["kind"]?.let {
|
||||
entity = when (it.asString) {
|
||||
"skeleton" -> EntityType.SKELETON_HORSE.create(fakeWorld)!!
|
||||
"zombie" -> EntityType.ZOMBIE_HORSE.create(fakeWorld)!!
|
||||
"mule" -> EntityType.MULE.create(fakeWorld)!!
|
||||
"donkey" -> EntityType.DONKEY.create(fakeWorld)!!
|
||||
"horse" -> EntityType.HORSE.create(fakeWorld)!!
|
||||
"skeleton" -> EntityType.SKELETON_HORSE.create(fakeWorld, SpawnReason.LOAD)!!
|
||||
"zombie" -> EntityType.ZOMBIE_HORSE.create(fakeWorld, SpawnReason.LOAD)!!
|
||||
"mule" -> EntityType.MULE.create(fakeWorld, SpawnReason.LOAD)!!
|
||||
"donkey" -> EntityType.DONKEY.create(fakeWorld, SpawnReason.LOAD)!!
|
||||
"horse" -> EntityType.HORSE.create(fakeWorld, SpawnReason.LOAD)!!
|
||||
else -> error("Unknown horse kind $it")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ package moe.nea.firmament.repo
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.bodyAsChannel
|
||||
import io.ktor.utils.io.jvm.nio.copyTo
|
||||
import io.ktor.utils.io.copyTo
|
||||
import java.io.IOException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
@@ -9,10 +9,12 @@ import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
|
||||
import kotlinx.coroutines.launch
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.network.packet.s2c.play.SynchronizeRecipesS2CPacket
|
||||
import net.minecraft.recipe.display.CuttingRecipeDisplay
|
||||
import moe.nea.firmament.Firmament
|
||||
import moe.nea.firmament.Firmament.logger
|
||||
import moe.nea.firmament.events.ReloadRegistrationEvent
|
||||
import moe.nea.firmament.gui.config.ManagedConfig
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.MinecraftDispatcher
|
||||
import moe.nea.firmament.util.SkyblockId
|
||||
import moe.nea.firmament.util.tr
|
||||
@@ -77,7 +79,7 @@ object RepoManager {
|
||||
|
||||
private fun trySendClientboundUpdateRecipesPacket(): Boolean {
|
||||
return MinecraftClient.getInstance().world != null && MinecraftClient.getInstance().networkHandler?.onSynchronizeRecipes(
|
||||
SynchronizeRecipesS2CPacket(mutableListOf())
|
||||
SynchronizeRecipesS2CPacket(mutableMapOf(), CuttingRecipeDisplay.Grouping.empty())
|
||||
) != null
|
||||
}
|
||||
|
||||
@@ -92,7 +94,7 @@ object RepoManager {
|
||||
|
||||
fun launchAsyncUpdate(force: Boolean = false) {
|
||||
Firmament.coroutineScope.launch {
|
||||
ItemCache.ReloadProgressHud.reportProgress("Downloading", 0, -1) // TODO: replace with a proper boundy bar
|
||||
ItemCache.ReloadProgressHud.reportProgress("Downloading", 0, -1) // TODO: replace with a proper bouncy bar
|
||||
ItemCache.ReloadProgressHud.isEnabled = true
|
||||
try {
|
||||
RepoDownloadManager.downloadUpdate(force)
|
||||
@@ -112,7 +114,7 @@ object RepoManager {
|
||||
ItemCache.ReloadProgressHud.isEnabled = true
|
||||
neuRepo.reload()
|
||||
} catch (exc: NEURepositoryException) {
|
||||
MinecraftClient.getInstance().player?.sendMessage(
|
||||
MC.sendChat(
|
||||
tr("firmament.repo.reloadfail",
|
||||
"Failed to reload repository. This will result in some mod features not working.")
|
||||
)
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
package moe.nea.firmament.repo
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import io.github.moulberry.repo.constants.PetNumbers
|
||||
import io.github.moulberry.repo.data.NEUIngredient
|
||||
import io.github.moulberry.repo.data.NEUItem
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.network.RegistryByteBuf
|
||||
import net.minecraft.network.codec.PacketCodec
|
||||
import net.minecraft.network.codec.PacketCodecs
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Formatting
|
||||
import moe.nea.firmament.repo.ItemCache.asItemStack
|
||||
@@ -40,6 +45,21 @@ data class SBItemStack constructor(
|
||||
}
|
||||
|
||||
companion object {
|
||||
val PACKET_CODEC: PacketCodec<in RegistryByteBuf, SBItemStack> = PacketCodec.tuple(
|
||||
SkyblockId.PACKET_CODEC, { it.skyblockId },
|
||||
PacketCodecs.VAR_INT, { it.stackSize },
|
||||
{ id, count -> SBItemStack(id, count) }
|
||||
)
|
||||
val CODEC: Codec<SBItemStack> = RecordCodecBuilder.create {
|
||||
it.group(
|
||||
SkyblockId.CODEC.fieldOf("skyblockId").forGetter { it.skyblockId },
|
||||
Codec.INT.fieldOf("count").forGetter { it.stackSize },
|
||||
).apply(it) { id, count ->
|
||||
SBItemStack(id, count)
|
||||
}
|
||||
}
|
||||
val EMPTY = SBItemStack(SkyblockId.NULL, 0)
|
||||
|
||||
operator fun invoke(itemStack: ItemStack): SBItemStack {
|
||||
val skyblockId = itemStack.skyBlockId ?: SkyblockId.NULL
|
||||
return SBItemStack(
|
||||
@@ -114,6 +134,8 @@ data class SBItemStack constructor(
|
||||
val itemStack = itemStack_ ?: run {
|
||||
if (skyblockId == SkyblockId.COINS)
|
||||
return@run ItemCache.coinItem(stackSize).also { it.appendLore(extraLore) }
|
||||
if (stackSize == 0)
|
||||
return@run ItemStack.EMPTY
|
||||
val replacementData = mutableMapOf<String, String>()
|
||||
injectReplacementDataForPets(replacementData)
|
||||
return@run neuItem.asItemStack(idHint = skyblockId, replacementData)
|
||||
|
||||
19
src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt
Normal file
19
src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt
Normal file
@@ -0,0 +1,19 @@
|
||||
package moe.nea.firmament.repo.recipes
|
||||
|
||||
import io.github.moulberry.repo.NEURepository
|
||||
import io.github.moulberry.repo.data.NEURecipe
|
||||
import me.shedaniel.math.Rectangle
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
import moe.nea.firmament.repo.SBItemStack
|
||||
|
||||
interface GenericRecipeRenderer<T : NEURecipe> {
|
||||
fun render(recipe: T, bounds: Rectangle, layouter: RecipeLayouter)
|
||||
fun getInputs(recipe: T): Collection<SBItemStack>
|
||||
fun getOutputs(recipe: T): Collection<SBItemStack>
|
||||
val icon: ItemStack
|
||||
val title: Text
|
||||
val identifier: Identifier
|
||||
fun findAllRecipes(neuRepository: NEURepository): Iterable<T>
|
||||
}
|
||||
33
src/main/kotlin/repo/recipes/RecipeLayouter.kt
Normal file
33
src/main/kotlin/repo/recipes/RecipeLayouter.kt
Normal file
@@ -0,0 +1,33 @@
|
||||
package moe.nea.firmament.repo.recipes
|
||||
|
||||
import io.github.notenoughupdates.moulconfig.gui.GuiComponent
|
||||
import net.minecraft.text.Text
|
||||
import moe.nea.firmament.repo.SBItemStack
|
||||
|
||||
interface RecipeLayouter {
|
||||
enum class SlotKind {
|
||||
SMALL_INPUT,
|
||||
SMALL_OUTPUT,
|
||||
|
||||
/**
|
||||
* Create a bigger background and mark the slot as output. The coordinates should still refer the upper left corner of the item stack, not of the bigger background.
|
||||
*/
|
||||
BIG_OUTPUT,
|
||||
}
|
||||
|
||||
fun createItemSlot(
|
||||
x: Int, y: Int,
|
||||
content: SBItemStack?,
|
||||
slotKind: SlotKind,
|
||||
)
|
||||
|
||||
fun createLabel(
|
||||
x: Int, y: Int,
|
||||
text: Text
|
||||
)
|
||||
|
||||
fun createArrow(x: Int, y: Int)
|
||||
|
||||
fun createMoulConfig(x: Int, y: Int, w: Int, h: Int, component: GuiComponent)
|
||||
}
|
||||
|
||||
50
src/main/kotlin/repo/recipes/SBCraftingRecipeRenderer.kt
Normal file
50
src/main/kotlin/repo/recipes/SBCraftingRecipeRenderer.kt
Normal file
@@ -0,0 +1,50 @@
|
||||
package moe.nea.firmament.repo.recipes
|
||||
|
||||
import io.github.moulberry.repo.NEURepository
|
||||
import io.github.moulberry.repo.data.NEUCraftingRecipe
|
||||
import me.shedaniel.math.Point
|
||||
import me.shedaniel.math.Rectangle
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
import moe.nea.firmament.Firmament
|
||||
import moe.nea.firmament.repo.SBItemStack
|
||||
import moe.nea.firmament.util.tr
|
||||
|
||||
class SBCraftingRecipeRenderer : GenericRecipeRenderer<NEUCraftingRecipe> {
|
||||
override fun render(recipe: NEUCraftingRecipe, bounds: Rectangle, layouter: RecipeLayouter) {
|
||||
val point = Point(bounds.centerX - 58, bounds.centerY - 27)
|
||||
layouter.createArrow(point.x + 60, point.y + 18)
|
||||
for (i in 0 until 3) {
|
||||
for (j in 0 until 3) {
|
||||
val item = recipe.inputs[i + j * 3]
|
||||
layouter.createItemSlot(point.x + 1 + i * 18,
|
||||
point.y + 1 + j * 18,
|
||||
SBItemStack(item),
|
||||
RecipeLayouter.SlotKind.SMALL_INPUT)
|
||||
}
|
||||
}
|
||||
layouter.createItemSlot(
|
||||
point.x + 95, point.y + 19,
|
||||
SBItemStack(recipe.output),
|
||||
RecipeLayouter.SlotKind.BIG_OUTPUT
|
||||
)
|
||||
}
|
||||
|
||||
override fun getInputs(recipe: NEUCraftingRecipe): Collection<SBItemStack> {
|
||||
return recipe.allInputs.mapNotNull { SBItemStack(it) }
|
||||
}
|
||||
|
||||
override fun getOutputs(recipe: NEUCraftingRecipe): Collection<SBItemStack> {
|
||||
return SBItemStack(recipe.output)?.let(::listOf) ?: emptyList()
|
||||
}
|
||||
|
||||
override fun findAllRecipes(neuRepository: NEURepository): Iterable<NEUCraftingRecipe> {
|
||||
return neuRepository.items.items.values.flatMap { it.recipes }.filterIsInstance<NEUCraftingRecipe>()
|
||||
}
|
||||
|
||||
override val icon: ItemStack = ItemStack(Blocks.CRAFTING_TABLE)
|
||||
override val title: Text = tr("firmament.category.crafting", "SkyBlock Crafting")
|
||||
override val identifier: Identifier = Firmament.identifier("crafting_recipe")
|
||||
}
|
||||
@@ -1,25 +1,46 @@
|
||||
@file:OptIn(ExperimentalContracts::class)
|
||||
|
||||
package moe.nea.firmament.util
|
||||
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import moe.nea.firmament.Firmament
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE") // Suppressed since i want the logger to not pick up the ErrorUtil stack-frame
|
||||
object ErrorUtil {
|
||||
var aggressiveErrors = run {
|
||||
Thread.currentThread().stackTrace.any { it.className.startsWith("org.junit.") } || Firmament.DEBUG
|
||||
|| ErrorUtil::class.java.desiredAssertionStatus()
|
||||
}
|
||||
|
||||
inline fun softCheck(message: String, func: () -> Boolean) {
|
||||
inline fun softCheck(message: String, check: Boolean) {
|
||||
if (!check) softError(message)
|
||||
}
|
||||
|
||||
inline fun lazyCheck(message: String, func: () -> Boolean) {
|
||||
contract {
|
||||
callsInPlace(func, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
if (!aggressiveErrors) return
|
||||
if (func()) return
|
||||
error(message)
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE") // Suppressed since i want the logger to not pick up the ErrorUtil stack-frame
|
||||
inline fun softError(message: String, exception: Throwable) {
|
||||
if (aggressiveErrors) throw IllegalStateException(message, exception)
|
||||
else Firmament.logger.error(message, exception)
|
||||
}
|
||||
|
||||
inline fun softError(message: String) {
|
||||
if (aggressiveErrors) error(message)
|
||||
else Firmament.logger.error(message)
|
||||
}
|
||||
|
||||
inline fun <T : Any> notNullOr(nullable: T?, message: String, orElse: () -> T): T {
|
||||
contract {
|
||||
callsInPlace(orElse, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
if (nullable == null) {
|
||||
softError(message)
|
||||
return orElse()
|
||||
|
||||
@@ -3,9 +3,13 @@ package moe.nea.firmament.util
|
||||
import io.github.moulberry.repo.data.Coordinate
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.gui.screen.Screen
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreen
|
||||
import net.minecraft.client.option.GameOptions
|
||||
import net.minecraft.client.network.ClientPlayerEntity
|
||||
import net.minecraft.client.render.WorldRenderer
|
||||
import net.minecraft.client.render.item.ItemRenderer
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.item.Item
|
||||
import net.minecraft.network.packet.c2s.play.CommandExecutionC2SPacket
|
||||
import net.minecraft.registry.BuiltinRegistries
|
||||
@@ -14,7 +18,9 @@ import net.minecraft.registry.RegistryWrapper
|
||||
import net.minecraft.resource.ReloadableResourceManagerImpl
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.World
|
||||
import moe.nea.firmament.events.TickEvent
|
||||
import moe.nea.firmament.events.WorldReadyEvent
|
||||
|
||||
object MC {
|
||||
|
||||
@@ -29,6 +35,9 @@ object MC {
|
||||
(nextTickTodos.poll() ?: break).invoke()
|
||||
}
|
||||
}
|
||||
WorldReadyEvent.subscribe("MC:ready") {
|
||||
this.lastWorld
|
||||
}
|
||||
}
|
||||
|
||||
fun sendChat(text: Text) {
|
||||
@@ -69,6 +78,7 @@ object MC {
|
||||
|
||||
|
||||
inline val resourceManager get() = (instance.resourceManager as ReloadableResourceManagerImpl)
|
||||
inline val itemRenderer: ItemRenderer get() = instance.itemRenderer
|
||||
inline val worldRenderer: WorldRenderer get() = instance.worldRenderer
|
||||
inline val networkHandler get() = player?.networkHandler
|
||||
inline val instance get() = MinecraftClient.getInstance()
|
||||
@@ -79,11 +89,11 @@ object MC {
|
||||
inline val inGameHud get() = instance.inGameHud
|
||||
inline val font get() = instance.textRenderer
|
||||
inline val soundManager get() = instance.soundManager
|
||||
inline val player get() = instance.player
|
||||
inline val camera get() = instance.cameraEntity
|
||||
inline val player: ClientPlayerEntity? get() = instance.player
|
||||
inline val camera: Entity? get() = instance.cameraEntity
|
||||
inline val guiAtlasManager get() = instance.guiAtlasManager
|
||||
inline val world get() = instance.world
|
||||
inline var screen
|
||||
inline val world: ClientWorld? get() = instance.world
|
||||
inline var screen: Screen?
|
||||
get() = instance.currentScreen
|
||||
set(value) = instance.setScreen(value)
|
||||
val screenName get() = screen?.title?.unformattedString?.trim()
|
||||
@@ -92,7 +102,13 @@ object MC {
|
||||
inline val currentRegistries: RegistryWrapper.WrapperLookup? get() = world?.registryManager
|
||||
val defaultRegistries: RegistryWrapper.WrapperLookup = BuiltinRegistries.createWrapperLookup()
|
||||
inline val currentOrDefaultRegistries get() = currentRegistries ?: defaultRegistries
|
||||
val defaultItems: RegistryWrapper.Impl<Item> = defaultRegistries.getWrapperOrThrow(RegistryKeys.ITEM)
|
||||
val defaultItems: RegistryWrapper.Impl<Item> = defaultRegistries.getOrThrow(RegistryKeys.ITEM)
|
||||
var lastWorld: World? = null
|
||||
get() {
|
||||
field = world ?: field
|
||||
return field
|
||||
}
|
||||
private set
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ object SBData {
|
||||
it.serverType.getOrNull()?.name?.uppercase(),
|
||||
it.mode.getOrNull(),
|
||||
it.map.getOrNull())
|
||||
SkyblockServerUpdateEvent.publish(SkyblockServerUpdateEvent(lastLocraw, null))
|
||||
SkyblockServerUpdateEvent.publish(SkyblockServerUpdateEvent(lastLocraw, locraw))
|
||||
profileIdCommandDebounce = TimeMark.now()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
package moe.nea.firmament.util
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import io.github.moulberry.repo.data.NEUIngredient
|
||||
import io.github.moulberry.repo.data.NEUItem
|
||||
import io.github.moulberry.repo.data.Rarity
|
||||
@@ -16,6 +17,9 @@ import net.minecraft.component.type.NbtComponent
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.network.RegistryByteBuf
|
||||
import net.minecraft.network.codec.PacketCodec
|
||||
import net.minecraft.network.codec.PacketCodecs
|
||||
import net.minecraft.util.Identifier
|
||||
import moe.nea.firmament.repo.ItemCache.asItemStack
|
||||
import moe.nea.firmament.repo.set
|
||||
@@ -68,6 +72,9 @@ value class SkyblockId(val neuItem: String) {
|
||||
val NULL: SkyblockId = SkyblockId("null")
|
||||
val PET_NULL: SkyblockId = SkyblockId("null_pet")
|
||||
private val illlegalPathRegex = "[^a-z0-9_.-/]".toRegex()
|
||||
val CODEC = Codec.STRING.xmap({ SkyblockId(it) }, { it.neuItem })
|
||||
val PACKET_CODEC: PacketCodec<in RegistryByteBuf, SkyblockId> =
|
||||
PacketCodecs.STRING.xmap({ SkyblockId(it) }, { it.neuItem })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,77 +1,71 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.util.data
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents
|
||||
import kotlin.reflect.KClass
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.server.command.CommandOutput
|
||||
import net.minecraft.text.Text
|
||||
import moe.nea.firmament.Firmament
|
||||
import moe.nea.firmament.events.ScreenChangeEvent
|
||||
import moe.nea.firmament.util.MC
|
||||
|
||||
interface IDataHolder<T> {
|
||||
companion object {
|
||||
internal var badLoads: MutableList<String> = CopyOnWriteArrayList()
|
||||
private val allConfigs: MutableMap<KClass<out IDataHolder<*>>, IDataHolder<*>> = mutableMapOf()
|
||||
private val dirty: MutableSet<KClass<out IDataHolder<*>>> = mutableSetOf()
|
||||
companion object {
|
||||
internal var badLoads: MutableList<String> = CopyOnWriteArrayList()
|
||||
private val allConfigs: MutableMap<KClass<out IDataHolder<*>>, IDataHolder<*>> = mutableMapOf()
|
||||
private val dirty: MutableSet<KClass<out IDataHolder<*>>> = mutableSetOf()
|
||||
|
||||
internal fun <T : IDataHolder<K>, K> putDataHolder(kClass: KClass<T>, inst: IDataHolder<K>) {
|
||||
allConfigs[kClass] = inst
|
||||
}
|
||||
internal fun <T : IDataHolder<K>, K> putDataHolder(kClass: KClass<T>, inst: IDataHolder<K>) {
|
||||
allConfigs[kClass] = inst
|
||||
}
|
||||
|
||||
fun <T : IDataHolder<K>, K> markDirty(kClass: KClass<T>) {
|
||||
if (kClass !in allConfigs) {
|
||||
Firmament.logger.error("Tried to markDirty '${kClass.qualifiedName}', which isn't registered as 'IConfigHolder'")
|
||||
return
|
||||
}
|
||||
dirty.add(kClass)
|
||||
}
|
||||
fun <T : IDataHolder<K>, K> markDirty(kClass: KClass<T>) {
|
||||
if (kClass !in allConfigs) {
|
||||
Firmament.logger.error("Tried to markDirty '${kClass.qualifiedName}', which isn't registered as 'IConfigHolder'")
|
||||
return
|
||||
}
|
||||
dirty.add(kClass)
|
||||
}
|
||||
|
||||
private fun performSaves() {
|
||||
val toSave = dirty.toList().also {
|
||||
dirty.clear()
|
||||
}
|
||||
for (it in toSave) {
|
||||
val obj = allConfigs[it]
|
||||
if (obj == null) {
|
||||
Firmament.logger.error("Tried to save '${it}', which isn't registered as 'ConfigHolder'")
|
||||
continue
|
||||
}
|
||||
obj.save()
|
||||
}
|
||||
}
|
||||
private fun performSaves() {
|
||||
val toSave = dirty.toList().also {
|
||||
dirty.clear()
|
||||
}
|
||||
for (it in toSave) {
|
||||
val obj = allConfigs[it]
|
||||
if (obj == null) {
|
||||
Firmament.logger.error("Tried to save '${it}', which isn't registered as 'ConfigHolder'")
|
||||
continue
|
||||
}
|
||||
obj.save()
|
||||
}
|
||||
}
|
||||
|
||||
private fun warnForResetConfigs(player: CommandOutput) {
|
||||
if (badLoads.isNotEmpty()) {
|
||||
player.sendMessage(
|
||||
Text.literal(
|
||||
"The following configs have been reset: ${badLoads.joinToString(", ")}. " +
|
||||
"This can be intentional, but probably isn't."
|
||||
)
|
||||
)
|
||||
badLoads.clear()
|
||||
}
|
||||
}
|
||||
private fun warnForResetConfigs() {
|
||||
if (badLoads.isNotEmpty()) {
|
||||
MC.sendChat(
|
||||
Text.literal(
|
||||
"The following configs have been reset: ${badLoads.joinToString(", ")}. " +
|
||||
"This can be intentional, but probably isn't."
|
||||
)
|
||||
)
|
||||
badLoads.clear()
|
||||
}
|
||||
}
|
||||
|
||||
fun registerEvents() {
|
||||
ScreenChangeEvent.subscribe("IDataHolder:saveOnScreenChange") { event ->
|
||||
performSaves()
|
||||
val p = MinecraftClient.getInstance().player
|
||||
if (p != null) {
|
||||
warnForResetConfigs(p)
|
||||
}
|
||||
}
|
||||
ClientLifecycleEvents.CLIENT_STOPPING.register(ClientLifecycleEvents.ClientStopping {
|
||||
performSaves()
|
||||
})
|
||||
}
|
||||
fun registerEvents() {
|
||||
ScreenChangeEvent.subscribe("IDataHolder:saveOnScreenChange") { event ->
|
||||
performSaves()
|
||||
warnForResetConfigs()
|
||||
}
|
||||
ClientLifecycleEvents.CLIENT_STOPPING.register(ClientLifecycleEvents.ClientStopping {
|
||||
performSaves()
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
val data: T
|
||||
fun save()
|
||||
fun markDirty()
|
||||
fun load()
|
||||
val data: T
|
||||
fun save()
|
||||
fun markDirty()
|
||||
fun load()
|
||||
}
|
||||
|
||||
@@ -4,12 +4,70 @@ import com.mojang.blaze3d.systems.RenderSystem
|
||||
import me.shedaniel.math.Color
|
||||
import org.joml.Matrix4f
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.RenderLayer.MultiPhaseParameters
|
||||
import net.minecraft.client.render.RenderPhase
|
||||
import net.minecraft.client.render.VertexFormat
|
||||
import net.minecraft.client.render.VertexFormats
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.TriState
|
||||
import net.minecraft.util.Util
|
||||
import moe.nea.firmament.util.MC
|
||||
|
||||
fun DrawContext.isUntranslatedGuiDrawContext(): Boolean {
|
||||
return (matrices.peek().positionMatrix.properties() and Matrix4f.PROPERTY_IDENTITY.toInt()) != 0
|
||||
}
|
||||
|
||||
object GuiRenderLayers {
|
||||
val GUI_TEXTURED_NO_DEPTH = Util.memoize<Identifier, RenderLayer> { texture: Identifier ->
|
||||
RenderLayer.of("firmament_gui_textured_no_depth",
|
||||
VertexFormats.POSITION_TEXTURE_COLOR,
|
||||
VertexFormat.DrawMode.QUADS,
|
||||
RenderLayer.CUTOUT_BUFFER_SIZE,
|
||||
MultiPhaseParameters.builder()
|
||||
.texture(RenderPhase.Texture(texture, TriState.FALSE, false))
|
||||
.program(RenderPhase.POSITION_TEXTURE_COLOR_PROGRAM)
|
||||
.transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY)
|
||||
.depthTest(RenderPhase.ALWAYS_DEPTH_TEST)
|
||||
.build(false))
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Use the other drawGuiTexture")
|
||||
fun DrawContext.drawGuiTexture(
|
||||
x: Int, y: Int, z: Int, width: Int, height: Int, sprite: Identifier
|
||||
) = this.drawGuiTexture(RenderLayer::getGuiTextured, sprite, x, y, width, height)
|
||||
|
||||
fun DrawContext.drawGuiTexture(
|
||||
sprite: Identifier,
|
||||
x: Int, y: Int, width: Int, height: Int
|
||||
) = this.drawGuiTexture(RenderLayer::getGuiTextured, sprite, x, y, width, height)
|
||||
|
||||
fun DrawContext.drawTexture(
|
||||
sprite: Identifier,
|
||||
x: Int,
|
||||
y: Int,
|
||||
u: Float,
|
||||
v: Float,
|
||||
width: Int,
|
||||
height: Int,
|
||||
textureWidth: Int,
|
||||
textureHeight: Int
|
||||
) {
|
||||
this.drawTexture(RenderLayer::getGuiTextured,
|
||||
sprite,
|
||||
x,
|
||||
y,
|
||||
u,
|
||||
v,
|
||||
width,
|
||||
height,
|
||||
width,
|
||||
height,
|
||||
textureWidth,
|
||||
textureHeight)
|
||||
}
|
||||
|
||||
fun DrawContext.drawLine(fromX: Int, fromY: Int, toX: Int, toY: Int, color: Color) {
|
||||
// TODO: push scissors
|
||||
// TODO: use matrix translations and a different render layer
|
||||
@@ -18,11 +76,12 @@ fun DrawContext.drawLine(fromX: Int, fromY: Int, toX: Int, toY: Int, color: Colo
|
||||
return
|
||||
}
|
||||
RenderSystem.lineWidth(MC.window.scaleFactor.toFloat())
|
||||
val buf = this.vertexConsumers.getBuffer(RenderInWorldContext.RenderLayers.LINES)
|
||||
buf.vertex(fromX.toFloat(), fromY.toFloat(), 0F).color(color.color)
|
||||
.normal(toX - fromX.toFloat(), toY - fromY.toFloat(), 0F)
|
||||
buf.vertex(toX.toFloat(), toY.toFloat(), 0F).color(color.color)
|
||||
.normal(toX - fromX.toFloat(), toY - fromY.toFloat(), 0F)
|
||||
this.draw()
|
||||
draw { vertexConsumers ->
|
||||
val buf = vertexConsumers.getBuffer(RenderInWorldContext.RenderLayers.LINES)
|
||||
buf.vertex(fromX.toFloat(), fromY.toFloat(), 0F).color(color.color)
|
||||
.normal(toX - fromX.toFloat(), toY - fromY.toFloat(), 0F)
|
||||
buf.vertex(toX.toFloat(), toY.toFloat(), 0F).color(color.color)
|
||||
.normal(toX - fromX.toFloat(), toY - fromY.toFloat(), 0F)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,13 +76,10 @@ class FacingThePlayerContext(val worldContext: RenderInWorldContext) {
|
||||
u1: Float, v1: Float,
|
||||
u2: Float, v2: Float,
|
||||
) {
|
||||
RenderSystem.setShaderTexture(0, texture)
|
||||
RenderSystem.setShader(GameRenderer::getPositionTexColorProgram)
|
||||
val buf = worldContext.vertexConsumers.getBuffer(RenderLayer.getGuiTexturedOverlay(texture))
|
||||
val hw = width / 2F
|
||||
val hh = height / 2F
|
||||
val matrix4f: Matrix4f = worldContext.matrixStack.peek().positionMatrix
|
||||
val buf = Tessellator.getInstance()
|
||||
.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR)
|
||||
buf.vertex(matrix4f, -hw, -hh, 0F)
|
||||
.color(-1)
|
||||
.texture(u1, v1).next()
|
||||
@@ -95,7 +92,7 @@ class FacingThePlayerContext(val worldContext: RenderInWorldContext) {
|
||||
buf.vertex(matrix4f, +hw, -hh, 0F)
|
||||
.color(-1)
|
||||
.texture(u2, v1).next()
|
||||
BufferRenderer.drawWithGlobalProgram(buf.end())
|
||||
worldContext.vertexConsumers.draw()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,23 +1,30 @@
|
||||
package moe.nea.firmament.util.render
|
||||
|
||||
import net.minecraft.client.gl.ShaderProgram
|
||||
import net.minecraft.client.gl.Defines
|
||||
import net.minecraft.client.gl.ShaderProgramKey
|
||||
import net.minecraft.client.render.RenderPhase
|
||||
import net.minecraft.client.render.VertexFormat
|
||||
import net.minecraft.client.render.VertexFormats
|
||||
import moe.nea.firmament.Firmament
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.events.RegisterCustomShadersEvent
|
||||
import moe.nea.firmament.events.DebugInstantiateEvent
|
||||
import moe.nea.firmament.util.MC
|
||||
|
||||
object FirmamentShaders {
|
||||
val shaders = mutableListOf<ShaderProgramKey>()
|
||||
|
||||
private fun shader(name: String, format: VertexFormat, defines: Defines): ShaderProgramKey {
|
||||
val key = ShaderProgramKey(Firmament.identifier(name), format, defines)
|
||||
shaders.add(key)
|
||||
return key
|
||||
}
|
||||
|
||||
private lateinit var _LINES: ShaderProgram
|
||||
val LINES = RenderPhase.ShaderProgram({ _LINES })
|
||||
val LINES = RenderPhase.ShaderProgram(shader("core/rendertype_lines", VertexFormats.LINES, Defines.EMPTY))
|
||||
|
||||
@Subscribe
|
||||
fun registerCustomShaders(event: RegisterCustomShadersEvent) {
|
||||
event.register(
|
||||
"firmament_rendertype_lines",
|
||||
VertexFormats.LINES,
|
||||
{ _LINES = it },
|
||||
)
|
||||
}
|
||||
@Subscribe
|
||||
fun debugLoad(event: DebugInstantiateEvent) {
|
||||
shaders.forEach {
|
||||
MC.instance.shaderLoader.getOrCreateProgram(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
package moe.nea.firmament.util.render
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
@@ -9,7 +8,8 @@ import kotlin.math.atan2
|
||||
import kotlin.math.tan
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.render.BufferRenderer
|
||||
import net.minecraft.client.render.GameRenderer
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.RenderPhase
|
||||
import net.minecraft.client.render.Tessellator
|
||||
import net.minecraft.client.render.VertexFormat.DrawMode
|
||||
import net.minecraft.client.render.VertexFormats
|
||||
@@ -17,79 +17,77 @@ import net.minecraft.util.Identifier
|
||||
|
||||
object RenderCircleProgress {
|
||||
|
||||
fun renderCircle(
|
||||
drawContext: DrawContext,
|
||||
texture: Identifier,
|
||||
progress: Float,
|
||||
u1: Float,
|
||||
u2: Float,
|
||||
v1: Float,
|
||||
v2: Float,
|
||||
) {
|
||||
RenderSystem.setShaderTexture(0, texture)
|
||||
RenderSystem.setShader(GameRenderer::getPositionTexColorProgram)
|
||||
RenderSystem.enableBlend()
|
||||
val matrix: Matrix4f = drawContext.matrices.peek().positionMatrix
|
||||
val bufferBuilder = Tessellator.getInstance().begin(DrawMode.TRIANGLES, VertexFormats.POSITION_TEXTURE_COLOR)
|
||||
fun renderCircle(
|
||||
drawContext: DrawContext,
|
||||
texture: Identifier,
|
||||
progress: Float,
|
||||
u1: Float,
|
||||
u2: Float,
|
||||
v1: Float,
|
||||
v2: Float,
|
||||
) {
|
||||
RenderSystem.enableBlend()
|
||||
drawContext.draw {
|
||||
val bufferBuilder = it.getBuffer(RenderLayer.getGuiTexturedOverlay(texture))
|
||||
val matrix: Matrix4f = drawContext.matrices.peek().positionMatrix
|
||||
|
||||
val corners = listOf(
|
||||
Vector2f(0F, -1F),
|
||||
Vector2f(1F, -1F),
|
||||
Vector2f(1F, 0F),
|
||||
Vector2f(1F, 1F),
|
||||
Vector2f(0F, 1F),
|
||||
Vector2f(-1F, 1F),
|
||||
Vector2f(-1F, 0F),
|
||||
Vector2f(-1F, -1F),
|
||||
)
|
||||
val corners = listOf(
|
||||
Vector2f(0F, -1F),
|
||||
Vector2f(1F, -1F),
|
||||
Vector2f(1F, 0F),
|
||||
Vector2f(1F, 1F),
|
||||
Vector2f(0F, 1F),
|
||||
Vector2f(-1F, 1F),
|
||||
Vector2f(-1F, 0F),
|
||||
Vector2f(-1F, -1F),
|
||||
)
|
||||
|
||||
for (i in (0 until 8)) {
|
||||
if (progress < i / 8F) {
|
||||
break
|
||||
}
|
||||
val second = corners[(i + 1) % 8]
|
||||
val first = corners[i]
|
||||
if (progress <= (i + 1) / 8F) {
|
||||
val internalProgress = 1 - (progress - i / 8F) * 8F
|
||||
val angle = lerpAngle(
|
||||
atan2(second.y, second.x),
|
||||
atan2(first.y, first.x),
|
||||
internalProgress
|
||||
)
|
||||
if (angle < tau / 8 || angle >= tau * 7 / 8) {
|
||||
second.set(1F, tan(angle))
|
||||
} else if (angle < tau * 3 / 8) {
|
||||
second.set(1 / tan(angle), 1F)
|
||||
} else if (angle < tau * 5 / 8) {
|
||||
second.set(-1F, -tan(angle))
|
||||
} else {
|
||||
second.set(-1 / tan(angle), -1F)
|
||||
}
|
||||
}
|
||||
for (i in (0 until 8)) {
|
||||
if (progress < i / 8F) {
|
||||
break
|
||||
}
|
||||
val second = corners[(i + 1) % 8]
|
||||
val first = corners[i]
|
||||
if (progress <= (i + 1) / 8F) {
|
||||
val internalProgress = 1 - (progress - i / 8F) * 8F
|
||||
val angle = lerpAngle(
|
||||
atan2(second.y, second.x),
|
||||
atan2(first.y, first.x),
|
||||
internalProgress
|
||||
)
|
||||
if (angle < tau / 8 || angle >= tau * 7 / 8) {
|
||||
second.set(1F, tan(angle))
|
||||
} else if (angle < tau * 3 / 8) {
|
||||
second.set(1 / tan(angle), 1F)
|
||||
} else if (angle < tau * 5 / 8) {
|
||||
second.set(-1F, -tan(angle))
|
||||
} else {
|
||||
second.set(-1 / tan(angle), -1F)
|
||||
}
|
||||
}
|
||||
|
||||
fun ilerp(f: Float): Float =
|
||||
ilerp(-1f, 1f, f)
|
||||
|
||||
bufferBuilder
|
||||
.vertex(matrix, second.x, second.y, 0F)
|
||||
.texture(lerp(u1, u2, ilerp(second.x)), lerp(v1, v2, ilerp(second.y)))
|
||||
.color(-1)
|
||||
.next()
|
||||
bufferBuilder
|
||||
.vertex(matrix, first.x, first.y, 0F)
|
||||
.texture(lerp(u1, u2, ilerp(first.x)), lerp(v1, v2, ilerp(first.y)))
|
||||
.color(-1)
|
||||
.next()
|
||||
bufferBuilder
|
||||
.vertex(matrix, 0F, 0F, 0F)
|
||||
.texture(lerp(u1, u2, ilerp(0F)), lerp(v1, v2, ilerp(0F)))
|
||||
.color(-1)
|
||||
.next()
|
||||
}
|
||||
BufferRenderer.drawWithGlobalProgram(bufferBuilder.end())
|
||||
RenderSystem.disableBlend()
|
||||
}
|
||||
fun ilerp(f: Float): Float =
|
||||
ilerp(-1f, 1f, f)
|
||||
|
||||
bufferBuilder
|
||||
.vertex(matrix, second.x, second.y, 0F)
|
||||
.texture(lerp(u1, u2, ilerp(second.x)), lerp(v1, v2, ilerp(second.y)))
|
||||
.color(-1)
|
||||
.next()
|
||||
bufferBuilder
|
||||
.vertex(matrix, first.x, first.y, 0F)
|
||||
.texture(lerp(u1, u2, ilerp(first.x)), lerp(v1, v2, ilerp(first.y)))
|
||||
.color(-1)
|
||||
.next()
|
||||
bufferBuilder
|
||||
.vertex(matrix, 0F, 0F, 0F)
|
||||
.texture(lerp(u1, u2, ilerp(0F)), lerp(v1, v2, ilerp(0F)))
|
||||
.color(-1)
|
||||
.next()
|
||||
}
|
||||
}
|
||||
RenderSystem.disableBlend()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.util.render
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
@@ -8,14 +6,12 @@ import java.lang.Math.pow
|
||||
import org.joml.Matrix4f
|
||||
import org.joml.Vector3f
|
||||
import net.minecraft.client.gl.VertexBuffer
|
||||
import net.minecraft.client.render.BufferBuilder
|
||||
import net.minecraft.client.render.BufferRenderer
|
||||
import net.minecraft.client.render.Camera
|
||||
import net.minecraft.client.render.GameRenderer
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.RenderPhase
|
||||
import net.minecraft.client.render.RenderTickCounter
|
||||
import net.minecraft.client.render.Tessellator
|
||||
import net.minecraft.client.render.VertexConsumer
|
||||
import net.minecraft.client.render.VertexConsumerProvider
|
||||
import net.minecraft.client.render.VertexFormat
|
||||
import net.minecraft.client.render.VertexFormats
|
||||
@@ -31,273 +27,287 @@ import moe.nea.firmament.util.MC
|
||||
|
||||
@RenderContextDSL
|
||||
class RenderInWorldContext private constructor(
|
||||
private val tesselator: Tessellator,
|
||||
val matrixStack: MatrixStack,
|
||||
private val camera: Camera,
|
||||
private val tickCounter: RenderTickCounter,
|
||||
val vertexConsumers: VertexConsumerProvider.Immediate,
|
||||
private val tesselator: Tessellator,
|
||||
val matrixStack: MatrixStack,
|
||||
private val camera: Camera,
|
||||
private val tickCounter: RenderTickCounter,
|
||||
val vertexConsumers: VertexConsumerProvider.Immediate,
|
||||
) {
|
||||
|
||||
object RenderLayers {
|
||||
val TRANSLUCENT_TRIS = RenderLayer.of("firmament_translucent_tris",
|
||||
VertexFormats.POSITION_COLOR,
|
||||
VertexFormat.DrawMode.TRIANGLES,
|
||||
RenderLayer.DEFAULT_BUFFER_SIZE,
|
||||
false, true,
|
||||
RenderLayer.MultiPhaseParameters.builder()
|
||||
.depthTest(RenderPhase.ALWAYS_DEPTH_TEST)
|
||||
.transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY)
|
||||
.program(RenderPhase.COLOR_PROGRAM)
|
||||
.build(false))
|
||||
val LINES = RenderLayer.of("firmament_rendertype_lines",
|
||||
VertexFormats.LINES,
|
||||
VertexFormat.DrawMode.LINES,
|
||||
RenderLayer.DEFAULT_BUFFER_SIZE,
|
||||
false, false, // do we need translucent? i dont think so
|
||||
RenderLayer.MultiPhaseParameters.builder()
|
||||
.depthTest(RenderPhase.ALWAYS_DEPTH_TEST)
|
||||
.program(FirmamentShaders.LINES)
|
||||
.build(false)
|
||||
)
|
||||
}
|
||||
object RenderLayers {
|
||||
val TRANSLUCENT_TRIS = RenderLayer.of("firmament_translucent_tris",
|
||||
VertexFormats.POSITION_COLOR,
|
||||
VertexFormat.DrawMode.TRIANGLES,
|
||||
RenderLayer.CUTOUT_BUFFER_SIZE,
|
||||
false, true,
|
||||
RenderLayer.MultiPhaseParameters.builder()
|
||||
.depthTest(RenderPhase.ALWAYS_DEPTH_TEST)
|
||||
.transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY)
|
||||
.program(RenderPhase.POSITION_COLOR_PROGRAM)
|
||||
.build(false))
|
||||
val LINES = RenderLayer.of("firmament_rendertype_lines",
|
||||
VertexFormats.LINES,
|
||||
VertexFormat.DrawMode.LINES,
|
||||
RenderLayer.CUTOUT_BUFFER_SIZE,
|
||||
false, false, // do we need translucent? i dont think so
|
||||
RenderLayer.MultiPhaseParameters.builder()
|
||||
.depthTest(RenderPhase.ALWAYS_DEPTH_TEST)
|
||||
.program(FirmamentShaders.LINES)
|
||||
.build(false)
|
||||
)
|
||||
val COLORED_QUADS = RenderLayer.of(
|
||||
"firmament_quads",
|
||||
VertexFormats.POSITION_COLOR,
|
||||
VertexFormat.DrawMode.QUADS,
|
||||
RenderLayer.CUTOUT_BUFFER_SIZE,
|
||||
false, true,
|
||||
RenderLayer.MultiPhaseParameters.builder()
|
||||
.depthTest(RenderPhase.ALWAYS_DEPTH_TEST)
|
||||
.program(RenderPhase.POSITION_COLOR_PROGRAM)
|
||||
.transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY)
|
||||
.build(false)
|
||||
)
|
||||
}
|
||||
|
||||
fun color(color: me.shedaniel.math.Color) {
|
||||
color(color.red / 255F, color.green / 255f, color.blue / 255f, color.alpha / 255f)
|
||||
}
|
||||
@Deprecated("stateful color management is no longer a thing")
|
||||
fun color(color: me.shedaniel.math.Color) {
|
||||
color(color.red / 255F, color.green / 255f, color.blue / 255f, color.alpha / 255f)
|
||||
}
|
||||
|
||||
fun color(red: Float, green: Float, blue: Float, alpha: Float) {
|
||||
RenderSystem.setShaderColor(red, green, blue, alpha)
|
||||
}
|
||||
@Deprecated("stateful color management is no longer a thing")
|
||||
fun color(red: Float, green: Float, blue: Float, alpha: Float) {
|
||||
RenderSystem.setShaderColor(red, green, blue, alpha)
|
||||
}
|
||||
|
||||
fun block(blockPos: BlockPos) {
|
||||
matrixStack.push()
|
||||
matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat())
|
||||
buildCube(matrixStack.peek().positionMatrix, tesselator)
|
||||
matrixStack.pop()
|
||||
}
|
||||
fun block(blockPos: BlockPos, color: Int) {
|
||||
matrixStack.push()
|
||||
matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat())
|
||||
buildCube(matrixStack.peek().positionMatrix, vertexConsumers.getBuffer(RenderLayers.COLORED_QUADS), color)
|
||||
matrixStack.pop()
|
||||
}
|
||||
|
||||
enum class VerticalAlign {
|
||||
TOP, BOTTOM, CENTER;
|
||||
enum class VerticalAlign {
|
||||
TOP, BOTTOM, CENTER;
|
||||
|
||||
fun align(index: Int, count: Int): Float {
|
||||
return when (this) {
|
||||
CENTER -> (index - count / 2F) * (1 + MC.font.fontHeight.toFloat())
|
||||
BOTTOM -> (index - count) * (1 + MC.font.fontHeight.toFloat())
|
||||
TOP -> (index) * (1 + MC.font.fontHeight.toFloat())
|
||||
}
|
||||
}
|
||||
}
|
||||
fun align(index: Int, count: Int): Float {
|
||||
return when (this) {
|
||||
CENTER -> (index - count / 2F) * (1 + MC.font.fontHeight.toFloat())
|
||||
BOTTOM -> (index - count) * (1 + MC.font.fontHeight.toFloat())
|
||||
TOP -> (index) * (1 + MC.font.fontHeight.toFloat())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun waypoint(position: BlockPos, vararg label: Text) {
|
||||
text(
|
||||
position.toCenterPos(),
|
||||
*label,
|
||||
Text.literal("§e${FirmFormatters.formatDistance(MC.player?.pos?.distanceTo(position.toCenterPos()) ?: 42069.0)}"),
|
||||
background = 0xAA202020.toInt()
|
||||
)
|
||||
}
|
||||
fun waypoint(position: BlockPos, vararg label: Text) {
|
||||
text(
|
||||
position.toCenterPos(),
|
||||
*label,
|
||||
Text.literal("§e${FirmFormatters.formatDistance(MC.player?.pos?.distanceTo(position.toCenterPos()) ?: 42069.0)}"),
|
||||
background = 0xAA202020.toInt()
|
||||
)
|
||||
}
|
||||
|
||||
fun withFacingThePlayer(position: Vec3d, block: FacingThePlayerContext.() -> Unit) {
|
||||
matrixStack.push()
|
||||
matrixStack.translate(position.x, position.y, position.z)
|
||||
val actualCameraDistance = position.distanceTo(camera.pos)
|
||||
val distanceToMoveTowardsCamera = if (actualCameraDistance < 10) 0.0 else -(actualCameraDistance - 10.0)
|
||||
val vec = position.subtract(camera.pos).multiply(distanceToMoveTowardsCamera / actualCameraDistance)
|
||||
matrixStack.translate(vec.x, vec.y, vec.z)
|
||||
matrixStack.multiply(camera.rotation)
|
||||
matrixStack.scale(0.025F, -0.025F, 1F)
|
||||
fun withFacingThePlayer(position: Vec3d, block: FacingThePlayerContext.() -> Unit) {
|
||||
matrixStack.push()
|
||||
matrixStack.translate(position.x, position.y, position.z)
|
||||
val actualCameraDistance = position.distanceTo(camera.pos)
|
||||
val distanceToMoveTowardsCamera = if (actualCameraDistance < 10) 0.0 else -(actualCameraDistance - 10.0)
|
||||
val vec = position.subtract(camera.pos).multiply(distanceToMoveTowardsCamera / actualCameraDistance)
|
||||
matrixStack.translate(vec.x, vec.y, vec.z)
|
||||
matrixStack.multiply(camera.rotation)
|
||||
matrixStack.scale(0.025F, -0.025F, 1F)
|
||||
|
||||
FacingThePlayerContext(this).run(block)
|
||||
FacingThePlayerContext(this).run(block)
|
||||
|
||||
matrixStack.pop()
|
||||
vertexConsumers.drawCurrentLayer()
|
||||
}
|
||||
matrixStack.pop()
|
||||
vertexConsumers.drawCurrentLayer()
|
||||
}
|
||||
|
||||
fun sprite(position: Vec3d, sprite: Sprite, width: Int, height: Int) {
|
||||
texture(
|
||||
position, sprite.atlasId, width, height, sprite.minU, sprite.minV, sprite.maxU, sprite.maxV
|
||||
)
|
||||
}
|
||||
fun sprite(position: Vec3d, sprite: Sprite, width: Int, height: Int) {
|
||||
texture(
|
||||
position, sprite.atlasId, width, height, sprite.minU, sprite.minV, sprite.maxU, sprite.maxV
|
||||
)
|
||||
}
|
||||
|
||||
fun texture(
|
||||
position: Vec3d, texture: Identifier, width: Int, height: Int,
|
||||
u1: Float, v1: Float,
|
||||
u2: Float, v2: Float,
|
||||
) {
|
||||
withFacingThePlayer(position) {
|
||||
texture(texture, width, height, u1, v1, u2, v2)
|
||||
}
|
||||
}
|
||||
fun texture(
|
||||
position: Vec3d, texture: Identifier, width: Int, height: Int,
|
||||
u1: Float, v1: Float,
|
||||
u2: Float, v2: Float,
|
||||
) {
|
||||
withFacingThePlayer(position) {
|
||||
texture(texture, width, height, u1, v1, u2, v2)
|
||||
}
|
||||
}
|
||||
|
||||
fun text(position: Vec3d, vararg texts: Text, verticalAlign: VerticalAlign = VerticalAlign.CENTER, background: Int = 0x70808080) {
|
||||
withFacingThePlayer(position) {
|
||||
text(*texts, verticalAlign = verticalAlign, background = background)
|
||||
}
|
||||
}
|
||||
fun text(
|
||||
position: Vec3d,
|
||||
vararg texts: Text,
|
||||
verticalAlign: VerticalAlign = VerticalAlign.CENTER,
|
||||
background: Int = 0x70808080
|
||||
) {
|
||||
withFacingThePlayer(position) {
|
||||
text(*texts, verticalAlign = verticalAlign, background = background)
|
||||
}
|
||||
}
|
||||
|
||||
fun tinyBlock(vec3d: Vec3d, size: Float) {
|
||||
RenderSystem.setShader(GameRenderer::getPositionColorProgram)
|
||||
matrixStack.push()
|
||||
matrixStack.translate(vec3d.x, vec3d.y, vec3d.z)
|
||||
matrixStack.scale(size, size, size)
|
||||
matrixStack.translate(-.5, -.5, -.5)
|
||||
buildCube(matrixStack.peek().positionMatrix, tesselator)
|
||||
matrixStack.pop()
|
||||
}
|
||||
fun tinyBlock(vec3d: Vec3d, size: Float, color: Int) {
|
||||
matrixStack.push()
|
||||
matrixStack.translate(vec3d.x, vec3d.y, vec3d.z)
|
||||
matrixStack.scale(size, size, size)
|
||||
matrixStack.translate(-.5, -.5, -.5)
|
||||
buildCube(matrixStack.peek().positionMatrix, vertexConsumers.getBuffer(RenderLayers.COLORED_QUADS), color)
|
||||
matrixStack.pop()
|
||||
vertexConsumers.draw()
|
||||
}
|
||||
|
||||
fun wireframeCube(blockPos: BlockPos, lineWidth: Float = 10F) {
|
||||
RenderSystem.setShader(GameRenderer::getRenderTypeLinesProgram)
|
||||
matrixStack.push()
|
||||
RenderSystem.lineWidth(lineWidth / pow(camera.pos.squaredDistanceTo(blockPos.toCenterPos()), 0.25).toFloat())
|
||||
matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat())
|
||||
buildWireFrameCube(matrixStack.peek(), tesselator)
|
||||
matrixStack.pop()
|
||||
}
|
||||
fun wireframeCube(blockPos: BlockPos, lineWidth: Float = 10F) {
|
||||
val buf = vertexConsumers.getBuffer(RenderLayer.LINES)
|
||||
matrixStack.push()
|
||||
// TODO: this does not render through blocks (or water layers) anymore
|
||||
RenderSystem.lineWidth(lineWidth / pow(camera.pos.squaredDistanceTo(blockPos.toCenterPos()), 0.25).toFloat())
|
||||
matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat())
|
||||
buildWireFrameCube(matrixStack.peek(), buf)
|
||||
matrixStack.pop()
|
||||
vertexConsumers.draw()
|
||||
}
|
||||
|
||||
fun line(vararg points: Vec3d, lineWidth: Float = 10F) {
|
||||
line(points.toList(), lineWidth)
|
||||
}
|
||||
fun line(vararg points: Vec3d, lineWidth: Float = 10F) {
|
||||
line(points.toList(), lineWidth)
|
||||
}
|
||||
|
||||
fun tracer(toWhere: Vec3d, lineWidth: Float = 3f) {
|
||||
val cameraForward = Vector3f(0f, 0f, -1f).rotate(camera.rotation)
|
||||
line(camera.pos.add(Vec3d(cameraForward)), toWhere, lineWidth = lineWidth)
|
||||
}
|
||||
fun tracer(toWhere: Vec3d, lineWidth: Float = 3f) {
|
||||
val cameraForward = Vector3f(0f, 0f, -1f).rotate(camera.rotation)
|
||||
line(camera.pos.add(Vec3d(cameraForward)), toWhere, lineWidth = lineWidth)
|
||||
}
|
||||
|
||||
fun line(points: List<Vec3d>, lineWidth: Float = 10F) {
|
||||
RenderSystem.lineWidth(lineWidth)
|
||||
val buffer = tesselator.begin(VertexFormat.DrawMode.LINES, VertexFormats.LINES)
|
||||
fun line(points: List<Vec3d>, lineWidth: Float = 10F) {
|
||||
RenderSystem.lineWidth(lineWidth)
|
||||
// TODO: replace with renderlayers
|
||||
val buffer = tesselator.begin(VertexFormat.DrawMode.LINES, VertexFormats.LINES)
|
||||
|
||||
val matrix = matrixStack.peek()
|
||||
var lastNormal: Vector3f? = null
|
||||
points.zipWithNext().forEach { (a, b) ->
|
||||
val normal = Vector3f(b.x.toFloat(), b.y.toFloat(), b.z.toFloat())
|
||||
.sub(a.x.toFloat(), a.y.toFloat(), a.z.toFloat())
|
||||
.normalize()
|
||||
val lastNormal0 = lastNormal ?: normal
|
||||
lastNormal = normal
|
||||
buffer.vertex(matrix.positionMatrix, a.x.toFloat(), a.y.toFloat(), a.z.toFloat())
|
||||
.color(-1)
|
||||
.normal(matrix, lastNormal0.x, lastNormal0.y, lastNormal0.z)
|
||||
.next()
|
||||
buffer.vertex(matrix.positionMatrix, b.x.toFloat(), b.y.toFloat(), b.z.toFloat())
|
||||
.color(-1)
|
||||
.normal(matrix, normal.x, normal.y, normal.z)
|
||||
.next()
|
||||
}
|
||||
val matrix = matrixStack.peek()
|
||||
var lastNormal: Vector3f? = null
|
||||
points.zipWithNext().forEach { (a, b) ->
|
||||
val normal = Vector3f(b.x.toFloat(), b.y.toFloat(), b.z.toFloat())
|
||||
.sub(a.x.toFloat(), a.y.toFloat(), a.z.toFloat())
|
||||
.normalize()
|
||||
val lastNormal0 = lastNormal ?: normal
|
||||
lastNormal = normal
|
||||
buffer.vertex(matrix.positionMatrix, a.x.toFloat(), a.y.toFloat(), a.z.toFloat())
|
||||
.color(-1)
|
||||
.normal(matrix, lastNormal0.x, lastNormal0.y, lastNormal0.z)
|
||||
.next()
|
||||
buffer.vertex(matrix.positionMatrix, b.x.toFloat(), b.y.toFloat(), b.z.toFloat())
|
||||
.color(-1)
|
||||
.normal(matrix, normal.x, normal.y, normal.z)
|
||||
.next()
|
||||
}
|
||||
|
||||
RenderLayers.LINES.draw(buffer.end())
|
||||
}
|
||||
RenderLayers.LINES.draw(buffer.end())
|
||||
}
|
||||
// TODO: put the favourite icons in front of items again
|
||||
|
||||
companion object {
|
||||
private fun doLine(
|
||||
matrix: MatrixStack.Entry,
|
||||
buf: BufferBuilder,
|
||||
i: Float,
|
||||
j: Float,
|
||||
k: Float,
|
||||
x: Float,
|
||||
y: Float,
|
||||
z: Float
|
||||
) {
|
||||
val normal = Vector3f(x, y, z)
|
||||
.sub(i, j, k)
|
||||
.normalize()
|
||||
buf.vertex(matrix.positionMatrix, i, j, k)
|
||||
.normal(matrix, normal.x, normal.y, normal.z)
|
||||
.color(-1)
|
||||
.next()
|
||||
buf.vertex(matrix.positionMatrix, x, y, z)
|
||||
.normal(matrix, normal.x, normal.y, normal.z)
|
||||
.color(-1)
|
||||
.next()
|
||||
}
|
||||
companion object {
|
||||
private fun doLine(
|
||||
matrix: MatrixStack.Entry,
|
||||
buf: VertexConsumer,
|
||||
i: Float,
|
||||
j: Float,
|
||||
k: Float,
|
||||
x: Float,
|
||||
y: Float,
|
||||
z: Float
|
||||
) {
|
||||
val normal = Vector3f(x, y, z)
|
||||
.sub(i, j, k)
|
||||
.normalize()
|
||||
buf.vertex(matrix.positionMatrix, i, j, k)
|
||||
.normal(matrix, normal.x, normal.y, normal.z)
|
||||
.color(-1)
|
||||
.next()
|
||||
buf.vertex(matrix.positionMatrix, x, y, z)
|
||||
.normal(matrix, normal.x, normal.y, normal.z)
|
||||
.color(-1)
|
||||
.next()
|
||||
}
|
||||
|
||||
|
||||
private fun buildWireFrameCube(matrix: MatrixStack.Entry, tessellator: Tessellator) {
|
||||
val buf = tessellator.begin(VertexFormat.DrawMode.LINES, VertexFormats.LINES)
|
||||
private fun buildWireFrameCube(matrix: MatrixStack.Entry, buf: VertexConsumer) {
|
||||
for (i in 0..1) {
|
||||
for (j in 0..1) {
|
||||
val i = i.toFloat()
|
||||
val j = j.toFloat()
|
||||
doLine(matrix, buf, 0F, i, j, 1F, i, j)
|
||||
doLine(matrix, buf, i, 0F, j, i, 1F, j)
|
||||
doLine(matrix, buf, i, j, 0F, i, j, 1F)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i in 0..1) {
|
||||
for (j in 0..1) {
|
||||
val i = i.toFloat()
|
||||
val j = j.toFloat()
|
||||
doLine(matrix, buf, 0F, i, j, 1F, i, j)
|
||||
doLine(matrix, buf, i, 0F, j, i, 1F, j)
|
||||
doLine(matrix, buf, i, j, 0F, i, j, 1F)
|
||||
}
|
||||
}
|
||||
BufferRenderer.drawWithGlobalProgram(buf.end())
|
||||
}
|
||||
|
||||
private fun buildCube(matrix: Matrix4f, tessellator: Tessellator) {
|
||||
val buf = tessellator.begin(VertexFormat.DrawMode.TRIANGLES, VertexFormats.POSITION_COLOR)
|
||||
buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next()
|
||||
buf.vertex(matrix, 0.0F, 0.0F, 1.0F).color(-1).next()
|
||||
buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next()
|
||||
buf.vertex(matrix, 1.0F, 1.0F, 0.0F).color(-1).next()
|
||||
buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next()
|
||||
buf.vertex(matrix, 0.0F, 1.0F, 0.0F).color(-1).next()
|
||||
buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next()
|
||||
buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next()
|
||||
buf.vertex(matrix, 1.0F, 0.0F, 0.0F).color(-1).next()
|
||||
buf.vertex(matrix, 1.0F, 1.0F, 0.0F).color(-1).next()
|
||||
buf.vertex(matrix, 1.0F, 0.0F, 0.0F).color(-1).next()
|
||||
buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next()
|
||||
buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next()
|
||||
buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next()
|
||||
buf.vertex(matrix, 0.0F, 1.0F, 0.0F).color(-1).next()
|
||||
buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next()
|
||||
buf.vertex(matrix, 0.0F, 0.0F, 1.0F).color(-1).next()
|
||||
buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next()
|
||||
buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next()
|
||||
buf.vertex(matrix, 0.0F, 0.0F, 1.0F).color(-1).next()
|
||||
buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next()
|
||||
buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next()
|
||||
buf.vertex(matrix, 1.0F, 0.0F, 0.0F).color(-1).next()
|
||||
buf.vertex(matrix, 1.0F, 1.0F, 0.0F).color(-1).next()
|
||||
buf.vertex(matrix, 1.0F, 0.0F, 0.0F).color(-1).next()
|
||||
buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next()
|
||||
buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next()
|
||||
buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next()
|
||||
buf.vertex(matrix, 1.0F, 1.0F, 0.0F).color(-1).next()
|
||||
buf.vertex(matrix, 0.0F, 1.0F, 0.0F).color(-1).next()
|
||||
buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next()
|
||||
buf.vertex(matrix, 0.0F, 1.0F, 0.0F).color(-1).next()
|
||||
buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next()
|
||||
buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next()
|
||||
buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next()
|
||||
buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next()
|
||||
RenderLayers.TRANSLUCENT_TRIS.draw(buf.end())
|
||||
}
|
||||
private fun buildCube(matrix: Matrix4f, buf: VertexConsumer, color: Int) {
|
||||
// Y-
|
||||
buf.vertex(matrix, 0F, 0F, 0F).color(color)
|
||||
buf.vertex(matrix, 0F, 0F, 1F).color(color)
|
||||
buf.vertex(matrix, 1F, 0F, 1F).color(color)
|
||||
buf.vertex(matrix, 1F, 0F, 0F).color(color)
|
||||
// Y+
|
||||
buf.vertex(matrix, 0F, 1F, 0F).color(color)
|
||||
buf.vertex(matrix, 1F, 1F, 0F).color(color)
|
||||
buf.vertex(matrix, 1F, 1F, 1F).color(color)
|
||||
buf.vertex(matrix, 0F, 1F, 1F).color(color)
|
||||
// X-
|
||||
buf.vertex(matrix, 0F, 0F, 0F).color(color)
|
||||
buf.vertex(matrix, 0F, 0F, 1F).color(color)
|
||||
buf.vertex(matrix, 0F, 1F, 1F).color(color)
|
||||
buf.vertex(matrix, 0F, 1F, 0F).color(color)
|
||||
// X+
|
||||
buf.vertex(matrix, 1F, 0F, 0F).color(color)
|
||||
buf.vertex(matrix, 1F, 1F, 0F).color(color)
|
||||
buf.vertex(matrix, 1F, 1F, 1F).color(color)
|
||||
buf.vertex(matrix, 1F, 0F, 1F).color(color)
|
||||
// Z-
|
||||
buf.vertex(matrix, 0F, 0F, 0F).color(color)
|
||||
buf.vertex(matrix, 1F, 0F, 0F).color(color)
|
||||
buf.vertex(matrix, 1F, 1F, 0F).color(color)
|
||||
buf.vertex(matrix, 0F, 1F, 0F).color(color)
|
||||
// Z+
|
||||
buf.vertex(matrix, 0F, 0F, 1F).color(color)
|
||||
buf.vertex(matrix, 0F, 1F, 1F).color(color)
|
||||
buf.vertex(matrix, 1F, 1F, 1F).color(color)
|
||||
buf.vertex(matrix, 1F, 0F, 1F).color(color)
|
||||
}
|
||||
|
||||
|
||||
fun renderInWorld(event: WorldRenderLastEvent, block: RenderInWorldContext. () -> Unit) {
|
||||
RenderSystem.disableDepthTest()
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.defaultBlendFunc()
|
||||
RenderSystem.disableCull()
|
||||
fun renderInWorld(event: WorldRenderLastEvent, block: RenderInWorldContext. () -> Unit) {
|
||||
// TODO: there should be *no more global state*. the only thing we should be doing is render layers. that includes settings like culling, blending, shader color, and depth testing
|
||||
// For now i will let these functions remain, but this needs to go before i do a full (non-beta) release
|
||||
RenderSystem.disableDepthTest()
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.defaultBlendFunc()
|
||||
RenderSystem.disableCull()
|
||||
|
||||
event.matrices.push()
|
||||
event.matrices.translate(-event.camera.pos.x, -event.camera.pos.y, -event.camera.pos.z)
|
||||
event.matrices.push()
|
||||
event.matrices.translate(-event.camera.pos.x, -event.camera.pos.y, -event.camera.pos.z)
|
||||
|
||||
val ctx = RenderInWorldContext(
|
||||
RenderSystem.renderThreadTesselator(),
|
||||
event.matrices,
|
||||
event.camera,
|
||||
event.tickCounter,
|
||||
event.vertexConsumers
|
||||
)
|
||||
val ctx = RenderInWorldContext(
|
||||
RenderSystem.renderThreadTesselator(),
|
||||
event.matrices,
|
||||
event.camera,
|
||||
event.tickCounter,
|
||||
event.vertexConsumers
|
||||
)
|
||||
|
||||
block(ctx)
|
||||
block(ctx)
|
||||
|
||||
event.matrices.pop()
|
||||
|
||||
RenderSystem.setShaderColor(1F, 1F, 1F, 1F)
|
||||
VertexBuffer.unbind()
|
||||
RenderSystem.enableDepthTest()
|
||||
RenderSystem.enableCull()
|
||||
RenderSystem.disableBlend()
|
||||
}
|
||||
}
|
||||
event.matrices.pop()
|
||||
event.vertexConsumers.draw()
|
||||
RenderSystem.setShaderColor(1F, 1F, 1F, 1F)
|
||||
VertexBuffer.unbind()
|
||||
RenderSystem.enableDepthTest()
|
||||
RenderSystem.enableCull()
|
||||
RenderSystem.disableBlend()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user