Revisit how android research datapacks work

Instead of "feature results" provide them with just "results", which reference arbitrary features from registry
Do the same to research description, with its own registry
This commit is contained in:
DBotThePony 2023-06-19 21:37:43 +07:00
parent dfa14ca2a1
commit 09db4ad9e9
Signed by: DBot
GPG Key ID: DCC23B5715498507
19 changed files with 508 additions and 709 deletions

View File

@ -4,6 +4,9 @@ import net.minecraft.tags.ItemTags
import net.minecraft.world.item.Items import net.minecraft.world.item.Items
import net.minecraftforge.common.Tags import net.minecraftforge.common.Tags
import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.android.AndroidResearchDescriptions
import ru.dbotthepony.mc.otm.android.AndroidResearchResult
import ru.dbotthepony.mc.otm.android.AndroidResearchResults
import ru.dbotthepony.mc.otm.android.AndroidResearchType import ru.dbotthepony.mc.otm.android.AndroidResearchType
import ru.dbotthepony.mc.otm.android.feature.EnderTeleporterFeature import ru.dbotthepony.mc.otm.android.feature.EnderTeleporterFeature
import ru.dbotthepony.mc.otm.android.feature.FallDampenersFeature import ru.dbotthepony.mc.otm.android.feature.FallDampenersFeature
@ -132,11 +135,13 @@ fun addResearchData(serializer: Consumer<AndroidResearchType>, lang: MatteryLang
) )
) )
.addItem(MItemTags.COPPER_WIRES, 4 + i * 2) .addItem(MItemTags.COPPER_WIRES, 4 + i * 2)
.addFeatureResult(OverdriveThatMatters.loc(MNames.LIMB_OVERCLOCKING), i)
if (i > 0) { if (i > 0) {
research.addFeatureLevel(AndroidFeatures.LIMB_OVERCLOCKING)
research.addPrerequisite(OverdriveThatMatters.loc(MNames.LIMB_OVERCLOCKING_LIST[i - 1]), rigid = true) research.addPrerequisite(OverdriveThatMatters.loc(MNames.LIMB_OVERCLOCKING_LIST[i - 1]), rigid = true)
research.addItem(MItemTags.GOLD_WIRES, i * 2) research.addItem(MItemTags.GOLD_WIRES, i * 2)
} else {
research.addFeatureResult(AndroidFeatures.LIMB_OVERCLOCKING)
} }
research.build() research.build()
@ -155,18 +160,20 @@ fun addResearchData(serializer: Consumer<AndroidResearchType>, lang: MatteryLang
(i + 1) * 15 (i + 1) * 15
) )
) )
.addFeatureResult(AndroidFeatures.ATTACK_BOOST, i)
.addBlocker(NANOBOTS_ARMOR) .addBlocker(NANOBOTS_ARMOR)
if (i > 0) { if (i > 0) {
research.addFeatureLevel(AndroidFeatures.ATTACK_BOOST)
research.addPrerequisite(OverdriveThatMatters.loc(MNames.ATTACK_BOOST_LIST[i - 1]), rigid = true) research.addPrerequisite(OverdriveThatMatters.loc(MNames.ATTACK_BOOST_LIST[i - 1]), rigid = true)
} else {
research.addFeatureResult(AndroidFeatures.ATTACK_BOOST)
} }
research.build() research.build()
}) })
regenList.add(run { regenList.add(run {
val regeneration = AndroidResearchType.Builder(modLocation(MNames.NANOBOTS_REGENERATION_LIST[i])) val research = AndroidResearchType.Builder(modLocation(MNames.NANOBOTS_REGENERATION_LIST[i]))
.withExperience(20 + i * 6) .withExperience(20 + i * 6)
.withIconText(TextComponent((i + 1).toString())) .withIconText(TextComponent((i + 1).toString()))
.withIcon(ResearchIcons.ICON_NANOBOTS) .withIcon(ResearchIcons.ICON_NANOBOTS)
@ -179,15 +186,16 @@ fun addResearchData(serializer: Consumer<AndroidResearchType>, lang: MatteryLang
.addItem(MItems.MATTER_CAPACITOR_PARTS, 1) .addItem(MItems.MATTER_CAPACITOR_PARTS, 1)
.addItem(Items.SUGAR, 2 + i * 2) .addItem(Items.SUGAR, 2 + i * 2)
.addItem(Tags.Items.DUSTS_REDSTONE, 2 + i * 2) .addItem(Tags.Items.DUSTS_REDSTONE, 2 + i * 2)
.addFeatureResult(AndroidFeatures.NANOBOTS_REGENERATION, i)
if (i > 0) { if (i > 0) {
regeneration.addPrerequisite(OverdriveThatMatters.loc(MNames.NANOBOTS_REGENERATION_LIST[i - 1]), rigid = true) research.addFeatureLevel(AndroidFeatures.NANOBOTS_REGENERATION)
research.addPrerequisite(OverdriveThatMatters.loc(MNames.NANOBOTS_REGENERATION_LIST[i - 1]), rigid = true)
} else { } else {
regeneration.addPrerequisite(OverdriveThatMatters.loc(MNames.NANOBOTS), rigid = true) research.addPrerequisite(OverdriveThatMatters.loc(MNames.NANOBOTS), rigid = true)
research.addFeatureResult(AndroidFeatures.NANOBOTS_REGENERATION)
} }
regeneration.build() research.build()
}) })
} }
@ -216,10 +224,7 @@ fun addResearchData(serializer: Consumer<AndroidResearchType>, lang: MatteryLang
.addItem(MItemTags.TRITANIUM_PLATES, 2 + i * 2) .addItem(MItemTags.TRITANIUM_PLATES, 2 + i * 2)
.addItem(Items.SUGAR, 1 + i) .addItem(Items.SUGAR, 1 + i)
.addItem(MItems.ELECTROMAGNET) .addItem(MItems.ELECTROMAGNET)
.addFeatureResult(AndroidFeatures.NANOBOTS_ARMOR, 0, .addResult(AndroidResearchResults.NANOBOTS_ARMOR_STRENGTH)
transformersUp = listOf(NanobotsArmorFeature.STRENGTH_TRANSFORMER_UP.bind(level)),
transformersDown = listOf(NanobotsArmorFeature.STRENGTH_TRANSFORMER_DOWN.bind(level)),
)
.build() .build()
}) })
@ -243,10 +248,7 @@ fun addResearchData(serializer: Consumer<AndroidResearchType>, lang: MatteryLang
) )
) )
.addItem(Tags.Items.DUSTS_REDSTONE, 4 + i * 4) .addItem(Tags.Items.DUSTS_REDSTONE, 4 + i * 4)
.addFeatureResult(AndroidFeatures.NANOBOTS_ARMOR, 0, .addResult(AndroidResearchResults.NANOBOTS_ARMOR_SPEED)
transformersUp = listOf(NanobotsArmorFeature.SPEED_TRANSFORMER_UP.bind(level)),
transformersDown = listOf(NanobotsArmorFeature.SPEED_TRANSFORMER_DOWN.bind(level)),
)
.build() .build()
}) })
} }
@ -261,7 +263,7 @@ fun addResearchData(serializer: Consumer<AndroidResearchType>, lang: MatteryLang
AndroidResearchType.Builder(modLocation(MNames.SHOCKWAVE)) AndroidResearchType.Builder(modLocation(MNames.SHOCKWAVE))
.withExperience(40) .withExperience(40)
.withDescription(0 .. 1) .withDescription(0 .. 1)
.appendDescription(ShockwaveFeature.POWER_COST_DESCRIPTION) .withDescription(AndroidResearchDescriptions.SHOCKWAVE)
.withIcon(ResearchIcons.ICON_SHOCKWAVE) .withIcon(ResearchIcons.ICON_SHOCKWAVE)
.addFeatureResult(AndroidFeatures.SHOCKWAVE) .addFeatureResult(AndroidFeatures.SHOCKWAVE)
.addPrerequisite(attackBoostList[2]) .addPrerequisite(attackBoostList[2])
@ -276,7 +278,7 @@ fun addResearchData(serializer: Consumer<AndroidResearchType>, lang: MatteryLang
AndroidResearchType.Builder(modLocation(MNames.ITEM_MAGNET)) AndroidResearchType.Builder(modLocation(MNames.ITEM_MAGNET))
.withExperience(28) .withExperience(28)
.withDescription(0 .. 1) .withDescription(0 .. 1)
.appendDescription(ItemMagnetFeature.POWER_COST_DESCRIPTION) .withDescription(AndroidResearchDescriptions.ITEM_MAGNET)
.withIcon(ResearchIcons.ICON_ITEM_MAGNET) .withIcon(ResearchIcons.ICON_ITEM_MAGNET)
.addFeatureResult(AndroidFeatures.ITEM_MAGNET) .addFeatureResult(AndroidFeatures.ITEM_MAGNET)
.addPrerequisite(STEP_ASSIST) .addPrerequisite(STEP_ASSIST)
@ -292,9 +294,9 @@ fun addResearchData(serializer: Consumer<AndroidResearchType>, lang: MatteryLang
AndroidResearchType.Builder(modLocation(MNames.FALL_DAMPENERS + "_1")) AndroidResearchType.Builder(modLocation(MNames.FALL_DAMPENERS + "_1"))
.withExperience(25) .withExperience(25)
.withDescription() .withDescription()
.appendDescription(FallDampenersFeature.DESCRIPTION.bind(1)) .withDescription(AndroidResearchDescriptions.FALL_DAMPENERS.Instance(1))
.withIcon(ResearchIcons.ICON_FEATHER_FALLING) .withIcon(ResearchIcons.ICON_FEATHER_FALLING)
.addFeatureResult(AndroidFeatures.FALL_DAMPENERS, 0) .addFeatureResult(AndroidFeatures.FALL_DAMPENERS)
.addPrerequisite(STEP_ASSIST) .addPrerequisite(STEP_ASSIST)
.addItem(MItems.ELECTROMAGNET, 2) .addItem(MItems.ELECTROMAGNET, 2)
.addItem(ItemTags.WOOL, 2) .addItem(ItemTags.WOOL, 2)
@ -305,9 +307,9 @@ fun addResearchData(serializer: Consumer<AndroidResearchType>, lang: MatteryLang
AndroidResearchType.Builder(modLocation(MNames.FALL_DAMPENERS + "_2")) AndroidResearchType.Builder(modLocation(MNames.FALL_DAMPENERS + "_2"))
.withExperience(30) .withExperience(30)
.withDescription() .withDescription()
.appendDescription(FallDampenersFeature.DESCRIPTION.bind(2)) .withDescription(AndroidResearchDescriptions.FALL_DAMPENERS.Instance(2))
.withIcon(ResearchIcons.ICON_FEATHER_FALLING) .withIcon(ResearchIcons.ICON_FEATHER_FALLING)
.addFeatureResult(AndroidFeatures.FALL_DAMPENERS, 1) .addFeatureLevel(AndroidFeatures.FALL_DAMPENERS)
.addPrerequisite(FALL_DAMPENERS_1) .addPrerequisite(FALL_DAMPENERS_1)
.addItem(MItemTags.GOLD_PLATES, 2) .addItem(MItemTags.GOLD_PLATES, 2)
.addItem(MItemTags.COPPER_WIRES, 4) .addItem(MItemTags.COPPER_WIRES, 4)
@ -319,9 +321,9 @@ fun addResearchData(serializer: Consumer<AndroidResearchType>, lang: MatteryLang
AndroidResearchType.Builder(modLocation(MNames.FALL_DAMPENERS + "_3")) AndroidResearchType.Builder(modLocation(MNames.FALL_DAMPENERS + "_3"))
.withExperience(35) .withExperience(35)
.withDescription(0 .. 1) .withDescription(0 .. 1)
.appendDescription(FallDampenersFeature.DESCRIPTION.bind(3)) .withDescription(AndroidResearchDescriptions.FALL_DAMPENERS.Instance(2))
.withIcon(ResearchIcons.ICON_FEATHER_FALLING) .withIcon(ResearchIcons.ICON_FEATHER_FALLING)
.addFeatureResult(AndroidFeatures.FALL_DAMPENERS, 2) .addFeatureLevel(AndroidFeatures.FALL_DAMPENERS)
.addPrerequisite(FALL_DAMPENERS_2) .addPrerequisite(FALL_DAMPENERS_2)
.addItem(MItemTags.ADVANCED_CIRCUIT, 2) .addItem(MItemTags.ADVANCED_CIRCUIT, 2)
.addItem(Tags.Items.GEMS_DIAMOND, 4) .addItem(Tags.Items.GEMS_DIAMOND, 4)
@ -337,7 +339,7 @@ fun addResearchData(serializer: Consumer<AndroidResearchType>, lang: MatteryLang
AndroidResearchType.Builder(modLocation(MNames.ENDER_TELEPORTER)) AndroidResearchType.Builder(modLocation(MNames.ENDER_TELEPORTER))
.withExperience(35) .withExperience(35)
.withDescription() .withDescription()
.appendDescription(EnderTeleporterFeature.POWER_COST_DESCRIPTION) .withDescription(AndroidResearchDescriptions.ENDER_TELEPORTER)
.withIcon(ResearchIcons.ICON_ENDER_TELEPORT) .withIcon(ResearchIcons.ICON_ENDER_TELEPORT)
.addFeatureResult(AndroidFeatures.ENDER_TELEPORTER) .addFeatureResult(AndroidFeatures.ENDER_TELEPORTER)
.addPrerequisite(FALL_DAMPENERS_1) .addPrerequisite(FALL_DAMPENERS_1)
@ -368,9 +370,9 @@ fun addResearchData(serializer: Consumer<AndroidResearchType>, lang: MatteryLang
AndroidResearchType.Builder(modLocation(MNames.JUMP_BOOST + "_1")) AndroidResearchType.Builder(modLocation(MNames.JUMP_BOOST + "_1"))
.withExperience(27) .withExperience(27)
.withDescription(0 .. 1) .withDescription(0 .. 1)
.appendDescription(JumpBoostFeature.POWER_COST_DESCRIPTION) .withDescription(AndroidResearchDescriptions.JUMP_BOOST)
.withIcon(ResearchIcons.ICON_JUMP_BOOST) .withIcon(ResearchIcons.ICON_JUMP_BOOST)
.addFeatureResult(AndroidFeatures.JUMP_BOOST, 0) .addFeatureResult(AndroidFeatures.JUMP_BOOST)
.addItem(MItemTags.PISTONS, 2) .addItem(MItemTags.PISTONS, 2)
.addItem(MItemTags.GOLD_WIRES, 4) .addItem(MItemTags.GOLD_WIRES, 4)
.addItem(MItems.ELECTROMAGNET, 2) .addItem(MItems.ELECTROMAGNET, 2)
@ -382,9 +384,9 @@ fun addResearchData(serializer: Consumer<AndroidResearchType>, lang: MatteryLang
AndroidResearchType.Builder(modLocation(MNames.JUMP_BOOST + "_2")) AndroidResearchType.Builder(modLocation(MNames.JUMP_BOOST + "_2"))
.withExperience(34) .withExperience(34)
.withDescription() .withDescription()
.appendDescription(JumpBoostFeature.POWER_COST_DESCRIPTION) .withDescription(AndroidResearchDescriptions.JUMP_BOOST)
.withIcon(ResearchIcons.ICON_JUMP_BOOST) .withIcon(ResearchIcons.ICON_JUMP_BOOST)
.addFeatureResult(AndroidFeatures.JUMP_BOOST, 1) .addFeatureLevel(AndroidFeatures.JUMP_BOOST)
.addItem(MItems.ELECTRIC_PARTS, 4) .addItem(MItems.ELECTRIC_PARTS, 4)
.addItem(MItems.ELECTROMAGNET, 4) .addItem(MItems.ELECTROMAGNET, 4)
.addPrerequisite(JUMP_BOOST_1) .addPrerequisite(JUMP_BOOST_1)

View File

@ -1,7 +1,6 @@
package ru.dbotthepony.mc.otm.android package ru.dbotthepony.mc.otm.android
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag import net.minecraft.nbt.ListTag
@ -15,7 +14,6 @@ import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
import ru.dbotthepony.mc.otm.capability.awareItemsStream import ru.dbotthepony.mc.otm.capability.awareItemsStream
import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.addAll
import ru.dbotthepony.mc.otm.core.nbt.getCompoundList import ru.dbotthepony.mc.otm.core.nbt.getCompoundList
import ru.dbotthepony.mc.otm.core.registryName import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
@ -46,62 +44,19 @@ class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlay
return return
} }
onUnResearch() onRefunded()
isResearched = false isResearched = false
} }
private data class RememberResearchLevel(val level: Int?) fun onRefunded() {
for (result in type.results) {
private val oldResearchLevel = Object2ObjectArrayMap<AndroidFeatureType<*>, RememberResearchLevel>() result.onRefunded(this)
fun onUnResearch() {
for (feature in type.resolvedFeatures) {
val level = oldResearchLevel[feature.feature]
val get = capability.getFeature(feature.feature)
if (level != null && get != null) {
if (get.level == feature.level) {
if (level.level == null) {
capability.removeFeature(feature.feature)
} else {
get.level = level.level
for (transformer in type.features.first { it.id == feature.feature.registryName }.transformersDown) {
transformer.apply(this to get)
}
}
}
}
} }
oldResearchLevel.clear()
} }
fun onResearched() { fun onResearched() {
oldResearchLevel.clear() for (result in type.results) {
result.onResearched(this)
try {
for (feature in type.resolvedFeatures) {
var get = capability.getFeature(feature.feature)
if (get == null) {
get = capability.addFeature(feature.feature)
get.level = feature.level
oldResearchLevel[feature.feature] = RememberResearchLevel(null)
} else {
if (get.level < feature.level) {
oldResearchLevel[feature.feature] = RememberResearchLevel(feature.level)
get.level = feature.level
}
}
for (transformer in type.features.first { it.id == feature.feature.registryName }.transformersUp) {
transformer.apply(this to get)
}
}
} catch(err: Throwable) {
oldResearchLevel.clear()
throw err
} }
} }
@ -297,7 +252,10 @@ class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlay
val tooltipLines: List<Component> get() { val tooltipLines: List<Component> get() {
val lines = ArrayList<Component>() val lines = ArrayList<Component>()
lines.add(type.displayName) lines.add(type.displayName)
lines.addAll(type.description.iterator())
for (line in type.description) {
line.addLines(this, lines)
}
return lines return lines
} }
@ -382,38 +340,10 @@ class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlay
override fun serializeNBT(): CompoundTag { override fun serializeNBT(): CompoundTag {
return CompoundTag().also { return CompoundTag().also {
it["researched"] = isResearched it["researched"] = isResearched
it["oldResearchLevel"] = ListTag().also {
for ((k, v) in oldResearchLevel) {
it.add(CompoundTag().also {
it["key"] = k.registryName!!.toString()
it["value"] = CompoundTag().also {
it["isPresent"] = v.level != null
if (v.level != null) {
it["value"] = v.level
}
}
})
}
}
} }
} }
override fun deserializeNBT(nbt: CompoundTag) { override fun deserializeNBT(nbt: CompoundTag) {
isResearched = nbt.getBoolean("researched") isResearched = nbt.getBoolean("researched")
oldResearchLevel.clear()
for (tag in nbt.getCompoundList("oldResearchLevel")) {
val key = tag.getString("key")
val type = MRegistry.ANDROID_FEATURES.getValue(ResourceLocation(key)) ?: continue
val value = tag.getCompound("value")
val isPresent = value.getBoolean("isPresent")
val int = value.getInt("value")
oldResearchLevel[type] = RememberResearchLevel(if (isPresent) int else null)
}
} }
} }

View File

@ -50,8 +50,6 @@ open class AndroidResearchDataProvider() : DataProvider {
} }
final override fun run(output: CachedOutput): CompletableFuture<*> { final override fun run(output: CachedOutput): CompletableFuture<*> {
AndroidResearchManager.fireRegistrationEvent()
val set = ObjectArraySet<ResourceLocation>() val set = ObjectArraySet<ResourceLocation>()
val added = LinkedList<AndroidResearchType>() val added = LinkedList<AndroidResearchType>()
val futures = ArrayList<CompletableFuture<*>>() val futures = ArrayList<CompletableFuture<*>>()

View File

@ -0,0 +1,133 @@
package ru.dbotthepony.mc.otm.android
import com.mojang.datafixers.util.Either
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.ChatFormatting
import net.minecraft.network.chat.Component
import net.minecraftforge.eventbus.api.IEventBus
import net.minecraftforge.registries.DeferredRegister
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
import ru.dbotthepony.mc.otm.config.AndroidConfig
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.getValue
import ru.dbotthepony.mc.otm.core.util.formatPower
import ru.dbotthepony.mc.otm.data.ComponentCodec
import ru.dbotthepony.mc.otm.data.SingletonCodec
import ru.dbotthepony.mc.otm.data.simpleCodec
import ru.dbotthepony.mc.otm.registry.RegistryDelegate
object AndroidResearchDescriptions {
private val registrar = DeferredRegister.create(AndroidResearchDescription.registryKey, OverdriveThatMatters.MOD_ID)
init {
registrar.register("plain") { PlainAndroidResearchDescription }
}
internal fun register(bus: IEventBus) {
registrar.register(bus)
}
val ENDER_TELEPORTER: AndroidResearchDescription.Singleton by registrar.register("ender_teleporter") {
AndroidResearchDescription.singleton {
TranslatableComponent("otm.gui.power_cost_per_use", AndroidConfig.EnderTeleporter.ENERGY_COST.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW)) }
}
val FALL_DAMPENERS: AndroidResearchDescription.Leveled by registrar.register("fall_dampeners") {
AndroidResearchDescription.Leveled { _, _, level -> TranslatableComponent("otm.fall_dampeners.description", TextComponent("%.1f".format((AndroidConfig.FALL_DAMAGE_REDUCTION_PER_LEVEL * level).toFloat().coerceAtLeast(0f).coerceAtMost(1f) * 100f)).withStyle(ChatFormatting.YELLOW)) }
}
val ITEM_MAGNET: AndroidResearchDescription.Singleton by registrar.register("item_magnet") {
AndroidResearchDescription.singleton { TranslatableComponent("otm.gui.power_cost_per_tick", AndroidConfig.Magnet.POWER_DRAW.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW)) }
}
val JUMP_BOOST: AndroidResearchDescription.Singleton by registrar.register("jump_boost") {
AndroidResearchDescription.singleton { TranslatableComponent("otm.gui.power_cost_per_use", AndroidConfig.JumpBoost.ENERGY_COST.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW)) }
}
val SHOCKWAVE: AndroidResearchDescription.Singleton by registrar.register("shockwave") {
AndroidResearchDescription.singleton { TranslatableComponent("otm.gui.power_cost_per_use", AndroidConfig.Shockwave.ENERGY_COST.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW)) }
}
}
/**
* Instance, representing ready to use description populator
*/
interface AndroidResearchDescription {
/**
* Type, representing raw description populator in registry
*/
interface Type<T : AndroidResearchDescription> {
val codec: Codec<T>
}
abstract class Singleton : AndroidResearchDescription, Type<Singleton> {
override val codec = SingletonCodec(this)
override val type: Type<*>
get() = this
}
class Leveled(val callback: (research: AndroidResearch, lines: MutableList<Component>, level: Int) -> Unit) : Type<Leveled.Instance> {
inner class Instance(val level: Int) : AndroidResearchDescription {
override fun addLines(research: AndroidResearch, lines: MutableList<Component>) {
callback.invoke(research, lines, level)
}
override val type: Type<*>
get() = this@Leveled
}
override val codec: Codec<Instance> by lazy {
RecordCodecBuilder.create {
it.group(Codec.INT.fieldOf("level").forGetter(Instance::level)).apply(it, ::Instance)
}
}
}
fun addLines(research: AndroidResearch, lines: MutableList<Component>)
val type: Type<*>
companion object {
private val delegate = RegistryDelegate<Type<*>>("android_research_description")
val registry by delegate
val registryKey get() = delegate.key
val CODEC: Codec<AndroidResearchDescription> by lazy {
registry.codec.dispatch({ it.type }, { it.codec })
}
internal fun register(bus: IEventBus) {
bus.addListener(delegate::build)
}
fun singleton(callback: (research: AndroidResearch) -> Component): Singleton {
return object : Singleton() {
override fun addLines(research: AndroidResearch, lines: MutableList<Component>) {
lines.add(callback.invoke(research))
}
}
}
}
}
object PlainAndroidResearchDescription : AndroidResearchDescription.Type<PlainAndroidResearchDescription.Instance> {
data class Instance(val line: Component) : AndroidResearchDescription {
override fun addLines(research: AndroidResearch, lines: MutableList<Component>) {
lines.add(line.copy())
}
override val type: PlainAndroidResearchDescription
get() = PlainAndroidResearchDescription
}
fun make(line: Component) = Instance(line)
override val codec: Codec<Instance> by lazy {
Codec
.either(ComponentCodec, simpleCodec(::Instance, Instance::line, ComponentCodec))
.xmap({ c -> c.map({ Instance(it) }, { it }) }, { c -> Either.left(c.line) })
}
}

View File

@ -6,7 +6,6 @@ import com.google.gson.JsonElement
import com.google.gson.JsonObject import com.google.gson.JsonObject
import net.minecraft.client.server.IntegratedServer import net.minecraft.client.server.IntegratedServer
import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
import net.minecraft.server.packs.resources.ResourceManager import net.minecraft.server.packs.resources.ResourceManager
import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener
@ -23,8 +22,6 @@ import ru.dbotthepony.mc.otm.NULLABLE_MINECRAFT_SERVER
import ru.dbotthepony.mc.otm.SERVER_IS_LIVE import ru.dbotthepony.mc.otm.SERVER_IS_LIVE
import ru.dbotthepony.mc.otm.capability.matteryPlayer import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.data.SerializedFunctionRegistry
import ru.dbotthepony.mc.otm.network.MatteryPacket import ru.dbotthepony.mc.otm.network.MatteryPacket
import ru.dbotthepony.mc.otm.network.RegistryNetworkChannel import ru.dbotthepony.mc.otm.network.RegistryNetworkChannel
import ru.dbotthepony.mc.otm.network.enqueueWork import ru.dbotthepony.mc.otm.network.enqueueWork
@ -33,47 +30,7 @@ import ru.dbotthepony.mc.otm.onceServer
import java.util.LinkedList import java.util.LinkedList
import java.util.function.Supplier import java.util.function.Supplier
typealias AndroidResultTransformer = SerializedFunctionRegistry.BoundFunction<Pair<AndroidResearch, AndroidFeature>, Unit>
typealias ComponentSupplier = SerializedFunctionRegistry.BoundFunction<Unit, Component>
object AndroidResearchManager : SimpleJsonResourceReloadListener(GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(), "otm_android_research"), Iterable<AndroidResearchType> { object AndroidResearchManager : SimpleJsonResourceReloadListener(GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(), "otm_android_research"), Iterable<AndroidResearchType> {
/**
* Feel free to register functions inside this thing from anywhere in your code
* (registration and querying is completely thread safe).
*
* Just make sure client and server has the same set of functions defined.
*/
val featureResultTransformers = SerializedFunctionRegistry<Pair<AndroidResearch, AndroidFeature>, Unit>()
/**
* Feel free to register functions inside this thing from anywhere in your code
* (registration and querying is completely thread safe).
*
* Just make sure client and server has the same set of functions defined.
*/
val descriptionFuncs = SerializedFunctionRegistry<Unit, Component>()
fun descriptionFunc(name: ResourceLocation, base: String, vararg argument: Supplier<Any>): ComponentSupplier {
return descriptionFuncs.register(name) {->
return@register TranslatableComponent(base, *argument.map { it.get() }.toTypedArray())
}.bind()
}
private var firedRegistrationEvent = false
/**
* Event-style registration of serializable functions, for those who prefer/need it
*
* Fired *once* on [MinecraftForge.EVENT_BUS] before loading android research
*/
object RegisterFuncsEvent : Event() {
val manager get() = AndroidResearchManager
val featureResults by ::featureResultTransformers
val descriptionFunctions by ::descriptionFuncs
fun descriptionFunc(name: ResourceLocation, base: String, vararg argument: Supplier<Any>): ComponentSupplier = AndroidResearchManager.descriptionFunc(name, base, *argument)
}
const val DIRECTORY = "otm_android_research" const val DIRECTORY = "otm_android_research"
private val LOGGER = LogManager.getLogger() private val LOGGER = LogManager.getLogger()
@ -97,23 +54,11 @@ object AndroidResearchManager : SimpleJsonResourceReloadListener(GsonBuilder().s
var researchMap: Map<ResourceLocation, AndroidResearchType> = mapOf() var researchMap: Map<ResourceLocation, AndroidResearchType> = mapOf()
private set private set
internal fun fireRegistrationEvent() {
if (!firedRegistrationEvent) {
try {
MinecraftForge.EVENT_BUS.post(RegisterFuncsEvent)
} finally {
firedRegistrationEvent = true
}
}
}
override fun apply( override fun apply(
jsonElementMap: Map<ResourceLocation, JsonElement>, jsonElementMap: Map<ResourceLocation, JsonElement>,
manager: ResourceManager, manager: ResourceManager,
profiler: ProfilerFiller profiler: ProfilerFiller
) { ) {
fireRegistrationEvent()
val builder = ImmutableMap.builder<ResourceLocation, AndroidResearchType>() val builder = ImmutableMap.builder<ResourceLocation, AndroidResearchType>()
for ((k, v) in jsonElementMap) { for ((k, v) in jsonElementMap) {

View File

@ -0,0 +1,180 @@
package ru.dbotthepony.mc.otm.android
import com.mojang.datafixers.util.Either
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.resources.ResourceLocation
import net.minecraftforge.eventbus.api.IEventBus
import net.minecraftforge.registries.DeferredRegister
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.getValue
import ru.dbotthepony.mc.otm.data.SingletonCodec
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import ru.dbotthepony.mc.otm.registry.MRegistry
import ru.dbotthepony.mc.otm.registry.RegistryDelegate
object AndroidResearchResults {
private val registrar = DeferredRegister.create(AndroidResearchResult.registryKey, OverdriveThatMatters.MOD_ID)
init {
registrar.register("feature") { AndroidResearchResult.Feature.Companion }
registrar.register("feature_level") { AndroidResearchResult.FeatureLevel.Companion }
}
private object NanobotsArmorStrength : AndroidResearchResult.Singleton<NanobotsArmorStrength> {
override val codec: Codec<NanobotsArmorStrength> = SingletonCodec(this)
override val type: AndroidResearchResult.Type<*>
get() = this
override fun onResearched(research: AndroidResearch) {
val feature = research.capability.getFeature(AndroidFeatures.NANOBOTS_ARMOR) ?: return
feature.strength++
}
override fun onRefunded(research: AndroidResearch) {
val feature = research.capability.getFeature(AndroidFeatures.NANOBOTS_ARMOR) ?: return
feature.strength--
}
}
private object NanobotsArmorSpeed : AndroidResearchResult.Singleton<NanobotsArmorSpeed> {
override val codec: Codec<NanobotsArmorSpeed> = SingletonCodec(this)
override val type: AndroidResearchResult.Type<*>
get() = this
override fun onResearched(research: AndroidResearch) {
val feature = research.capability.getFeature(AndroidFeatures.NANOBOTS_ARMOR) ?: return
feature.speed++
}
override fun onRefunded(research: AndroidResearch) {
val feature = research.capability.getFeature(AndroidFeatures.NANOBOTS_ARMOR) ?: return
feature.speed--
}
}
val NANOBOTS_ARMOR_STRENGTH: AndroidResearchResult.Singleton<*> by registrar.register("nanobots_armor_strength") { NanobotsArmorStrength }
val NANOBOTS_ARMOR_SPEED: AndroidResearchResult.Singleton<*> by registrar.register("nanobots_armor_speed") { NanobotsArmorSpeed }
internal fun register(bus: IEventBus) {
registrar.register(bus)
}
}
interface AndroidResearchResult {
interface Type<T : AndroidResearchResult> {
val codec: Codec<T>
}
interface Singleton<T : Singleton<T>> : Type<T>, AndroidResearchResult
/**
* Adds specific android feature [id] to target, does nothing if target already has specified feature
*/
class Feature(val id: ResourceLocation, val optional: Boolean = false) : AndroidResearchResult {
val feature = MRegistry.ANDROID_FEATURES.getValue(id) ?: if (optional) null else throw NoSuchElementException("Unknown android feature $id")
override val type: Type<*>
get() = Companion
override fun onResearched(research: AndroidResearch) {
research.capability.addFeature(feature ?: return)
}
override fun onRefunded(research: AndroidResearch) {
research.capability.removeFeature(feature ?: return)
}
companion object : Type<Feature> {
override val codec: Codec<Feature> by lazy {
Codec
.either(ResourceLocation.CODEC, RecordCodecBuilder.create<Feature> { // KT-52757
it.group(
ResourceLocation.CODEC.fieldOf("id").forGetter(Feature::id),
Codec.BOOL.optionalFieldOf("optional", false).forGetter(Feature::optional)
).apply(it, ::Feature)
})
.xmap<Feature>(
{ c -> c.map({ Feature(it) }, { it }) },
{ c -> if (c.optional) Either.left(c.id) else Either.right(c) }
)
}
}
}
/**
* Increases level of specific android feature [id] by specified amount [levels]
*/
class FeatureLevel(val id: ResourceLocation, val optional: Boolean = false, val levels: Int = 1) : AndroidResearchResult {
val feature = MRegistry.ANDROID_FEATURES.getValue(id) ?: if (optional) null else throw NoSuchElementException("Unknown android feature $id")
override val type: Type<*>
get() = Companion
override fun onResearched(research: AndroidResearch) {
val get = research.capability.getFeature(feature ?: return)
if (get == null) {
LOGGER.warn("Unable to advance level of android feature $id for ${research.ply} because they have no such android feature.")
} else {
get.level += levels
}
}
override fun onRefunded(research: AndroidResearch) {
val get = research.capability.getFeature(feature ?: return)
if (get == null) {
LOGGER.warn("Unable to decrease level of android feature $id for ${research.ply} because they have no such android feature.")
} else {
get.level += levels
}
}
companion object : Type<FeatureLevel> {
override val codec: Codec<FeatureLevel> by lazy {
Codec
.either(ResourceLocation.CODEC, RecordCodecBuilder.create<FeatureLevel> { // KT-52757
it.group(
ResourceLocation.CODEC.fieldOf("id").forGetter(FeatureLevel::id),
Codec.BOOL.optionalFieldOf("optional", false).forGetter(FeatureLevel::optional),
Codec.INT.optionalFieldOf("levels", 1).forGetter(FeatureLevel::levels),
).apply(it, ::FeatureLevel)
})
.xmap(
{ c -> c.map({ FeatureLevel(it) }, { it }) },
{ c -> if (c.optional && c.levels == 1) Either.left(c.id) else Either.right(c) }
)
}
}
}
val type: Type<*>
/**
* Called when research is applied
*/
fun onResearched(research: AndroidResearch) {}
/**
* Called when research is refunded
*/
fun onRefunded(research: AndroidResearch) {}
companion object {
private val LOGGER = LogManager.getLogger()
private val delegate = RegistryDelegate<Type<*>>("android_research_result")
val registry by delegate
val registryKey get() = delegate.key
val CODEC: Codec<AndroidResearchResult> by lazy {
registry.codec.dispatch({ it.type }, { it.codec })
}
internal fun register(bus: IEventBus) {
bus.addListener(delegate::build)
}
}
}

View File

@ -1,13 +1,13 @@
package ru.dbotthepony.mc.otm.android package ru.dbotthepony.mc.otm.android
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import com.google.common.collect.Streams
import com.google.gson.JsonArray import com.google.gson.JsonArray
import com.google.gson.JsonElement import com.google.gson.JsonElement
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive import com.google.gson.JsonPrimitive
import com.google.gson.JsonSyntaxException import com.google.gson.JsonSyntaxException
import com.google.gson.internal.bind.TypeAdapters import com.google.gson.internal.bind.TypeAdapters
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.network.chat.ComponentContents import net.minecraft.network.chat.ComponentContents
@ -28,10 +28,12 @@ import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.isActuallyEmpty import ru.dbotthepony.mc.otm.core.isActuallyEmpty
import ru.dbotthepony.mc.otm.core.registryName import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.core.set import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.core.toImmutableList
import ru.dbotthepony.mc.otm.core.collect.stream import ru.dbotthepony.mc.otm.core.collect.stream
import ru.dbotthepony.mc.otm.core.fromJsonStrict
import ru.dbotthepony.mc.otm.core.toJsonStrict
import ru.dbotthepony.mc.otm.core.util.readJson
import ru.dbotthepony.mc.otm.core.util.writeJson
import ru.dbotthepony.mc.otm.isClient import ru.dbotthepony.mc.otm.isClient
import ru.dbotthepony.mc.otm.registry.MRegistry
import java.util.LinkedList import java.util.LinkedList
import java.util.stream.Stream import java.util.stream.Stream
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -39,7 +41,7 @@ import kotlin.collections.HashSet
private fun findPrerequisites( private fun findPrerequisites(
initial: Collection<AndroidResearchType>, initial: Collection<AndroidResearchType>,
add: MutableSet<AndroidResearchType> = HashSet(), add: MutableSet<AndroidResearchType> = ObjectOpenHashSet(),
top: Boolean = true top: Boolean = true
): Set<AndroidResearchType> { ): Set<AndroidResearchType> {
for (value in initial) { for (value in initial) {
@ -55,7 +57,7 @@ private fun findPrerequisites(
private fun findAllPrerequisites( private fun findAllPrerequisites(
initial: Collection<AndroidResearchType>, initial: Collection<AndroidResearchType>,
add: MutableSet<AndroidResearchType> = HashSet(), add: MutableSet<AndroidResearchType> = ObjectOpenHashSet(),
): Set<AndroidResearchType> { ): Set<AndroidResearchType> {
for (value in initial) { for (value in initial) {
add.add(value) add.add(value)
@ -67,7 +69,7 @@ private fun findAllPrerequisites(
private fun findAllChildren( private fun findAllChildren(
initial: Collection<AndroidResearchType>, initial: Collection<AndroidResearchType>,
add: MutableSet<AndroidResearchType> = HashSet(), add: MutableSet<AndroidResearchType> = ObjectOpenHashSet(),
): Set<AndroidResearchType> { ): Set<AndroidResearchType> {
for (value in initial) { for (value in initial) {
add.add(value) add.add(value)
@ -83,10 +85,9 @@ class AndroidResearchType(
blockedBy: Collection<Reference>, blockedBy: Collection<Reference>,
items: Collection<Pair<Ingredient, Int>>, items: Collection<Pair<Ingredient, Int>>,
features: Collection<FeatureReference>, results: Collection<AndroidResearchResult>,
descriptionLines: Collection<Component>, description: Collection<AndroidResearchDescription>,
descriptionSuppliers: Collection<ComponentSupplier> = listOf(),
val experienceLevels: Int = 0, val experienceLevels: Int = 0,
private val customName: Component? = null, private val customName: Component? = null,
@ -145,74 +146,6 @@ class AndroidResearchType(
} }
} }
data class FeatureReference(
val id: ResourceLocation,
val level: Int = 0,
val isRigid: Boolean,
val transformersUp: Collection<AndroidResultTransformer> = listOf(),
val transformersDown: Collection<AndroidResultTransformer> = listOf(),
) {
fun toJson(): JsonObject {
return JsonObject().also {
it["id"] = JsonPrimitive(id.toString())
it["level"] = JsonPrimitive(level)
it["is_rigid"] = JsonPrimitive(isRigid)
it["functions_up"] = JsonArray().also {
for (transformer in transformersUp) {
it.add(transformer.toJson())
}
}
it["functions_down"] = JsonArray().also {
for (transformer in transformersDown) {
it.add(transformer.toJson())
}
}
}
}
fun toNetwork(buff: FriendlyByteBuf) {
buff.writeUtf(id.toString())
buff.writeVarInt(level)
buff.writeBoolean(isRigid)
buff.writeCollection(transformersUp) { a, b -> b.toNetwork(a) }
buff.writeCollection(transformersDown) { a, b -> b.toNetwork(a) }
}
companion object {
fun fromNetwork(buff: FriendlyByteBuf): FeatureReference {
return FeatureReference(
ResourceLocation(buff.readUtf()),
buff.readVarInt(),
buff.readBoolean(),
buff.readCollection({ LinkedList() }, AndroidResearchManager.featureResultTransformers::fromNetwork).filterNotNull().toImmutableList(),
buff.readCollection({ LinkedList() }, AndroidResearchManager.featureResultTransformers::fromNetwork).filterNotNull().toImmutableList()
)
}
fun fromJson(value: JsonElement): FeatureReference {
if (value is JsonPrimitive) {
return FeatureReference(ResourceLocation(value.asString), 0, true)
} else if (value is JsonObject) {
return FeatureReference(
ResourceLocation((value["id"] as? JsonPrimitive ?: throw JsonSyntaxException("Invalid `id` value")).asString),
(value["level"] as JsonPrimitive?)?.asInt ?: 0,
(value["is_rigid"] as JsonPrimitive?)?.asBoolean ?: true,
(value["functions_up"] as JsonArray? ?: JsonArray()).stream().map { AndroidResearchManager.featureResultTransformers.fromJson(it) }.filter { it != null }.collect(ImmutableList.toImmutableList<AndroidResultTransformer>()),
(value["functions_down"] as JsonArray? ?: JsonArray()).stream().map { AndroidResearchManager.featureResultTransformers.fromJson(it) }.filter { it != null }.collect(ImmutableList.toImmutableList<AndroidResultTransformer>()),
)
} else {
throw JsonSyntaxException("Unknown element type ${value::class.qualifiedName}")
}
}
}
}
data class ResolvedFeature(
val feature: AndroidFeatureType<*>,
val level: Int,
)
val researchTreeDepth: Int by lazy { val researchTreeDepth: Int by lazy {
if (flatPrerequisites.isEmpty()) { if (flatPrerequisites.isEmpty()) {
return@lazy 0 return@lazy 0
@ -248,31 +181,11 @@ class AndroidResearchType(
ImmutableList.copyOf(this.blockedBy.mapNotNull { AndroidResearchManager[it.id].also { e -> if (e == null && it.isRigid) throw NoSuchElementException("Unable to find research ${it.id}") } }) ImmutableList.copyOf(this.blockedBy.mapNotNull { AndroidResearchManager[it.id].also { e -> if (e == null && it.isRigid) throw NoSuchElementException("Unable to find research ${it.id}") } })
} }
val features: List<FeatureReference> = ImmutableList.copyOf(features)
val resolvedFeatures: List<ResolvedFeature> by lazy {
ImmutableList.copyOf(features.mapNotNull {
MRegistry.ANDROID_FEATURES.getValue(it.id)
.let { e -> if (e == null && it.isRigid)
throw NoSuchElementException("Unable to find research ${it.id}")
else if (e != null)
ResolvedFeature(e, it.level)
else
null } })
}
private val definedItems: List<Pair<Ingredient, Int>> = ImmutableList.copyOf(items) private val definedItems: List<Pair<Ingredient, Int>> = ImmutableList.copyOf(items)
val results: ImmutableList<AndroidResearchResult> = ImmutableList.copyOf(results)
val items: Stream<out Pair<Ingredient, Int>> get() = definedItems.stream().filter { !it.first.isActuallyEmpty } val items: Stream<out Pair<Ingredient, Int>> get() = definedItems.stream().filter { !it.first.isActuallyEmpty }
val description: ImmutableList<AndroidResearchDescription> = ImmutableList.copyOf(description)
private val descriptionLines: List<MutableComponent> = ImmutableList.copyOf(descriptionLines.map { it.copy() })
private val descriptionSuppliers: List<ComponentSupplier> = ImmutableList.copyOf(descriptionSuppliers)
/**
* Stream containing copies of original [Component]s in list
*/
val description: Stream<out Component> get() = Streams.concat(descriptionLines.stream().map { it.copy() }, descriptionSuppliers.stream().map { it.apply(Unit) })
/** /**
* Flat list of research preceding this research. * Flat list of research preceding this research.
@ -469,14 +382,11 @@ class AndroidResearchType(
fun toJson(): JsonElement { fun toJson(): JsonElement {
return JsonObject().also { return JsonObject().also {
// it["id"] = JsonPrimitive(id.toString())
it["prerequisites"] = JsonArray().also { for (value in prerequisites) it.add(value.toJson()) } it["prerequisites"] = JsonArray().also { for (value in prerequisites) it.add(value.toJson()) }
it["blocked_by"] = JsonArray().also { for (value in blockedBy) it.add(value.toJson()) } it["blocked_by"] = JsonArray().also { for (value in blockedBy) it.add(value.toJson()) }
it["required_items"] = JsonArray().also { for (item in definedItems) it.add(JsonObject().also { it["count"] = JsonPrimitive(item.second); it["ingredient"] = item.first.toJson() }) } it["required_items"] = JsonArray().also { for (item in definedItems) it.add(JsonObject().also { it["count"] = JsonPrimitive(item.second); it["ingredient"] = item.first.toJson() }) }
it["feature_result"] = JsonArray().also { for (feature in features) it.add(feature.toJson()) } it["results"] = JsonArray().also { for (result in results) it.add(AndroidResearchResult.CODEC.toJsonStrict(result)) }
it["description"] = JsonArray().also { for (line in descriptionLines) it.add(Component.Serializer.toJsonTree(line)) } it["description"] = JsonArray().also { for (line in description) it.add(AndroidResearchDescription.CODEC.toJsonStrict(line)) }
it["description_funcs"] = JsonArray().also { for (line in descriptionSuppliers) it.add(line.toJson()) }
it["experience"] = JsonPrimitive(experienceLevels) it["experience"] = JsonPrimitive(experienceLevels)
if (skinIcon != null) { if (skinIcon != null) {
@ -498,7 +408,6 @@ class AndroidResearchType(
} }
fun validate() { fun validate() {
resolvedFeatures
resolvedBlockedBy resolvedBlockedBy
resolvedPrerequisites resolvedPrerequisites
} }
@ -508,9 +417,8 @@ class AndroidResearchType(
buff.writeCollection(prerequisites) { a, b -> b.toNetwork(a) } buff.writeCollection(prerequisites) { a, b -> b.toNetwork(a) }
buff.writeCollection(blockedBy) { a, b -> b.toNetwork(a) } buff.writeCollection(blockedBy) { a, b -> b.toNetwork(a) }
buff.writeCollection(definedItems) { a, b -> b.first.toNetwork(a); a.writeVarInt(b.second) } buff.writeCollection(definedItems) { a, b -> b.first.toNetwork(a); a.writeVarInt(b.second) }
buff.writeCollection(features) { a, b -> b.toNetwork(a) } buff.writeCollection(results) { a, b -> a.writeJson(AndroidResearchResult.CODEC.toJsonStrict(b)) }
buff.writeCollection(descriptionLines) { a, b -> a.writeComponent(b) } buff.writeCollection(description) { a, b -> a.writeJson(AndroidResearchDescription.CODEC.toJsonStrict(b)) }
buff.writeCollection(descriptionSuppliers) { a, b -> b.toNetwork(a) }
buff.writeVarInt(experienceLevels) buff.writeVarInt(experienceLevels)
buff.writeBoolean(customName != null) buff.writeBoolean(customName != null)
@ -532,9 +440,8 @@ class AndroidResearchType(
val prerequisites = buff.readCollection({ LinkedList() }, Reference::fromNetwork) val prerequisites = buff.readCollection({ LinkedList() }, Reference::fromNetwork)
val blockedBy = buff.readCollection({ LinkedList() }, Reference::fromNetwork) val blockedBy = buff.readCollection({ LinkedList() }, Reference::fromNetwork)
val items = buff.readCollection({ LinkedList() }, { Ingredient.fromNetwork(it) to it.readVarInt() }) val items = buff.readCollection({ LinkedList() }, { Ingredient.fromNetwork(it) to it.readVarInt() })
val features = buff.readCollection({ LinkedList() }, FeatureReference::fromNetwork) val results = buff.readCollection({ LinkedList() }, { AndroidResearchResult.CODEC.fromJsonStrict(it.readJson()) })
val descriptionLines = buff.readCollection({ LinkedList() }, FriendlyByteBuf::readComponent) val description = buff.readCollection({ LinkedList() }, { AndroidResearchDescription.CODEC.fromJsonStrict(it.readJson()) })
val descriptionSuppliers = buff.readCollection({ LinkedList() }, { AndroidResearchManager.descriptionFuncs.fromNetwork(it) })
val experienceLevels = buff.readVarInt() val experienceLevels = buff.readVarInt()
val customName = if (buff.readBoolean()) { val customName = if (buff.readBoolean()) {
@ -566,9 +473,8 @@ class AndroidResearchType(
prerequisites = prerequisites, prerequisites = prerequisites,
blockedBy = blockedBy, blockedBy = blockedBy,
items = items, items = items,
features = features, results = results,
descriptionLines = descriptionLines, description = description,
descriptionSuppliers = descriptionSuppliers.filterNotNull(),
experienceLevels = experienceLevels, experienceLevels = experienceLevels,
customName = customName, customName = customName,
iconText = iconTextValue, iconText = iconTextValue,
@ -587,7 +493,6 @@ class AndroidResearchType(
val items = value["required_items"] as JsonArray? ?: JsonArray() val items = value["required_items"] as JsonArray? ?: JsonArray()
val features = value["feature_result"] as JsonArray? ?: JsonArray() val features = value["feature_result"] as JsonArray? ?: JsonArray()
val description = value["description"] as JsonArray? ?: JsonArray() val description = value["description"] as JsonArray? ?: JsonArray()
val description_funcs = value["description_funcs"] as JsonArray? ?: JsonArray()
val experience = value["experience"]?.asInt ?: 0 val experience = value["experience"]?.asInt ?: 0
val customName = value["custom_name"]?.let(Component.Serializer::fromJson) val customName = value["custom_name"]?.let(Component.Serializer::fromJson)
val iconText = value["icon_text"]?.let(Component.Serializer::fromJson) val iconText = value["icon_text"]?.let(Component.Serializer::fromJson)
@ -598,15 +503,12 @@ class AndroidResearchType(
id = id, id = id,
prerequisites = prerequisites.stream().map { Reference.fromJson(it) }.toList(), prerequisites = prerequisites.stream().map { Reference.fromJson(it) }.toList(),
blockedBy = blocked_by.stream().map { Reference.fromJson(it) }.toList(), blockedBy = blocked_by.stream().map { Reference.fromJson(it) }.toList(),
features = features.stream().map { FeatureReference.fromJson(it) }.toList(), results = features.stream().map { AndroidResearchResult.CODEC.fromJsonStrict(it) }.toList(),
items = items.stream() items = items.stream()
.map { it as? JsonObject ?: throw JsonSyntaxException("One of items is not an JsonObject") } .map { it as? JsonObject ?: throw JsonSyntaxException("One of items is not an JsonObject") }
.map { Ingredient.fromJson(it["ingredient"] ?: throw JsonSyntaxException("Missing ingredient key")) to (it["count"]?.asInt ?: throw JsonSyntaxException("Missing count key")) } .map { Ingredient.fromJson(it["ingredient"] ?: throw JsonSyntaxException("Missing ingredient key")) to (it["count"]?.asInt ?: throw JsonSyntaxException("Missing count key")) }
.toList(), .toList(),
descriptionLines = description.stream().map { Component.Serializer.fromJson(it) }.toList() as List<MutableComponent>, description = description.stream().map { AndroidResearchDescription.CODEC.fromJsonStrict(it) }.toList(),
descriptionSuppliers = description_funcs.stream()
.map { AndroidResearchManager.descriptionFuncs.fromJson(it) ?: throw NullPointerException("$id is missing description supplier function or it is invalid! JSON: $it") }
.toList() as List<ComponentSupplier>,
experienceLevels = experience, experienceLevels = experience,
customName = customName, customName = customName,
iconText = iconText, iconText = iconText,
@ -621,18 +523,18 @@ class AndroidResearchType(
val id: ResourceLocation, val id: ResourceLocation,
var experience: Int = 0, var experience: Int = 0,
var customName: Component? = null, var customName: Component? = null,
var description: MutableList<Component>? = null, description: MutableList<AndroidResearchDescription>? = null,
var descriptionSuppliers: MutableList<ComponentSupplier>? = null,
var itemIcon: Item? = null, var itemIcon: Item? = null,
var skinIcon: AbstractMatterySprite? = null, var skinIcon: AbstractMatterySprite? = null,
var iconText: Component? = null, var iconText: Component? = null,
) { ) {
val description = ArrayList<AndroidResearchDescription>(description ?: listOf())
val results = ArrayList<AndroidResearchResult>()
private val items = ArrayList<Pair<Ingredient, Int>>() private val items = ArrayList<Pair<Ingredient, Int>>()
private val prerequisites = ArrayList<Reference>() private val prerequisites = ArrayList<Reference>()
private val blockers = ArrayList<Reference>() private val blockers = ArrayList<Reference>()
private val features = ArrayList<FeatureReference>()
fun withIconText(icon: Component? = null): Builder { fun withIconText(icon: Component? = null): Builder {
this.iconText = icon this.iconText = icon
return this return this
@ -661,19 +563,20 @@ class AndroidResearchType(
return this return this
} }
fun withDescription(): Builder { fun clearDescription(): Builder {
this.description = mutableListOf(TranslatableComponent("android_research.${id.namespace}.${id.path}.description")) this.description.clear()
return this return this
} }
fun withDescription(range: IntRange): Builder { fun withDescription(): Builder {
val result = ArrayList<Component>() return withDescription(TranslatableComponent("android_research.${id.namespace}.${id.path}.description"))
}
fun withDescription(range: IntRange): Builder {
for (i in range) { for (i in range) {
result.add(TranslatableComponent("android_research.${id.namespace}.${id.path}.description$i")) withDescription(TranslatableComponent("android_research.${id.namespace}.${id.path}.description$i"))
} }
this.description = result
return this return this
} }
@ -683,73 +586,21 @@ class AndroidResearchType(
} }
fun withDescription(vararg description: Component): Builder { fun withDescription(vararg description: Component): Builder {
this.description = description.toMutableList() for (component in description)
withDescription(PlainAndroidResearchDescription.Instance(component))
return this return this
} }
fun withDescription(description: Collection<Component>): Builder { fun withDescription(description: Collection<Component>): Builder {
this.description = ArrayList<Component>(description.size).also { it.addAll(description) } for (component in description)
withDescription(PlainAndroidResearchDescription.Instance(component))
return this return this
} }
fun appendDescription(range: IntRange): Builder { fun withDescription(vararg description: AndroidResearchDescription): Builder {
val result = this.description ?: ArrayList() this.description.addAll(description)
for (i in range) {
result.add(TranslatableComponent("android_research.${id.namespace}.${id.path}.description$i"))
}
this.description = result
return this
}
fun appendDescription(description: Component): Builder {
this.description = (this.description ?: mutableListOf()).also { it.add(description) }
return this
}
fun appendDescription(vararg description: Component): Builder {
this.description = (this.description ?: mutableListOf()).also { it.addAll(description) }
return this
}
fun appendDescription(description: Collection<Component>): Builder {
this.description = (this.description ?: mutableListOf()).also { it.addAll(description) }
return this
}
fun withDescription(vararg description: ComponentSupplier): Builder {
this.descriptionSuppliers = description.toMutableList()
return this
}
fun withDescriptionSupplier(description: Collection<ComponentSupplier>): Builder {
this.descriptionSuppliers = ArrayList<ComponentSupplier>(description.size).also { it.addAll(description) }
return this
}
fun appendDescription(description: ComponentSupplier): Builder {
this.descriptionSuppliers = (this.descriptionSuppliers ?: mutableListOf()).also { it.add(description) }
return this
}
fun appendDescriptionSupplier(description: ComponentSupplier): Builder {
this.descriptionSuppliers = (this.descriptionSuppliers ?: mutableListOf()).also { it.add(description) }
return this
}
fun appendDescription(vararg description: ComponentSupplier): Builder {
this.descriptionSuppliers = (this.descriptionSuppliers ?: mutableListOf()).also { it.addAll(description) }
return this
}
fun appendDescriptionSupplier(vararg description: ComponentSupplier): Builder {
this.descriptionSuppliers = (this.descriptionSuppliers ?: mutableListOf()).also { it.addAll(description) }
return this
}
fun appendDescriptionSupplier(description: Collection<ComponentSupplier>): Builder {
this.descriptionSuppliers = (this.descriptionSuppliers ?: mutableListOf()).also { it.addAll(description) }
return this return this
} }
@ -776,43 +627,25 @@ class AndroidResearchType(
fun addPrerequisite(type: AndroidResearchType, rigid: Boolean = true) = addPrerequisite(type.id, rigid) fun addPrerequisite(type: AndroidResearchType, rigid: Boolean = true) = addPrerequisite(type.id, rigid)
fun addBlocker(type: AndroidResearchType, rigid: Boolean = true) = addBlocker(type.id, rigid) fun addBlocker(type: AndroidResearchType, rigid: Boolean = true) = addBlocker(type.id, rigid)
@JvmOverloads fun addResult(result: AndroidResearchResult): Builder {
fun addFeatureResult( results.add(result)
id: ResourceLocation,
level: Int = 0,
rigid: Boolean = false,
transformersUp: Collection<AndroidResultTransformer> = listOf(),
transformersDown: Collection<AndroidResultTransformer> = listOf(),
): Builder {
features.add(FeatureReference(id, level, rigid, transformersUp, transformersDown))
return this return this
} }
@JvmOverloads fun addFeatureResult(id: ResourceLocation, optional: Boolean = false): Builder {
fun addFeatureResult( return addResult(AndroidResearchResult.Feature(id, optional))
feature: AndroidFeatureType<*>,
level: Int = 0,
rigid: Boolean = true,
transformersUp: Collection<AndroidResultTransformer> = listOf(),
transformersDown: Collection<AndroidResultTransformer> = listOf(),
): Builder {
features.add(FeatureReference(feature.registryName ?: throw NullPointerException("Feature $feature does not have registry name"), level, rigid, transformersUp, transformersDown))
return this
} }
fun addFeatureResult( fun addFeatureResult(feature: AndroidFeatureType<*>, optional: Boolean = false): Builder {
id: ResourceLocation, return addFeatureResult(feature.registryName!!, optional)
rigid: Boolean = false,
transformersUp: Collection<AndroidResultTransformer> = listOf(),
transformersDown: Collection<AndroidResultTransformer> = listOf(),
): Builder {
features.add(FeatureReference(id, 0, rigid, transformersUp, transformersDown))
return this
} }
fun addFeatureResult(ref: FeatureReference): Builder { fun addFeatureLevel(id: ResourceLocation, optional: Boolean = false, levels: Int = 1): Builder {
features.add(ref) return addResult(AndroidResearchResult.FeatureLevel(id, optional, levels))
return this }
fun addFeatureLevel(feature: AndroidFeatureType<*>, optional: Boolean = false, levels: Int = 1): Builder {
return addFeatureLevel(feature.registryName!!, optional, levels)
} }
fun addItem(cost: ItemStack): Builder { fun addItem(cost: ItemStack): Builder {
@ -844,9 +677,8 @@ class AndroidResearchType(
prerequisites = prerequisites, prerequisites = prerequisites,
blockedBy = blockers, blockedBy = blockers,
items = items, items = items,
features = features, results = results,
descriptionLines = description ?: listOf(), description = description,
descriptionSuppliers = descriptionSuppliers ?: listOf(),
experienceLevels = experience, experienceLevels = experience,
customName = customName, customName = customName,
skinIcon = skinIcon?.toJson(), skinIcon = skinIcon?.toJson(),

View File

@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.android.feature
import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.PoseStack import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.ChatFormatting
import net.minecraft.client.Camera import net.minecraft.client.Camera
import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.renderer.LevelRenderer import net.minecraft.client.renderer.LevelRenderer
@ -28,25 +27,22 @@ import net.minecraftforge.event.entity.living.LivingDeathEvent
import ru.dbotthepony.mc.otm.NULLABLE_MINECRAFT_SERVER import ru.dbotthepony.mc.otm.NULLABLE_MINECRAFT_SERVER
import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.android.AndroidActiveFeature import ru.dbotthepony.mc.otm.android.AndroidActiveFeature
import ru.dbotthepony.mc.otm.android.AndroidResearchManager
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
import ru.dbotthepony.mc.otm.capability.energy.extractEnergyExact import ru.dbotthepony.mc.otm.capability.energy.extractEnergyExact
import ru.dbotthepony.mc.otm.capability.matteryPlayer import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
import ru.dbotthepony.mc.otm.client.render.DynamicBufferSource import ru.dbotthepony.mc.otm.client.render.DynamicBufferSource
import ru.dbotthepony.mc.otm.client.render.ResearchIcons import ru.dbotthepony.mc.otm.client.render.ResearchIcons
import ru.dbotthepony.mc.otm.client.render.sprite
import ru.dbotthepony.mc.otm.client.render.linesIgnoreZRenderType import ru.dbotthepony.mc.otm.client.render.linesIgnoreZRenderType
import ru.dbotthepony.mc.otm.client.render.sprite
import ru.dbotthepony.mc.otm.config.AndroidConfig import ru.dbotthepony.mc.otm.config.AndroidConfig
import ru.dbotthepony.mc.otm.core.genericPositions
import ru.dbotthepony.mc.otm.core.holder
import ru.dbotthepony.mc.otm.core.isFall
import ru.dbotthepony.mc.otm.core.math.Vector import ru.dbotthepony.mc.otm.core.math.Vector
import ru.dbotthepony.mc.otm.core.math.asVector import ru.dbotthepony.mc.otm.core.math.asVector
import ru.dbotthepony.mc.otm.core.math.component1 import ru.dbotthepony.mc.otm.core.math.component1
import ru.dbotthepony.mc.otm.core.math.component2 import ru.dbotthepony.mc.otm.core.math.component2
import ru.dbotthepony.mc.otm.core.math.component3 import ru.dbotthepony.mc.otm.core.math.component3
import ru.dbotthepony.mc.otm.core.util.formatPower
import ru.dbotthepony.mc.otm.core.genericPositions
import ru.dbotthepony.mc.otm.core.holder
import ru.dbotthepony.mc.otm.core.isFall
import ru.dbotthepony.mc.otm.core.math.plus import ru.dbotthepony.mc.otm.core.math.plus
import ru.dbotthepony.mc.otm.core.math.rotateXDegrees import ru.dbotthepony.mc.otm.core.math.rotateXDegrees
import ru.dbotthepony.mc.otm.core.math.rotateYDegrees import ru.dbotthepony.mc.otm.core.math.rotateYDegrees
@ -54,7 +50,6 @@ import ru.dbotthepony.mc.otm.core.math.shortestDistanceBetween
import ru.dbotthepony.mc.otm.core.math.times import ru.dbotthepony.mc.otm.core.math.times
import ru.dbotthepony.mc.otm.milliTime import ru.dbotthepony.mc.otm.milliTime
import ru.dbotthepony.mc.otm.registry.AndroidFeatures import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import ru.dbotthepony.mc.otm.registry.MNames
import ru.dbotthepony.mc.otm.triggers.EnderTeleporterFallDeathTrigger import ru.dbotthepony.mc.otm.triggers.EnderTeleporterFallDeathTrigger
import java.util.* import java.util.*
import kotlin.math.sin import kotlin.math.sin
@ -399,12 +394,6 @@ class EnderTeleporterFeature(capability: MatteryPlayerCapability) : AndroidActiv
val SPRITE = ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/item/black_hole.png").sprite(0f, 0f, 16f, 16f, 16f, 16f) val SPRITE = ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/item/black_hole.png").sprite(0f, 0f, 16f, 16f, 16f, 16f)
val POWER_COST_DESCRIPTION =
AndroidResearchManager.descriptionFunc(
ResourceLocation(OverdriveThatMatters.MOD_ID, MNames.ENDER_TELEPORTER),
"otm.gui.power_cost_per_use",
{ AndroidConfig.EnderTeleporter.ENERGY_COST.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW) })
fun onEntityDeath(event: LivingDeathEvent) { fun onEntityDeath(event: LivingDeathEvent) {
val android = event.entity.matteryPlayer ?: return val android = event.entity.matteryPlayer ?: return
val server = NULLABLE_MINECRAFT_SERVER ?: return val server = NULLABLE_MINECRAFT_SERVER ?: return

View File

@ -35,10 +35,4 @@ class FallDampenersFeature(capability: MatteryPlayerCapability) : AndroidFeature
} }
} }
} }
companion object {
val DESCRIPTION = AndroidResearchManager.descriptionFuncs.register(ResourceLocation(OverdriveThatMatters.MOD_ID, MNames.FALL_DAMPENERS)) { level: Int ->
TranslatableComponent("otm.fall_dampeners.description", TextComponent("%.1f".format((AndroidConfig.FALL_DAMAGE_REDUCTION_PER_LEVEL * level).toFloat().coerceAtLeast(0f).coerceAtMost(1f) * 100f)).withStyle(ChatFormatting.YELLOW))
}
}
} }

View File

@ -132,12 +132,4 @@ class ItemMagnetFeature(capability: MatteryPlayerCapability) : AndroidSwitchable
override fun renderIcon(graphics: GuiGraphics, x: Float, y: Float, width: Float, height: Float) { override fun renderIcon(graphics: GuiGraphics, x: Float, y: Float, width: Float, height: Float) {
ResearchIcons.ICON_ITEM_MAGNET.render(graphics, x, y, width, height) ResearchIcons.ICON_ITEM_MAGNET.render(graphics, x, y, width, height)
} }
companion object {
val POWER_COST_DESCRIPTION =
AndroidResearchManager.descriptionFunc(ResourceLocation(
OverdriveThatMatters.MOD_ID, MNames.ITEM_MAGNET),
"otm.gui.power_cost_per_tick",
{ AndroidConfig.Magnet.POWER_DRAW.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW) })
}
} }

View File

@ -125,13 +125,4 @@ class JumpBoostFeature(capability: MatteryPlayerCapability) : AndroidSwitchableF
RenderSystem.setShaderColor(1f, 1f, 1f, 1f) RenderSystem.setShaderColor(1f, 1f, 1f, 1f)
} }
} }
companion object {
val POWER_COST_DESCRIPTION =
AndroidResearchManager.descriptionFunc(
ResourceLocation(OverdriveThatMatters.MOD_ID, MNames.JUMP_BOOST),
"otm.gui.power_cost_per_use",
{ AndroidConfig.JumpBoost.ENERGY_COST.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW) })
}
} }

View File

@ -106,33 +106,5 @@ class NanobotsArmorFeature(android: MatteryPlayerCapability) : AndroidFeature(An
0.45f, 0.45f,
0.6f, 0.6f,
) )
val STRENGTH_TRANSFORMER_UP = AndroidResearchManager.featureResultTransformers.register(ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_armor_strength_up"))
{ level: Int ->
if (second is NanobotsArmorFeature && (second as NanobotsArmorFeature).strength == level - 1) {
(second as NanobotsArmorFeature).strength = level
}
}
val STRENGTH_TRANSFORMER_DOWN = AndroidResearchManager.featureResultTransformers.register(ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_armor_strength_down"))
{ level: Int ->
if (second is NanobotsArmorFeature && (second as NanobotsArmorFeature).strength == level) {
(second as NanobotsArmorFeature).strength = level - 1
}
}
val SPEED_TRANSFORMER_UP = AndroidResearchManager.featureResultTransformers.register(ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_armor_speed_up"))
{ level: Int ->
if (second is NanobotsArmorFeature && (second as NanobotsArmorFeature).speed == level - 1) {
(second as NanobotsArmorFeature).speed = level
}
}
val SPEED_TRANSFORMER_DOWN = AndroidResearchManager.featureResultTransformers.register(ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_armor_speed_down"))
{ level: Int ->
if (second is NanobotsArmorFeature && (second as NanobotsArmorFeature).speed == level) {
(second as NanobotsArmorFeature).speed = level - 1
}
}
} }
} }

View File

@ -251,12 +251,4 @@ class ShockwaveFeature(capability: MatteryPlayerCapability) : AndroidSwitchableF
RenderSystem.setShaderColor(1f, 1f, 1f, 1f) RenderSystem.setShaderColor(1f, 1f, 1f, 1f)
} }
} }
companion object {
val POWER_COST_DESCRIPTION =
AndroidResearchManager.descriptionFunc(
ResourceLocation(OverdriveThatMatters.MOD_ID, MNames.SHOCKWAVE),
"otm.gui.power_cost_per_use",
{ AndroidConfig.Shockwave.ENERGY_COST.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW) })
}
} }

View File

@ -0,0 +1,33 @@
package ru.dbotthepony.mc.otm.data
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import kotlin.reflect.KProperty1
fun <V, T1> simpleCodec(factory: (T1) -> V, field1: KProperty1<V, T1>, codec1: Codec<T1>): Codec<V> {
return RecordCodecBuilder.create {
it.group(
codec1.fieldOf(field1.name).forGetter(field1::get)
).apply(it, factory)
}
}
fun <V, T1, T2> simpleCodec(factory: (T1, T2) -> V, field1: KProperty1<V, T1>, codec1: Codec<T1>, field2: KProperty1<V, T2>, codec2: Codec<T2>): Codec<V> {
return RecordCodecBuilder.create {
it.group(
codec1.fieldOf(field1.name).forGetter(field1::get),
codec2.fieldOf(field2.name).forGetter(field2::get),
).apply(it, factory)
}
}
fun <V, T1, T2, T3> simpleCodec(factory: (T1, T2, T3) -> V, field1: KProperty1<V, T1>, codec1: Codec<T1>, field2: KProperty1<V, T2>, codec2: Codec<T2>, field3: KProperty1<V, T3>, codec3: Codec<T3>): Codec<V> {
return RecordCodecBuilder.create {
it.group(
codec1.fieldOf(field1.name).forGetter(field1::get),
codec2.fieldOf(field2.name).forGetter(field2::get),
codec3.fieldOf(field3.name).forGetter(field3::get),
).apply(it, factory)
}
}

View File

@ -0,0 +1,25 @@
package ru.dbotthepony.mc.otm.data
import com.google.gson.JsonSyntaxException
import com.mojang.datafixers.util.Pair
import com.mojang.serialization.Codec
import com.mojang.serialization.DataResult
import com.mojang.serialization.DynamicOps
import com.mojang.serialization.JsonOps
import net.minecraft.network.chat.Component
object ComponentCodec : Codec<Component> {
override fun <T : Any> encode(input: Component, ops: DynamicOps<T>, prefix: T): DataResult<T> {
return DataResult.success(JsonOps.INSTANCE.convertTo(ops, Component.Serializer.toJsonTree(input)))
}
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<Component, T>> {
val value = ops.convertTo(JsonOps.INSTANCE, input)
try {
return DataResult.success(Pair(Component.Serializer.fromJson(value), ops.empty()))
} catch (err: JsonSyntaxException) {
return DataResult.error { err.message }
}
}
}

View File

@ -1,225 +0,0 @@
package ru.dbotthepony.mc.otm.data
import com.google.gson.Gson
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapter
import com.google.gson.internal.bind.TypeAdapters
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import io.netty.buffer.ByteBufInputStream
import io.netty.buffer.ByteBufOutputStream
import it.unimi.dsi.fastutil.io.FastByteArrayInputStream
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
import net.minecraft.nbt.NbtAccounter
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import ru.dbotthepony.mc.otm.core.util.readType
import ru.dbotthepony.mc.otm.core.util.readVarIntLE
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.core.util.writeType
import ru.dbotthepony.mc.otm.core.util.writeVarIntLE
import java.io.DataInputStream
import java.io.DataOutputStream
import java.lang.reflect.Type
import java.util.Base64
import java.util.Collections
import java.util.LinkedList
class SerializedFunctionRegistry<R, T>(val gson: Gson = Gson()) : JsonSerializer<SerializedFunctionRegistry.BoundFunction<R, T>>, JsonDeserializer<SerializedFunctionRegistry.BoundFunction<R, T>>, TypeAdapter<SerializedFunctionRegistry.BoundFunction<R, T>>() {
fun interface AnonymousFunction<R, T> {
fun invoke(receiver: R, arguments: List<Any?>): T
}
data class BoundFunction<R, T>(
val function: Function<R, T>,
val arguments: List<Any?>
) : java.util.function.Function<R, T> {
fun toJson(): JsonElement {
return JsonObject().also {
it["function"] = function.toJson()
val stream = FastByteArrayOutputStream()
val dataStream = DataOutputStream(stream)
stream.writeVarIntLE(arguments.size)
for (argument in arguments) {
dataStream.writeType(argument)
}
it["arguments"] = JsonPrimitive(Base64.getEncoder().encode(stream.array.copyOfRange(0, stream.length)).toString(Charsets.UTF_8))
}
}
fun toNetwork(buff: FriendlyByteBuf) {
function.toNetwork(buff)
val stream = DataOutputStream(ByteBufOutputStream(buff))
stream.writeVarIntLE(arguments.size)
for (argument in arguments) {
stream.writeType(argument)
}
}
override fun apply(t: R): T {
return function.body.invoke(t, arguments)
}
}
data class Function<R, T>(
val id: ResourceLocation,
val body: AnonymousFunction<R, T>,
val registry: SerializedFunctionRegistry<R, T>
) {
fun bind(vararg arguments: Any?): BoundFunction<R, T> {
validate(arguments.iterator().withIndex())
return BoundFunction(this, Collections.unmodifiableList(LinkedList<Any?>().also { it.addAll(arguments) }))
}
fun bind(arguments: List<Any?>): BoundFunction<R, T> {
validate(arguments.iterator().withIndex())
return BoundFunction(this, Collections.unmodifiableList(LinkedList<Any?>().also { it.addAll(arguments) }))
}
fun toJson(): JsonElement {
return JsonPrimitive(id.toString())
}
fun toNetwork(buff: FriendlyByteBuf) {
buff.writeUtf(id.toString())
}
companion object {
private fun validate(iterator: Iterator<IndexedValue<Any?>>) {
try {
val stream = DataOutputStream(FastByteArrayOutputStream())
for ((i, argument) in iterator) {
try {
stream.writeType(argument)
} catch(err: Exception) {
throw IllegalArgumentException("Argument at $i can not be serialized", err)
}
}
} catch(err: Exception) {
throw IllegalArgumentException("Argument list validation failed", err)
}
}
}
}
private val map = HashMap<ResourceLocation, Function<R, T>>()
fun register(id: ResourceLocation, function: AnonymousFunction<R, T>): Function<R, T> {
synchronized(map) {
return map.computeIfAbsent(id) { Function(id, function, this) }
}
}
fun register(id: ResourceLocation, function: R.() -> T): Function<R, T> {
return register(id, AnonymousFunction { r, args ->
check(args.isEmpty()) { "Invalid amount of arguments. No arguments are required" }
function.invoke(r)
})
}
inline fun <reified A : Any> register(id: ResourceLocation, noinline function: R.(A) -> T): Function<R, T> {
return register(id, AnonymousFunction { r, args ->
check(args.size == 1) { "Invalid amount of arguments. 1 is required" }
function.invoke(
r,
args[0] as? A ?: throw ClassCastException("Argument at 0 is supposed to be ${A::class.qualifiedName}, ${args[0]?.let { it::class.qualifiedName }} given")
)
})
}
inline fun <reified A : Any, reified B : Any> register(id: ResourceLocation, noinline function: R.(A, B) -> T): Function<R, T> {
return register(id, AnonymousFunction { r, args ->
check(args.size == 2) { "Invalid amount of arguments. 2 is required" }
function.invoke(
r,
args[0] as? A ?: throw ClassCastException("Argument at 0 is supposed to be ${A::class.qualifiedName}, ${args[0]?.let { it::class.qualifiedName }} given"),
args[1] as? B ?: throw ClassCastException("Argument at 1 is supposed to be ${B::class.qualifiedName}, ${args[1]?.let { it::class.qualifiedName }} given"),
)
})
}
inline fun <reified A : Any, reified B : Any, reified C : Any> register(id: ResourceLocation, noinline function: R.(A, B, C) -> T): Function<R, T> {
return register(id, AnonymousFunction { r, args ->
check(args.size == 3) { "Invalid amount of arguments. 3 is required" }
function.invoke(
r,
args[0] as? A ?: throw ClassCastException("Argument at 0 is supposed to be ${A::class.qualifiedName}, ${args[0]?.let { it::class.qualifiedName }} given"),
args[1] as? B ?: throw ClassCastException("Argument at 1 is supposed to be ${B::class.qualifiedName}, ${args[1]?.let { it::class.qualifiedName }} given"),
args[2] as? C ?: throw ClassCastException("Argument at 2 is supposed to be ${B::class.qualifiedName}, ${args[2]?.let { it::class.qualifiedName }} given"),
)
})
}
fun get(id: ResourceLocation): Function<R, T>? {
synchronized(map) {
return map[id]
}
}
fun fromNetwork(buff: FriendlyByteBuf): BoundFunction<R, T>? {
val id = ResourceLocation(buff.readUtf())
val stream = DataInputStream(ByteBufInputStream(buff))
val arguments = LinkedList<Any?>()
val sizeLimit = NbtAccounter(1L shl 18 /* 256 KiB */)
for (i in 0 until stream.readVarIntLE(sizeLimit)) {
arguments.add(stream.readType(sizeLimit))
}
return map[id]?.bind(arguments)
}
fun fromJson(value: JsonElement): BoundFunction<R, T>? {
if (value !is JsonObject) {
return null
}
val id = value["function"]?.asString ?: return null
val argumentString = value["arguments"]?.asString ?: return null
val stream = DataInputStream(FastByteArrayInputStream(Base64.getDecoder().decode(argumentString)))
val arguments = LinkedList<Any?>()
val sizeLimit = NbtAccounter(1L shl 18 /* 256 KiB */)
for (i in 0 until stream.readVarIntLE(sizeLimit)) {
arguments.add(stream.readType(sizeLimit))
}
return map[ResourceLocation(id)]?.bind(arguments)
}
override fun serialize(src: BoundFunction<R, T>, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
return src.toJson()
}
override fun deserialize(
json: JsonElement,
typeOfT: Type,
context: JsonDeserializationContext
): BoundFunction<R, T> {
return fromJson(json) ?: throw JsonSyntaxException("Function is invalid")
}
override fun write(out: JsonWriter, value: BoundFunction<R, T>) {
TypeAdapters.JSON_ELEMENT.write(out, value.toJson())
}
override fun read(`in`: JsonReader): BoundFunction<R, T> {
return fromJson(TypeAdapters.JSON_ELEMENT.read(`in`)) ?: throw JsonSyntaxException("Function is invalid")
}
}

View File

@ -0,0 +1,16 @@
package ru.dbotthepony.mc.otm.data
import com.mojang.datafixers.util.Pair
import com.mojang.serialization.Codec
import com.mojang.serialization.DataResult
import com.mojang.serialization.DynamicOps
class SingletonCodec<V : Any>(val value: V) : Codec<V> {
override fun <T : Any> encode(input: V, ops: DynamicOps<T>, prefix: T): DataResult<T> {
return DataResult.success(ops.empty())
}
override fun <T : Any> decode(ops: DynamicOps<T>, input: T? /* Так то, оно должно быть null */): DataResult<Pair<V, T>> {
return DataResult.success(Pair(value, ops.empty()))
}
}

View File

@ -10,20 +10,20 @@ import ru.dbotthepony.mc.otm.android.feature.*
object AndroidFeatures { object AndroidFeatures {
private val registry = DeferredRegister.create(MRegistry.ANDROID_FEATURES_KEY, OverdriveThatMatters.MOD_ID) private val registry = DeferredRegister.create(MRegistry.ANDROID_FEATURES_KEY, OverdriveThatMatters.MOD_ID)
val AIR_BAGS: AndroidFeatureType<*> by registry.register(MNames.AIR_BAGS) { AndroidFeatureType(::DummyAndroidFeature) } val AIR_BAGS: AndroidFeatureType<DummyAndroidFeature> by registry.register(MNames.AIR_BAGS) { AndroidFeatureType(::DummyAndroidFeature) }
val STEP_ASSIST: AndroidFeatureType<*> by registry.register(MNames.STEP_ASSIST) { AndroidFeatureType(::StepAssistFeature) } val STEP_ASSIST: AndroidFeatureType<StepAssistFeature> by registry.register(MNames.STEP_ASSIST) { AndroidFeatureType(::StepAssistFeature) }
val LIMB_OVERCLOCKING: AndroidFeatureType<*> by registry.register(MNames.LIMB_OVERCLOCKING) { AndroidFeatureType(::LimbOverclockingFeature) } val LIMB_OVERCLOCKING: AndroidFeatureType<LimbOverclockingFeature> by registry.register(MNames.LIMB_OVERCLOCKING) { AndroidFeatureType(::LimbOverclockingFeature) }
val ATTACK_BOOST: AndroidFeatureType<*> by registry.register(MNames.ATTACK_BOOST) { AndroidFeatureType(::AttackBoostFeature) } val ATTACK_BOOST: AndroidFeatureType<AttackBoostFeature> by registry.register(MNames.ATTACK_BOOST) { AndroidFeatureType(::AttackBoostFeature) }
val NANOBOTS_REGENERATION: AndroidFeatureType<*> by registry.register(MNames.NANOBOTS_REGENERATION) { AndroidFeatureType(::NanobotsRegenerationFeature) } val NANOBOTS_REGENERATION: AndroidFeatureType<NanobotsRegenerationFeature> by registry.register(MNames.NANOBOTS_REGENERATION) { AndroidFeatureType(::NanobotsRegenerationFeature) }
val NANOBOTS_ARMOR: AndroidFeatureType<*> by registry.register(MNames.NANOBOTS_ARMOR) { AndroidFeatureType(::NanobotsArmorFeature) } val NANOBOTS_ARMOR: AndroidFeatureType<NanobotsArmorFeature> by registry.register(MNames.NANOBOTS_ARMOR) { AndroidFeatureType(::NanobotsArmorFeature) }
val EXTENDED_REACH: AndroidFeatureType<*> by registry.register(MNames.EXTENDED_REACH) { AndroidFeatureType(::ExtendedReachFeature) } val EXTENDED_REACH: AndroidFeatureType<ExtendedReachFeature> by registry.register(MNames.EXTENDED_REACH) { AndroidFeatureType(::ExtendedReachFeature) }
val NIGHT_VISION: AndroidFeatureType<*> by registry.register(MNames.NIGHT_VISION) { AndroidFeatureType(::NightVisionFeature) } val NIGHT_VISION: AndroidFeatureType<NightVisionFeature> by registry.register(MNames.NIGHT_VISION) { AndroidFeatureType(::NightVisionFeature) }
val SHOCKWAVE: AndroidFeatureType<*> by registry.register(MNames.SHOCKWAVE) { AndroidFeatureType(::ShockwaveFeature) } val SHOCKWAVE: AndroidFeatureType<ShockwaveFeature> by registry.register(MNames.SHOCKWAVE) { AndroidFeatureType(::ShockwaveFeature) }
val ITEM_MAGNET: AndroidFeatureType<*> by registry.register(MNames.ITEM_MAGNET) { AndroidFeatureType(::ItemMagnetFeature) } val ITEM_MAGNET: AndroidFeatureType<ItemMagnetFeature> by registry.register(MNames.ITEM_MAGNET) { AndroidFeatureType(::ItemMagnetFeature) }
val FALL_DAMPENERS: AndroidFeatureType<*> by registry.register(MNames.FALL_DAMPENERS) { AndroidFeatureType(::FallDampenersFeature) } val FALL_DAMPENERS: AndroidFeatureType<FallDampenersFeature> by registry.register(MNames.FALL_DAMPENERS) { AndroidFeatureType(::FallDampenersFeature) }
val PHANTOM_ATTRACTOR: AndroidFeatureType<*> by registry.register(MNames.PHANTOM_ATTRACTOR) { AndroidFeatureType(::PhantomAttractorFeature) } val PHANTOM_ATTRACTOR: AndroidFeatureType<PhantomAttractorFeature> by registry.register(MNames.PHANTOM_ATTRACTOR) { AndroidFeatureType(::PhantomAttractorFeature) }
val JUMP_BOOST: AndroidFeatureType<*> by registry.register(MNames.JUMP_BOOST) { AndroidFeatureType(::JumpBoostFeature) } val JUMP_BOOST: AndroidFeatureType<JumpBoostFeature> by registry.register(MNames.JUMP_BOOST) { AndroidFeatureType(::JumpBoostFeature) }
val ENDER_TELEPORTER: AndroidFeatureType<*> by registry.register(MNames.ENDER_TELEPORTER) { AndroidFeatureType(::EnderTeleporterFeature) } val ENDER_TELEPORTER: AndroidFeatureType<EnderTeleporterFeature> by registry.register(MNames.ENDER_TELEPORTER) { AndroidFeatureType(::EnderTeleporterFeature) }
internal fun register(bus: IEventBus) { internal fun register(bus: IEventBus) {
registry.register(bus) registry.register(bus)

View File

@ -16,13 +16,13 @@ import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent
import net.minecraftforge.registries.NewRegistryEvent import net.minecraftforge.registries.NewRegistryEvent
import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.android.AndroidResearchResult
import ru.dbotthepony.mc.otm.android.AndroidResearchResults
import ru.dbotthepony.mc.otm.android.AndroidFeatureType import ru.dbotthepony.mc.otm.android.AndroidFeatureType
import ru.dbotthepony.mc.otm.android.AndroidResearchDescription
import ru.dbotthepony.mc.otm.android.AndroidResearchDescriptions
import ru.dbotthepony.mc.otm.android.feature.EnderTeleporterFeature import ru.dbotthepony.mc.otm.android.feature.EnderTeleporterFeature
import ru.dbotthepony.mc.otm.android.feature.FallDampenersFeature
import ru.dbotthepony.mc.otm.android.feature.ItemMagnetFeature
import ru.dbotthepony.mc.otm.android.feature.JumpBoostFeature
import ru.dbotthepony.mc.otm.android.feature.NanobotsArmorFeature import ru.dbotthepony.mc.otm.android.feature.NanobotsArmorFeature
import ru.dbotthepony.mc.otm.android.feature.ShockwaveFeature
import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock
import ru.dbotthepony.mc.otm.block.decorative.TritaniumPressurePlate import ru.dbotthepony.mc.otm.block.decorative.TritaniumPressurePlate
import ru.dbotthepony.mc.otm.capability.matteryEnergy import ru.dbotthepony.mc.otm.capability.matteryEnergy
@ -208,6 +208,10 @@ object MRegistry {
MCreativeTabs.initialize(bus) MCreativeTabs.initialize(bus)
DecimalProvider.register(bus) DecimalProvider.register(bus)
AndroidResearchDescription.register(bus)
AndroidResearchDescriptions.register(bus)
AndroidResearchResult.register(bus)
AndroidResearchResults.register(bus)
MBlocks.register(bus) MBlocks.register(bus)
MBlockEntities.register(bus) MBlockEntities.register(bus)
@ -224,10 +228,6 @@ object MRegistry {
// call static constructors // call static constructors
NanobotsArmorFeature.Companion NanobotsArmorFeature.Companion
ShockwaveFeature.Companion
ItemMagnetFeature.Companion
FallDampenersFeature.Companion
JumpBoostFeature.Companion
EnderTeleporterFeature.Companion EnderTeleporterFeature.Companion
} }