From 48d58c42dcecfc0ad5e5266575f4874b5806227c Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Tue, 27 Jun 2023 21:59:14 +0700 Subject: [PATCH] Further update hunger handling and patch FoodData --- .../mc/otm/mixin/FoodDataMixin.java | 49 +++++++++++++++++++ .../feature/NanobotsRegenerationFeature.kt | 3 +- .../otm/capability/MatteryPlayerCapability.kt | 45 +++++++++++++---- .../mc/otm/config/AndroidConfig.kt | 13 ++++- .../overdrive_that_matters.mixins.json | 1 + 5 files changed, 100 insertions(+), 11 deletions(-) create mode 100644 src/main/java/ru/dbotthepony/mc/otm/mixin/FoodDataMixin.java diff --git a/src/main/java/ru/dbotthepony/mc/otm/mixin/FoodDataMixin.java b/src/main/java/ru/dbotthepony/mc/otm/mixin/FoodDataMixin.java new file mode 100644 index 000000000..bcccde05d --- /dev/null +++ b/src/main/java/ru/dbotthepony/mc/otm/mixin/FoodDataMixin.java @@ -0,0 +1,49 @@ +package ru.dbotthepony.mc.otm.mixin; + +import net.minecraft.world.Difficulty; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.food.FoodData; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import ru.dbotthepony.mc.otm.capability.MatteryCapability; + +@Mixin(FoodData.class) +public class FoodDataMixin { + @Shadow + private int lastFoodLevel; + @Shadow + private int tickTimer; + @Shadow + private int foodLevel; + @Shadow + private float exhaustionLevel; + + @Inject( + method = "tick(Lnet/minecraft/world/entity/player/Player;)V", + at = @At("HEAD"), + cancellable = true + ) + private void tick(Player player, CallbackInfo info) { + player.getCapability(MatteryCapability.MATTERY_PLAYER).ifPresent(it -> { + if (it.isAndroid()) { + info.cancel(); + + // полностью подменяем логику если андроид + lastFoodLevel = foodLevel; + + if (player.level().getDifficulty() == Difficulty.PEACEFUL) { + exhaustionLevel = 0f; + } else { + tickTimer = 0; + } + + // не обновляем уровень истощения ибо он обнуляется логикой внутри MatteryPlayerCapability + // а так же не регенерируем + // ну и не получаем урон от "голодания" + } + }); + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/NanobotsRegenerationFeature.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/NanobotsRegenerationFeature.kt index 539fd57c4..b5cd2d73c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/NanobotsRegenerationFeature.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/NanobotsRegenerationFeature.kt @@ -2,6 +2,7 @@ package ru.dbotthepony.mc.otm.android.feature import net.minecraft.nbt.CompoundTag import net.minecraft.server.level.ServerPlayer +import net.minecraft.world.level.GameRules import net.minecraftforge.event.entity.living.LivingHurtEvent import ru.dbotthepony.mc.otm.config.AndroidConfig import ru.dbotthepony.mc.otm.android.AndroidFeature @@ -16,7 +17,7 @@ class NanobotsRegenerationFeature(android: MatteryPlayerCapability) : AndroidFea private var healTicks = 0 override fun tickServer() { - if (ply.health > 0f && ply.health < ply.maxHealth) { + if (ply.isHurt && ply.level().gameRules.getBoolean(GameRules.RULE_NATURAL_REGENERATION)) { ticksPassed++ val waitTime = AndroidConfig.NanobotsRegeneration.COOLDOWN.getOrElse(healTicks) { AndroidConfig.NanobotsRegeneration.COOLDOWN.last() } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt index eb75317a7..110ba7170 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt @@ -26,6 +26,7 @@ import net.minecraft.world.item.ItemStack import net.minecraft.world.item.Items import net.minecraft.world.item.ProjectileWeaponItem import net.minecraft.world.item.enchantment.EnchantmentHelper.hasVanishingCurse +import net.minecraft.world.level.GameRules import net.minecraft.world.level.Level import net.minecraft.world.phys.Vec3 import net.minecraftforge.common.ForgeHooks @@ -281,6 +282,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial private var shouldPlaySound = false private val research = IdentityHashMap() private var nextDischargeHurt = 20 + private var nextHealTick = 0 // players tracking us // stored separately because EntityTracker and ChunkMup, etc are buried deep and @@ -340,6 +342,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial savetables.bool(::displayExoPack, "displayExoSuit") savetables.bool(::isExoPackCraftingUpgraded, "isExoSuitCraftingUpgraded") savetables.int(::nextDischargeHurt) + savetables.int(::nextHealTick) savetables.vector(::lastOutsideLiquid) savetables.location(::lastDimension) @@ -883,6 +886,12 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial val stats = ply.foodData val fourTimesTheHunger = AndroidConfig.ANDROID_ENERGY_PER_HUNGER_POINT * 4 + // истощение + if (stats.exhaustionLevel > 0f) { + val extracted = androidEnergy.extractEnergy(AndroidConfig.ANDROID_ENERGY_PER_HUNGER_POINT * (stats.exhaustionLevel / 4f), false) + stats.setExhaustion(stats.exhaustionLevel - (extracted / AndroidConfig.ANDROID_ENERGY_PER_HUNGER_POINT).toFloat() * 4f) + } + // Обычный голод while ( stats.foodLevel < 18 && @@ -894,10 +903,10 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial // "поглощение" излишек голода, как при мирном режиме, так и при поедании обычной еды if (AndroidConfig.REGENERATE_ENERGY) { - while (stats.foodLevel > 18 && androidEnergy.receiveEnergyExact(AndroidConfig.ANDROID_ENERGY_PER_HUNGER_POINT, false)) { + while (stats.foodLevel > 18 && androidEnergy.receiveEnergyExact(AndroidConfig.ANDROID_ENERGY_PER_HUNGER_POINT / 2, false)) { stats.foodLevel-- } - } else if (ply.level().server?.worldData?.difficulty != Difficulty.PEACEFUL) { + } else if (ply.level().difficulty != Difficulty.PEACEFUL) { stats.foodLevel = stats.foodLevel.coerceAtMost(18) } @@ -909,14 +918,23 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial stats.setSaturation(stats.saturationLevel + (extracted / AndroidConfig.ANDROID_ENERGY_PER_HUNGER_POINT).toFloat()) } - // истощение - if (stats.exhaustionLevel > 0f && androidEnergy.batteryLevel >= fourTimesTheHunger) { - val extracted = androidEnergy.extractEnergy(AndroidConfig.ANDROID_ENERGY_PER_HUNGER_POINT * (stats.exhaustionLevel / 4f), false) - stats.setExhaustion(stats.exhaustionLevel - (extracted / AndroidConfig.ANDROID_ENERGY_PER_HUNGER_POINT).toFloat() * 4f) - } + if (androidEnergy.batteryLevel <= Decimal.TEN && !ply.isCreative && ply.level().difficulty != Difficulty.PEACEFUL) { + if (stats.saturationLevel > 1f) { + if (androidEnergy.receiveEnergyExact(AndroidConfig.ANDROID_ENERGY_PER_HUNGER_POINT, false)) { + stats.setSaturation(stats.saturationLevel - 1f) + } + } else if (stats.saturationLevel > 0f) { + val received = androidEnergy.receiveEnergy(AndroidConfig.ANDROID_ENERGY_PER_HUNGER_POINT * stats.saturationLevel, false) + stats.setSaturation(stats.saturationLevel - (received / AndroidConfig.ANDROID_ENERGY_PER_HUNGER_POINT).toFloat()) + } else if (stats.foodLevel > 0) { + // так как голод не тикает для андроидов, "умереть с голоду" мы не можем + // но со стороны будет выглядеть как будто мы умираем с голода + if (androidEnergy.receiveEnergyExact(AndroidConfig.ANDROID_ENERGY_PER_HUNGER_POINT, false)) { + stats.foodLevel-- + } + } - if (androidEnergy.batteryLevel <= Decimal.TEN && !ply.isCreative && ply.level().server?.worldData?.difficulty != Difficulty.PEACEFUL) { - if (stats.foodLevel <= 6 || !androidEnergy.receiveEnergyExact(AndroidConfig.ANDROID_ENERGY_PER_HUNGER_POINT, false).also { if (it) stats.foodLevel-- }) { + if (androidEnergy.batteryLevel <= Decimal.TEN) { if (--nextDischargeHurt <= 0 && ply.hurt(DamageSource(ply.level().registryAccess().damageType(MDamageTypes.ANDROID_DISCHARGE)), 1f)) { nextDischargeHurt = 20 } @@ -929,6 +947,15 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial } } else { nextDischargeHurt = 20 + + if (ply.isHurt && ply.level().gameRules.getBoolean(GameRules.RULE_NATURAL_REGENERATION)) { + if (--nextHealTick <= 0) { + nextHealTick = if (ply.level().difficulty == Difficulty.PEACEFUL) 10 else AndroidConfig.TIME_BETWEEN_NATURAL_REGENERATION + ply.heal(1f) + } + } else { + nextHealTick = if (ply.level().difficulty == Difficulty.PEACEFUL) 10 else AndroidConfig.TIME_BETWEEN_NATURAL_REGENERATION + } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/config/AndroidConfig.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/config/AndroidConfig.kt index 3a6981e53..55cfb0e5c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/config/AndroidConfig.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/config/AndroidConfig.kt @@ -11,6 +11,17 @@ object AndroidConfig : AbstractConfig("androids") { .comment("If this is disabled, any (technically) excess hunger will be nullified, unless playing on peaceful difficulty.") .define("REGENERATE_ENERGY", true) + val TIME_BETWEEN_NATURAL_REGENERATION: Int by builder + .comment("Time in ticks between natural health regeneration ticks") + .comment("Default value is meant to be one of downsides of being an android,") + .comment("so please, don't blindly buff it, players have ability to research into Nanobots Regeneration,") + .comment("which provide superior regeneration on average than human players.") + .comment("---") + .comment("This regeneration obeys natural regeneration gamerule") + .comment("---") + .comment("Reason for this config option is that FoodData does not tick") + .comment("for android players, since 'hunger' (for compatibility) is managed by mod in such case") + .defineInRange("TIME_BETWEEN_NATURAL_REGENERATION", 120, 0, Int.MAX_VALUE) object NanobotsRegeneration { init { @@ -29,7 +40,7 @@ object AndroidConfig : AbstractConfig("androids") { .comment(" ticksSinceTakingDamage = 0") .comment(" this.ply.heal(...)") .comment("}") - .defineList("COOLDOWN", { mutableListOf(80, 60, 40, 20) }) { it is Int } + .defineList("COOLDOWN", { mutableListOf(60, 50, 40, 30) }) { it is Int } val ENERGY_PER_HITPOINT by builder .comment("Energy required to regenerate 1 health point (half a heart)") diff --git a/src/main/resources/overdrive_that_matters.mixins.json b/src/main/resources/overdrive_that_matters.mixins.json index 626eb012c..3d4d5f070 100644 --- a/src/main/resources/overdrive_that_matters.mixins.json +++ b/src/main/resources/overdrive_that_matters.mixins.json @@ -7,6 +7,7 @@ "refmap": "overdrive_that_matters.refmap.json", "mixins": [ "EnchantmentHelperMixin", + "FoodDataMixin", "MixinPatchProjectileFinder", "MixinLivingEntity", "MixinAnvilBlock",