Move all logic regarding food / regeneration to MatteryFoodData
This commit is contained in:
parent
a34b485e68
commit
c4d5ffefa5
@ -5,15 +5,22 @@ import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||
import ru.dbotthepony.mc.otm.core.math.defineDecimal
|
||||
|
||||
object PlayerConfig : AbstractConfig("player") {
|
||||
init {
|
||||
builder.push("Android")
|
||||
}
|
||||
|
||||
val REGENERATE_ENERGY: Boolean by builder
|
||||
.comment("If (technically) hunger is above threshold, it turns into energy")
|
||||
.comment("This setting controls whenever to regenerate small amount of energy while eating as Android")
|
||||
.comment("And also whenever to regenerate energy while in peaceful")
|
||||
.comment("If this is disabled, any (technically) excess hunger will be nullified, unless playing on peaceful difficulty.")
|
||||
.define("REGENERATE_ENERGY", true)
|
||||
|
||||
val REGENERATE_ENERGY_IN_PEACEFUL: Boolean by builder
|
||||
.comment("Regenerate energy while in peaceful")
|
||||
.comment("This is disabled by default because this is easily exploitable")
|
||||
.define("REGENERATE_ENERGY_IN_PEACEFUL", false)
|
||||
|
||||
val TIME_BETWEEN_NATURAL_REGENERATION: Int by builder
|
||||
.comment("Time in ticks between natural health regeneration ticks")
|
||||
.comment("Time in ticks between natural health regeneration ticks for Android")
|
||||
.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.")
|
||||
@ -24,6 +31,10 @@ object PlayerConfig : AbstractConfig("player") {
|
||||
.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)
|
||||
|
||||
init {
|
||||
builder.pop()
|
||||
}
|
||||
|
||||
object NanobotsRegeneration {
|
||||
init {
|
||||
builder.push("NanobotsRegeneration")
|
||||
|
@ -2,6 +2,9 @@ package ru.dbotthepony.mc.otm.player
|
||||
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.world.Difficulty
|
||||
import net.minecraft.world.damagesource.DamageSource
|
||||
import net.minecraft.world.effect.MobEffectInstance
|
||||
import net.minecraft.world.effect.MobEffects
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.food.FoodConstants
|
||||
import net.minecraft.world.food.FoodData
|
||||
@ -9,15 +12,24 @@ import net.minecraft.world.food.FoodProperties
|
||||
import net.minecraft.world.level.GameRules
|
||||
import ru.dbotthepony.mc.otm.config.IFoodRegenerationValues
|
||||
import ru.dbotthepony.mc.otm.config.PlayerConfig
|
||||
import ru.dbotthepony.mc.otm.core.damageType
|
||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||
import ru.dbotthepony.mc.otm.core.math.getDecimal
|
||||
import ru.dbotthepony.mc.otm.core.math.set
|
||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||
import ru.dbotthepony.mc.otm.registry.MDamageTypes
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class MatteryFoodData(private var player: Player) : FoodData() {
|
||||
private fun add(foodLevel: Int, saturation: Float) {
|
||||
this.foodLevel = min(this.foodLevel + foodLevel, PlayerConfig.Food.HARD_FOOD_LIMIT)
|
||||
this.saturationLevel = min(this.saturationLevel + saturation, this.foodLevel + PlayerConfig.Food.OVERSATURATION_LIMIT.toFloat()).coerceAtLeast(0f)
|
||||
if (player.matteryPlayer.isAndroid && PlayerConfig.REGENERATE_ENERGY) {
|
||||
energyToDrain -= PlayerConfig.ANDROID_ENERGY_PER_HUNGER_POINT * foodLevel + PlayerConfig.ANDROID_ENERGY_PER_HUNGER_POINT * saturation
|
||||
} else if (!player.matteryPlayer.isAndroid) {
|
||||
this.foodLevel = min(this.foodLevel + foodLevel, PlayerConfig.Food.HARD_FOOD_LIMIT)
|
||||
this.saturationLevel = min(this.saturationLevel + saturation, this.foodLevel + PlayerConfig.Food.OVERSATURATION_LIMIT.toFloat()).coerceAtLeast(0f)
|
||||
}
|
||||
}
|
||||
|
||||
override fun eat(foodLevelModifier: Int, saturationLevelModifier: Float) {
|
||||
@ -29,11 +41,12 @@ class MatteryFoodData(private var player: Player) : FoodData() {
|
||||
}
|
||||
|
||||
override fun needsFood(): Boolean {
|
||||
return foodLevel < PlayerConfig.Food.SOFT_FOOD_LIMIT
|
||||
return player.matteryPlayer.isAndroid || foodLevel < PlayerConfig.Food.SOFT_FOOD_LIMIT
|
||||
}
|
||||
|
||||
override fun addExhaustion(exhaustion: Float) {
|
||||
this.exhaustionLevel = min(this.exhaustionLevel + exhaustion, PlayerConfig.Food.EXHAUSTION_LIMIT.toFloat())
|
||||
// store exhaustion as usual, and handle its reduction in tick for both humans and androids
|
||||
this.exhaustionLevel = min(this.exhaustionLevel + exhaustion, PlayerConfig.Food.EXHAUSTION_LIMIT.toFloat()).coerceAtLeast(0f)
|
||||
}
|
||||
|
||||
override fun readAdditionalSaveData(compoundTag: CompoundTag) {
|
||||
@ -41,25 +54,35 @@ class MatteryFoodData(private var player: Player) : FoodData() {
|
||||
|
||||
if ("lastFoodLevel" in compoundTag)
|
||||
lastFoodLevel = compoundTag.getInt("lastFoodLevel")
|
||||
|
||||
if ("foodAndroidEnergyToDrain" in compoundTag)
|
||||
energyToDrain = compoundTag.getDecimal("foodAndroidEnergyToDrain")
|
||||
}
|
||||
|
||||
override fun addAdditionalSaveData(compoundTag: CompoundTag) {
|
||||
super.addAdditionalSaveData(compoundTag)
|
||||
compoundTag["lastFoodLevel"] = lastFoodLevel
|
||||
compoundTag["foodAndroidEnergyToDrain"] = energyToDrain
|
||||
}
|
||||
|
||||
private var energyToDrain = Decimal.ZERO
|
||||
|
||||
private fun tickExhaustion() {
|
||||
if (exhaustionLevel >= EXHAUSTION_PER_HUNGER_POINT) {
|
||||
var points = (exhaustionLevel / EXHAUSTION_PER_HUNGER_POINT).toInt()
|
||||
exhaustionLevel %= EXHAUSTION_PER_HUNGER_POINT
|
||||
|
||||
if (saturationLevel > 0f) {
|
||||
val satisfied = min(saturationLevel.roundToInt(), points)
|
||||
points -= satisfied
|
||||
saturationLevel -= satisfied
|
||||
}
|
||||
if (player.matteryPlayer.isAndroid) {
|
||||
energyToDrain += PlayerConfig.ANDROID_ENERGY_PER_HUNGER_POINT * points
|
||||
} else {
|
||||
if (saturationLevel > 0f) {
|
||||
val satisfied = min(saturationLevel.roundToInt(), points)
|
||||
points -= satisfied
|
||||
saturationLevel -= satisfied
|
||||
}
|
||||
|
||||
foodLevel = max(0, foodLevel - points)
|
||||
foodLevel = max(0, foodLevel - points)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,37 +105,96 @@ class MatteryFoodData(private var player: Player) : FoodData() {
|
||||
override fun tick(player: Player) {
|
||||
this.player = player
|
||||
|
||||
if (player.matteryPlayer.isAndroid)
|
||||
return
|
||||
|
||||
lastFoodLevel = foodLevel
|
||||
tickExhaustion()
|
||||
|
||||
if (
|
||||
player.level().gameRules.getBoolean(GameRules.RULE_NATURAL_REGENERATION) &&
|
||||
(tickRegeneration(PlayerConfig.Food.FAST_REGEN) || tickRegeneration(PlayerConfig.Food.SLOW_REGEN))
|
||||
) {
|
||||
// do nothing
|
||||
} else if (PlayerConfig.Food.ENABLE_STARVATION && foodLevel <= 0) {
|
||||
if (++tickTimer >= PlayerConfig.Food.STARVATION_TICKS) {
|
||||
tickTimer = 0
|
||||
if (player.matteryPlayer.isAndroid) {
|
||||
if (energyToDrain > Decimal.ZERO)
|
||||
energyToDrain -= player.matteryPlayer.androidEnergy.extractEnergy(energyToDrain, false)
|
||||
else if (energyToDrain < Decimal.ZERO)
|
||||
energyToDrain += player.matteryPlayer.androidEnergy.receiveEnergy(-energyToDrain, false)
|
||||
|
||||
val threshold = when (player.level().difficulty) {
|
||||
Difficulty.PEACEFUL -> Float.POSITIVE_INFINITY
|
||||
Difficulty.EASY -> PlayerConfig.Food.STARVATION_HEALTH_LIMIT_EASY.toFloat()
|
||||
Difficulty.NORMAL -> PlayerConfig.Food.STARVATION_HEALTH_LIMIT_NORMAL.toFloat()
|
||||
Difficulty.HARD -> PlayerConfig.Food.STARVATION_HEALTH_LIMIT_HARD.toFloat()
|
||||
if (player.level().difficulty == Difficulty.PEACEFUL && PlayerConfig.REGENERATE_ENERGY_IN_PEACEFUL)
|
||||
player.matteryPlayer.androidEnergy.receiveEnergy(PlayerConfig.ANDROID_ENERGY_PER_HUNGER_POINT, false)
|
||||
|
||||
if (!player.matteryPlayer.androidHasEnergy) {
|
||||
if (++tickTimer >= 20 && player.hurt(DamageSource(player.level().registryAccess().damageType(MDamageTypes.ANDROID_DISCHARGE)), 1f)) {
|
||||
tickTimer = 0
|
||||
}
|
||||
|
||||
if (player.health > threshold) {
|
||||
player.hurt(player.damageSources().starve(), 1.0f)
|
||||
val effect = player.activeEffectsMap[MobEffects.MOVEMENT_SLOWDOWN]
|
||||
|
||||
if (effect == null || effect.duration < 40 || effect.amplifier < 2) {
|
||||
player.addEffect(MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, effect?.duration?.coerceAtLeast(60) ?: 60, effect?.amplifier?.coerceAtLeast(2) ?: 2, false, false))
|
||||
}
|
||||
} else {
|
||||
if (player.isHurt && player.level().gameRules.getBoolean(GameRules.RULE_NATURAL_REGENERATION)) {
|
||||
if (++tickTimer >= PlayerConfig.TIME_BETWEEN_NATURAL_REGENERATION) {
|
||||
player.heal(1f)
|
||||
addExhaustion(6f)
|
||||
tickTimer = 0
|
||||
}
|
||||
} else {
|
||||
tickTimer = 0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tickTimer = 0
|
||||
lastFoodLevel = foodLevel
|
||||
energyToDrain = Decimal.ZERO
|
||||
|
||||
if (
|
||||
player.level().gameRules.getBoolean(GameRules.RULE_NATURAL_REGENERATION) &&
|
||||
(tickRegeneration(PlayerConfig.Food.FAST_REGEN) || tickRegeneration(PlayerConfig.Food.SLOW_REGEN))
|
||||
) {
|
||||
// do nothing
|
||||
} else if (PlayerConfig.Food.ENABLE_STARVATION && foodLevel <= 0) {
|
||||
if (++tickTimer >= PlayerConfig.Food.STARVATION_TICKS) {
|
||||
tickTimer = 0
|
||||
|
||||
val threshold = when (player.level().difficulty) {
|
||||
Difficulty.PEACEFUL -> Float.POSITIVE_INFINITY
|
||||
Difficulty.EASY -> PlayerConfig.Food.STARVATION_HEALTH_LIMIT_EASY.toFloat()
|
||||
Difficulty.NORMAL -> PlayerConfig.Food.STARVATION_HEALTH_LIMIT_NORMAL.toFloat()
|
||||
Difficulty.HARD -> PlayerConfig.Food.STARVATION_HEALTH_LIMIT_HARD.toFloat()
|
||||
}
|
||||
|
||||
if (player.health > threshold) {
|
||||
player.hurt(player.damageSources().starve(), 1.0f)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tickTimer = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getFoodLevel(): Int {
|
||||
if (player.matteryPlayer.isAndroid)
|
||||
return if (player.matteryPlayer.androidHasEnergy) PlayerConfig.Food.SOFT_FOOD_LIMIT else 0
|
||||
|
||||
return super.getFoodLevel()
|
||||
}
|
||||
|
||||
override fun getLastFoodLevel(): Int {
|
||||
if (player.matteryPlayer.isAndroid)
|
||||
return getFoodLevel()
|
||||
|
||||
return super.getLastFoodLevel()
|
||||
}
|
||||
|
||||
override fun getExhaustionLevel(): Float {
|
||||
if (player.matteryPlayer.isAndroid && player.matteryPlayer.androidHasEnergy)
|
||||
return 0f
|
||||
|
||||
return super.getExhaustionLevel()
|
||||
}
|
||||
|
||||
override fun getSaturationLevel(): Float {
|
||||
if (player.matteryPlayer.isAndroid)
|
||||
return if (player.matteryPlayer.androidHasEnergy) player.matteryPlayer.androidEnergy.batteryLevel.percentage(player.matteryPlayer.androidEnergy.maxBatteryLevel) * PlayerConfig.Food.SOFT_FOOD_LIMIT else 0f
|
||||
|
||||
return super.getSaturationLevel()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val EXHAUSTION_PER_HUNGER_POINT = 4f
|
||||
}
|
||||
|
@ -436,8 +436,6 @@ class MatteryPlayer(val ply: Player) {
|
||||
|
||||
private var shouldPlaySound = false
|
||||
private val research = IdentityHashMap<AndroidResearchType, AndroidResearch>()
|
||||
private var nextDischargeHurt = 20
|
||||
private var nextHealTick = 0
|
||||
|
||||
/**
|
||||
* This returns if player is an Android or will become one on death/sleep/etc
|
||||
@ -563,6 +561,9 @@ class MatteryPlayer(val ply: Player) {
|
||||
*/
|
||||
val androidEnergy = BatteryBackedEnergyStorage(ply, syncher, PlayerConfig.ANDROID_MAX_ENERGY, PlayerConfig.ANDROID_MAX_ENERGY, true)
|
||||
|
||||
val androidHasEnergy: Boolean
|
||||
get() = androidEnergy.batteryLevel > Decimal.TEN
|
||||
|
||||
/**
|
||||
* [IMatteryEnergyStorage] instance, representing Exopack battery charge
|
||||
*/
|
||||
@ -584,8 +585,6 @@ class MatteryPlayer(val ply: Player) {
|
||||
savetables.bool(::isExopackCraftingUpgraded, "isExoSuitCraftingUpgraded")
|
||||
savetables.bool(::isExopackEnderAccessInstalled, "isExopackEnderAccessUpgraded")
|
||||
savetables.bool(::acceptExopackChargeFromWirelessCharger)
|
||||
savetables.int(::nextDischargeHurt)
|
||||
savetables.int(::nextHealTick)
|
||||
|
||||
savetables.vector(::lastLiquidPosition)
|
||||
savetables.codec(::lastDimension, ResourceLocation.CODEC)
|
||||
@ -1213,81 +1212,6 @@ class MatteryPlayer(val ply: Player) {
|
||||
|
||||
lastLiquidPosition = ply.position
|
||||
}
|
||||
|
||||
val stats = ply.foodData
|
||||
val fourTimesTheHunger = PlayerConfig.ANDROID_ENERGY_PER_HUNGER_POINT * 4
|
||||
|
||||
// истощение
|
||||
if (stats.exhaustionLevel > 0f) {
|
||||
val extracted = androidEnergy.extractEnergy(PlayerConfig.ANDROID_ENERGY_PER_HUNGER_POINT * (stats.exhaustionLevel / 4f), false)
|
||||
stats.setExhaustion(stats.exhaustionLevel - (extracted / PlayerConfig.ANDROID_ENERGY_PER_HUNGER_POINT).toFloat() * 4f)
|
||||
}
|
||||
|
||||
// Обычный голод
|
||||
while (
|
||||
stats.foodLevel < 18 &&
|
||||
androidEnergy.batteryLevel >= fourTimesTheHunger &&
|
||||
androidEnergy.extractEnergyExact(PlayerConfig.ANDROID_ENERGY_PER_HUNGER_POINT, false)
|
||||
) {
|
||||
stats.foodLevel++
|
||||
}
|
||||
|
||||
// "поглощение" излишек голода, как при мирном режиме, так и при поедании обычной еды
|
||||
if (PlayerConfig.REGENERATE_ENERGY) {
|
||||
while (stats.foodLevel > 18 && androidEnergy.receiveEnergyExact(PlayerConfig.ANDROID_ENERGY_PER_HUNGER_POINT / 2, false)) {
|
||||
stats.foodLevel--
|
||||
}
|
||||
} else if (ply.level().difficulty != Difficulty.PEACEFUL) {
|
||||
stats.foodLevel = stats.foodLevel.coerceAtMost(18)
|
||||
}
|
||||
|
||||
val foodLevel = stats.foodLevel.toFloat()
|
||||
|
||||
// насыщение
|
||||
if (stats.saturationLevel < foodLevel && androidEnergy.batteryLevel >= fourTimesTheHunger) {
|
||||
val extracted = androidEnergy.extractEnergy(PlayerConfig.ANDROID_ENERGY_PER_HUNGER_POINT * (foodLevel - stats.saturationLevel), false)
|
||||
stats.setSaturation(stats.saturationLevel + (extracted / PlayerConfig.ANDROID_ENERGY_PER_HUNGER_POINT).toFloat())
|
||||
}
|
||||
|
||||
if (androidEnergy.batteryLevel <= Decimal.TEN && !ply.isCreative && ply.level().difficulty != Difficulty.PEACEFUL) {
|
||||
if (stats.saturationLevel > 1f) {
|
||||
if (androidEnergy.receiveEnergyExact(PlayerConfig.ANDROID_ENERGY_PER_HUNGER_POINT, false)) {
|
||||
stats.setSaturation(stats.saturationLevel - 1f)
|
||||
}
|
||||
} else if (stats.saturationLevel > 0f) {
|
||||
val received = androidEnergy.receiveEnergy(PlayerConfig.ANDROID_ENERGY_PER_HUNGER_POINT * stats.saturationLevel, false)
|
||||
stats.setSaturation(stats.saturationLevel - (received / PlayerConfig.ANDROID_ENERGY_PER_HUNGER_POINT).toFloat())
|
||||
} else if (stats.foodLevel > 0) {
|
||||
// так как голод не тикает для андроидов, "умереть с голоду" мы не можем
|
||||
// но со стороны будет выглядеть как будто мы умираем с голода
|
||||
if (androidEnergy.receiveEnergyExact(PlayerConfig.ANDROID_ENERGY_PER_HUNGER_POINT, false)) {
|
||||
stats.foodLevel--
|
||||
}
|
||||
}
|
||||
|
||||
if (androidEnergy.batteryLevel <= Decimal.TEN) {
|
||||
if (--nextDischargeHurt <= 0 && ply.hurt(DamageSource(ply.level().registryAccess().damageType(MDamageTypes.ANDROID_DISCHARGE)), 1f)) {
|
||||
nextDischargeHurt = 20
|
||||
}
|
||||
|
||||
val effect = ply.activeEffectsMap[MobEffects.MOVEMENT_SLOWDOWN]
|
||||
|
||||
if (effect == null || effect.duration < 40 || effect.amplifier < 2) {
|
||||
ply.addEffect(MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, effect?.duration?.coerceAtLeast(60) ?: 60, effect?.amplifier?.coerceAtLeast(2) ?: 2, false, false))
|
||||
}
|
||||
}
|
||||
} 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 PlayerConfig.TIME_BETWEEN_NATURAL_REGENERATION
|
||||
ply.heal(1f)
|
||||
}
|
||||
} else {
|
||||
nextHealTick = if (ply.level().difficulty == Difficulty.PEACEFUL) 10 else PlayerConfig.TIME_BETWEEN_NATURAL_REGENERATION
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (feature in featureMap.values) {
|
||||
|
Loading…
Reference in New Issue
Block a user