Advancements!

This commit is contained in:
DBotThePony 2022-10-02 13:00:47 +07:00
parent 6e78972ea1
commit 1a468f9817
Signed by: DBot
GPG Key ID: DCC23B5715498507
16 changed files with 664 additions and 14 deletions

View File

@ -30,6 +30,7 @@ import ru.dbotthepony.mc.otm.registry.*
import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.datagen.advancements.AdvancementProvider
import ru.dbotthepony.mc.otm.datagen.advancements.addAdvancements
import ru.dbotthepony.mc.otm.datagen.advancements.addAndroidAdvancements
import ru.dbotthepony.mc.otm.datagen.blocks.addBlockStates
import ru.dbotthepony.mc.otm.datagen.blocks.addComplexBlockStates
import ru.dbotthepony.mc.otm.datagen.items.addItemModels
@ -69,6 +70,8 @@ object DataGen {
private set
var languageProvider: MatteryLanguageProvider by WriteOnce()
private set
var researchProvider: AndroidResearchDataProvider by WriteOnce()
private set
fun decorativeCubeAll(vararg blocks: Block) {
blockModelProvider.decorativeCubeAll(*blocks)
@ -300,6 +303,7 @@ object DataGen {
val recipeProvider = MatteryRecipeProvider(event.generator)
val lootModifier = LootModifiers(event.generator)
val languageProvider = MatteryLanguageProvider(event.generator)
val researchProvider = AndroidResearchDataProvider(event.generator).also { it.exec { addResearchData(it, languageProvider) } }
this.blockModelProvider = blockModelProvider
this.blockStateProvider = blockStateProvider
@ -308,6 +312,7 @@ object DataGen {
this.recipeProvider = recipeProvider
this.lootModifier = lootModifier
this.languageProvider = languageProvider
this.researchProvider = researchProvider
val tagsProvider = TagsProvider(event)
val advancementProvider = AdvancementProvider(event)
@ -323,7 +328,7 @@ 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) } })
event.generator.addProvider(true, researchProvider)
event.generator.addProvider(true, advancementProvider)
AddEnglishLanguage(languageProvider)
@ -348,6 +353,7 @@ object DataGen {
advancementProvider.exec { it, files ->
addAdvancements(it, files, languageProvider)
addAndroidAdvancements(it, files, languageProvider)
}
addPlatePressRecipes(recipeProvider)

View File

@ -458,6 +458,9 @@ fun addResearchData(serializer: Consumer<AndroidResearchType>, lang: MatteryLang
add(ENDER_TELEPORTER, "Ender Teleporter")
add(ENDER_TELEPORTER, "description", "Allows unit to perform instant, short distance teleports without damage to internal systems")
add(NIGHT_VISION, "Night Vision")
add(NIGHT_VISION, "description", "Allows unit to clearly see in the dark")
add(attackBoostList[0], "Attack Boost %s")
add(attackBoostList[0], "description", "Increases total melee attack strength by %s%%")
}

View File

@ -40,4 +40,50 @@ fun addAdvancements(serializer: Consumer<Advancement>, existingFileHelper: Exist
)
.addCriterion("pulled_by_black_hole", BlackHoleTrigger.Instance)
.save(serializer, modLocation("regular/black_hole"), existingFileHelper)
val ore = AdvancementBuilder()
.parent(root)
.requirements(RequirementsStrategy.OR)
.display(
itemStack = ItemStack(MItems.TRITANIUM_ORE_CLUMP),
title = TranslatableComponent(translation.add("ore", "Blue Metal Discovery")),
description = TranslatableComponent(translation.add("ore.desc", "Mine some Tritanium")),
)
.addCriterion("has_tritanium_ore", criterion(MItemTags.TRITANIUM_ORES))
.addCriterion("has_tritanium_ore_clump", criterion(MItemTags.TRITANIUM_ORE_CLUMPS))
.save(serializer, modLocation("regular/ore"), existingFileHelper)
val ingot = AdvancementBuilder()
.parent(ore)
.display(
itemStack = ItemStack(MItems.TRITANIUM_INGOT),
title = TranslatableComponent(translation.add("ingot", "Acquire Harder-ware")),
description = TranslatableComponent(translation.add("ingot.desc", "Smelt a Tritanium ingot")),
)
.addCriterion("has_tritanium_ingot", criterion(MItemTags.TRITANIUM_INGOTS))
.save(serializer, modLocation("regular/ingot"), existingFileHelper)
AdvancementBuilder()
.parent(ingot)
.display(
itemStack = ItemStack(MItems.TRITANIUM_PICKAXE),
title = TranslatableComponent(translation.add("pickaxe", "A Tool for Patient Miners")),
description = TranslatableComponent(translation.add("pickaxe.desc", "Craft a Tritanium Pickaxe")),
)
.addCriterion("has_tritanium_pickaxe", criterion(MItems.TRITANIUM_PICKAXE))
.save(serializer, modLocation("regular/pickaxe"), existingFileHelper)
val pill = AdvancementBuilder()
.parent(root)
.display(
itemStack = ItemStack(MItems.PILL_ANDROID),
title = TranslatableComponent(translation.add("pill", "Side Colored Mystery")),
description = TranslatableComponent(translation.add("pill.desc", "Find some of those mysterious pills. Consult with Cleric before trying to ingest them.")),
)
.requirements(RequirementsStrategy.OR)
.addCriterion("pill1", criterion(MItems.PILL_ANDROID))
.addCriterion("pill2", criterion(MItems.PILL_HEAL))
.addCriterion("pill3", criterion(MItems.PILL_HUMANE))
.addCriterion("pill4", criterion(MItems.PILL_OBLIVION))
.save(serializer, modLocation("regular/pill"), existingFileHelper)
}

View File

@ -0,0 +1,183 @@
package ru.dbotthepony.mc.otm.datagen.advancements
import net.minecraft.advancements.Advancement
import net.minecraft.advancements.FrameType
import net.minecraft.advancements.RequirementsStrategy
import net.minecraft.advancements.critereon.DamagePredicate
import net.minecraft.advancements.critereon.DamageSourcePredicate
import net.minecraft.advancements.critereon.EntityPredicate
import net.minecraft.advancements.critereon.InventoryChangeTrigger
import net.minecraft.advancements.critereon.PlayerHurtEntityTrigger
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.EntityType
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraftforge.common.data.ExistingFileHelper
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.datagen.DataGen
import ru.dbotthepony.mc.otm.datagen.lang.MatteryLanguageProvider
import ru.dbotthepony.mc.otm.datagen.modLocation
import ru.dbotthepony.mc.otm.registry.MItemTags
import ru.dbotthepony.mc.otm.registry.MItems
import ru.dbotthepony.mc.otm.registry.MNames
import ru.dbotthepony.mc.otm.triggers.AndroidResearchTrigger
import ru.dbotthepony.mc.otm.triggers.BecomeAndroidDeathTrigger
import ru.dbotthepony.mc.otm.triggers.BecomeAndroidSleepTrigger
import ru.dbotthepony.mc.otm.triggers.BecomeAndroidTrigger
import ru.dbotthepony.mc.otm.triggers.BecomeHumaneTrigger
import ru.dbotthepony.mc.otm.triggers.ShockwaveDamageMobTrigger
import ru.dbotthepony.mc.otm.triggers.ShockwaveTrigger
import java.util.function.Consumer
fun addAndroidAdvancements(serializer: Consumer<Advancement>, existingFileHelper: ExistingFileHelper, lang: MatteryLanguageProvider) {
val translation = lang.english.Prepended("otm.advancements.android")
val root = AdvancementBuilder()
.display(
itemStack = ItemStack(MItems.PILL_ANDROID),
title = TranslatableComponent(translation.add("root", "Androids and Humans")),
description = TranslatableComponent(translation.add("root.desc", "Can you make out who is cruel machine and who can make love?")),
showToast = false,
announceChat = false,
background = modLocation("textures/block/decorative/metal_beam_top.png")
)
.addCriterion("became_android", BecomeAndroidTrigger.Instance)
.save(serializer, modLocation("android/root"), existingFileHelper)
AdvancementBuilder()
.parent(root)
.display(
itemStack = ItemStack(MItems.PILL_ANDROID),
title = TranslatableComponent(translation.add("normal", "Last Sweet Dreams")),
description = TranslatableComponent(translation.add("normal.desc", "Become an Android in your dreams, a soulless machine... Or is it?")),
hidden = true,
)
.addCriterion("became_android", BecomeAndroidSleepTrigger.Instance)
.save(serializer, modLocation("android/become_thru_sleep"), existingFileHelper)
AdvancementBuilder()
.parent(root)
.display(
itemStack = ItemStack(MItems.PILL_ANDROID),
title = TranslatableComponent(translation.add("death", "The Old Way")),
description = TranslatableComponent(translation.add("death.desc", "Become an Android in event of death, veteran's favorite")),
hidden = true,
)
.addCriterion("became_android", BecomeAndroidDeathTrigger.Instance)
.save(serializer, modLocation("android/become_thru_death"), existingFileHelper)
AdvancementBuilder()
.parent(root)
.display(
itemStack = ItemStack(MItems.PILL_HUMANE),
title = TranslatableComponent(translation.add("unandroid", "Feel Humane Again")),
description = TranslatableComponent(translation.add("unandroid.desc", "Become fleshy after being a machine, yet something is still missing that you had before...")),
hidden = true,
frameType = FrameType.GOAL
)
.addCriterion("become_humane", BecomeHumaneTrigger.Instance)
.save(serializer, modLocation("android/become_humane"), existingFileHelper)
val researchAnything = AdvancementBuilder()
.parent(root)
.display(
itemStack = ItemStack(MItems.ANDROID_STATION),
title = TranslatableComponent(translation.add("research_anything", "New Trick")),
description = TranslatableComponent(translation.add("research_anything.desc", "Research anything as Android")),
)
.addCriterion("research_anything", AndroidResearchTrigger.Instance(null))
.save(serializer, modLocation("android/research_anything"), existingFileHelper)
AdvancementBuilder()
.parent(researchAnything)
.display(
itemStack = ItemStack(Items.WATER_BUCKET),
title = TranslatableComponent(translation.add("air_bags", "Patching Up Wooden Vessel")),
description = TranslatableComponent(translation.add("air_bags.desc", "Research Air Bags as Android, to float in water")),
)
.addCriterion("air_bags", AndroidResearchTrigger.Instance(modLocation(MNames.AIR_BAGS)))
.save(serializer, modLocation("android/research_air_bags"), existingFileHelper)
AdvancementBuilder()
.parent(researchAnything)
.display(
itemStack = ItemStack(Items.ENDER_EYE),
title = TranslatableComponent(translation.add("night_vision", "Second Pair of Eyes")),
description = TranslatableComponent(translation.add("night_vision.desc", "Research Night Vision as Android, to see in the dark")),
)
.addCriterion("night_vision", AndroidResearchTrigger.Instance(modLocation(MNames.NIGHT_VISION)))
.save(serializer, modLocation("android/research_night_vision"), existingFileHelper)
val nanobots = AdvancementBuilder()
.parent(researchAnything)
.display(
itemStack = ItemStack(MItems.MATTER_TRANSFORM_MATRIX),
title = TranslatableComponent(translation.add("nanobots", "Nanomachines, Son!")),
description = TranslatableComponent(translation.add("nanobots.desc", "Research Nanobots as Android, to unlock potent research that come after it")),
hidden = true
)
.addCriterion("nanobots", AndroidResearchTrigger.Instance(modLocation(MNames.NANOBOTS)))
.save(serializer, modLocation("android/research_nanobots"), existingFileHelper)
val regen = AdvancementBuilder()
.parent(nanobots)
.display(
itemStack = ItemStack(Items.GOLDEN_APPLE),
title = TranslatableComponent(translation.add("regen", "Field Repair Done Easy")),
description = TranslatableComponent(translation.add("regen.desc", "Research Nanobots Regeneration as Android")),
)
.addCriterion("regen0", AndroidResearchTrigger.Instance(modLocation(MNames.NANOBOTS_REGENERATION_1)))
.save(serializer, modLocation("android/regen"), existingFileHelper)
AdvancementBuilder()
.parent(regen)
.display(
itemStack = ItemStack(Items.ENCHANTED_GOLDEN_APPLE),
title = TranslatableComponent(translation.add("regen_all", "Field Repair Done Effortless")),
description = TranslatableComponent(translation.add("regen_all.desc", "Max out Nanobots Regeneration research")),
frameType = FrameType.GOAL,
)
.addCriterion("regen0", AndroidResearchTrigger.Instance(modLocation(MNames.NANOBOTS_REGENERATION_1)))
.addCriterion("regen1", AndroidResearchTrigger.Instance(modLocation(MNames.NANOBOTS_REGENERATION_2)))
.addCriterion("regen2", AndroidResearchTrigger.Instance(modLocation(MNames.NANOBOTS_REGENERATION_3)))
.addCriterion("regen3", AndroidResearchTrigger.Instance(modLocation(MNames.NANOBOTS_REGENERATION_4)))
.save(serializer, modLocation("android/regen_all"), existingFileHelper)
AdvancementBuilder()
.parent(researchAnything)
.display(
itemStack = ItemStack(MItems.ANDROID_STATION),
title = TranslatableComponent(translation.add("research_all", "Mecha-agnomination")),
description = TranslatableComponent(translation.add("research_all.desc", "Research everything (that don't block any other research)")),
frameType = FrameType.CHALLENGE
)
.also { advancement ->
DataGen.researchProvider.generatedView.stream()
.filter { it.allBlockedBy.isEmpty() && it.allBlocking.isEmpty() }
.forEach {
advancement.addCriterion(it.id.toString(), AndroidResearchTrigger.Instance(it))
}
}
.save(serializer, modLocation("android/research_everything"), existingFileHelper)
val shockwave = AdvancementBuilder()
.parent(researchAnything)
.display(
itemStack = ItemStack(Items.PISTON),
title = TranslatableComponent(translation.add("shockwave", "Supersonic Landing")),
description = TranslatableComponent(translation.add("shockwave.desc", "Perform a Shockwave upon landing")),
)
.addCriterion("shockwave", ShockwaveTrigger.Instance)
.save(serializer, modLocation("android/shockwave"), existingFileHelper)
AdvancementBuilder()
.parent(shockwave)
.display(
itemStack = ItemStack(Items.WARDEN_SPAWN_EGG),
title = TranslatableComponent(translation.add("shockwave_warden", "Music To Their Ears")),
description = TranslatableComponent(translation.add("shockwave_warden.desc", "Hurt Warden using Shockwave ability")),
frameType = FrameType.GOAL
)
.addCriterion("shockwave_warden", ShockwaveDamageMobTrigger.Instance(EntityPredicate.Builder.entity().of(EntityType.WARDEN).build().wrap()))
.save(serializer, modLocation("android/shockwave_warden"), existingFileHelper)
}

View File

@ -4,6 +4,7 @@ import net.minecraft.advancements.Advancement
import net.minecraft.advancements.CriterionTriggerInstance
import net.minecraft.advancements.DisplayInfo
import net.minecraft.advancements.FrameType
import net.minecraft.advancements.critereon.EntityPredicate
import net.minecraft.advancements.critereon.InventoryChangeTrigger
import net.minecraft.advancements.critereon.ItemPredicate
import net.minecraft.network.chat.Component
@ -11,6 +12,7 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.tags.TagKey
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.ItemLike
import ru.dbotthepony.mc.otm.core.TextComponent
fun AdvancementBuilder(): Advancement.Builder = Advancement.Builder.advancement()
@ -57,3 +59,13 @@ fun predicate(tag: TagKey<Item>): ItemPredicate {
fun criterion(tag: TagKey<Item>): CriterionTriggerInstance {
return InventoryChangeTrigger.TriggerInstance.hasItems(predicate(tag))
}
fun predicate(item: ItemLike): ItemPredicate {
return ItemPredicate.Builder.item().of(item).build()
}
fun criterion(item: ItemLike): CriterionTriggerInstance {
return InventoryChangeTrigger.TriggerInstance.hasItems(predicate(item))
}
fun EntityPredicate.wrap(): EntityPredicate.Composite = EntityPredicate.Composite.wrap(this)

View File

@ -200,8 +200,13 @@ object ServerConfig {
val COOLDOWN: Int by specBuilder.comment("In ticks").defineInRange("cooldown", 30, 1)
val RADIUS_HORIZONTAL: Double by specBuilder.comment("In meters").defineInRange("radiusHorizontal", 4.0, 0.0)
val RADIUS_VERTICAL: Double by specBuilder.comment("In meters").defineInRange("radiusVertical", 1.0, 0.0)
val RADIUS_HORIZONTAL_WARDEN: Double by specBuilder.comment("In meters, when searching for Warden").defineInRange("radiusHorizontalWarden", 16.0, 0.0)
val RADIUS_VERTICAL_WARDEN: Double by specBuilder.comment("In meters, when searching for Warden").defineInRange("radiusVerticalWarden", 6.0, 0.0)
val BREAK_BLOCKS: Boolean by specBuilder.comment("Break blocks without any blast resistance").define("breakBlocks", true)
val DAMAGE: Double by specBuilder.comment("Max potential damage done by shockwave").defineInRange("damage", 12.0, 0.0, Float.MAX_VALUE.toDouble())
val WARDEN_DAMAGE_MULT: Double by specBuilder.defineInRange("wardenDamageMultiplier", 4.0, 0.0, Float.MAX_VALUE.toDouble())
val ENERGY_COST by specBuilder.defineImpreciseFraction("energyCost", ImpreciseFraction(2048), ImpreciseFraction.ZERO)
init {

View File

@ -7,6 +7,7 @@ import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraftforge.common.util.INBTSerializable
@ -25,6 +26,7 @@ import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.network.FieldSynchronizer
import ru.dbotthepony.mc.otm.registry.MRegistry
import ru.dbotthepony.mc.otm.triggers.AndroidResearchTrigger
import java.io.DataInputStream
import java.io.InputStream
import kotlin.math.absoluteValue
@ -273,6 +275,11 @@ class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlay
if (force || canResearch && consumeResearchCost(false)) {
onResearched()
isResearched = true
if (ply is ServerPlayer) {
AndroidResearchTrigger.trigger(ply as ServerPlayer, this.type)
}
return true
}

View File

@ -5,6 +5,7 @@ import net.minecraft.data.CachedOutput
import net.minecraft.data.DataGenerator
import net.minecraft.data.DataProvider
import net.minecraft.resources.ResourceLocation
import java.util.Collections
import java.util.LinkedList
import java.util.function.Consumer
@ -14,6 +15,9 @@ open class AndroidResearchDataProvider(protected val dataGenerator: DataGenerato
protected val callbacks = LinkedList<(Consumer<AndroidResearchType>) -> Unit>()
private val generated = LinkedList<AndroidResearchType>()
val generatedView: List<AndroidResearchType> = Collections.unmodifiableList(generated)
/**
* override this
*/
@ -49,6 +53,7 @@ open class AndroidResearchDataProvider(protected val dataGenerator: DataGenerato
for (value in added) {
value.validate()
generated.add(value)
}
}

View File

@ -2,10 +2,14 @@ package ru.dbotthepony.mc.otm.android.feature
import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.PoseStack
import it.unimi.dsi.fastutil.objects.ReferenceArraySet
import net.minecraft.ChatFormatting
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.monster.warden.Warden
import net.minecraftforge.network.NetworkEvent
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.ServerConfig
@ -33,6 +37,8 @@ import ru.dbotthepony.mc.otm.network.sender
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import ru.dbotthepony.mc.otm.registry.MNames
import ru.dbotthepony.mc.otm.registry.ShockwaveDamageSource
import ru.dbotthepony.mc.otm.triggers.ShockwaveDamageMobTrigger
import ru.dbotthepony.mc.otm.triggers.ShockwaveTrigger
import java.util.function.Supplier
import kotlin.math.pow
import kotlin.math.roundToInt
@ -77,6 +83,10 @@ class ShockwaveFeature(capability: MatteryPlayerCapability) : AndroidSwitchableF
cooldown = ServerConfig.Shockwave.COOLDOWN
if (ply is ServerPlayer) {
ShockwaveTrigger.trigger(ply as ServerPlayer)
}
// TODO: raycasting
val entities = ply.level.getEntitiesInEllipsoid(
ply.position,
@ -84,15 +94,45 @@ class ShockwaveFeature(capability: MatteryPlayerCapability) : AndroidSwitchableF
except = ply,
) { (it !is LivingEntity || !it.isSpectator && it.isAlive) }
for ((entity, distanceMultiplier) in entities) {
val multiplier = (1.0 - distanceMultiplier).pow(1.25)
val wardens = ply.level.getEntitiesInEllipsoid(
Warden::class.java,
ply.position,
Vector(ServerConfig.Shockwave.RADIUS_HORIZONTAL_WARDEN, ServerConfig.Shockwave.RADIUS_VERTICAL_WARDEN, ServerConfig.Shockwave.RADIUS_HORIZONTAL_WARDEN),
) { true }
// don't hurt items, arrows, etc etc
if (entity is LivingEntity) {
entity.hurt(ShockwaveDamageSource(ply), multiplier.toFloat() * ServerConfig.Shockwave.DAMAGE.toFloat())
entity.deltaMovement += (entity.position - ply.position).normalize() * (multiplier * 3.0)
} else {
entity.deltaMovement += (entity.position - ply.position).normalize() * (multiplier * 6.0)
val seen = ReferenceArraySet<Entity>()
for ((entity, distanceMultiplier) in wardens) {
seen.add(entity)
val multiplier = (1.0 - distanceMultiplier).pow(0.5)
val source = ShockwaveDamageSource(ply)
val damage = multiplier.toFloat() * ServerConfig.Shockwave.DAMAGE.toFloat() * ServerConfig.Shockwave.WARDEN_DAMAGE_MULT.toFloat()
entity.hurt(source, damage)
entity.deltaMovement += (entity.position - ply.position).normalize() * (multiplier * 3.0)
if (ply is ServerPlayer) {
ShockwaveDamageMobTrigger.trigger(ply as ServerPlayer, entity, source, damage)
}
}
for ((entity, distanceMultiplier) in entities) {
if (seen.add(entity)) {
val multiplier = (1.0 - distanceMultiplier).pow(1.25)
// don't hurt items, arrows, etc etc
if (entity is LivingEntity) {
val source = ShockwaveDamageSource(ply)
val damage = multiplier.toFloat() * ServerConfig.Shockwave.DAMAGE.toFloat()
entity.hurt(source, damage)
entity.deltaMovement += (entity.position - ply.position).normalize() * (multiplier * 3.0)
if (ply is ServerPlayer) {
ShockwaveDamageMobTrigger.trigger(ply as ServerPlayer, entity, source, damage)
}
} else {
entity.deltaMovement += (entity.position - ply.position).normalize() * (multiplier * 6.0)
}
}
}

View File

@ -32,11 +32,9 @@ import net.minecraftforge.event.entity.living.LivingDropsEvent
import net.minecraftforge.event.entity.living.LivingHurtEvent
import net.minecraftforge.event.entity.living.LivingSpawnEvent
import net.minecraftforge.event.entity.living.MobEffectEvent
import net.minecraftforge.event.entity.player.EntityItemPickupEvent
import net.minecraftforge.event.entity.player.PlayerEvent
import net.minecraftforge.eventbus.api.Event
import net.minecraftforge.registries.ForgeRegistries
import org.apache.commons.lang3.mutable.MutableInt
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.*
import ru.dbotthepony.mc.otm.android.AndroidFeature
@ -51,6 +49,11 @@ import ru.dbotthepony.mc.otm.menu.ExoSuitInventoryMenu
import ru.dbotthepony.mc.otm.network.*
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import ru.dbotthepony.mc.otm.registry.MRegistry
import ru.dbotthepony.mc.otm.triggers.AndroidResearchTrigger
import ru.dbotthepony.mc.otm.triggers.BecomeAndroidDeathTrigger
import ru.dbotthepony.mc.otm.triggers.BecomeAndroidSleepTrigger
import ru.dbotthepony.mc.otm.triggers.BecomeAndroidTrigger
import ru.dbotthepony.mc.otm.triggers.BecomeHumaneTrigger
import java.util.*
import java.util.stream.Stream
import kotlin.collections.ArrayDeque
@ -245,6 +248,10 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
deathLog.clear()
androidEnergy.batteryLevel = ServerConfig.ANDROID_MAX_ENERGY
androidEnergy.maxBatteryLevel = ServerConfig.ANDROID_MAX_ENERGY
if (ply is ServerPlayer) {
BecomeAndroidTrigger.trigger(ply)
}
}
fun becomeAndroidAndKill() {
@ -265,6 +272,10 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
androidEnergy.batteryLevel = ImpreciseFraction.ZERO
androidEnergy.maxBatteryLevel = ServerConfig.ANDROID_MAX_ENERGY
dropBattery()
if (ply is ServerPlayer) {
BecomeHumaneTrigger.trigger(ply)
}
}
fun becomeHumaneAndKill() {
@ -524,6 +535,20 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
val instance = AndroidResearch(research, this)
instance.deserializeNBT(researchTag)
this.research[research] = instance
if (instance.isResearched && ply is ServerPlayer) {
onceServer(20) {
if (!ply.hasDisconnected())
AndroidResearchTrigger.trigger(ply, instance.type)
}
}
}
}
if (isAndroid && ply is ServerPlayer) {
onceServer(20) {
if (!ply.hasDisconnected())
BecomeAndroidTrigger.trigger(ply)
}
}
}
@ -590,7 +615,11 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
if (ply.isSleeping && ply.sleepTimer > SLEEP_TICKS_LIMIT) {
becomeAndroid()
shouldPlaySound = true
(ply as? ServerPlayer)?.displayClientMessage(TranslatableComponent("otm.pill.message_finish").withStyle(ChatFormatting.DARK_RED), false)
if (ply is ServerPlayer) {
ply.displayClientMessage(TranslatableComponent("otm.pill.message_finish").withStyle(ChatFormatting.DARK_RED), false)
BecomeAndroidSleepTrigger.trigger(ply)
}
}
}
@ -839,7 +868,13 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
if (original.willBecomeAndroid && event.isWasDeath) {
original.becomeAndroid()
(event.entity as? ServerPlayer)?.displayClientMessage(TranslatableComponent("otm.pill.message_finish").withStyle(ChatFormatting.DARK_RED), false)
val ply = event.entity
if (ply is ServerPlayer) {
ply.displayClientMessage(TranslatableComponent("otm.pill.message_finish").withStyle(ChatFormatting.DARK_RED), false)
BecomeAndroidDeathTrigger.trigger(ply)
}
}
it.deserializeNBT(original.serializeNBT())

View File

@ -3,6 +3,7 @@ package ru.dbotthepony.mc.otm.core
import net.minecraft.world.entity.Entity
import net.minecraft.world.level.Level
import net.minecraft.world.phys.AABB
import java.util.LinkedList
import java.util.function.Predicate
import kotlin.math.pow
@ -20,7 +21,46 @@ fun Level.getEntitiesInEllipsoid(pos: Vector, dimensions: Vector, except: Entity
pos.z + dimensions.z,
), predicate)
val result = ArrayList<Pair<Entity, Double>>()
val result = LinkedList<Pair<Entity, Double>>()
for (it in entities) {
val a = (it.position - pos).let { vec ->
vec.x.pow(2.0) / dimensions.x.pow(2.0) +
vec.y.pow(2.0) / dimensions.y.pow(2.0) +
vec.z.pow(2.0) / dimensions.z.pow(2.0)
}
val b = it.boundingBox.center.let { vec ->
(vec.x - pos.x).pow(2.0) / dimensions.x.pow(2.0) +
(vec.y - pos.y).pow(2.0) / dimensions.y.pow(2.0) +
(vec.z - pos.z).pow(2.0) / dimensions.z.pow(2.0)
}
val min = a.coerceAtMost(b)
if (min <= 1.0) {
result.add(it to min)
}
}
return result
}
/**
* Pair of entity and distance fraction to center of ellipsoid
*/
fun <T : Entity> Level.getEntitiesInEllipsoid(type: Class<out T>, pos: Vector, dimensions: Vector, predicate: Predicate<in Entity>): List<Pair<T, Double>> {
val entities = getEntitiesOfClass(type, AABB(
pos.x - dimensions.x,
pos.y - dimensions.y,
pos.z - dimensions.z,
pos.x + dimensions.x,
pos.y + dimensions.y,
pos.z + dimensions.z,
), predicate)
val result = LinkedList<Pair<T, Double>>()
for (it in entities) {
val a = (it.position - pos).let { vec ->
@ -65,3 +105,10 @@ fun Level.getEntitiesInSphere(pos: Vector, radius: Double, except: Entity?, pred
fun Level.getEntitiesInSphere(pos: Vector, radius: Double, predicate: Predicate<in Entity>): List<Pair<Entity, Double>> {
return getEntitiesInEllipsoid(pos, Vector(radius, radius, radius), null, predicate)
}
/**
* Pair of entity and distance fraction to center of ellipsoid
*/
fun Level.getEntitiesInSphere(type: Class<out Entity>, pos: Vector, radius: Double, predicate: Predicate<in Entity>): List<Pair<Entity, Double>> {
return getEntitiesInEllipsoid(type, pos, Vector(radius, radius, radius), predicate)
}

View File

@ -44,7 +44,14 @@ import ru.dbotthepony.mc.otm.registry.objects.ColoredDecorativeBlock
import ru.dbotthepony.mc.otm.registry.objects.CrateProperties
import ru.dbotthepony.mc.otm.registry.objects.DecorativeBlock
import ru.dbotthepony.mc.otm.registry.objects.StripedColoredDecorativeBlock
import ru.dbotthepony.mc.otm.triggers.AndroidResearchTrigger
import ru.dbotthepony.mc.otm.triggers.BecomeAndroidDeathTrigger
import ru.dbotthepony.mc.otm.triggers.BecomeAndroidSleepTrigger
import ru.dbotthepony.mc.otm.triggers.BecomeAndroidTrigger
import ru.dbotthepony.mc.otm.triggers.BecomeHumaneTrigger
import ru.dbotthepony.mc.otm.triggers.BlackHoleTrigger
import ru.dbotthepony.mc.otm.triggers.ShockwaveDamageMobTrigger
import ru.dbotthepony.mc.otm.triggers.ShockwaveTrigger
import java.util.function.Supplier
import kotlin.properties.ReadOnlyProperty
import kotlin.properties.ReadWriteProperty
@ -261,6 +268,13 @@ object MRegistry {
private fun initializeCommon(event: FMLCommonSetupEvent) {
event.enqueueWork {
CriteriaTriggers.register(BlackHoleTrigger)
CriteriaTriggers.register(BecomeAndroidTrigger)
CriteriaTriggers.register(BecomeAndroidDeathTrigger)
CriteriaTriggers.register(BecomeAndroidSleepTrigger)
CriteriaTriggers.register(BecomeHumaneTrigger)
CriteriaTriggers.register(AndroidResearchTrigger)
CriteriaTriggers.register(ShockwaveDamageMobTrigger)
CriteriaTriggers.register(ShockwaveTrigger)
}
}

View File

@ -0,0 +1,54 @@
package ru.dbotthepony.mc.otm.triggers
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import net.minecraft.advancements.critereon.AbstractCriterionTriggerInstance
import net.minecraft.advancements.critereon.DeserializationContext
import net.minecraft.advancements.critereon.EntityPredicate
import net.minecraft.advancements.critereon.SerializationContext
import net.minecraft.advancements.critereon.SimpleCriterionTrigger
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.android.AndroidResearchType
import ru.dbotthepony.mc.otm.core.set
import java.util.function.Predicate
object AndroidResearchTrigger : SimpleCriterionTrigger<AndroidResearchTrigger.Instance>() {
val ID = ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research")
override fun getId(): ResourceLocation {
return ID
}
override fun createInstance(
p_66248_: JsonObject,
p_66249_: EntityPredicate.Composite,
p_66250_: DeserializationContext
): Instance {
return Instance(
p_66248_["research"]?.asString?.let(::ResourceLocation)
)
}
fun trigger(player: ServerPlayer, research: AndroidResearchType) {
trigger(player) {
it.test(research)
}
}
class Instance(val research: ResourceLocation?) : AbstractCriterionTriggerInstance(ID, EntityPredicate.Composite.ANY), Predicate<AndroidResearchType> {
constructor(research: AndroidResearchType) : this(research.id)
override fun test(t: AndroidResearchType): Boolean {
return research == null || t.id == research
}
override fun serializeToJson(p_16979_: SerializationContext): JsonObject {
return super.serializeToJson(p_16979_).also {
if (research != null)
it["research"] = JsonPrimitive(research.toString())
}
}
}
}

View File

@ -0,0 +1,98 @@
package ru.dbotthepony.mc.otm.triggers
import com.google.gson.JsonObject
import net.minecraft.advancements.critereon.AbstractCriterionTriggerInstance
import net.minecraft.advancements.critereon.DeserializationContext
import net.minecraft.advancements.critereon.EntityPredicate
import net.minecraft.advancements.critereon.SimpleCriterionTrigger
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import ru.dbotthepony.mc.otm.OverdriveThatMatters
object BecomeAndroidTrigger : SimpleCriterionTrigger<BecomeAndroidTrigger.Instance>() {
val ID = ResourceLocation(OverdriveThatMatters.MOD_ID, "become_android")
override fun getId(): ResourceLocation {
return ID
}
override fun createInstance(
p_66248_: JsonObject,
p_66249_: EntityPredicate.Composite,
p_66250_: DeserializationContext
): Instance {
return Instance
}
fun trigger(player: ServerPlayer) {
trigger(player) { true }
}
object Instance : AbstractCriterionTriggerInstance(ID, EntityPredicate.Composite.ANY)
}
object BecomeAndroidSleepTrigger : SimpleCriterionTrigger<BecomeAndroidSleepTrigger.Instance>() {
val ID = ResourceLocation(OverdriveThatMatters.MOD_ID, "become_android_sleep")
override fun getId(): ResourceLocation {
return ID
}
override fun createInstance(
p_66248_: JsonObject,
p_66249_: EntityPredicate.Composite,
p_66250_: DeserializationContext
): Instance {
return Instance
}
fun trigger(player: ServerPlayer) {
trigger(player) { true }
}
object Instance : AbstractCriterionTriggerInstance(ID, EntityPredicate.Composite.ANY)
}
object BecomeAndroidDeathTrigger : SimpleCriterionTrigger<BecomeAndroidDeathTrigger.Instance>() {
val ID = ResourceLocation(OverdriveThatMatters.MOD_ID, "become_android_death")
override fun getId(): ResourceLocation {
return ID
}
override fun createInstance(
p_66248_: JsonObject,
p_66249_: EntityPredicate.Composite,
p_66250_: DeserializationContext
): Instance {
return Instance
}
fun trigger(player: ServerPlayer) {
trigger(player) { true }
}
object Instance : AbstractCriterionTriggerInstance(ID, EntityPredicate.Composite.ANY)
}
object BecomeHumaneTrigger : SimpleCriterionTrigger<BecomeHumaneTrigger.Instance>() {
val ID = ResourceLocation(OverdriveThatMatters.MOD_ID, "become_humane")
override fun getId(): ResourceLocation {
return ID
}
override fun createInstance(
p_66248_: JsonObject,
p_66249_: EntityPredicate.Composite,
p_66250_: DeserializationContext
): Instance {
return Instance
}
fun trigger(player: ServerPlayer) {
trigger(player) { true }
}
object Instance : AbstractCriterionTriggerInstance(ID, EntityPredicate.Composite.ANY)
}

View File

@ -0,0 +1,63 @@
package ru.dbotthepony.mc.otm.triggers
import com.google.gson.JsonObject
import com.google.gson.JsonSyntaxException
import net.minecraft.advancements.critereon.AbstractCriterionTriggerInstance
import net.minecraft.advancements.critereon.DamagePredicate
import net.minecraft.advancements.critereon.DamageSourcePredicate
import net.minecraft.advancements.critereon.DeserializationContext
import net.minecraft.advancements.critereon.EntityPredicate
import net.minecraft.advancements.critereon.MinMaxBounds
import net.minecraft.advancements.critereon.SerializationContext
import net.minecraft.advancements.critereon.SimpleCriterionTrigger
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.damagesource.DamageSource
import net.minecraft.world.entity.Entity
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.set
object ShockwaveDamageMobTrigger: SimpleCriterionTrigger<ShockwaveDamageMobTrigger.Instance>() {
val ID = ResourceLocation(OverdriveThatMatters.MOD_ID, "shockwave_damage_mob")
override fun getId(): ResourceLocation {
return ID
}
override fun createInstance(
p_66248_: JsonObject,
p_66249_: EntityPredicate.Composite,
p_66250_: DeserializationContext
): Instance {
return Instance(
EntityPredicate.Composite.fromJson(p_66248_, "entity_predicate", p_66250_),
(p_66248_["damage"] as? JsonObject)?.let(DamagePredicate::fromJson) ?: DamagePredicate.ANY
)
}
fun trigger(player: ServerPlayer, entity: Entity, damageSource: DamageSource, damage: Float) {
val context = EntityPredicate.createContext(player, entity)
trigger(player) {
it.predicate.matches(context) && it.damagePredicate.matches(player, damageSource, damage, damage, false)
}
}
class Instance(
val predicate: EntityPredicate.Composite = EntityPredicate.Composite.ANY,
val damagePredicate: DamagePredicate = DamagePredicate(
MinMaxBounds.Doubles.atLeast(1.0),
MinMaxBounds.Doubles.atLeast(1.0),
EntityPredicate.ANY,
null,
DamageSourcePredicate.ANY
)
) : AbstractCriterionTriggerInstance(ID, EntityPredicate.Composite.ANY) {
override fun serializeToJson(p_16979_: SerializationContext): JsonObject {
return super.serializeToJson(p_16979_).also {
it["entity_predicate"] = predicate.toJson(p_16979_)
it["damage"] = damagePredicate.serializeToJson()
}
}
}
}

View File

@ -0,0 +1,32 @@
package ru.dbotthepony.mc.otm.triggers
import com.google.gson.JsonObject
import net.minecraft.advancements.critereon.AbstractCriterionTriggerInstance
import net.minecraft.advancements.critereon.DeserializationContext
import net.minecraft.advancements.critereon.EntityPredicate.Composite
import net.minecraft.advancements.critereon.SimpleCriterionTrigger
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import ru.dbotthepony.mc.otm.OverdriveThatMatters
object ShockwaveTrigger : SimpleCriterionTrigger<ShockwaveTrigger.Instance>() {
val ID = ResourceLocation(OverdriveThatMatters.MOD_ID, "shockwave")
override fun getId(): ResourceLocation {
return ID
}
override fun createInstance(
p_66248_: JsonObject,
p_66249_: Composite,
p_66250_: DeserializationContext
): Instance {
return Instance
}
fun trigger(player: ServerPlayer) {
trigger(player) { true }
}
object Instance : AbstractCriterionTriggerInstance(ID, Composite.ANY)
}