Android Research is now loaded through JSON files
This commit is contained in:
parent
672441021d
commit
feb0c4d551
@ -10,6 +10,7 @@ import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||
import net.minecraftforge.fml.common.Mod
|
||||
import net.minecraftforge.data.event.GatherDataEvent
|
||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearchDataProvider
|
||||
import ru.dbotthepony.mc.otm.block.*
|
||||
import ru.dbotthepony.mc.otm.core.get
|
||||
import ru.dbotthepony.mc.otm.datagen.blocks.BatteryBankProvider
|
||||
@ -214,7 +215,6 @@ object DataGen {
|
||||
val tagsProvider = TagsProvider(event)
|
||||
|
||||
addTags(tagsProvider)
|
||||
AddEnglishLanguage(languageProvider)
|
||||
|
||||
event.generator.addProvider(true, blockModelProvider)
|
||||
event.generator.addProvider(true, itemModelProvider)
|
||||
@ -225,6 +225,9 @@ object DataGen {
|
||||
event.generator.addProvider(true, lootTableProvider)
|
||||
event.generator.addProvider(true, lootModifier)
|
||||
event.generator.addProvider(true, SoundDataProvider(event))
|
||||
event.generator.addProvider(true, AndroidResearchDataProvider(event.generator).also { it.exec { addResearchData(it, languageProvider) } })
|
||||
|
||||
AddEnglishLanguage(languageProvider)
|
||||
|
||||
blockModelProvider.resourceCubeAll(MBlocks.TRITANIUM_ORE)
|
||||
blockModelProvider.resourceCubeAll(MBlocks.TRITANIUM_RAW_BLOCK)
|
||||
@ -274,5 +277,7 @@ object DataGen {
|
||||
lootModifier.lambda {
|
||||
addLootModifiers(it)
|
||||
}
|
||||
|
||||
languageProvider.registerProviders()
|
||||
}
|
||||
}
|
||||
|
258
src/data/kotlin/ru/dbotthepony/mc/otm/datagen/ResearchData.kt
Normal file
258
src/data/kotlin/ru/dbotthepony/mc/otm/datagen/ResearchData.kt
Normal file
@ -0,0 +1,258 @@
|
||||
package ru.dbotthepony.mc.otm.datagen
|
||||
|
||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearchType
|
||||
import ru.dbotthepony.mc.otm.client.render.ResearchIcons
|
||||
import ru.dbotthepony.mc.otm.core.TextComponent
|
||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||
import ru.dbotthepony.mc.otm.datagen.lang.MatteryLanguageProvider
|
||||
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
|
||||
import ru.dbotthepony.mc.otm.registry.MNames
|
||||
import java.util.LinkedList
|
||||
import java.util.function.Consumer
|
||||
|
||||
@Suppress("LocalVariableName")
|
||||
fun addResearchData(serializer: Consumer<AndroidResearchType>, lang: MatteryLanguageProvider) {
|
||||
val AIR_BAGS = AndroidResearchType.Builder(modLocation(MNames.AIR_BAGS))
|
||||
.withExperience(18)
|
||||
.addFeatureResult(AndroidFeatures.AIR_BAGS)
|
||||
.withDescription()
|
||||
.withIcon(ResearchIcons.ICON_AIR_BAGS)
|
||||
.build()
|
||||
|
||||
serializer.accept(AIR_BAGS)
|
||||
|
||||
val IMPROVED_LIMBS = AndroidResearchType.Builder(modLocation(MNames.IMPROVED_LIMBS))
|
||||
.withExperience(20)
|
||||
.withDescription()
|
||||
.withIcon(ResearchIcons.ICON_EXTENDED_REACH)
|
||||
.build()
|
||||
|
||||
serializer.accept(IMPROVED_LIMBS)
|
||||
|
||||
val STEP_ASSIST = AndroidResearchType.Builder(modLocation(MNames.STEP_ASSIST))
|
||||
.withExperience(24)
|
||||
.addFeatureResult(AndroidFeatures.STEP_ASSIST)
|
||||
.withDescription()
|
||||
.withIcon(ResearchIcons.ICON_STEP_ASSIST)
|
||||
.addPrerequisite(IMPROVED_LIMBS)
|
||||
.build()
|
||||
|
||||
serializer.accept(STEP_ASSIST)
|
||||
|
||||
val EXTENDED_REACH = AndroidResearchType.Builder(modLocation(MNames.EXTENDED_REACH))
|
||||
.withExperience(40)
|
||||
.addFeatureResult(AndroidFeatures.EXTENDED_REACH)
|
||||
.addPrerequisite(IMPROVED_LIMBS)
|
||||
.withDescription()
|
||||
.withIcon(ResearchIcons.ICON_EXTENDED_REACH)
|
||||
.build()
|
||||
|
||||
serializer.accept(EXTENDED_REACH)
|
||||
|
||||
val NIGHT_VISION = AndroidResearchType.Builder(modLocation(MNames.NIGHT_VISION))
|
||||
.withExperience(40)
|
||||
.withDescription()
|
||||
.withIcon(ResearchIcons.ICON_NIGHT_VISION)
|
||||
.addFeatureResult(AndroidFeatures.NIGHT_VISION)
|
||||
.build()
|
||||
|
||||
serializer.accept(NIGHT_VISION)
|
||||
|
||||
val NANOBOTS = AndroidResearchType.Builder(modLocation(MNames.NANOBOTS))
|
||||
.withExperience(15)
|
||||
.withDescription()
|
||||
.withIcon(ResearchIcons.ICON_NANOBOTS)
|
||||
.build()
|
||||
|
||||
serializer.accept(NANOBOTS)
|
||||
|
||||
val NANOBOTS_ARMOR = AndroidResearchType.Builder(modLocation(MNames.NANOBOTS_ARMOR))
|
||||
.withExperience(25)
|
||||
.withDescription()
|
||||
.addPrerequisite(OverdriveThatMatters.loc(MNames.NANOBOTS))
|
||||
.addFeatureResult(OverdriveThatMatters.loc(MNames.NANOBOTS_ARMOR))
|
||||
.withIcon(ResearchIcons.ICON_ARMOR)
|
||||
.addBlocker(OverdriveThatMatters.loc(MNames.ATTACK_BOOST_1), rigid = true)
|
||||
.build()
|
||||
|
||||
serializer.accept(NANOBOTS_ARMOR)
|
||||
|
||||
val limbList = LinkedList<AndroidResearchType>()
|
||||
val regenList = LinkedList<AndroidResearchType>()
|
||||
val armorStrengthList = LinkedList<AndroidResearchType>()
|
||||
val armorSpeedList = LinkedList<AndroidResearchType>()
|
||||
val attackBoostList = LinkedList<AndroidResearchType>()
|
||||
|
||||
for (i in 0 until 4) {
|
||||
limbList.add(run {
|
||||
val research = AndroidResearchType.Builder(modLocation(MNames.LIMB_OVERCLOCKING_LIST[i]))
|
||||
.withExperience(18 + i * 8)
|
||||
.withIconText(TextComponent((i + 1).toString()))
|
||||
.addPrerequisite(IMPROVED_LIMBS)
|
||||
.withIcon(ResearchIcons.ICON_LIMB_OVERCLOCKING)
|
||||
.withName(TranslatableComponent("android_research.overdrive_that_matters.limb_overclocking", i + 1))
|
||||
.withDescription(
|
||||
TranslatableComponent(
|
||||
"android_research.overdrive_that_matters.limb_overclocking.description",
|
||||
(i + 1) * 8,
|
||||
(i + 1) * 6
|
||||
)
|
||||
)
|
||||
.addFeatureResult(OverdriveThatMatters.loc(MNames.LIMB_OVERCLOCKING), i)
|
||||
|
||||
if (i > 0) {
|
||||
research.addPrerequisite(OverdriveThatMatters.loc(MNames.LIMB_OVERCLOCKING_LIST[i - 1]), rigid = true)
|
||||
}
|
||||
|
||||
research.build()
|
||||
})
|
||||
|
||||
attackBoostList.add(run {
|
||||
val research = AndroidResearchType.Builder(modLocation(MNames.ATTACK_BOOST_LIST[i]))
|
||||
.withExperience(18 + i * 8)
|
||||
.withIconText(TextComponent((i + 1).toString()))
|
||||
.addPrerequisite(IMPROVED_LIMBS)
|
||||
.withIcon(ResearchIcons.ICON_ATTACK_BOOST)
|
||||
.withName(TranslatableComponent("android_research.overdrive_that_matters.attack_boost", i + 1))
|
||||
.withDescription(
|
||||
TranslatableComponent(
|
||||
"android_research.overdrive_that_matters.attack_boost.description",
|
||||
(i + 1) * 6
|
||||
)
|
||||
)
|
||||
.addFeatureResult(AndroidFeatures.ATTACK_BOOST, i)
|
||||
.addBlocker(NANOBOTS_ARMOR)
|
||||
|
||||
if (i > 0) {
|
||||
research.addPrerequisite(OverdriveThatMatters.loc(MNames.ATTACK_BOOST_LIST[i - 1]), rigid = true)
|
||||
}
|
||||
|
||||
research.build()
|
||||
})
|
||||
|
||||
regenList.add(run {
|
||||
val regeneration = AndroidResearchType.Builder(modLocation(MNames.NANOBOTS_REGENERATION_LIST[i]))
|
||||
.withExperience(20 + i * 6)
|
||||
.withIconText(TextComponent((i + 1).toString()))
|
||||
.withIcon(ResearchIcons.ICON_NANOBOTS)
|
||||
.withName(TranslatableComponent("android_research.overdrive_that_matters.nanobots_regeneration", i + 1))
|
||||
.withDescription(
|
||||
if (i > 0) TranslatableComponent("android_research.overdrive_that_matters.nanobots_regeneration.description_improve") else TranslatableComponent(
|
||||
"android_research.overdrive_that_matters.nanobots_regeneration.description"
|
||||
)
|
||||
)
|
||||
.addFeatureResult(AndroidFeatures.NANOBOTS_REGENERATION, i)
|
||||
|
||||
if (i > 0) {
|
||||
regeneration.addPrerequisite(OverdriveThatMatters.loc(MNames.NANOBOTS_REGENERATION_LIST[i - 1]), rigid = true)
|
||||
} else {
|
||||
regeneration.addPrerequisite(OverdriveThatMatters.loc(MNames.NANOBOTS), rigid = true)
|
||||
}
|
||||
|
||||
regeneration.build()
|
||||
})
|
||||
}
|
||||
|
||||
for (i in 0 until 3) {
|
||||
val level = i + 1
|
||||
|
||||
armorStrengthList.add(run {
|
||||
AndroidResearchType.Builder(modLocation(MNames.NANOBOTS_ARMOR_STRENGTH_LIST[i]))
|
||||
.withExperience(20 + i * 8)
|
||||
.withIconText(TextComponent((i + 1).toString()))
|
||||
.withIcon(ResearchIcons.ICON_ARMOR)
|
||||
.addPrerequisite(OverdriveThatMatters.loc(if (i > 0) MNames.NANOBOTS_ARMOR_STRENGTH_LIST[i - 1] else MNames.NANOBOTS_ARMOR))
|
||||
.withName(
|
||||
TranslatableComponent(
|
||||
"android_research.overdrive_that_matters.nanobots_armor_strength",
|
||||
i + 1
|
||||
)
|
||||
)
|
||||
.withDescription(
|
||||
TranslatableComponent(
|
||||
"android_research.overdrive_that_matters.nanobots_armor_strength.description",
|
||||
(i + 1) * 8,
|
||||
(i + 1) * 6
|
||||
)
|
||||
)
|
||||
.addFeatureResult(AndroidFeatures.NANOBOTS_ARMOR, 0) // TODO
|
||||
.build()
|
||||
})
|
||||
|
||||
armorSpeedList.add(run {
|
||||
AndroidResearchType.Builder(modLocation(MNames.NANOBOTS_ARMOR_SPEED_LIST[i]))
|
||||
.withExperience(18 + i * 6)
|
||||
.withIconText(TextComponent((i + 1).toString()))
|
||||
.withIcon(ResearchIcons.ICON_ARMOR)
|
||||
.addPrerequisite(OverdriveThatMatters.loc(if (i > 0) MNames.NANOBOTS_ARMOR_SPEED_LIST[i - 1] else MNames.NANOBOTS_ARMOR))
|
||||
.withName(
|
||||
TranslatableComponent(
|
||||
"android_research.overdrive_that_matters.nanobots_armor_speed",
|
||||
i + 1
|
||||
)
|
||||
)
|
||||
.withDescription(
|
||||
TranslatableComponent(
|
||||
"android_research.overdrive_that_matters.nanobots_armor_speed.description",
|
||||
(i + 1) * 8,
|
||||
(i + 1) * 6
|
||||
)
|
||||
)
|
||||
.addFeatureResult(AndroidFeatures.NANOBOTS_ARMOR, 0) // TODO
|
||||
.build()
|
||||
})
|
||||
}
|
||||
|
||||
limbList.forEach(serializer::accept)
|
||||
regenList.forEach(serializer::accept)
|
||||
armorStrengthList.forEach(serializer::accept)
|
||||
armorSpeedList.forEach(serializer::accept)
|
||||
attackBoostList.forEach(serializer::accept)
|
||||
|
||||
val SHOCKWAVE =
|
||||
AndroidResearchType.Builder(modLocation(MNames.SHOCKWAVE))
|
||||
.withExperience(40)
|
||||
.withDescription()
|
||||
.withIcon(ResearchIcons.ICON_SHOCKWAVE)
|
||||
.addFeatureResult(AndroidFeatures.SHOCKWAVE)
|
||||
.addPrerequisite(attackBoostList[2])
|
||||
.build()
|
||||
|
||||
serializer.accept(SHOCKWAVE)
|
||||
|
||||
with(lang.english) {
|
||||
add(limbList[0], "Limb Overclocking %s")
|
||||
add(limbList[0], "description", "Boosts unit's mobility by %s%% and attack speed by %s%%")
|
||||
|
||||
add(AIR_BAGS, "Air Bags")
|
||||
add(NANOBOTS, "Nanobots")
|
||||
add(AIR_BAGS, "description", "Allows unit to swim in water")
|
||||
add(NANOBOTS, "description", "Various useful nanobots for doing various tasks")
|
||||
|
||||
add(regenList[0], "Regeneration %s")
|
||||
add(regenList[0], "description", "Nanobots get ability to repair unit's internal systems on the move")
|
||||
add(regenList[0], "description_improve", "Improves regeneration speed")
|
||||
|
||||
add(NANOBOTS_ARMOR, "Nanobots Armor")
|
||||
add(NANOBOTS_ARMOR, "description", "Allows nanobots to align themselves in cell shape, reducing incoming damage by a %% by absorbing impacts")
|
||||
|
||||
add(armorSpeedList[0], "Nanobots Armor Build Speed %s")
|
||||
add(armorSpeedList[0], "description", "Reduces time required for nanobots to form protection layer")
|
||||
|
||||
add(armorStrengthList[0], "Nanobots Armor Strength %s")
|
||||
add(armorStrengthList[0], "description", "Increases impact absorption strength of nanobots")
|
||||
|
||||
add(EXTENDED_REACH, "Extended Reach")
|
||||
add(EXTENDED_REACH, "description", "Increases block interaction distance")
|
||||
|
||||
add(IMPROVED_LIMBS, "Improved Limbs")
|
||||
add(IMPROVED_LIMBS, "description", "Allows limbs to be upgraded")
|
||||
|
||||
add(STEP_ASSIST, "Step Assist")
|
||||
add(STEP_ASSIST, "description", "Allows unit to step up whole blocks")
|
||||
|
||||
add(attackBoostList[0], "Improved Arms Servo %s")
|
||||
add(attackBoostList[0], "description", "Increases total melee attack strength by %s%%")
|
||||
}
|
||||
}
|
@ -455,39 +455,6 @@ private fun research(provider: MatteryLanguageProvider) {
|
||||
add("android_research.status.requires", "Requires %s to be researched")
|
||||
add("android_research.status.blocks", "Locks %s")
|
||||
add("android_research.status.blocked_by", "Locked by %s")
|
||||
|
||||
add(AndroidResearch.LIMB_OVERCLOCKING[0], "Limb Overclocking %s")
|
||||
add(AndroidResearch.LIMB_OVERCLOCKING[0], "description", "Boosts unit's mobility by %s%% and attack speed by %s%%")
|
||||
|
||||
add(AndroidResearch.AIR_BAGS, "Air Bags")
|
||||
add(AndroidResearch.NANOBOTS, "Nanobots")
|
||||
add(AndroidResearch.AIR_BAGS, "description", "Allows unit to swim in water")
|
||||
add(AndroidResearch.NANOBOTS, "description", "Various useful nanobots for doing various tasks")
|
||||
|
||||
add(AndroidResearch.NANOBOTS_REGENERATION[0], "Regeneration %s")
|
||||
add(AndroidResearch.NANOBOTS_REGENERATION[0], "description", "Nanobots get ability to repair unit's internal systems on the move")
|
||||
add(AndroidResearch.NANOBOTS_REGENERATION[0], "description_improve", "Improves regeneration speed")
|
||||
|
||||
add(AndroidResearch.NANOBOTS_ARMOR, "Nanobots Armor")
|
||||
add(AndroidResearch.NANOBOTS_ARMOR, "description", "Allows nanobots to align themselves in cell shape, reducing incoming damage by a %% by absorbing impacts")
|
||||
|
||||
add(AndroidResearch.NANOBOTS_ARMOR_SPEED[0], "Nanobots Armor Build Speed %s")
|
||||
add(AndroidResearch.NANOBOTS_ARMOR_SPEED[0], "description", "Reduces time required for nanobots to form protection layer")
|
||||
|
||||
add(AndroidResearch.NANOBOTS_ARMOR_STRENGTH[0], "Nanobots Armor Strength %s")
|
||||
add(AndroidResearch.NANOBOTS_ARMOR_STRENGTH[0], "description", "Increases impact absorption strength of nanobots")
|
||||
|
||||
add(AndroidResearch.EXTENDED_REACH, "Extended Reach")
|
||||
add(AndroidResearch.EXTENDED_REACH, "description", "Increases block interaction distance")
|
||||
|
||||
add(AndroidResearch.IMPROVED_LIMBS, "Improved Limbs")
|
||||
add(AndroidResearch.IMPROVED_LIMBS, "description", "Allows limbs to be upgraded")
|
||||
|
||||
add(AndroidResearch.STEP_ASSIST, "Step Assist")
|
||||
add(AndroidResearch.STEP_ASSIST, "description", "Allows unit to step up whole blocks")
|
||||
|
||||
add(AndroidResearch.ATTACK_BOOST[0], "Improved Arms Servo %s")
|
||||
add(AndroidResearch.ATTACK_BOOST[0], "description", "Increases total melee attack strength by %s%%")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ import ru.dbotthepony.mc.otm.android.AndroidFeatureType
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearchType
|
||||
import ru.dbotthepony.mc.otm.registry.objects.ColoredDecorativeBlock
|
||||
|
||||
private fun researchString(key: AndroidResearchType<*>): String {
|
||||
private fun researchString(key: AndroidResearchType): String {
|
||||
val displayName = key.displayName
|
||||
|
||||
if (displayName is MutableComponent) {
|
||||
@ -36,17 +36,19 @@ private fun researchString(key: AndroidResearchType<*>): String {
|
||||
|
||||
class MatteryLanguageProvider(private val gen: DataGenerator) {
|
||||
private inner class Slave(language: String) : LanguageProvider(gen, OverdriveThatMatters.MOD_ID, language) {
|
||||
init {
|
||||
gen.addProvider(true, this)
|
||||
}
|
||||
|
||||
override fun addTranslations() {}
|
||||
}
|
||||
|
||||
private val slaves = Object2ObjectArrayMap<String, Slave>()
|
||||
|
||||
fun registerProviders() {
|
||||
for (slave in slaves.values) {
|
||||
gen.addProvider(true, slave)
|
||||
}
|
||||
}
|
||||
|
||||
inner class Builder(language: String) {
|
||||
val slave: LanguageProvider = slaves.computeIfAbsent(language, ::Slave)
|
||||
val slave: LanguageProvider by lazy { slaves.computeIfAbsent(language, ::Slave) }
|
||||
|
||||
fun add(key: String, value: String) = slave.add(key, value)
|
||||
fun add(key: Block, value: String) = slave.add(key, value)
|
||||
@ -61,9 +63,9 @@ class MatteryLanguageProvider(private val gen: DataGenerator) {
|
||||
fun death(key: String, value: String) = slave.add("death.attack.$key", value)
|
||||
fun stat(key: String, value: String) = slave.add("stat.${OverdriveThatMatters.MOD_ID}.$key", value)
|
||||
|
||||
fun add(key: AndroidResearchType<*>, value: String) = slave.add(researchString(key), value)
|
||||
fun add(key: AndroidResearchType, value: String) = slave.add(researchString(key), value)
|
||||
fun research(key: String, value: String) = slave.add("android_research.overdrive_that_matters.$key", value)
|
||||
fun add(key: AndroidResearchType<*>, suffix: String, value: String) = slave.add("${researchString(key)}.$suffix", value)
|
||||
fun add(key: AndroidResearchType, suffix: String, value: String) = slave.add("${researchString(key)}.$suffix", value)
|
||||
fun research(key: String, suffix: String, value: String) = slave.add("android_research.overdrive_that_matters.$key.$suffix", value)
|
||||
|
||||
fun add(key: AndroidFeatureType<*>, value: String) = slave.add(key.displayId, value)
|
||||
@ -362,8 +364,8 @@ class MatteryLanguageProvider(private val gen: DataGenerator) {
|
||||
"Black",
|
||||
)
|
||||
|
||||
val english = Builder("en_us")
|
||||
val russian = Builder("ru_ru")
|
||||
val english by lazy { Builder("en_us") }
|
||||
val russian by lazy { Builder("ru_ru") }
|
||||
|
||||
fun getProvider(language: String): LanguageProvider = slaves.computeIfAbsent(language, ::Slave)
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearchManager;
|
||||
import ru.dbotthepony.mc.otm.block.entity.blackhole.ExplosionQueue;
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability;
|
||||
@ -130,6 +131,9 @@ public final class OverdriveThatMatters {
|
||||
EVENT_BUS.addListener(EventPriority.NORMAL, QuantumBatteryItem.Companion::tick);
|
||||
EVENT_BUS.addListener(EventPriority.LOWEST, PortableCondensationDriveItem.Companion::onPickupEvent);
|
||||
|
||||
EVENT_BUS.addListener(EventPriority.NORMAL, AndroidResearchManager.INSTANCE::reloadEvent);
|
||||
EVENT_BUS.addListener(EventPriority.NORMAL, AndroidResearchManager.INSTANCE::syncEvent);
|
||||
|
||||
MatteryPlayerNetworkChannel.INSTANCE.register();
|
||||
MenuNetworkChannel.INSTANCE.register();
|
||||
WeaponNetworkChannel.INSTANCE.register();
|
||||
|
@ -1,31 +1,41 @@
|
||||
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
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraftforge.common.util.INBTSerializable
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
|
||||
import ru.dbotthepony.mc.otm.capability.itemsStream
|
||||
import ru.dbotthepony.mc.otm.client.render.SkinElement
|
||||
import ru.dbotthepony.mc.otm.container.iterator
|
||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||
import ru.dbotthepony.mc.otm.core.addAll
|
||||
import ru.dbotthepony.mc.otm.core.getCompoundList
|
||||
import ru.dbotthepony.mc.otm.core.nonEmpty
|
||||
import ru.dbotthepony.mc.otm.core.set
|
||||
import ru.dbotthepony.mc.otm.network.FieldSynchronizer
|
||||
import ru.dbotthepony.mc.otm.registry.MRegistry
|
||||
import java.io.DataInputStream
|
||||
import java.io.InputStream
|
||||
|
||||
abstract class AndroidResearch(val type: AndroidResearchType<*>, val capability: MatteryPlayerCapability) : INBTSerializable<CompoundTag> {
|
||||
class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlayerCapability) : INBTSerializable<CompoundTag> {
|
||||
val ply: Player get() = capability.ply
|
||||
|
||||
val synchronizer = FieldSynchronizer()
|
||||
|
||||
var isResearched by synchronizer.bool()
|
||||
protected set
|
||||
private set
|
||||
|
||||
/**
|
||||
* Called when it is required to network everything again
|
||||
*/
|
||||
open fun invalidateNetwork() {
|
||||
fun invalidateNetwork() {
|
||||
synchronizer.invalidate()
|
||||
}
|
||||
|
||||
@ -38,8 +48,52 @@ abstract class AndroidResearch(val type: AndroidResearchType<*>, val capability:
|
||||
isResearched = false
|
||||
}
|
||||
|
||||
abstract fun onUnResearch()
|
||||
abstract fun onResearched()
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(err: Throwable) {
|
||||
oldResearchLevel.clear()
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes required resources for researching this technology.
|
||||
@ -48,24 +102,91 @@ abstract class AndroidResearch(val type: AndroidResearchType<*>, val capability:
|
||||
*
|
||||
* This function MUST be atomic, hence it SHOULD NOT consume ANY resources if it returns false.
|
||||
*/
|
||||
abstract fun consumeResearchCost(simulate: Boolean): Boolean
|
||||
fun consumeResearchCost(simulate: Boolean): Boolean {
|
||||
if (capability.ply.abilities.instabuild) {
|
||||
// creative
|
||||
return true
|
||||
}
|
||||
|
||||
if (!simulate && !consumeResearchCost(true)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (type.experienceLevels > 0) {
|
||||
if (capability.ply.experienceLevel < type.experienceLevels) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!simulate) {
|
||||
capability.ply.giveExperienceLevels(-type.experienceLevels)
|
||||
}
|
||||
}
|
||||
|
||||
for (item in type.items) {
|
||||
var required = item.count
|
||||
val iterator = capability.ply.inventory.iterator().nonEmpty()
|
||||
|
||||
for (invItem in iterator) {
|
||||
if (ItemStack.isSameItemSameTags(invItem, item)) {
|
||||
val toExtract = required.coerceAtMost(invItem.count)
|
||||
required -= toExtract
|
||||
|
||||
if (!simulate) {
|
||||
if (toExtract == invItem.count) {
|
||||
iterator.remove()
|
||||
} else {
|
||||
invItem.count -= toExtract
|
||||
}
|
||||
}
|
||||
|
||||
if (required <= 0) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (required > 0) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Grants all (or some) resources back consumed by [consumeResearchCost].
|
||||
*
|
||||
* Returns true whenever player accepted all resources refunded, false otherwise.
|
||||
*/
|
||||
abstract fun refund(simulate: Boolean): Boolean
|
||||
fun refund(simulate: Boolean): Boolean {
|
||||
if (simulate) {
|
||||
return true
|
||||
}
|
||||
|
||||
open fun collectNetworkPayload(): FastByteArrayOutputStream? {
|
||||
if (type.experienceLevels > 0) {
|
||||
capability.ply.giveExperienceLevels(type.experienceLevels)
|
||||
}
|
||||
|
||||
for (item in type.items) {
|
||||
capability.ply.inventory.add(item)
|
||||
|
||||
if (!item.isEmpty) {
|
||||
capability.ply.spawnAtLocation(item)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun collectNetworkPayload(): FastByteArrayOutputStream? {
|
||||
return synchronizer.collectNetworkPayload()
|
||||
}
|
||||
|
||||
open fun applyNetworkPayload(stream: InputStream) {
|
||||
fun applyNetworkPayload(stream: InputStream) {
|
||||
synchronizer.applyNetworkPayload(DataInputStream(stream))
|
||||
}
|
||||
|
||||
open val canResearch: Boolean get() {
|
||||
val canResearch: Boolean get() {
|
||||
if (!consumeResearchCost(simulate = true)) {
|
||||
return false
|
||||
}
|
||||
@ -155,37 +276,98 @@ abstract class AndroidResearch(val type: AndroidResearchType<*>, val capability:
|
||||
/**
|
||||
* List of all tooltip lines for this research
|
||||
*/
|
||||
open val tooltipLines: List<Component> by lazy {
|
||||
return@lazy listOf(type.displayName.copy().withStyle(ChatFormatting.WHITE))
|
||||
val tooltipLines: List<Component> get() {
|
||||
val lines = ArrayList<Component>()
|
||||
lines.add(type.displayName)
|
||||
lines.addAll(type.description.iterator())
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
/**
|
||||
* List of all tooltip lines for this research, for local player at research screen (clientside only)
|
||||
*/
|
||||
open val screenTooltipLines: List<Component> by this::tooltipLines
|
||||
val screenTooltipLines: List<Component> get() {
|
||||
val builder = tooltipLines as ArrayList<Component>
|
||||
|
||||
/**
|
||||
* This should return whatever can be used by other code to display this research's name
|
||||
*/
|
||||
open val tooltipHeader: Component by lazy { tooltipLines[0] }
|
||||
if (type.experienceLevels != 0) {
|
||||
builder.add(
|
||||
TranslatableComponent("otm.android_station.research.xp_cost", type.experienceLevels).withStyle(
|
||||
if (capability.ply.experienceLevel >= type.experienceLevels)
|
||||
ChatFormatting.DARK_GREEN
|
||||
else
|
||||
ChatFormatting.DARK_RED
|
||||
))
|
||||
}
|
||||
|
||||
for (value in this.type.flatPrerequisites) {
|
||||
val instance = capability.getResearch(value)
|
||||
|
||||
builder.add(
|
||||
TranslatableComponent("android_research.status.requires", instance.screenTooltipHeader).withStyle(
|
||||
if (instance.isResearched)
|
||||
ChatFormatting.DARK_GREEN
|
||||
else
|
||||
ChatFormatting.DARK_RED
|
||||
))
|
||||
}
|
||||
|
||||
for (value in this.type.flatBlockedBy) {
|
||||
builder.add(TranslatableComponent("android_research.status.blocked_by", capability.getResearch(value).screenTooltipHeader).withStyle(ChatFormatting.DARK_RED))
|
||||
}
|
||||
|
||||
for (value in this.type.flatBlocking) {
|
||||
builder.add(TranslatableComponent("android_research.status.blocks", capability.getResearch(value).screenTooltipHeader).withStyle(ChatFormatting.DARK_RED))
|
||||
}
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
/**
|
||||
* This should return whatever can be used by other code to display this research's name,
|
||||
* for local player at research screen (clientside only)
|
||||
*/
|
||||
open val screenTooltipHeader: Component by lazy { screenTooltipLines[0] }
|
||||
val screenTooltipHeader: Component get() = type.displayName
|
||||
|
||||
open val skinIcon: SkinElement? get() = null
|
||||
open val stackIcon: ItemStack? get() = null
|
||||
open val iconText: Component? get() = null
|
||||
val skinIcon: SkinElement? get() = null
|
||||
val stackIcon: ItemStack? get() = null
|
||||
val iconText: Component? get() = null
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,460 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.android
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
||||
import net.minecraft.ChatFormatting
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||
import ru.dbotthepony.mc.otm.client.render.SkinElement
|
||||
import ru.dbotthepony.mc.otm.container.iterator
|
||||
import ru.dbotthepony.mc.otm.core.getCompoundList
|
||||
import ru.dbotthepony.mc.otm.core.map
|
||||
import ru.dbotthepony.mc.otm.core.nonEmpty
|
||||
import ru.dbotthepony.mc.otm.core.set
|
||||
import ru.dbotthepony.mc.otm.registry.MRegistry
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
typealias ResearchCallback = ((research: AndroidResearch, feature: AndroidFeature) -> Unit)
|
||||
|
||||
@Suppress("unused")
|
||||
class AndroidResearchBuilder(
|
||||
var experience: Int = 0,
|
||||
var name: Component? = null,
|
||||
var customDescription: MutableList<Component>? = null,
|
||||
var hasDescription: Boolean = false,
|
||||
var skinIcon: SkinElement? = null,
|
||||
var iconText: Component? = null,
|
||||
) {
|
||||
private val items = ArrayList<ItemStack>()
|
||||
private val prerequisites = ArrayList<Pair<() -> ResourceLocation, Boolean>>()
|
||||
private val blockers = ArrayList<Pair<() -> ResourceLocation, Boolean>>()
|
||||
|
||||
private val features = ArrayList<DeferredFeature>()
|
||||
private val resolvedFeatures = ArrayList<ResolvedFeature>()
|
||||
|
||||
private class DeferredFeature(
|
||||
val id: ResourceLocation,
|
||||
val level: Int,
|
||||
val callbackResearched: ResearchCallback?
|
||||
)
|
||||
|
||||
private class ResolvedFeature(
|
||||
val feature: AndroidFeatureType<*>,
|
||||
val level: Int,
|
||||
val callbackResearched: ResearchCallback?
|
||||
)
|
||||
|
||||
fun withIconText(icon: Component?): AndroidResearchBuilder {
|
||||
this.iconText = icon
|
||||
return this
|
||||
}
|
||||
|
||||
fun withIconText() = withIconText(null)
|
||||
|
||||
fun withIcon(icon: SkinElement): AndroidResearchBuilder {
|
||||
this.skinIcon = icon
|
||||
return this
|
||||
}
|
||||
|
||||
fun withDescription(): AndroidResearchBuilder {
|
||||
this.hasDescription = true
|
||||
this.customDescription = null
|
||||
return this
|
||||
}
|
||||
|
||||
fun withExperience(experience: Int): AndroidResearchBuilder {
|
||||
this.experience = experience
|
||||
return this
|
||||
}
|
||||
|
||||
fun withDescription(vararg description: Component): AndroidResearchBuilder {
|
||||
this.customDescription = description.toMutableList()
|
||||
return this
|
||||
}
|
||||
|
||||
fun withDescription(description: List<Component>): AndroidResearchBuilder {
|
||||
this.customDescription = ArrayList<Component>(description.size).also { it.addAll(description) }
|
||||
return this
|
||||
}
|
||||
|
||||
fun withName(name: Component): AndroidResearchBuilder {
|
||||
this.name = name
|
||||
return this
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun addPrerequisite(id: ResourceLocation, rigid: Boolean = false): AndroidResearchBuilder {
|
||||
prerequisites.add({ id } to rigid)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Please avoid having multiple prerequisites as case with more than 1 prerequisite does not have proper
|
||||
* research tree render logic (yet).
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun addPrerequisite(id: () -> ResourceLocation, rigid: Boolean = false): AndroidResearchBuilder {
|
||||
prerequisites.add(id to rigid)
|
||||
return this
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun addBlocker(id: ResourceLocation, rigid: Boolean = false): AndroidResearchBuilder {
|
||||
blockers.add({ id } to rigid)
|
||||
return this
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun addBlocker(id: () -> ResourceLocation, rigid: Boolean = false): AndroidResearchBuilder {
|
||||
blockers.add(id to rigid)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Please avoid having multiple prerequisites as case with more than 1 prerequisite does not have proper
|
||||
* research tree render logic (yet).
|
||||
*/
|
||||
fun addPrerequisite(type: AndroidResearchType<*>) = addPrerequisite({ type.registryName ?: throw NullPointerException("Provided $type has no registryName defined") })
|
||||
fun addBlocker(type: AndroidResearchType<*>) = addBlocker({ type.registryName ?: throw NullPointerException("Provided $type has no registryName defined") })
|
||||
|
||||
@JvmOverloads
|
||||
fun addFeatureResult(id: ResourceLocation, level: Int = 0, callback: ResearchCallback? = null): AndroidResearchBuilder {
|
||||
features.add(DeferredFeature(id, level, callback))
|
||||
return this
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun addFeatureResult(feature: AndroidFeatureType<*>, level: Int = 0, callback: ResearchCallback? = null): AndroidResearchBuilder {
|
||||
resolvedFeatures.add(ResolvedFeature(feature, level, callback))
|
||||
return this
|
||||
}
|
||||
|
||||
fun addFeatureResult(id: ResourceLocation, callback: ResearchCallback): AndroidResearchBuilder {
|
||||
features.add(DeferredFeature(id, 0, callback))
|
||||
return this
|
||||
}
|
||||
|
||||
fun addItem(cost: ItemStack): AndroidResearchBuilder {
|
||||
items.add(cost)
|
||||
return this
|
||||
}
|
||||
|
||||
private class RememberResearchLevel(val level: Int?)
|
||||
|
||||
fun build(): AndroidResearchType<AndroidResearch> {
|
||||
val items: List<ItemStack> = ImmutableList.builder<ItemStack>().let {
|
||||
for (item in this.items) {
|
||||
it.add(item.copy())
|
||||
}
|
||||
|
||||
it.build()
|
||||
}
|
||||
|
||||
val prerequisites: List<Pair<() -> ResourceLocation, Boolean>> = ImmutableList.copyOf(this.prerequisites)
|
||||
val blockers: List<Pair<() -> ResourceLocation, Boolean>> = ImmutableList.copyOf(this.blockers)
|
||||
val experience = this.experience
|
||||
|
||||
val name = name?.copy()
|
||||
val description = customDescription?.let {
|
||||
val builder = ImmutableList.builder<Component>()
|
||||
|
||||
for (component in it) {
|
||||
builder.add(component.copy())
|
||||
}
|
||||
|
||||
builder.build()
|
||||
}
|
||||
|
||||
val hasDescription = hasDescription
|
||||
val defFeatures = ImmutableList.copyOf(features)
|
||||
val defResolvedFeatures = ImmutableList.copyOf(resolvedFeatures)
|
||||
|
||||
val skinIcon = skinIcon
|
||||
val iconText = iconText?.copy()
|
||||
|
||||
val features: List<ResolvedFeature> by lazy {
|
||||
val builder = ImmutableList.builder<ResolvedFeature>()
|
||||
|
||||
for (df in defFeatures) {
|
||||
builder.add(ResolvedFeature(
|
||||
feature = MRegistry.ANDROID_FEATURES.getValue(df.id) ?: throw NoSuchElementException("Can't find android feature ${df.id}"),
|
||||
level = df.level,
|
||||
callbackResearched = df.callbackResearched
|
||||
))
|
||||
}
|
||||
|
||||
for (df in defResolvedFeatures) {
|
||||
df.feature.registryName ?: throw NullPointerException("Feature ${df.feature} is missing registry name")
|
||||
builder.add(df)
|
||||
}
|
||||
|
||||
builder.build()
|
||||
}
|
||||
|
||||
return object : AndroidResearchType<AndroidResearch>(factory@{ it, capability ->
|
||||
return@factory object : AndroidResearch(it, capability) {
|
||||
override fun onUnResearch() {
|
||||
for (feature in features) {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
oldResearchLevel.clear()
|
||||
}
|
||||
|
||||
private val oldResearchLevel = Object2ObjectArrayMap<AndroidFeatureType<*>, RememberResearchLevel>()
|
||||
|
||||
override fun onResearched() {
|
||||
oldResearchLevel.clear()
|
||||
|
||||
try {
|
||||
for (feature in features) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
feature.callbackResearched?.invoke(this, get)
|
||||
}
|
||||
} catch(err: Throwable) {
|
||||
oldResearchLevel.clear()
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
override fun serializeNBT(): CompoundTag {
|
||||
return super.serializeNBT().also {
|
||||
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) {
|
||||
super.deserializeNBT(nbt)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
override fun consumeResearchCost(simulate: Boolean): Boolean {
|
||||
if (capability.ply.abilities.instabuild) {
|
||||
// creative
|
||||
return true
|
||||
}
|
||||
|
||||
if (!simulate && !consumeResearchCost(true)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (experience > 0) {
|
||||
if (capability.ply.experienceLevel < experience) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!simulate) {
|
||||
capability.ply.giveExperienceLevels(-experience)
|
||||
}
|
||||
}
|
||||
|
||||
for (item in items) {
|
||||
var required = item.count
|
||||
val iterator = capability.ply.inventory.iterator().nonEmpty()
|
||||
|
||||
for (invItem in iterator) {
|
||||
if (ItemStack.isSameItemSameTags(invItem, item)) {
|
||||
val toExtract = required.coerceAtMost(invItem.count)
|
||||
required -= toExtract
|
||||
|
||||
if (!simulate) {
|
||||
if (toExtract == invItem.count) {
|
||||
iterator.remove()
|
||||
} else {
|
||||
invItem.count -= toExtract
|
||||
}
|
||||
}
|
||||
|
||||
if (required <= 0) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (required > 0) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun refund(simulate: Boolean): Boolean {
|
||||
if (simulate) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (experience > 0) {
|
||||
capability.ply.giveExperienceLevels(experience)
|
||||
}
|
||||
|
||||
for (item in items) {
|
||||
val leftover = item.copy()
|
||||
capability.ply.inventory.add(leftover)
|
||||
|
||||
if (!leftover.isEmpty) {
|
||||
capability.ply.spawnAtLocation(leftover)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override val tooltipLines: List<Component> by lazy {
|
||||
val builder = ImmutableList.builder<Component>()
|
||||
|
||||
builder.add(name ?: this.type.displayName)
|
||||
|
||||
if (hasDescription) {
|
||||
builder.addAll(this.type.displayDescription)
|
||||
} else if (description != null) {
|
||||
builder.addAll(description)
|
||||
}
|
||||
|
||||
builder.build()
|
||||
}
|
||||
|
||||
override val screenTooltipHeader: Component
|
||||
get() = name ?: this.type.displayName
|
||||
|
||||
override val screenTooltipLines: List<Component> get() {
|
||||
val builder = ArrayList<Component>()
|
||||
|
||||
builder.add(name ?: this.type.displayName)
|
||||
|
||||
if (hasDescription) {
|
||||
builder.addAll(this.type.displayDescription)
|
||||
} else if (description != null) {
|
||||
builder.addAll(description)
|
||||
}
|
||||
|
||||
if (experience != 0) {
|
||||
builder.add(
|
||||
TranslatableComponent("otm.android_station.research.xp_cost", experience).withStyle(
|
||||
if (capability.ply.experienceLevel >= experience)
|
||||
ChatFormatting.DARK_GREEN
|
||||
else
|
||||
ChatFormatting.DARK_RED
|
||||
))
|
||||
}
|
||||
|
||||
for (value in this.type.flatPrerequisites) {
|
||||
val instance = capability.getResearch(value)
|
||||
|
||||
builder.add(
|
||||
TranslatableComponent("android_research.status.requires", instance.screenTooltipHeader).withStyle(
|
||||
if (instance.isResearched)
|
||||
ChatFormatting.DARK_GREEN
|
||||
else
|
||||
ChatFormatting.DARK_RED
|
||||
))
|
||||
}
|
||||
|
||||
for (value in this.type.flatBlockedBy) {
|
||||
builder.add(TranslatableComponent("android_research.status.blocked_by", capability.getResearch(value).screenTooltipHeader).withStyle(ChatFormatting.DARK_RED))
|
||||
}
|
||||
|
||||
for (value in this.type.flatBlocking) {
|
||||
builder.add(TranslatableComponent("android_research.status.blocks", capability.getResearch(value).screenTooltipHeader).withStyle(ChatFormatting.DARK_RED))
|
||||
}
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
override val skinIcon: SkinElement? = skinIcon
|
||||
override val iconText: Component? = iconText
|
||||
}
|
||||
}) {
|
||||
override val displayName: Component
|
||||
get() = name ?: super.displayName
|
||||
|
||||
override val displayDescription: List<Component>
|
||||
get() = description ?: super.displayDescription
|
||||
|
||||
override val definedPrerequisites: List<AndroidResearchType<*>> by lazy {
|
||||
val builder = ImmutableList.builder<AndroidResearchType<*>>()
|
||||
|
||||
for ((value, rigid) in prerequisites) {
|
||||
val get = MRegistry.ANDROID_RESEARCH.getValue(value())
|
||||
|
||||
if (get == null && rigid) {
|
||||
throw NullPointerException("Unable to find research with name $value")
|
||||
} else if (get != null) {
|
||||
builder.add(get)
|
||||
}
|
||||
}
|
||||
|
||||
builder.build()
|
||||
}
|
||||
|
||||
override val definedBlockedBy: List<AndroidResearchType<*>> by lazy {
|
||||
val builder = ImmutableList.builder<AndroidResearchType<*>>()
|
||||
|
||||
for ((value, rigid) in blockers) {
|
||||
val get = MRegistry.ANDROID_RESEARCH.getValue(value())
|
||||
|
||||
if (get == null && rigid) {
|
||||
throw NullPointerException("Unable to find research with name $value")
|
||||
} else if (get != null) {
|
||||
builder.add(get)
|
||||
}
|
||||
}
|
||||
|
||||
builder.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,56 @@
|
||||
package ru.dbotthepony.mc.otm.android
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||
import net.minecraft.data.CachedOutput
|
||||
import net.minecraft.data.DataGenerator
|
||||
import net.minecraft.data.DataProvider
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.util.LinkedList
|
||||
import java.util.function.Consumer
|
||||
|
||||
@Suppress("unused")
|
||||
open class AndroidResearchDataProvider(protected val dataGenerator: DataGenerator) : DataProvider {
|
||||
protected val pathProvider: DataGenerator.PathProvider = dataGenerator.createPathProvider(DataGenerator.Target.DATA_PACK, AndroidResearchManager.DIRECTORY)
|
||||
|
||||
protected val callbacks = LinkedList<(Consumer<AndroidResearchType>) -> Unit>()
|
||||
|
||||
/**
|
||||
* override this
|
||||
*/
|
||||
protected open fun addEverything(serializer: Consumer<AndroidResearchType>) {
|
||||
for (callback in callbacks) {
|
||||
callback.invoke(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* or call this
|
||||
*/
|
||||
fun exec(callback: (Consumer<AndroidResearchType>) -> Unit): AndroidResearchDataProvider {
|
||||
callbacks.add(callback)
|
||||
return this
|
||||
}
|
||||
|
||||
final override fun run(output: CachedOutput) {
|
||||
val set = ObjectArraySet<ResourceLocation>()
|
||||
val added = LinkedList<AndroidResearchType>()
|
||||
|
||||
addEverything {
|
||||
if (set.add(it.id)) {
|
||||
DataProvider.saveStable(output, it.toJson(), pathProvider.json(it.id))
|
||||
AndroidResearchManager.put(it)
|
||||
added.add(it)
|
||||
} else {
|
||||
throw IllegalStateException("Duplicate android research with ID ${it.id}")
|
||||
}
|
||||
}
|
||||
|
||||
for (value in added) {
|
||||
value.validate()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getName(): String {
|
||||
return "Android Research"
|
||||
}
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
package ru.dbotthepony.mc.otm.android
|
||||
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.client.server.IntegratedServer
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener
|
||||
import net.minecraft.util.profiling.ProfilerFiller
|
||||
import net.minecraftforge.event.AddReloadListenerEvent
|
||||
import net.minecraftforge.event.OnDatapackSyncEvent
|
||||
import net.minecraftforge.network.NetworkEvent
|
||||
import net.minecraftforge.network.PacketDistributor
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import ru.dbotthepony.mc.otm.MINECRAFT_SERVER
|
||||
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.network.MatteryPacket
|
||||
import ru.dbotthepony.mc.otm.network.RegistryNetworkChannel
|
||||
import ru.dbotthepony.mc.otm.network.enqueueWork
|
||||
import ru.dbotthepony.mc.otm.network.packetHandled
|
||||
import ru.dbotthepony.mc.otm.onceServer
|
||||
import java.util.LinkedList
|
||||
import java.util.function.Supplier
|
||||
|
||||
object AndroidResearchManager : SimpleJsonResourceReloadListener(GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(), "otm_android_research"), Iterable<AndroidResearchType> {
|
||||
const val DIRECTORY = "otm_android_research"
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
|
||||
operator fun get(id: ResourceLocation): AndroidResearchType? {
|
||||
return researchMap[id] ?: maskedMap[id]
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<AndroidResearchType> {
|
||||
return researchMap.values.iterator()
|
||||
}
|
||||
|
||||
private val maskedMap = HashMap<ResourceLocation, AndroidResearchType>()
|
||||
|
||||
/**
|
||||
* Internal use only, used by [AndroidResearchDataProvider] for validation purposes
|
||||
*/
|
||||
internal fun put(value: AndroidResearchType) {
|
||||
maskedMap[value.id] = value
|
||||
}
|
||||
|
||||
var researchMap: Map<ResourceLocation, AndroidResearchType> = mapOf()
|
||||
private set
|
||||
|
||||
override fun apply(
|
||||
jsonElementMap: Map<ResourceLocation, JsonElement>,
|
||||
manager: ResourceManager,
|
||||
profiler: ProfilerFiller
|
||||
) {
|
||||
val builder = ImmutableMap.builder<ResourceLocation, AndroidResearchType>()
|
||||
|
||||
for ((k, v) in jsonElementMap) {
|
||||
if (v !is JsonObject) {
|
||||
LOGGER.error("Skipping Android Feature $k because top value is not a Object")
|
||||
continue
|
||||
}
|
||||
|
||||
builder.put(k, AndroidResearchType.fromJson(v, k))
|
||||
}
|
||||
|
||||
researchMap = builder.build()
|
||||
|
||||
for (value in researchMap.values) {
|
||||
value.validate()
|
||||
}
|
||||
|
||||
if (SERVER_IS_LIVE) {
|
||||
onceServer {
|
||||
for (ply in MINECRAFT_SERVER.playerList.players) {
|
||||
ply.matteryPlayer?.reloadResearch()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun reloadEvent(event: AddReloadListenerEvent) {
|
||||
event.addListener(this)
|
||||
}
|
||||
|
||||
fun syncEvent(event: OnDatapackSyncEvent) {
|
||||
val packet = SyncPacket(researchMap.values)
|
||||
|
||||
if (event.player != null) {
|
||||
RegistryNetworkChannel.send(event.player!!, packet)
|
||||
} else {
|
||||
RegistryNetworkChannel.send(PacketDistributor.ALL.noArg(), packet)
|
||||
}
|
||||
}
|
||||
|
||||
class SyncPacket(val collection: Collection<AndroidResearchType>) : MatteryPacket {
|
||||
override fun write(buff: FriendlyByteBuf) {
|
||||
buff.writeCollection(collection) { a, b -> b.toNetwork(a) }
|
||||
}
|
||||
|
||||
override fun play(context: Supplier<NetworkEvent.Context>) {
|
||||
context.packetHandled = true
|
||||
|
||||
if (NULLABLE_MINECRAFT_SERVER is IntegratedServer) {
|
||||
return
|
||||
}
|
||||
|
||||
val builder = ImmutableMap.builder<ResourceLocation, AndroidResearchType>()
|
||||
|
||||
for (v in collection) {
|
||||
builder.put(v.id, v)
|
||||
}
|
||||
|
||||
researchMap = builder.build()
|
||||
|
||||
for (value in researchMap.values) {
|
||||
value.validate()
|
||||
}
|
||||
|
||||
context.enqueueWork {
|
||||
minecraft.player?.matteryPlayer?.reloadResearch()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun readSyncPacket(buff: FriendlyByteBuf): SyncPacket {
|
||||
return SyncPacket(buff.readCollection({ LinkedList() }, AndroidResearchType.Companion::fromNetwork))
|
||||
}
|
||||
}
|
@ -1,43 +1,51 @@
|
||||
package ru.dbotthepony.mc.otm.android
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.collect.ImmutableSet
|
||||
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 net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.ComponentContents
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.network.chat.contents.TranslatableContents
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import ru.dbotthepony.mc.otm.client.render.SkinElement
|
||||
import ru.dbotthepony.mc.otm.core.ListSet
|
||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
|
||||
import ru.dbotthepony.mc.otm.core.getKeyNullable
|
||||
import ru.dbotthepony.mc.otm.core.set
|
||||
import ru.dbotthepony.mc.otm.core.stream
|
||||
import ru.dbotthepony.mc.otm.core.toImmutableList
|
||||
import ru.dbotthepony.mc.otm.data.ItemStackCodec
|
||||
import ru.dbotthepony.mc.otm.registry.MRegistry
|
||||
import java.util.*
|
||||
import java.util.ArrayList
|
||||
import java.util.LinkedList
|
||||
import java.util.stream.Stream
|
||||
import kotlin.collections.HashSet
|
||||
import kotlin.collections.RandomAccess
|
||||
|
||||
fun interface AndroidResearchFactory<R : AndroidResearch> {
|
||||
fun factory(type: AndroidResearchType<*>, capability: MatteryPlayerCapability): R
|
||||
}
|
||||
|
||||
private fun findPrerequisites(
|
||||
initial: Collection<AndroidResearchType<*>>,
|
||||
add: MutableSet<AndroidResearchType<*>> = HashSet(),
|
||||
initial: Collection<AndroidResearchType>,
|
||||
add: MutableSet<AndroidResearchType> = HashSet(),
|
||||
top: Boolean = true
|
||||
): Set<AndroidResearchType<*>> {
|
||||
): Set<AndroidResearchType> {
|
||||
for (value in initial) {
|
||||
if (!top) {
|
||||
add.add(value)
|
||||
}
|
||||
|
||||
findPrerequisites(value.definedPrerequisites, add, false)
|
||||
findPrerequisites(value.resolvedPrerequisites, add, false)
|
||||
}
|
||||
|
||||
return add
|
||||
}
|
||||
|
||||
private fun findAllPrerequisites(
|
||||
initial: Collection<AndroidResearchType<*>>,
|
||||
add: MutableSet<AndroidResearchType<*>> = HashSet(),
|
||||
): Set<AndroidResearchType<*>> {
|
||||
initial: Collection<AndroidResearchType>,
|
||||
add: MutableSet<AndroidResearchType> = HashSet(),
|
||||
): Set<AndroidResearchType> {
|
||||
for (value in initial) {
|
||||
add.add(value)
|
||||
findAllPrerequisites(value.flatPrerequisites, add)
|
||||
@ -47,9 +55,9 @@ private fun findAllPrerequisites(
|
||||
}
|
||||
|
||||
private fun findAllChildren(
|
||||
initial: Collection<AndroidResearchType<*>>,
|
||||
add: MutableSet<AndroidResearchType<*>> = HashSet(),
|
||||
): Set<AndroidResearchType<*>> {
|
||||
initial: Collection<AndroidResearchType>,
|
||||
add: MutableSet<AndroidResearchType> = HashSet(),
|
||||
): Set<AndroidResearchType> {
|
||||
for (value in initial) {
|
||||
add.add(value)
|
||||
findAllChildren(value.flatUnlocks, add)
|
||||
@ -58,62 +66,114 @@ private fun findAllChildren(
|
||||
return add
|
||||
}
|
||||
|
||||
class ListSet<T>(private val list: ImmutableList<T>) : List<T>, Set<T>, RandomAccess {
|
||||
constructor(list: Collection<T>) : this(ImmutableList.copyOf(list))
|
||||
class AndroidResearchType(
|
||||
val id: ResourceLocation,
|
||||
prerequisites: Collection<Reference>,
|
||||
blockedBy: Collection<Reference>,
|
||||
|
||||
private val set: ImmutableSet<T> by lazy { ImmutableSet.copyOf(list) }
|
||||
items: Collection<ItemStack>,
|
||||
features: Collection<FeatureReference>,
|
||||
|
||||
override val size: Int
|
||||
get() = list.size
|
||||
descriptionLines: Collection<Component>,
|
||||
|
||||
override fun contains(element: T): Boolean {
|
||||
return set.contains(element)
|
||||
}
|
||||
val experienceLevels: Int = 0,
|
||||
private val customName: Component? = null,
|
||||
|
||||
override fun containsAll(elements: Collection<T>): Boolean {
|
||||
return set.containsAll(elements)
|
||||
}
|
||||
|
||||
override fun get(index: Int): T {
|
||||
return list[index]
|
||||
}
|
||||
|
||||
override fun indexOf(element: T): Int {
|
||||
return list.indexOf(element)
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return list.isEmpty()
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<T> {
|
||||
return list.iterator()
|
||||
}
|
||||
|
||||
override fun lastIndexOf(element: T): Int {
|
||||
return list.lastIndexOf(element)
|
||||
}
|
||||
|
||||
override fun listIterator(): ListIterator<T> {
|
||||
return list.listIterator()
|
||||
}
|
||||
|
||||
override fun listIterator(index: Int): ListIterator<T> {
|
||||
return list.listIterator(index)
|
||||
}
|
||||
|
||||
override fun spliterator(): Spliterator<T> {
|
||||
return list.spliterator()
|
||||
}
|
||||
|
||||
override fun subList(fromIndex: Int, toIndex: Int): List<T> {
|
||||
return list.subList(fromIndex, toIndex)
|
||||
}
|
||||
}
|
||||
|
||||
open class AndroidResearchType<R : AndroidResearch>(
|
||||
protected val factory: AndroidResearchFactory<R>
|
||||
val skinIcon: SkinElement? = null,
|
||||
stackIcon: ItemStack? = null,
|
||||
iconText: Component? = null,
|
||||
) {
|
||||
private val stackIconValue = stackIcon?.copy()
|
||||
private val iconTextValue = iconText?.copy()
|
||||
|
||||
val stackIcon get() = stackIconValue?.copy()
|
||||
val iconText get() = iconTextValue?.copy()
|
||||
|
||||
data class Reference(
|
||||
val id: ResourceLocation,
|
||||
val isRigid: Boolean
|
||||
) {
|
||||
fun toJson(): JsonObject {
|
||||
return JsonObject().also {
|
||||
it["id"] = JsonPrimitive(id.toString())
|
||||
it["is_rigid"] = JsonPrimitive(isRigid)
|
||||
}
|
||||
}
|
||||
|
||||
fun toNetwork(buff: FriendlyByteBuf) {
|
||||
buff.writeUtf(id.toString())
|
||||
buff.writeBoolean(isRigid)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromNetwork(buff: FriendlyByteBuf): Reference {
|
||||
return Reference(
|
||||
ResourceLocation(buff.readUtf()),
|
||||
buff.readBoolean()
|
||||
)
|
||||
}
|
||||
|
||||
fun fromJson(value: JsonElement): Reference {
|
||||
if (value is JsonPrimitive) {
|
||||
return Reference(ResourceLocation(value.asString), true)
|
||||
} else if (value is JsonObject) {
|
||||
return Reference(
|
||||
ResourceLocation((value["id"] as? JsonPrimitive ?: throw JsonSyntaxException("Invalid `id` value")).asString),
|
||||
(value["is_rigid"] as JsonPrimitive?)?.asBoolean ?: true)
|
||||
} else {
|
||||
throw JsonSyntaxException("Unknown element type ${value::class.qualifiedName}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class FeatureReference(
|
||||
val id: ResourceLocation,
|
||||
val level: Int = 0,
|
||||
val isRigid: Boolean
|
||||
) {
|
||||
fun toJson(): JsonObject {
|
||||
return JsonObject().also {
|
||||
it["id"] = JsonPrimitive(id.toString())
|
||||
it["level"] = JsonPrimitive(level)
|
||||
it["is_rigid"] = JsonPrimitive(isRigid)
|
||||
}
|
||||
}
|
||||
|
||||
fun toNetwork(buff: FriendlyByteBuf) {
|
||||
buff.writeUtf(id.toString())
|
||||
buff.writeVarInt(level)
|
||||
buff.writeBoolean(isRigid)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromNetwork(buff: FriendlyByteBuf): FeatureReference {
|
||||
return FeatureReference(
|
||||
ResourceLocation(buff.readUtf()),
|
||||
buff.readVarInt(),
|
||||
buff.readBoolean()
|
||||
)
|
||||
}
|
||||
|
||||
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)
|
||||
} 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
|
||||
@ -134,12 +194,47 @@ open class AndroidResearchType<R : AndroidResearch>(
|
||||
* Please avoid having more than one prerequisite, as this case doesn't have proper research tree
|
||||
* rendering code (yet).
|
||||
*/
|
||||
open val definedPrerequisites: List<AndroidResearchType<*>> get() = emptyList()
|
||||
val prerequisites: List<Reference> = ImmutableList.copyOf(prerequisites)
|
||||
|
||||
/**
|
||||
* Blocked by list as-is
|
||||
*/
|
||||
open val definedBlockedBy: List<AndroidResearchType<*>> get() = emptyList()
|
||||
val blockedBy: List<Reference> = ImmutableList.copyOf(blockedBy)
|
||||
|
||||
val resolvedPrerequisites: List<AndroidResearchType> by lazy {
|
||||
ImmutableList.copyOf(this.prerequisites.mapNotNull { AndroidResearchManager[it.id].also { e -> if (e == null && it.isRigid) throw NoSuchElementException("Unable to find research ${it.id}") } })
|
||||
}
|
||||
|
||||
val resolvedBlockedBy: List<AndroidResearchType> by lazy {
|
||||
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 itemCollection: List<ItemStack> = items.stream().filter { !it.isEmpty }.map { it.copy() }.toList().toImmutableList()
|
||||
|
||||
/**
|
||||
* Stream containing copies of original items in list
|
||||
*/
|
||||
val items: Stream<out ItemStack> get() = itemCollection.stream().map { it.copy() }
|
||||
|
||||
private val descriptionLines: List<MutableComponent> = ImmutableList.copyOf(descriptionLines.map { it.copy() })
|
||||
|
||||
/**
|
||||
* Stream containing copies of original [Component]s in list
|
||||
*/
|
||||
val description: Stream<out Component> get() = descriptionLines.stream().map { it.copy() }
|
||||
|
||||
/**
|
||||
* Flat list of research preceding this research.
|
||||
@ -151,16 +246,16 @@ open class AndroidResearchType<R : AndroidResearch>(
|
||||
* * C depends on B
|
||||
* * B depends on A
|
||||
*
|
||||
* C specify both B and A as it's prerequisites, [flatPrerequisites] will contain only B, when [definedPrerequisites] will contain
|
||||
* C specify both B and A as it's prerequisites, [flatPrerequisites] will contain only B, when [prerequisites] will contain
|
||||
* both B and A
|
||||
*
|
||||
* Returns list which also doubles as set (for contains method).
|
||||
*/
|
||||
val flatPrerequisites: List<AndroidResearchType<*>> by lazy {
|
||||
val parentPrerequisites = findPrerequisites(definedPrerequisites)
|
||||
val builder = ImmutableList.builder<AndroidResearchType<*>>()
|
||||
val flatPrerequisites: List<AndroidResearchType> by lazy {
|
||||
val parentPrerequisites = findPrerequisites(resolvedPrerequisites)
|
||||
val builder = ImmutableList.builder<AndroidResearchType>()
|
||||
|
||||
for (value in definedPrerequisites) {
|
||||
for (value in resolvedPrerequisites) {
|
||||
if (value !in parentPrerequisites) {
|
||||
builder.add(value)
|
||||
}
|
||||
@ -174,7 +269,7 @@ open class AndroidResearchType<R : AndroidResearch>(
|
||||
*
|
||||
* Returns list which also doubles as set (for contains method).
|
||||
*/
|
||||
val allPrerequisites: List<AndroidResearchType<*>> by lazy {
|
||||
val allPrerequisites: List<AndroidResearchType> by lazy {
|
||||
ListSet(findAllPrerequisites(flatPrerequisites))
|
||||
}
|
||||
|
||||
@ -193,15 +288,15 @@ open class AndroidResearchType<R : AndroidResearch>(
|
||||
*
|
||||
* Returns list which also doubles as set (for contains method).
|
||||
*/
|
||||
val flatBlocking: List<AndroidResearchType<*>> by lazy {
|
||||
val list = ImmutableList.builder<AndroidResearchType<*>>()
|
||||
val flatBlocking: List<AndroidResearchType> by lazy {
|
||||
val list = ImmutableList.builder<AndroidResearchType>()
|
||||
|
||||
for (research in MRegistry.ANDROID_RESEARCH) {
|
||||
if (this in research.definedBlockedBy) {
|
||||
for (research in AndroidResearchManager.researchMap.values) {
|
||||
if (this in research.resolvedBlockedBy) {
|
||||
var hit = false
|
||||
|
||||
for (parent in research.allPrerequisites) {
|
||||
if (this in parent.definedBlockedBy) {
|
||||
if (this in parent.resolvedBlockedBy) {
|
||||
hit = true
|
||||
break
|
||||
}
|
||||
@ -231,10 +326,10 @@ open class AndroidResearchType<R : AndroidResearch>(
|
||||
*
|
||||
* Returns list which also doubles as set (for contains method).
|
||||
*/
|
||||
val flatBlockedBy: ListSet<AndroidResearchType<*>> by lazy {
|
||||
val list = ImmutableList.builder<AndroidResearchType<*>>()
|
||||
val flatBlockedBy: ListSet<AndroidResearchType> by lazy {
|
||||
val list = ImmutableList.builder<AndroidResearchType>()
|
||||
|
||||
for (blocker in definedBlockedBy) {
|
||||
for (blocker in resolvedBlockedBy) {
|
||||
var hit = false
|
||||
|
||||
for (research in allPrerequisites) {
|
||||
@ -257,8 +352,8 @@ open class AndroidResearchType<R : AndroidResearch>(
|
||||
*
|
||||
* Returns list which also doubles as set (for contains method).
|
||||
*/
|
||||
val allBlockedBy: ListSet<AndroidResearchType<*>> by lazy {
|
||||
val list = HashSet<AndroidResearchType<*>>()
|
||||
val allBlockedBy: ListSet<AndroidResearchType> by lazy {
|
||||
val list = HashSet<AndroidResearchType>()
|
||||
|
||||
list.addAll(flatBlockedBy)
|
||||
|
||||
@ -274,10 +369,10 @@ open class AndroidResearchType<R : AndroidResearch>(
|
||||
*
|
||||
* Returns list which also doubles as set (for contains method).
|
||||
*/
|
||||
val flatUnlocks: List<AndroidResearchType<*>> by lazy {
|
||||
val list = ImmutableList.builder<AndroidResearchType<*>>()
|
||||
val flatUnlocks: List<AndroidResearchType> by lazy {
|
||||
val list = ImmutableList.builder<AndroidResearchType>()
|
||||
|
||||
for (research in MRegistry.ANDROID_RESEARCH) {
|
||||
for (research in AndroidResearchManager.researchMap.values) {
|
||||
if (this in research.flatPrerequisites) {
|
||||
list.add(research)
|
||||
}
|
||||
@ -291,7 +386,7 @@ open class AndroidResearchType<R : AndroidResearch>(
|
||||
*
|
||||
* Returns list which also doubles as set (for contains method).
|
||||
*/
|
||||
val allUnlocks: List<AndroidResearchType<*>> by lazy {
|
||||
val allUnlocks: List<AndroidResearchType> by lazy {
|
||||
ListSet(findAllChildren(flatUnlocks))
|
||||
}
|
||||
|
||||
@ -300,8 +395,8 @@ open class AndroidResearchType<R : AndroidResearch>(
|
||||
*
|
||||
* Returns list which also doubles as set (for contains method)
|
||||
*/
|
||||
val allBlocking: List<AndroidResearchType<*>> by lazy {
|
||||
val set = HashSet<AndroidResearchType<*>>()
|
||||
val allBlocking: List<AndroidResearchType> by lazy {
|
||||
val set = HashSet<AndroidResearchType>()
|
||||
|
||||
for (research in flatBlocking) {
|
||||
set.add(research)
|
||||
@ -318,30 +413,231 @@ open class AndroidResearchType<R : AndroidResearch>(
|
||||
ListSet(set)
|
||||
}
|
||||
|
||||
fun factory(capability: MatteryPlayerCapability) = factory.factory(this, capability)
|
||||
|
||||
val registryName by lazy {
|
||||
MRegistry.ANDROID_RESEARCH.getKeyNullable(this)
|
||||
}
|
||||
|
||||
val displayId by lazy {
|
||||
val registryName = registryName ?: throw NullPointerException("No registry name present")
|
||||
return@lazy "android_research.${registryName.namespace}.${registryName.path}".intern()
|
||||
return@lazy "android_research.${id.namespace}.${id.path}".intern()
|
||||
}
|
||||
|
||||
val descriptionId by lazy {
|
||||
return@lazy "$displayId.description".intern()
|
||||
}
|
||||
|
||||
open val displayContents: ComponentContents by lazy {
|
||||
val displayContents: ComponentContents by lazy {
|
||||
TranslatableContents(displayId)
|
||||
}
|
||||
|
||||
open val displayName: Component by lazy {
|
||||
MutableComponent.create(displayContents)
|
||||
val displayName: Component get() {
|
||||
return customName?.copy() ?: MutableComponent.create(displayContents)
|
||||
}
|
||||
|
||||
open val displayDescription: List<Component> by lazy {
|
||||
listOf(TranslatableComponent(descriptionId))
|
||||
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 itemCollection) it.add(ItemStackCodec.serialize(item)) }
|
||||
|
||||
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["experience"] = JsonPrimitive(experienceLevels)
|
||||
|
||||
if (skinIcon != null) {
|
||||
it["skin_icon"] = skinIcon.toJson()
|
||||
}
|
||||
|
||||
if (customName != null) {
|
||||
it["custom_name"] = Component.Serializer.toJsonTree(customName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun validate() {
|
||||
resolvedFeatures
|
||||
resolvedBlockedBy
|
||||
resolvedPrerequisites
|
||||
}
|
||||
|
||||
fun toNetwork(buff: FriendlyByteBuf) {
|
||||
buff.writeUtf(id.toString())
|
||||
buff.writeCollection(prerequisites) { a, b -> b.toNetwork(a) }
|
||||
buff.writeCollection(blockedBy) { a, b -> b.toNetwork(a) }
|
||||
buff.writeCollection(itemCollection) { a, b -> a.writeItem(b) }
|
||||
buff.writeCollection(features) { a, b -> b.toNetwork(a) }
|
||||
buff.writeCollection(descriptionLines) { a, b -> a.writeComponent(b) }
|
||||
buff.writeVarInt(experienceLevels)
|
||||
|
||||
buff.writeBoolean(customName != null)
|
||||
|
||||
if (customName != null) {
|
||||
buff.writeComponent(customName)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromNetwork(buff: FriendlyByteBuf): AndroidResearchType {
|
||||
val id = ResourceLocation(buff.readUtf())
|
||||
val prerequisites = buff.readCollection({ LinkedList() }, Reference::fromNetwork)
|
||||
val blockedBy = buff.readCollection({ LinkedList() }, Reference::fromNetwork)
|
||||
val items = buff.readCollection({ LinkedList() }, FriendlyByteBuf::readItem)
|
||||
val features = buff.readCollection({ LinkedList() }, FeatureReference::fromNetwork)
|
||||
val descriptionLines = buff.readCollection({ LinkedList() }, FriendlyByteBuf::readComponent)
|
||||
val experienceLevels = buff.readVarInt()
|
||||
|
||||
val customName: Component? = if (buff.readBoolean()) {
|
||||
buff.readComponent()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
return AndroidResearchType(
|
||||
id = id,
|
||||
prerequisites = prerequisites,
|
||||
blockedBy = blockedBy,
|
||||
items = items,
|
||||
features = features,
|
||||
descriptionLines = descriptionLines,
|
||||
experienceLevels = experienceLevels,
|
||||
customName = customName
|
||||
)
|
||||
}
|
||||
|
||||
fun fromJson(value: JsonElement, id: ResourceLocation): AndroidResearchType {
|
||||
if (value !is JsonObject) {
|
||||
throw JsonSyntaxException("Android research type must be of Json Object")
|
||||
}
|
||||
|
||||
val prerequisites = value["prerequisites"] as JsonArray? ?: JsonArray()
|
||||
val blocked_by = value["blocked_by"] as JsonArray? ?: JsonArray()
|
||||
val items = value["required_items"] as JsonArray? ?: JsonArray()
|
||||
val features = value["feature_result"] as JsonArray? ?: JsonArray()
|
||||
val description = value["description"] as JsonArray? ?: JsonArray()
|
||||
val experience = value["experience"]?.asInt ?: 0
|
||||
val customName = value["custom_name"]?.let(Component.Serializer::fromJson)
|
||||
|
||||
return 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(),
|
||||
items = items.stream().map { ItemStackCodec.deserialize(it) }.filter { !it.isEmpty }.toList(),
|
||||
descriptionLines = description.stream().map { Component.Serializer.fromJson(it) }.toList() as List<MutableComponent>,
|
||||
experienceLevels = experience,
|
||||
customName = customName
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
class Builder(
|
||||
val id: ResourceLocation,
|
||||
var experience: Int = 0,
|
||||
var customName: Component? = null,
|
||||
var description: MutableList<Component>? = null,
|
||||
var skinIcon: SkinElement? = null,
|
||||
var iconText: Component? = null,
|
||||
) {
|
||||
private val items = ArrayList<ItemStack>()
|
||||
private val prerequisites = LinkedList<Reference>()
|
||||
private val blockers = ArrayList<Reference>()
|
||||
|
||||
private val features = ArrayList<FeatureReference>()
|
||||
|
||||
fun withIconText(icon: Component?): Builder {
|
||||
this.iconText = icon
|
||||
return this
|
||||
}
|
||||
|
||||
fun withIconText() = withIconText(null)
|
||||
|
||||
fun withIcon(icon: SkinElement): Builder {
|
||||
this.skinIcon = icon
|
||||
return this
|
||||
}
|
||||
|
||||
fun withName(customName: Component): Builder {
|
||||
this.customName = customName
|
||||
return this
|
||||
}
|
||||
|
||||
fun withDescription(): Builder {
|
||||
this.description = mutableListOf(TranslatableComponent("android_research.${id.namespace}.${id.path}.description"))
|
||||
return this
|
||||
}
|
||||
|
||||
fun withExperience(experience: Int): Builder {
|
||||
this.experience = experience
|
||||
return this
|
||||
}
|
||||
|
||||
fun withDescription(vararg description: Component): Builder {
|
||||
this.description = description.toMutableList()
|
||||
return this
|
||||
}
|
||||
|
||||
fun withDescription(description: List<Component>): Builder {
|
||||
this.description = ArrayList<Component>(description.size).also { it.addAll(description) }
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Please avoid having multiple prerequisites as case with more than 1 prerequisite does not have proper
|
||||
* research tree render logic (yet).
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun addPrerequisite(id: ResourceLocation, rigid: Boolean = false): Builder {
|
||||
prerequisites.add(Reference(id, rigid))
|
||||
return this
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun addBlocker(id: ResourceLocation, rigid: Boolean = false): Builder {
|
||||
blockers.add(Reference(id, rigid))
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Please avoid having multiple prerequisites as case with more than 1 prerequisite does not have proper
|
||||
* research tree render logic (yet).
|
||||
*/
|
||||
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): Builder {
|
||||
features.add(FeatureReference(id, level, rigid))
|
||||
return this
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun addFeatureResult(feature: AndroidFeatureType<*>, level: Int = 0, rigid: Boolean = true): Builder {
|
||||
features.add(FeatureReference(feature.registryName ?: throw NullPointerException("Feature $feature does not have registry name"), level, rigid))
|
||||
return this
|
||||
}
|
||||
|
||||
fun addFeatureResult(id: ResourceLocation, rigid: Boolean = false): Builder {
|
||||
features.add(FeatureReference(id, 0, rigid))
|
||||
return this
|
||||
}
|
||||
|
||||
fun addItem(cost: ItemStack): Builder {
|
||||
items.add(cost)
|
||||
return this
|
||||
}
|
||||
|
||||
fun build(validate: Boolean = false): AndroidResearchType {
|
||||
return AndroidResearchType(
|
||||
id = id,
|
||||
prerequisites = prerequisites,
|
||||
blockedBy = blockers,
|
||||
items = items,
|
||||
features = features,
|
||||
descriptionLines = description ?: listOf(),
|
||||
experienceLevels = experience,
|
||||
customName = customName
|
||||
).also { if (validate) it.validate() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,14 +3,12 @@ package ru.dbotthepony.mc.otm.android.feature
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.world.effect.MobEffectInstance
|
||||
import net.minecraft.world.effect.MobEffects
|
||||
import net.minecraftforge.api.distmarker.Dist
|
||||
import net.minecraftforge.api.distmarker.OnlyIn
|
||||
import ru.dbotthepony.mc.otm.ServerConfig
|
||||
import ru.dbotthepony.mc.otm.android.AndroidSwitchableFeature
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
|
||||
import ru.dbotthepony.mc.otm.capability.extractEnergyInnerExact
|
||||
import ru.dbotthepony.mc.otm.client.render.ResearchIcons
|
||||
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
|
||||
import ru.dbotthepony.mc.otm.registry.AndroidResearch
|
||||
|
||||
class NightVisionFeature(android: MatteryPlayerCapability) : AndroidSwitchableFeature(AndroidFeatures.NIGHT_VISION, android) {
|
||||
override fun tickServer() {
|
||||
@ -33,6 +31,6 @@ class NightVisionFeature(android: MatteryPlayerCapability) : AndroidSwitchableFe
|
||||
}
|
||||
|
||||
override fun renderIcon(stack: PoseStack, x: Float, y: Float, width: Float, height: Float) {
|
||||
AndroidResearch.ICON_NIGHT_VISION.render(stack, x, y, width, height)
|
||||
ResearchIcons.ICON_NIGHT_VISION.render(stack, x, y, width, height)
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import net.minecraft.world.phys.AABB
|
||||
import ru.dbotthepony.mc.otm.ServerConfig
|
||||
import ru.dbotthepony.mc.otm.android.AndroidSwitchableFeature
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
|
||||
import ru.dbotthepony.mc.otm.client.render.ResearchIcons
|
||||
import ru.dbotthepony.mc.otm.core.Vector
|
||||
import ru.dbotthepony.mc.otm.core.getEllipsoidBlockPositions
|
||||
import ru.dbotthepony.mc.otm.core.getExplosionResistance
|
||||
@ -18,7 +19,6 @@ import ru.dbotthepony.mc.otm.core.times
|
||||
import ru.dbotthepony.mc.otm.network.MatteryPlayerNetworkChannel
|
||||
import ru.dbotthepony.mc.otm.network.TriggerShockwavePacket
|
||||
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
|
||||
import ru.dbotthepony.mc.otm.registry.AndroidResearch
|
||||
import ru.dbotthepony.mc.otm.registry.ShockwaveDamageSource
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.roundToInt
|
||||
@ -148,6 +148,6 @@ class ShockwaveFeature(capability: MatteryPlayerCapability) : AndroidSwitchableF
|
||||
}
|
||||
|
||||
override fun renderIcon(stack: PoseStack, x: Float, y: Float, width: Float, height: Float) {
|
||||
AndroidResearch.ICON_SHOCKWAVE.render(stack, x, y, width, height)
|
||||
ResearchIcons.ICON_SHOCKWAVE.render(stack, x, y, width, height)
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ import ru.dbotthepony.mc.otm.*
|
||||
import ru.dbotthepony.mc.otm.android.AndroidFeature
|
||||
import ru.dbotthepony.mc.otm.android.AndroidFeatureType
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearch
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearchManager
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearchType
|
||||
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
||||
import ru.dbotthepony.mc.otm.core.*
|
||||
@ -178,7 +179,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
private var tickedOnce = false
|
||||
|
||||
private var shouldPlaySound = false
|
||||
private val research = IdentityHashMap<AndroidResearchType<*>, AndroidResearch>()
|
||||
private val research = IdentityHashMap<AndroidResearchType, AndroidResearch>()
|
||||
|
||||
private var invalidateNetworkIn = 10
|
||||
|
||||
@ -264,10 +265,24 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
deathLog.clear()
|
||||
}
|
||||
|
||||
fun <T : AndroidResearch> getResearch(type: AndroidResearchType<T>): T {
|
||||
fun getResearch(type: AndroidResearchType): AndroidResearch {
|
||||
return research.computeIfAbsent(type) {
|
||||
return@computeIfAbsent type.factory(this)
|
||||
} as T
|
||||
return@computeIfAbsent AndroidResearch(type, this)
|
||||
}
|
||||
}
|
||||
|
||||
fun reloadResearch() {
|
||||
val old = ArrayList<AndroidResearch>(research.size)
|
||||
old.addAll(research.values)
|
||||
research.clear()
|
||||
|
||||
for (v in old) {
|
||||
val new = AndroidResearchManager.get(v.type.id) ?: continue
|
||||
|
||||
research[new] = AndroidResearch(new, this).also {
|
||||
it.deserializeNBT(v.serializeNBT())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val features: Stream<out AndroidFeature> get() = featureMap.values.stream()
|
||||
@ -413,7 +428,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
|
||||
for ((type, instance) in research) {
|
||||
researchList.add(instance.serializeNBT().also {
|
||||
it["id"] = type.registryName!!.toString()
|
||||
it["id"] = type.id.toString()
|
||||
})
|
||||
}
|
||||
|
||||
@ -472,10 +487,10 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
|
||||
for (researchTag in tag.getCompoundList("research")) {
|
||||
val research = MRegistry.ANDROID_RESEARCH.getValue(ResourceLocation(researchTag.getString("id")))
|
||||
val research = AndroidResearchManager[ResourceLocation(researchTag.getString("id"))]
|
||||
|
||||
if (research != null) {
|
||||
val instance = research.factory(this)
|
||||
val instance = AndroidResearch(research, this)
|
||||
instance.deserializeNBT(researchTag)
|
||||
this.research[research] = instance
|
||||
}
|
||||
|
@ -0,0 +1,63 @@
|
||||
package ru.dbotthepony.mc.otm.client.render
|
||||
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||
|
||||
object ResearchIcons {
|
||||
val ICONS = ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/android_upgrades.png")
|
||||
val ICON_TRANSFER: SkinElement
|
||||
val ICON_ATTACK_BOOST: SkinElement
|
||||
val ICON_PLASMA_SHIELD_BOOST: SkinElement
|
||||
val ICON_CLOAK: SkinElement
|
||||
val ICON_GRAVITATIONAL_STABILIZER: SkinElement
|
||||
val ICON_AIR_BAGS: SkinElement
|
||||
val ICON_JUMP_BOOST: SkinElement
|
||||
|
||||
val ICON_FEATHER_FALLING: SkinElement
|
||||
val ICON_ARC: SkinElement
|
||||
val ICON_ARROW: SkinElement
|
||||
val ICON_ARMOR: SkinElement
|
||||
val ICON_NANOBOTS: SkinElement
|
||||
val ICON_NIGHT_VISION: SkinElement
|
||||
val ICON_OXYGEN_SUPPLY: SkinElement
|
||||
|
||||
val ICON_PLASMA_SHIELD: SkinElement
|
||||
val ICON_SHOCKWAVE: SkinElement
|
||||
val ICON_LIMB_OVERCLOCKING: SkinElement
|
||||
val ICON_STEP_ASSIST: SkinElement
|
||||
val ICON_ENDER_TELEPORT: SkinElement
|
||||
val ICON_WIRELESS_CHARGING: SkinElement
|
||||
val ICON_UNKNOWN: SkinElement
|
||||
|
||||
val ICON_EXTENDED_REACH: SkinElement
|
||||
|
||||
init {
|
||||
val grid = SkinGrid(ICONS, 18f, 18f, 7, 7)
|
||||
|
||||
ICON_TRANSFER = grid.next()
|
||||
ICON_ATTACK_BOOST = grid.next()
|
||||
ICON_PLASMA_SHIELD_BOOST = grid.next()
|
||||
ICON_CLOAK = grid.next()
|
||||
ICON_GRAVITATIONAL_STABILIZER = grid.next()
|
||||
ICON_AIR_BAGS = grid.next()
|
||||
ICON_JUMP_BOOST = grid.next()
|
||||
|
||||
ICON_FEATHER_FALLING = grid.next()
|
||||
ICON_ARC = grid.next()
|
||||
ICON_ARROW = grid.next()
|
||||
ICON_ARMOR = grid.next()
|
||||
ICON_NANOBOTS = grid.next()
|
||||
ICON_NIGHT_VISION = grid.next()
|
||||
ICON_OXYGEN_SUPPLY = grid.next()
|
||||
|
||||
ICON_PLASMA_SHIELD = grid.next()
|
||||
ICON_SHOCKWAVE = grid.next()
|
||||
ICON_LIMB_OVERCLOCKING = grid.next()
|
||||
ICON_STEP_ASSIST = grid.next()
|
||||
ICON_ENDER_TELEPORT = grid.next()
|
||||
ICON_WIRELESS_CHARGING = grid.next()
|
||||
ICON_UNKNOWN = grid.next()
|
||||
|
||||
ICON_EXTENDED_REACH = grid.next()
|
||||
}
|
||||
}
|
@ -1,9 +1,23 @@
|
||||
package ru.dbotthepony.mc.otm.client.render
|
||||
|
||||
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 com.mojang.blaze3d.systems.RenderSystem
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.DockProperty
|
||||
import ru.dbotthepony.mc.otm.core.set
|
||||
import java.lang.reflect.Type
|
||||
|
||||
@Suppress("unused")
|
||||
data class SkinGrid(
|
||||
@ -102,6 +116,55 @@ data class SkinElement @JvmOverloads constructor(
|
||||
val imageHeight: Float = 256f,
|
||||
val winding: UVWindingOrder = UVWindingOrder.NORMAL
|
||||
) {
|
||||
fun toJson(): JsonObject {
|
||||
return JsonObject().also {
|
||||
it["texture"] = JsonPrimitive(texture.toString())
|
||||
it["x"] = JsonPrimitive(x)
|
||||
it["y"] = JsonPrimitive(y)
|
||||
it["width"] = JsonPrimitive(width)
|
||||
it["height"] = JsonPrimitive(height)
|
||||
it["imageWidth"] = JsonPrimitive(imageWidth)
|
||||
it["imageHeight"] = JsonPrimitive(imageHeight)
|
||||
it["winding"] = JsonPrimitive(winding.name)
|
||||
}
|
||||
}
|
||||
|
||||
companion object : TypeAdapter<SkinElement>(), JsonSerializer<SkinElement>, JsonDeserializer<SkinElement> {
|
||||
fun fromJson(value: JsonObject): SkinElement {
|
||||
val texture = value["texture"]?.asString ?: throw JsonSyntaxException("Missing texture element")
|
||||
val x = value["x"]?.asFloat ?: throw JsonSyntaxException("Missing x element")
|
||||
val y = value["y"]?.asFloat ?: throw JsonSyntaxException("Missing y element")
|
||||
val width = value["width"]?.asFloat ?: throw JsonSyntaxException("Missing width element")
|
||||
val height = value["height"]?.asFloat ?: throw JsonSyntaxException("Missing height element")
|
||||
val imageWidth = value["imageWidth"]?.asFloat ?: throw JsonSyntaxException("Missing imageWidth element")
|
||||
val imageHeight = value["imageHeight"]?.asFloat ?: throw JsonSyntaxException("Missing imageHeight element")
|
||||
val winding = value["winding"]?.asString ?: throw JsonSyntaxException("Missing winding element")
|
||||
|
||||
return SkinElement(ResourceLocation(texture), x, y, width, height, imageWidth, imageHeight, UVWindingOrder.valueOf(winding))
|
||||
}
|
||||
|
||||
override fun write(out: JsonWriter, value: SkinElement) {
|
||||
TypeAdapters.JSON_ELEMENT.write(out, value.toJson())
|
||||
}
|
||||
|
||||
override fun read(reader: JsonReader): SkinElement {
|
||||
val read = TypeAdapters.JSON_ELEMENT.read(reader)
|
||||
return fromJson(read as? JsonObject ?: throw JsonSyntaxException("Expected JsonObject, got ${read::class.qualifiedName}"))
|
||||
}
|
||||
|
||||
override fun serialize(src: SkinElement, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
|
||||
return src.toJson()
|
||||
}
|
||||
|
||||
override fun deserialize(
|
||||
json: JsonElement,
|
||||
typeOfT: Type,
|
||||
context: JsonDeserializationContext
|
||||
): SkinElement {
|
||||
return fromJson(json as? JsonObject ?: throw JsonSyntaxException("Expected JsonObject, got ${json::class.qualifiedName}"))
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
require(x >= 0f) { "Invalid x $x" }
|
||||
require(y >= 0f) { "Invalid y $y" }
|
||||
|
@ -11,6 +11,7 @@ import net.minecraft.network.chat.Component
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearch
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearchManager
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearchType
|
||||
import ru.dbotthepony.mc.otm.block.entity.AndroidStationBlockEntity
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
|
||||
@ -33,7 +34,7 @@ import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
private fun exploreTree(research: AndroidResearchType<*>, seen: MutableSet<AndroidResearchType<*>>, result: MutableList<AndroidResearchType<*>>) {
|
||||
private fun exploreTree(research: AndroidResearchType, seen: MutableSet<AndroidResearchType>, result: MutableList<AndroidResearchType>) {
|
||||
if (!seen.add(research)) {
|
||||
return
|
||||
}
|
||||
@ -45,13 +46,13 @@ private fun exploreTree(research: AndroidResearchType<*>, seen: MutableSet<Andro
|
||||
}
|
||||
}
|
||||
|
||||
private fun findGraphs(): List<Pair<AndroidResearchType<*>, List<AndroidResearchType<*>>>> {
|
||||
val seen = HashSet<AndroidResearchType<*>>()
|
||||
val list = ArrayList<Pair<AndroidResearchType<*>, ArrayList<AndroidResearchType<*>>>>()
|
||||
private fun findGraphs(): List<Pair<AndroidResearchType, List<AndroidResearchType>>> {
|
||||
val seen = HashSet<AndroidResearchType>()
|
||||
val list = ArrayList<Pair<AndroidResearchType, ArrayList<AndroidResearchType>>>()
|
||||
|
||||
for (research in MRegistry.ANDROID_RESEARCH) {
|
||||
for (research in AndroidResearchManager) {
|
||||
if (research.flatPrerequisites.isEmpty()) {
|
||||
val tree = ArrayList<AndroidResearchType<*>>()
|
||||
val tree = ArrayList<AndroidResearchType>()
|
||||
|
||||
exploreTree(research, seen, tree)
|
||||
|
||||
@ -64,7 +65,7 @@ private fun findGraphs(): List<Pair<AndroidResearchType<*>, List<AndroidResearch
|
||||
return list
|
||||
}
|
||||
|
||||
private fun isTree(root: AndroidResearchType<*>): Boolean {
|
||||
private fun isTree(root: AndroidResearchType): Boolean {
|
||||
if (root.flatPrerequisites.size > 1) {
|
||||
return false
|
||||
}
|
||||
@ -80,7 +81,7 @@ private fun isTree(root: AndroidResearchType<*>): Boolean {
|
||||
|
||||
private typealias LinePos = Pair<Pair<Float, Float>, Pair<Float, Float>>
|
||||
|
||||
private class Tree(val node: AndroidResearchType<*>) : Iterable<Tree> {
|
||||
private class Tree(val node: AndroidResearchType) : Iterable<Tree> {
|
||||
val subtrees = ArrayList<Tree>()
|
||||
|
||||
val height: Int
|
||||
@ -161,7 +162,7 @@ private class Tree(val node: AndroidResearchType<*>) : Iterable<Tree> {
|
||||
val totalWidth = width * 24f
|
||||
|
||||
val lines = ArrayList<LinePos>()
|
||||
val linesToResearch = IdentityHashMap<AndroidResearchType<*>, ArrayList<LinePos>>()
|
||||
val linesToResearch = IdentityHashMap<AndroidResearchType, ArrayList<LinePos>>()
|
||||
|
||||
val button = AndroidResearchButton(rows[node.researchTreeDepth], capability.getResearch(node), lines, linesToResearch)
|
||||
button.x = left + totalWidth / 2f - button.width / 2f
|
||||
@ -251,7 +252,7 @@ private class AndroidResearchButton(
|
||||
parent: EditablePanel<AndroidStationScreen>,
|
||||
private val node: AndroidResearch,
|
||||
private val lines: List<LinePos>,
|
||||
private val highlightLines: Map<AndroidResearchType<*>, List<LinePos>>
|
||||
private val highlightLines: Map<AndroidResearchType, List<LinePos>>
|
||||
): EditablePanel<AndroidStationScreen>(
|
||||
parent.screen,
|
||||
parent,
|
||||
|
@ -31,6 +31,15 @@ import net.minecraftforge.registries.RegistryManager
|
||||
import ru.dbotthepony.mc.otm.ClientConfig
|
||||
import ru.dbotthepony.mc.otm.item.BatteryItem
|
||||
import java.math.BigInteger
|
||||
import java.util.Spliterator
|
||||
import java.util.Spliterators
|
||||
import java.util.function.BiConsumer
|
||||
import java.util.function.BinaryOperator
|
||||
import java.util.function.Function
|
||||
import java.util.function.Supplier
|
||||
import java.util.stream.Collector
|
||||
import java.util.stream.Stream
|
||||
import java.util.stream.StreamSupport
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/**
|
||||
@ -361,3 +370,23 @@ fun BlockState.getExplosionResistance(level: BlockGetter, pos: BlockPos): Float
|
||||
block.explosionResistance
|
||||
}
|
||||
}
|
||||
|
||||
fun <E> MutableCollection<E>.addAll(elements: Iterator<E>) {
|
||||
for (item in elements) {
|
||||
add(item)
|
||||
}
|
||||
}
|
||||
|
||||
fun <E> MutableCollection<E>.addAll(elements: Stream<out E>) {
|
||||
for (item in elements) {
|
||||
add(item)
|
||||
}
|
||||
}
|
||||
|
||||
fun <E> Iterable<E>.stream(): Stream<out E> {
|
||||
return StreamSupport.stream(this.spliterator(), false)
|
||||
}
|
||||
|
||||
fun <E> Iterator<E>.stream(): Stream<out E> {
|
||||
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(this, 0), false)
|
||||
}
|
||||
|
59
src/main/kotlin/ru/dbotthepony/mc/otm/core/ListSet.kt
Normal file
59
src/main/kotlin/ru/dbotthepony/mc/otm/core/ListSet.kt
Normal file
@ -0,0 +1,59 @@
|
||||
package ru.dbotthepony.mc.otm.core
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import java.util.*
|
||||
import kotlin.collections.RandomAccess
|
||||
|
||||
class ListSet<T>(private val list: ImmutableList<T>) : List<T>, Set<T>, RandomAccess {
|
||||
constructor(list: Collection<T>) : this(ImmutableList.copyOf(list))
|
||||
|
||||
private val set: ImmutableSet<T> by lazy { ImmutableSet.copyOf(list) }
|
||||
|
||||
override val size: Int
|
||||
get() = list.size
|
||||
|
||||
override fun contains(element: T): Boolean {
|
||||
return set.contains(element)
|
||||
}
|
||||
|
||||
override fun containsAll(elements: Collection<T>): Boolean {
|
||||
return set.containsAll(elements)
|
||||
}
|
||||
|
||||
override fun get(index: Int): T {
|
||||
return list[index]
|
||||
}
|
||||
|
||||
override fun indexOf(element: T): Int {
|
||||
return list.indexOf(element)
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return list.isEmpty()
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<T> {
|
||||
return list.iterator()
|
||||
}
|
||||
|
||||
override fun lastIndexOf(element: T): Int {
|
||||
return list.lastIndexOf(element)
|
||||
}
|
||||
|
||||
override fun listIterator(): ListIterator<T> {
|
||||
return list.listIterator()
|
||||
}
|
||||
|
||||
override fun listIterator(index: Int): ListIterator<T> {
|
||||
return list.listIterator(index)
|
||||
}
|
||||
|
||||
override fun spliterator(): Spliterator<T> {
|
||||
return list.spliterator()
|
||||
}
|
||||
|
||||
override fun subList(fromIndex: Int, toIndex: Int): List<T> {
|
||||
return list.subList(fromIndex, toIndex)
|
||||
}
|
||||
}
|
@ -1,5 +1,17 @@
|
||||
package ru.dbotthepony.mc.otm.data
|
||||
|
||||
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.stream.JsonReader
|
||||
import com.google.gson.stream.JsonToken
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import com.mojang.datafixers.util.Pair
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.DataResult
|
||||
@ -9,8 +21,11 @@ import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraftforge.registries.ForgeRegistries
|
||||
import ru.dbotthepony.mc.otm.core.registryName
|
||||
import ru.dbotthepony.mc.otm.core.set
|
||||
import java.lang.reflect.Type
|
||||
|
||||
object ItemStackCodec : Codec<ItemStack> {
|
||||
object ItemStackCodec : Codec<ItemStack>, TypeAdapter<ItemStack>(), JsonSerializer<ItemStack>, JsonDeserializer<ItemStack> {
|
||||
override fun <T : Any?> encode(input: ItemStack, ops: DynamicOps<T>, prefix: T): DataResult<T> {
|
||||
require(prefix == ops.empty()) { "Non-empty prefix: $prefix" }
|
||||
|
||||
@ -36,4 +51,63 @@ object ItemStackCodec : Codec<ItemStack> {
|
||||
}
|
||||
|
||||
val LIST = ListCodec(this)
|
||||
|
||||
override fun write(out: JsonWriter, value: ItemStack) {
|
||||
out.beginObject()
|
||||
|
||||
out.name("id")
|
||||
out.value(value.item.registryName!!.toString())
|
||||
|
||||
out.name("count")
|
||||
out.value(value.count)
|
||||
|
||||
out.endObject()
|
||||
}
|
||||
|
||||
override fun read(reader: JsonReader): ItemStack {
|
||||
reader.beginObject()
|
||||
|
||||
var id: String? = null
|
||||
var count: Int? = null
|
||||
|
||||
while (reader.peek() != JsonToken.END_OBJECT) {
|
||||
when (val it = reader.nextName()) {
|
||||
"id" -> id = reader.nextString()
|
||||
"count" -> count = reader.nextInt()
|
||||
else -> throw JsonSyntaxException("Unknown json key $it")
|
||||
}
|
||||
}
|
||||
|
||||
reader.endObject()
|
||||
|
||||
val item = ForgeRegistries.ITEMS.getValue(ResourceLocation(id ?: return ItemStack.EMPTY)) ?: return ItemStack.EMPTY
|
||||
|
||||
return ItemStack(item, count ?: 1)
|
||||
}
|
||||
|
||||
override fun serialize(src: ItemStack, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
|
||||
return serialize(src)
|
||||
}
|
||||
|
||||
fun serialize(src: ItemStack): JsonElement {
|
||||
return JsonObject().also {
|
||||
it["id"] = JsonPrimitive(src.item.registryName!!.toString())
|
||||
it["count"] = JsonPrimitive(src.count)
|
||||
}
|
||||
}
|
||||
|
||||
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): ItemStack {
|
||||
return deserialize(json)
|
||||
}
|
||||
|
||||
fun deserialize(json: JsonElement): ItemStack {
|
||||
if (json !is JsonObject) {
|
||||
throw JsonSyntaxException("ItemStack json element must be JsonObject, ${json::class.qualifiedName} given")
|
||||
}
|
||||
|
||||
val item = ForgeRegistries.ITEMS.getValue(ResourceLocation(json["id"]?.asString ?: return ItemStack.EMPTY)) ?: return ItemStack.EMPTY
|
||||
val count = json["count"]?.asInt ?: 1
|
||||
|
||||
return ItemStack(item, count)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,30 @@
|
||||
package ru.dbotthepony.mc.otm.data
|
||||
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSpliterator
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSpliterators
|
||||
import java.util.stream.Stream
|
||||
import java.util.stream.StreamSupport
|
||||
|
||||
class JsonArraySpliterator(private val obj: JsonArray, offset: Int = 0, private val maxPos: Int = obj.size()) : ObjectSpliterators.AbstractIndexBasedSpliterator<JsonElement>(offset) {
|
||||
init {
|
||||
require(offset >= 0) { "Invalid offset $offset" }
|
||||
require(offset + maxPos <= obj.size()) { "$offset -> $maxPos while having only size of ${obj.size()}!" }
|
||||
}
|
||||
|
||||
override fun get(location: Int): JsonElement {
|
||||
return obj[location]
|
||||
}
|
||||
|
||||
override fun getMaxPos(): Int {
|
||||
return maxPos
|
||||
}
|
||||
|
||||
override fun makeForSplit(pos: Int, maxPos: Int): ObjectSpliterator<JsonElement> {
|
||||
return JsonArraySpliterator(obj, pos, maxPos)
|
||||
}
|
||||
}
|
||||
|
||||
fun JsonArray.elementSpliterator() = JsonArraySpliterator(this)
|
||||
fun JsonArray.stream(): Stream<out JsonElement> = StreamSupport.stream(elementSpliterator(), false)
|
@ -3,6 +3,7 @@ package ru.dbotthepony.mc.otm.network
|
||||
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraftforge.network.NetworkDirection.PLAY_TO_CLIENT
|
||||
import net.minecraftforge.network.NetworkDirection.PLAY_TO_SERVER
|
||||
@ -11,6 +12,7 @@ import org.apache.logging.log4j.LogManager
|
||||
import ru.dbotthepony.mc.otm.android.AndroidFeature
|
||||
import ru.dbotthepony.mc.otm.android.AndroidFeatureType
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearch
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearchManager
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearchType
|
||||
import ru.dbotthepony.mc.otm.android.AndroidSwitchableFeature
|
||||
import ru.dbotthepony.mc.otm.android.feature.ShockwaveFeature
|
||||
@ -47,14 +49,14 @@ class MatteryPlayerFieldPacket(val bytes: ByteArray, val length: Int) : MatteryP
|
||||
}
|
||||
}
|
||||
|
||||
class AndroidResearchRequestPacket(val type: AndroidResearchType<*>) : MatteryPacket {
|
||||
class AndroidResearchRequestPacket(val type: AndroidResearchType) : MatteryPacket {
|
||||
override fun write(buff: FriendlyByteBuf) {
|
||||
buff.writeInt(MRegistry.ANDROID_RESEARCH.getID(type))
|
||||
buff.writeUtf(type.id.toString())
|
||||
}
|
||||
|
||||
override fun play(context: Supplier<NetworkEvent.Context>) {
|
||||
context.get().packetHandled = true
|
||||
context.get().enqueueWork {
|
||||
context.packetHandled = true
|
||||
context.enqueueWork {
|
||||
val ply = context.get().sender ?: return@enqueueWork
|
||||
if (ply.isSpectator) return@enqueueWork
|
||||
val android = ply.matteryPlayer ?: return@enqueueWork
|
||||
@ -68,15 +70,15 @@ class AndroidResearchRequestPacket(val type: AndroidResearchType<*>) : MatteryPa
|
||||
|
||||
companion object {
|
||||
fun read(buff: FriendlyByteBuf): AndroidResearchRequestPacket {
|
||||
return AndroidResearchRequestPacket(MRegistry.ANDROID_RESEARCH.getValue(buff.readInt()))
|
||||
return AndroidResearchRequestPacket(AndroidResearchManager[ResourceLocation(buff.readUtf())] ?: throw NoSuchElementException())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AndroidResearchSyncPacket(val type: AndroidResearchType<*>, val dataList: FastByteArrayOutputStream?, val dataBytes: ByteArray?) : MatteryPacket {
|
||||
class AndroidResearchSyncPacket(val type: AndroidResearchType, val dataList: FastByteArrayOutputStream?, val dataBytes: ByteArray?) : MatteryPacket {
|
||||
override fun write(buff: FriendlyByteBuf) {
|
||||
dataList ?: throw NullPointerException("No byte list is present")
|
||||
buff.writeInt(MRegistry.ANDROID_RESEARCH.getID(type))
|
||||
buff.writeUtf(type.id.toString())
|
||||
buff.writeBytes(dataList.array, 0, dataList.length)
|
||||
}
|
||||
|
||||
@ -94,7 +96,7 @@ class AndroidResearchSyncPacket(val type: AndroidResearchType<*>, val dataList:
|
||||
companion object {
|
||||
fun read(buff: FriendlyByteBuf): AndroidResearchSyncPacket {
|
||||
return AndroidResearchSyncPacket(
|
||||
MRegistry.ANDROID_RESEARCH.getValue(buff.readInt()),
|
||||
AndroidResearchManager[ResourceLocation(buff.readUtf())] ?: throw NoSuchElementException(),
|
||||
null, ByteArray(buff.readableBytes()).also { buff.readBytes(it) }
|
||||
)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package ru.dbotthepony.mc.otm.network
|
||||
|
||||
import net.minecraftforge.network.NetworkDirection
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearchManager
|
||||
import ru.dbotthepony.mc.otm.matter.RegistryPacketClear
|
||||
import ru.dbotthepony.mc.otm.matter.RegistryPacketFullUpdate
|
||||
import ru.dbotthepony.mc.otm.matter.RegistryPacketRemove
|
||||
@ -15,5 +16,7 @@ object RegistryNetworkChannel : MatteryNetworkChannel(
|
||||
add(RegistryPacketClear::class.java, RegistryPacketClear.Companion::read, NetworkDirection.PLAY_TO_CLIENT)
|
||||
add(RegistryPacketFullUpdate::class.java, RegistryPacketFullUpdate.Companion::read, NetworkDirection.PLAY_TO_CLIENT)
|
||||
add(RegistryPacketUpdate::class.java, RegistryPacketUpdate.Companion::read, NetworkDirection.PLAY_TO_CLIENT)
|
||||
|
||||
add(AndroidResearchManager.SyncPacket::class.java, AndroidResearchManager::readSyncPacket, NetworkDirection.PLAY_TO_CLIENT)
|
||||
}
|
||||
}
|
||||
|
@ -1,302 +0,0 @@
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package ru.dbotthepony.mc.otm.registry
|
||||
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraftforge.eventbus.api.IEventBus
|
||||
import net.minecraftforge.registries.DeferredRegister
|
||||
import net.minecraftforge.registries.RegistryObject
|
||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||
import ru.dbotthepony.mc.otm.core.TextComponent
|
||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearchBuilder
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearchType
|
||||
import ru.dbotthepony.mc.otm.android.feature.NanobotsArmorFeature
|
||||
import ru.dbotthepony.mc.otm.client.render.SkinElement
|
||||
import ru.dbotthepony.mc.otm.client.render.SkinGrid
|
||||
|
||||
object AndroidResearch {
|
||||
val ICONS = ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/android_upgrades.png")
|
||||
val ICON_TRANSFER: SkinElement
|
||||
val ICON_ATTACK_BOOST: SkinElement
|
||||
val ICON_PLASMA_SHIELD_BOOST: SkinElement
|
||||
val ICON_CLOAK: SkinElement
|
||||
val ICON_GRAVITATIONAL_STABILIZER: SkinElement
|
||||
val ICON_AIR_BAGS: SkinElement
|
||||
val ICON_JUMP_BOOST: SkinElement
|
||||
|
||||
val ICON_FEATHER_FALLING: SkinElement
|
||||
val ICON_ARC: SkinElement
|
||||
val ICON_ARROW: SkinElement
|
||||
val ICON_ARMOR: SkinElement
|
||||
val ICON_NANOBOTS: SkinElement
|
||||
val ICON_NIGHT_VISION: SkinElement
|
||||
val ICON_OXYGEN_SUPPLY: SkinElement
|
||||
|
||||
val ICON_PLASMA_SHIELD: SkinElement
|
||||
val ICON_SHOCKWAVE: SkinElement
|
||||
val ICON_LIMB_OVERCLOCKING: SkinElement
|
||||
val ICON_STEP_ASSIST: SkinElement
|
||||
val ICON_ENDER_TELEPORT: SkinElement
|
||||
val ICON_WIRELESS_CHARGING: SkinElement
|
||||
val ICON_UNKNOWN: SkinElement
|
||||
|
||||
val ICON_EXTENDED_REACH: SkinElement
|
||||
|
||||
init {
|
||||
val grid = SkinGrid(ICONS, 18f, 18f, 7, 7)
|
||||
|
||||
ICON_TRANSFER = grid.next()
|
||||
ICON_ATTACK_BOOST = grid.next()
|
||||
ICON_PLASMA_SHIELD_BOOST = grid.next()
|
||||
ICON_CLOAK = grid.next()
|
||||
ICON_GRAVITATIONAL_STABILIZER = grid.next()
|
||||
ICON_AIR_BAGS = grid.next()
|
||||
ICON_JUMP_BOOST = grid.next()
|
||||
|
||||
ICON_FEATHER_FALLING = grid.next()
|
||||
ICON_ARC = grid.next()
|
||||
ICON_ARROW = grid.next()
|
||||
ICON_ARMOR = grid.next()
|
||||
ICON_NANOBOTS = grid.next()
|
||||
ICON_NIGHT_VISION = grid.next()
|
||||
ICON_OXYGEN_SUPPLY = grid.next()
|
||||
|
||||
ICON_PLASMA_SHIELD = grid.next()
|
||||
ICON_SHOCKWAVE = grid.next()
|
||||
ICON_LIMB_OVERCLOCKING = grid.next()
|
||||
ICON_STEP_ASSIST = grid.next()
|
||||
ICON_ENDER_TELEPORT = grid.next()
|
||||
ICON_WIRELESS_CHARGING = grid.next()
|
||||
ICON_UNKNOWN = grid.next()
|
||||
|
||||
ICON_EXTENDED_REACH = grid.next()
|
||||
}
|
||||
|
||||
private val registry = DeferredRegister.create(MRegistry.ANDROID_RESEARCH_KEY, OverdriveThatMatters.MOD_ID)
|
||||
|
||||
internal fun register(bus: IEventBus) {
|
||||
registry.register(bus)
|
||||
}
|
||||
|
||||
val AIR_BAGS: AndroidResearchType<*> by registry.register(MNames.AIR_BAGS) {
|
||||
AndroidResearchBuilder()
|
||||
.withExperience(18)
|
||||
.addFeatureResult(AndroidFeatures.AIR_BAGS)
|
||||
.withDescription()
|
||||
.withIcon(ICON_AIR_BAGS)
|
||||
.build()
|
||||
}
|
||||
|
||||
val IMPROVED_LIMBS: AndroidResearchType<*> by registry.register(MNames.IMPROVED_LIMBS) {
|
||||
AndroidResearchBuilder()
|
||||
.withExperience(20)
|
||||
.withDescription()
|
||||
.withIcon(ICON_EXTENDED_REACH)
|
||||
.build()
|
||||
}
|
||||
|
||||
val STEP_ASSIST: AndroidResearchType<*> by registry.register(MNames.STEP_ASSIST) {
|
||||
AndroidResearchBuilder()
|
||||
.withExperience(24)
|
||||
.addFeatureResult(AndroidFeatures.STEP_ASSIST)
|
||||
.withDescription()
|
||||
.withIcon(ICON_STEP_ASSIST)
|
||||
.addPrerequisite(IMPROVED_LIMBS)
|
||||
.build()
|
||||
}
|
||||
|
||||
val EXTENDED_REACH: AndroidResearchType<*> by registry.register(MNames.EXTENDED_REACH) {
|
||||
AndroidResearchBuilder()
|
||||
.withExperience(40)
|
||||
.addFeatureResult(AndroidFeatures.EXTENDED_REACH)
|
||||
.addPrerequisite(IMPROVED_LIMBS)
|
||||
.withDescription()
|
||||
.withIcon(ICON_EXTENDED_REACH)
|
||||
.build()
|
||||
}
|
||||
|
||||
val NIGHT_VISION: AndroidResearchType<*> by registry.register(MNames.NIGHT_VISION) {
|
||||
AndroidResearchBuilder()
|
||||
.withExperience(40)
|
||||
.withDescription()
|
||||
.withIcon(ICON_NIGHT_VISION)
|
||||
.addFeatureResult(AndroidFeatures.NIGHT_VISION)
|
||||
.build()
|
||||
}
|
||||
|
||||
val NANOBOTS: AndroidResearchType<*> by registry.register(MNames.NANOBOTS) {
|
||||
AndroidResearchBuilder()
|
||||
.withExperience(15)
|
||||
.withDescription()
|
||||
.withIcon(ICON_NANOBOTS)
|
||||
.build()
|
||||
}
|
||||
|
||||
val NANOBOTS_ARMOR: AndroidResearchType<*> by registry.register(MNames.NANOBOTS_ARMOR) {
|
||||
AndroidResearchBuilder()
|
||||
.withExperience(25)
|
||||
.withDescription()
|
||||
.addPrerequisite(OverdriveThatMatters.loc(MNames.NANOBOTS))
|
||||
.addFeatureResult(OverdriveThatMatters.loc(MNames.NANOBOTS_ARMOR))
|
||||
.withIcon(ICON_ARMOR)
|
||||
.addBlocker(OverdriveThatMatters.loc(MNames.ATTACK_BOOST_1))
|
||||
.build()
|
||||
}
|
||||
|
||||
val LIMB_OVERCLOCKING: List<AndroidResearchType<*>>
|
||||
val NANOBOTS_ARMOR_SPEED: List<AndroidResearchType<*>>
|
||||
val NANOBOTS_ARMOR_STRENGTH: List<AndroidResearchType<*>>
|
||||
val NANOBOTS_REGENERATION: List<AndroidResearchType<*>>
|
||||
val ATTACK_BOOST: List<AndroidResearchType<*>>
|
||||
|
||||
init {
|
||||
val limbList = ArrayList<RegistryObject<AndroidResearchType<*>>>()
|
||||
val regenList = ArrayList<RegistryObject<AndroidResearchType<*>>>()
|
||||
val armorStrengthList = ArrayList<RegistryObject<AndroidResearchType<*>>>()
|
||||
val armorSpeedList = ArrayList<RegistryObject<AndroidResearchType<*>>>()
|
||||
val attackBoostList = ArrayList<RegistryObject<AndroidResearchType<*>>>()
|
||||
|
||||
for (i in 0 until 4) {
|
||||
limbList.add(registry.register(MNames.LIMB_OVERCLOCKING_LIST[i]) {
|
||||
val research = AndroidResearchBuilder()
|
||||
.withExperience(18 + i * 8)
|
||||
.withIconText(TextComponent((i + 1).toString()))
|
||||
.addPrerequisite(IMPROVED_LIMBS)
|
||||
.withIcon(ICON_LIMB_OVERCLOCKING)
|
||||
.withName(TranslatableComponent("android_research.overdrive_that_matters.limb_overclocking", i + 1))
|
||||
.withDescription(
|
||||
TranslatableComponent(
|
||||
"android_research.overdrive_that_matters.limb_overclocking.description",
|
||||
(i + 1) * 8,
|
||||
(i + 1) * 6
|
||||
)
|
||||
)
|
||||
.addFeatureResult(OverdriveThatMatters.loc(MNames.LIMB_OVERCLOCKING), i)
|
||||
|
||||
if (i > 0) {
|
||||
research.addPrerequisite(OverdriveThatMatters.loc(MNames.LIMB_OVERCLOCKING_LIST[i - 1]))
|
||||
}
|
||||
|
||||
research.build()
|
||||
})
|
||||
|
||||
attackBoostList.add(registry.register(MNames.ATTACK_BOOST_LIST[i]) {
|
||||
val research = AndroidResearchBuilder()
|
||||
.withExperience(18 + i * 8)
|
||||
.withIconText(TextComponent((i + 1).toString()))
|
||||
.addPrerequisite(IMPROVED_LIMBS)
|
||||
.withIcon(ICON_ATTACK_BOOST)
|
||||
.withName(TranslatableComponent("android_research.overdrive_that_matters.attack_boost", i + 1))
|
||||
.withDescription(
|
||||
TranslatableComponent(
|
||||
"android_research.overdrive_that_matters.attack_boost.description",
|
||||
(i + 1) * 6
|
||||
)
|
||||
)
|
||||
.addFeatureResult(OverdriveThatMatters.loc(MNames.ATTACK_BOOST), i)
|
||||
.addBlocker(NANOBOTS_ARMOR)
|
||||
|
||||
if (i > 0) {
|
||||
research.addPrerequisite(OverdriveThatMatters.loc(MNames.ATTACK_BOOST_LIST[i - 1]))
|
||||
}
|
||||
|
||||
research.build()
|
||||
})
|
||||
|
||||
regenList.add(registry.register(MNames.NANOBOTS_REGENERATION_LIST[i]) {
|
||||
val regeneration = AndroidResearchBuilder()
|
||||
.withExperience(20 + i * 6)
|
||||
.withIconText(TextComponent((i + 1).toString()))
|
||||
.withIcon(ICON_NANOBOTS)
|
||||
.withName(TranslatableComponent("android_research.overdrive_that_matters.nanobots_regeneration", i + 1))
|
||||
.withDescription(
|
||||
if (i > 0) TranslatableComponent("android_research.overdrive_that_matters.nanobots_regeneration.description_improve") else TranslatableComponent(
|
||||
"android_research.overdrive_that_matters.nanobots_regeneration.description"
|
||||
)
|
||||
)
|
||||
.addFeatureResult(OverdriveThatMatters.loc(MNames.NANOBOTS_REGENERATION), i)
|
||||
|
||||
|
||||
if (i > 0) {
|
||||
regeneration.addPrerequisite(OverdriveThatMatters.loc(MNames.NANOBOTS_REGENERATION_LIST[i - 1]))
|
||||
} else {
|
||||
regeneration.addPrerequisite(OverdriveThatMatters.loc(MNames.NANOBOTS))
|
||||
}
|
||||
|
||||
regeneration.build()
|
||||
})
|
||||
}
|
||||
|
||||
for (i in 0 until 3) {
|
||||
val level = i + 1
|
||||
|
||||
armorStrengthList.add(registry.register(MNames.NANOBOTS_ARMOR_STRENGTH_LIST[i]) {
|
||||
AndroidResearchBuilder()
|
||||
.withExperience(20 + i * 8)
|
||||
.withIconText(TextComponent((i + 1).toString()))
|
||||
.withIcon(ICON_ARMOR)
|
||||
.addPrerequisite(OverdriveThatMatters.loc(if (i > 0) MNames.NANOBOTS_ARMOR_STRENGTH_LIST[i - 1] else MNames.NANOBOTS_ARMOR))
|
||||
.withName(
|
||||
TranslatableComponent(
|
||||
"android_research.overdrive_that_matters.nanobots_armor_strength",
|
||||
i + 1
|
||||
)
|
||||
)
|
||||
.withDescription(
|
||||
TranslatableComponent(
|
||||
"android_research.overdrive_that_matters.nanobots_armor_strength.description",
|
||||
(i + 1) * 8,
|
||||
(i + 1) * 6
|
||||
)
|
||||
)
|
||||
.addFeatureResult(OverdriveThatMatters.loc(MNames.NANOBOTS_ARMOR), 0) { _, feature ->
|
||||
if ((feature as NanobotsArmorFeature).strength < level) feature.strength = level
|
||||
}
|
||||
.build()
|
||||
})
|
||||
|
||||
armorSpeedList.add(registry.register(MNames.NANOBOTS_ARMOR_SPEED_LIST[i]) {
|
||||
AndroidResearchBuilder()
|
||||
.withExperience(18 + i * 6)
|
||||
.withIconText(TextComponent((i + 1).toString()))
|
||||
.withIcon(ICON_ARMOR)
|
||||
.addPrerequisite(OverdriveThatMatters.loc(if (i > 0) MNames.NANOBOTS_ARMOR_SPEED_LIST[i - 1] else MNames.NANOBOTS_ARMOR))
|
||||
.withName(
|
||||
TranslatableComponent(
|
||||
"android_research.overdrive_that_matters.nanobots_armor_speed",
|
||||
i + 1
|
||||
)
|
||||
)
|
||||
.withDescription(
|
||||
TranslatableComponent(
|
||||
"android_research.overdrive_that_matters.nanobots_armor_speed.description",
|
||||
(i + 1) * 8,
|
||||
(i + 1) * 6
|
||||
)
|
||||
)
|
||||
.addFeatureResult(OverdriveThatMatters.loc(MNames.NANOBOTS_ARMOR), 0) { _, feature ->
|
||||
if ((feature as NanobotsArmorFeature).speed < level) feature.speed = level
|
||||
}
|
||||
.build()
|
||||
})
|
||||
}
|
||||
|
||||
LIMB_OVERCLOCKING = RegistryObjectList(limbList)
|
||||
NANOBOTS_REGENERATION = RegistryObjectList(regenList)
|
||||
NANOBOTS_ARMOR_SPEED = RegistryObjectList(armorSpeedList)
|
||||
NANOBOTS_ARMOR_STRENGTH = RegistryObjectList(armorStrengthList)
|
||||
ATTACK_BOOST = RegistryObjectList(attackBoostList)
|
||||
}
|
||||
|
||||
val SHOCKWAVE: AndroidResearchType<*> by registry.register(MNames.SHOCKWAVE) {
|
||||
AndroidResearchBuilder()
|
||||
.withExperience(40)
|
||||
.withDescription()
|
||||
.withIcon(ICON_SHOCKWAVE)
|
||||
.addFeatureResult(AndroidFeatures.SHOCKWAVE)
|
||||
.addPrerequisite(ATTACK_BOOST[2])
|
||||
.build()
|
||||
}
|
||||
}
|
@ -85,20 +85,12 @@ class WriteOnce<V> : ReadWriteProperty<Any?, V> {
|
||||
|
||||
object MRegistry {
|
||||
private val features = RegistryDelegate<AndroidFeatureType<*>>("android_features")
|
||||
private val research = RegistryDelegate<AndroidResearchType<*>>("android_research")
|
||||
|
||||
val ANDROID_FEATURES by features
|
||||
val ANDROID_RESEARCH by research
|
||||
|
||||
val ANDROID_FEATURES_LOCATION get() = features.location
|
||||
val ANDROID_RESEARCH_LOCATION get() = research.location
|
||||
|
||||
val ANDROID_FEATURES_KEY get() = features.key
|
||||
val ANDROID_RESEARCH_KEY get() = research.key
|
||||
|
||||
private fun register(event: NewRegistryEvent) {
|
||||
features.build(event)
|
||||
research.build(event)
|
||||
}
|
||||
|
||||
val CARGO_CRATES = DecorativeBlock(MNames.CARGO_CRATE, ::CargoCrateBlock, baseItemGroup = OverdriveThatMatters.INSTANCE.CREATIVE_TAB)
|
||||
@ -234,7 +226,6 @@ object MRegistry {
|
||||
MMenus.register(bus)
|
||||
MItems.register(bus)
|
||||
AndroidFeatures.register(bus)
|
||||
AndroidResearch.register(bus)
|
||||
MSoundEvents.register(bus)
|
||||
LootModifiers.register(bus)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user