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

View File

@ -1,7 +1,6 @@
package ru.dbotthepony.mc.otm.android
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
import net.minecraft.ChatFormatting
import net.minecraft.nbt.CompoundTag
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.core.TextComponent
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.registryName
import ru.dbotthepony.mc.otm.core.nbt.set
@ -46,62 +44,19 @@ class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlay
return
}
onUnResearch()
onRefunded()
isResearched = false
}
private data class RememberResearchLevel(val level: Int?)
private val oldResearchLevel = Object2ObjectArrayMap<AndroidFeatureType<*>, RememberResearchLevel>()
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)
fun onRefunded() {
for (result in type.results) {
result.onRefunded(this)
}
}
}
}
}
oldResearchLevel.clear()
}
fun onResearched() {
oldResearchLevel.clear()
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
for (result in type.results) {
result.onResearched(this)
}
}
@ -297,7 +252,10 @@ class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlay
val tooltipLines: List<Component> get() {
val lines = ArrayList<Component>()
lines.add(type.displayName)
lines.addAll(type.description.iterator())
for (line in type.description) {
line.addLines(this, lines)
}
return lines
}
@ -382,38 +340,10 @@ class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlay
override fun serializeNBT(): CompoundTag {
return CompoundTag().also {
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) {
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<*> {
AndroidResearchManager.fireRegistrationEvent()
val set = ObjectArraySet<ResourceLocation>()
val added = LinkedList<AndroidResearchType>()
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 net.minecraft.client.server.IntegratedServer
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.packs.resources.ResourceManager
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.capability.matteryPlayer
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.RegistryNetworkChannel
import ru.dbotthepony.mc.otm.network.enqueueWork
@ -33,47 +30,7 @@ import ru.dbotthepony.mc.otm.onceServer
import java.util.LinkedList
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> {
/**
* 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"
private val LOGGER = LogManager.getLogger()
@ -97,23 +54,11 @@ object AndroidResearchManager : SimpleJsonResourceReloadListener(GsonBuilder().s
var researchMap: Map<ResourceLocation, AndroidResearchType> = mapOf()
private set
internal fun fireRegistrationEvent() {
if (!firedRegistrationEvent) {
try {
MinecraftForge.EVENT_BUS.post(RegisterFuncsEvent)
} finally {
firedRegistrationEvent = true
}
}
}
override fun apply(
jsonElementMap: Map<ResourceLocation, JsonElement>,
manager: ResourceManager,
profiler: ProfilerFiller
) {
fireRegistrationEvent()
val builder = ImmutableMap.builder<ResourceLocation, AndroidResearchType>()
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
import com.google.common.collect.ImmutableList
import com.google.common.collect.Streams
import com.google.gson.JsonArray
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSyntaxException
import com.google.gson.internal.bind.TypeAdapters
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component
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.registryName
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.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.registry.MRegistry
import java.util.LinkedList
import java.util.stream.Stream
import kotlin.collections.ArrayList
@ -39,7 +41,7 @@ import kotlin.collections.HashSet
private fun findPrerequisites(
initial: Collection<AndroidResearchType>,
add: MutableSet<AndroidResearchType> = HashSet(),
add: MutableSet<AndroidResearchType> = ObjectOpenHashSet(),
top: Boolean = true
): Set<AndroidResearchType> {
for (value in initial) {
@ -55,7 +57,7 @@ private fun findPrerequisites(
private fun findAllPrerequisites(
initial: Collection<AndroidResearchType>,
add: MutableSet<AndroidResearchType> = HashSet(),
add: MutableSet<AndroidResearchType> = ObjectOpenHashSet(),
): Set<AndroidResearchType> {
for (value in initial) {
add.add(value)
@ -67,7 +69,7 @@ private fun findAllPrerequisites(
private fun findAllChildren(
initial: Collection<AndroidResearchType>,
add: MutableSet<AndroidResearchType> = HashSet(),
add: MutableSet<AndroidResearchType> = ObjectOpenHashSet(),
): Set<AndroidResearchType> {
for (value in initial) {
add.add(value)
@ -83,10 +85,9 @@ class AndroidResearchType(
blockedBy: Collection<Reference>,
items: Collection<Pair<Ingredient, Int>>,
features: Collection<FeatureReference>,
results: Collection<AndroidResearchResult>,
descriptionLines: Collection<Component>,
descriptionSuppliers: Collection<ComponentSupplier> = listOf(),
description: Collection<AndroidResearchDescription>,
val experienceLevels: Int = 0,
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 {
if (flatPrerequisites.isEmpty()) {
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}") } })
}
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)
val results: ImmutableList<AndroidResearchResult> = ImmutableList.copyOf(results)
val items: Stream<out Pair<Ingredient, Int>> get() = definedItems.stream().filter { !it.first.isActuallyEmpty }
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) })
val description: ImmutableList<AndroidResearchDescription> = ImmutableList.copyOf(description)
/**
* Flat list of research preceding this research.
@ -469,14 +382,11 @@ class AndroidResearchType(
fun toJson(): JsonElement {
return JsonObject().also {
// it["id"] = JsonPrimitive(id.toString())
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["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["description"] = JsonArray().also { for (line in descriptionLines) it.add(Component.Serializer.toJsonTree(line)) }
it["description_funcs"] = JsonArray().also { for (line in descriptionSuppliers) it.add(line.toJson()) }
it["results"] = JsonArray().also { for (result in results) it.add(AndroidResearchResult.CODEC.toJsonStrict(result)) }
it["description"] = JsonArray().also { for (line in description) it.add(AndroidResearchDescription.CODEC.toJsonStrict(line)) }
it["experience"] = JsonPrimitive(experienceLevels)
if (skinIcon != null) {
@ -498,7 +408,6 @@ class AndroidResearchType(
}
fun validate() {
resolvedFeatures
resolvedBlockedBy
resolvedPrerequisites
}
@ -508,9 +417,8 @@ class AndroidResearchType(
buff.writeCollection(prerequisites) { 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(features) { a, b -> b.toNetwork(a) }
buff.writeCollection(descriptionLines) { a, b -> a.writeComponent(b) }
buff.writeCollection(descriptionSuppliers) { a, b -> b.toNetwork(a) }
buff.writeCollection(results) { a, b -> a.writeJson(AndroidResearchResult.CODEC.toJsonStrict(b)) }
buff.writeCollection(description) { a, b -> a.writeJson(AndroidResearchDescription.CODEC.toJsonStrict(b)) }
buff.writeVarInt(experienceLevels)
buff.writeBoolean(customName != null)
@ -532,9 +440,8 @@ class AndroidResearchType(
val prerequisites = buff.readCollection({ LinkedList() }, Reference::fromNetwork)
val blockedBy = buff.readCollection({ LinkedList() }, Reference::fromNetwork)
val items = buff.readCollection({ LinkedList() }, { Ingredient.fromNetwork(it) to it.readVarInt() })
val features = buff.readCollection({ LinkedList() }, FeatureReference::fromNetwork)
val descriptionLines = buff.readCollection({ LinkedList() }, FriendlyByteBuf::readComponent)
val descriptionSuppliers = buff.readCollection({ LinkedList() }, { AndroidResearchManager.descriptionFuncs.fromNetwork(it) })
val results = buff.readCollection({ LinkedList() }, { AndroidResearchResult.CODEC.fromJsonStrict(it.readJson()) })
val description = buff.readCollection({ LinkedList() }, { AndroidResearchDescription.CODEC.fromJsonStrict(it.readJson()) })
val experienceLevels = buff.readVarInt()
val customName = if (buff.readBoolean()) {
@ -566,9 +473,8 @@ class AndroidResearchType(
prerequisites = prerequisites,
blockedBy = blockedBy,
items = items,
features = features,
descriptionLines = descriptionLines,
descriptionSuppliers = descriptionSuppliers.filterNotNull(),
results = results,
description = description,
experienceLevels = experienceLevels,
customName = customName,
iconText = iconTextValue,
@ -587,7 +493,6 @@ class AndroidResearchType(
val items = value["required_items"] as JsonArray? ?: JsonArray()
val features = value["feature_result"] 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 customName = value["custom_name"]?.let(Component.Serializer::fromJson)
val iconText = value["icon_text"]?.let(Component.Serializer::fromJson)
@ -598,15 +503,12 @@ class AndroidResearchType(
id = id,
prerequisites = prerequisites.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()
.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")) }
.toList(),
descriptionLines = description.stream().map { Component.Serializer.fromJson(it) }.toList() as List<MutableComponent>,
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>,
description = description.stream().map { AndroidResearchDescription.CODEC.fromJsonStrict(it) }.toList(),
experienceLevels = experience,
customName = customName,
iconText = iconText,
@ -621,18 +523,18 @@ class AndroidResearchType(
val id: ResourceLocation,
var experience: Int = 0,
var customName: Component? = null,
var description: MutableList<Component>? = null,
var descriptionSuppliers: MutableList<ComponentSupplier>? = null,
description: MutableList<AndroidResearchDescription>? = null,
var itemIcon: Item? = null,
var skinIcon: AbstractMatterySprite? = null,
var iconText: Component? = null,
) {
val description = ArrayList<AndroidResearchDescription>(description ?: listOf())
val results = ArrayList<AndroidResearchResult>()
private val items = ArrayList<Pair<Ingredient, Int>>()
private val prerequisites = ArrayList<Reference>()
private val blockers = ArrayList<Reference>()
private val features = ArrayList<FeatureReference>()
fun withIconText(icon: Component? = null): Builder {
this.iconText = icon
return this
@ -661,19 +563,20 @@ class AndroidResearchType(
return this
}
fun withDescription(): Builder {
this.description = mutableListOf(TranslatableComponent("android_research.${id.namespace}.${id.path}.description"))
fun clearDescription(): Builder {
this.description.clear()
return this
}
fun withDescription(range: IntRange): Builder {
val result = ArrayList<Component>()
for (i in range) {
result.add(TranslatableComponent("android_research.${id.namespace}.${id.path}.description$i"))
fun withDescription(): Builder {
return withDescription(TranslatableComponent("android_research.${id.namespace}.${id.path}.description"))
}
fun withDescription(range: IntRange): Builder {
for (i in range) {
withDescription(TranslatableComponent("android_research.${id.namespace}.${id.path}.description$i"))
}
this.description = result
return this
}
@ -683,73 +586,21 @@ class AndroidResearchType(
}
fun withDescription(vararg description: Component): Builder {
this.description = description.toMutableList()
for (component in description)
withDescription(PlainAndroidResearchDescription.Instance(component))
return this
}
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
}
fun appendDescription(range: IntRange): Builder {
val result = this.description ?: ArrayList()
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) }
fun withDescription(vararg description: AndroidResearchDescription): Builder {
this.description.addAll(description)
return this
}
@ -776,43 +627,25 @@ class AndroidResearchType(
fun addPrerequisite(type: AndroidResearchType, rigid: Boolean = true) = addPrerequisite(type.id, rigid)
fun addBlocker(type: AndroidResearchType, rigid: Boolean = true) = addBlocker(type.id, rigid)
@JvmOverloads
fun addFeatureResult(
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))
fun addResult(result: AndroidResearchResult): Builder {
results.add(result)
return this
}
@JvmOverloads
fun addFeatureResult(
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(id: ResourceLocation, optional: Boolean = false): Builder {
return addResult(AndroidResearchResult.Feature(id, optional))
}
fun addFeatureResult(
id: ResourceLocation,
rigid: Boolean = false,
transformersUp: Collection<AndroidResultTransformer> = listOf(),
transformersDown: Collection<AndroidResultTransformer> = listOf(),
): Builder {
features.add(FeatureReference(id, 0, rigid, transformersUp, transformersDown))
return this
fun addFeatureResult(feature: AndroidFeatureType<*>, optional: Boolean = false): Builder {
return addFeatureResult(feature.registryName!!, optional)
}
fun addFeatureResult(ref: FeatureReference): Builder {
features.add(ref)
return this
fun addFeatureLevel(id: ResourceLocation, optional: Boolean = false, levels: Int = 1): Builder {
return addResult(AndroidResearchResult.FeatureLevel(id, optional, levels))
}
fun addFeatureLevel(feature: AndroidFeatureType<*>, optional: Boolean = false, levels: Int = 1): Builder {
return addFeatureLevel(feature.registryName!!, optional, levels)
}
fun addItem(cost: ItemStack): Builder {
@ -844,9 +677,8 @@ class AndroidResearchType(
prerequisites = prerequisites,
blockedBy = blockers,
items = items,
features = features,
descriptionLines = description ?: listOf(),
descriptionSuppliers = descriptionSuppliers ?: listOf(),
results = results,
description = description,
experienceLevels = experience,
customName = customName,
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.vertex.PoseStack
import net.minecraft.ChatFormatting
import net.minecraft.client.Camera
import net.minecraft.client.gui.GuiGraphics
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.OverdriveThatMatters
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.energy.extractEnergyExact
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.ResearchIcons
import ru.dbotthepony.mc.otm.client.render.sprite
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.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.asVector
import ru.dbotthepony.mc.otm.core.math.component1
import ru.dbotthepony.mc.otm.core.math.component2
import ru.dbotthepony.mc.otm.core.math.component3
import ru.dbotthepony.mc.otm.core.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.rotateXDegrees
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.milliTime
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import ru.dbotthepony.mc.otm.registry.MNames
import ru.dbotthepony.mc.otm.triggers.EnderTeleporterFallDeathTrigger
import java.util.*
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 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) {
val android = event.entity.matteryPlayer ?: 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) {
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)
}
}
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.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)
}
}
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 {
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 STEP_ASSIST: AndroidFeatureType<*> by registry.register(MNames.STEP_ASSIST) { AndroidFeatureType(::StepAssistFeature) }
val LIMB_OVERCLOCKING: AndroidFeatureType<*> by registry.register(MNames.LIMB_OVERCLOCKING) { AndroidFeatureType(::LimbOverclockingFeature) }
val ATTACK_BOOST: AndroidFeatureType<*> by registry.register(MNames.ATTACK_BOOST) { AndroidFeatureType(::AttackBoostFeature) }
val NANOBOTS_REGENERATION: AndroidFeatureType<*> by registry.register(MNames.NANOBOTS_REGENERATION) { AndroidFeatureType(::NanobotsRegenerationFeature) }
val NANOBOTS_ARMOR: AndroidFeatureType<*> by registry.register(MNames.NANOBOTS_ARMOR) { AndroidFeatureType(::NanobotsArmorFeature) }
val EXTENDED_REACH: AndroidFeatureType<*> by registry.register(MNames.EXTENDED_REACH) { AndroidFeatureType(::ExtendedReachFeature) }
val NIGHT_VISION: AndroidFeatureType<*> by registry.register(MNames.NIGHT_VISION) { AndroidFeatureType(::NightVisionFeature) }
val SHOCKWAVE: AndroidFeatureType<*> by registry.register(MNames.SHOCKWAVE) { AndroidFeatureType(::ShockwaveFeature) }
val ITEM_MAGNET: AndroidFeatureType<*> by registry.register(MNames.ITEM_MAGNET) { AndroidFeatureType(::ItemMagnetFeature) }
val FALL_DAMPENERS: AndroidFeatureType<*> by registry.register(MNames.FALL_DAMPENERS) { AndroidFeatureType(::FallDampenersFeature) }
val PHANTOM_ATTRACTOR: AndroidFeatureType<*> by registry.register(MNames.PHANTOM_ATTRACTOR) { AndroidFeatureType(::PhantomAttractorFeature) }
val JUMP_BOOST: AndroidFeatureType<*> by registry.register(MNames.JUMP_BOOST) { AndroidFeatureType(::JumpBoostFeature) }
val ENDER_TELEPORTER: AndroidFeatureType<*> by registry.register(MNames.ENDER_TELEPORTER) { AndroidFeatureType(::EnderTeleporterFeature) }
val AIR_BAGS: AndroidFeatureType<DummyAndroidFeature> by registry.register(MNames.AIR_BAGS) { AndroidFeatureType(::DummyAndroidFeature) }
val STEP_ASSIST: AndroidFeatureType<StepAssistFeature> by registry.register(MNames.STEP_ASSIST) { AndroidFeatureType(::StepAssistFeature) }
val LIMB_OVERCLOCKING: AndroidFeatureType<LimbOverclockingFeature> by registry.register(MNames.LIMB_OVERCLOCKING) { AndroidFeatureType(::LimbOverclockingFeature) }
val ATTACK_BOOST: AndroidFeatureType<AttackBoostFeature> by registry.register(MNames.ATTACK_BOOST) { AndroidFeatureType(::AttackBoostFeature) }
val NANOBOTS_REGENERATION: AndroidFeatureType<NanobotsRegenerationFeature> by registry.register(MNames.NANOBOTS_REGENERATION) { AndroidFeatureType(::NanobotsRegenerationFeature) }
val NANOBOTS_ARMOR: AndroidFeatureType<NanobotsArmorFeature> by registry.register(MNames.NANOBOTS_ARMOR) { AndroidFeatureType(::NanobotsArmorFeature) }
val EXTENDED_REACH: AndroidFeatureType<ExtendedReachFeature> by registry.register(MNames.EXTENDED_REACH) { AndroidFeatureType(::ExtendedReachFeature) }
val NIGHT_VISION: AndroidFeatureType<NightVisionFeature> by registry.register(MNames.NIGHT_VISION) { AndroidFeatureType(::NightVisionFeature) }
val SHOCKWAVE: AndroidFeatureType<ShockwaveFeature> by registry.register(MNames.SHOCKWAVE) { AndroidFeatureType(::ShockwaveFeature) }
val ITEM_MAGNET: AndroidFeatureType<ItemMagnetFeature> by registry.register(MNames.ITEM_MAGNET) { AndroidFeatureType(::ItemMagnetFeature) }
val FALL_DAMPENERS: AndroidFeatureType<FallDampenersFeature> by registry.register(MNames.FALL_DAMPENERS) { AndroidFeatureType(::FallDampenersFeature) }
val PHANTOM_ATTRACTOR: AndroidFeatureType<PhantomAttractorFeature> by registry.register(MNames.PHANTOM_ATTRACTOR) { AndroidFeatureType(::PhantomAttractorFeature) }
val JUMP_BOOST: AndroidFeatureType<JumpBoostFeature> by registry.register(MNames.JUMP_BOOST) { AndroidFeatureType(::JumpBoostFeature) }
val ENDER_TELEPORTER: AndroidFeatureType<EnderTeleporterFeature> by registry.register(MNames.ENDER_TELEPORTER) { AndroidFeatureType(::EnderTeleporterFeature) }
internal fun register(bus: IEventBus) {
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.registries.NewRegistryEvent
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.AndroidResearchDescription
import ru.dbotthepony.mc.otm.android.AndroidResearchDescriptions
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.ShockwaveFeature
import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock
import ru.dbotthepony.mc.otm.block.decorative.TritaniumPressurePlate
import ru.dbotthepony.mc.otm.capability.matteryEnergy
@ -208,6 +208,10 @@ object MRegistry {
MCreativeTabs.initialize(bus)
DecimalProvider.register(bus)
AndroidResearchDescription.register(bus)
AndroidResearchDescriptions.register(bus)
AndroidResearchResult.register(bus)
AndroidResearchResults.register(bus)
MBlocks.register(bus)
MBlockEntities.register(bus)
@ -224,10 +228,6 @@ object MRegistry {
// call static constructors
NanobotsArmorFeature.Companion
ShockwaveFeature.Companion
ItemMagnetFeature.Companion
FallDampenersFeature.Companion
JumpBoostFeature.Companion
EnderTeleporterFeature.Companion
}