Merge android capability into rigid single class

since it is relevant only for player anyway
This commit is contained in:
DBotThePony 2022-08-20 22:43:48 +07:00
parent 589d3a5499
commit c24176374c
Signed by: DBot
GPG Key ID: DCC23B5715498507
24 changed files with 811 additions and 930 deletions

View File

@ -13,12 +13,9 @@ 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.AndroidFeatureType;
import ru.dbotthepony.mc.otm.android.AndroidResearchType;
import ru.dbotthepony.mc.otm.block.entity.blackhole.ExplosionQueue;
import ru.dbotthepony.mc.otm.capability.AndroidCapability;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.capability.android.AndroidCapability;
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer;
import ru.dbotthepony.mc.otm.capability.drive.DrivePool;
import ru.dbotthepony.mc.otm.client.AndroidGui;
import ru.dbotthepony.mc.otm.client.EventHandlerKt;
@ -86,7 +83,6 @@ public final class OverdriveThatMatters {
MinecraftForge.EVENT_BUS.register(this);
MinecraftForge.EVENT_BUS.register(TickerKt.class);
MinecraftForge.EVENT_BUS.register(AndroidCapabilityPlayer.Companion);
MinecraftForge.EVENT_BUS.register(AndroidCapability.Companion);
MinecraftForge.EVENT_BUS.register(MatterRegistryKt.class);
MinecraftForge.EVENT_BUS.register(MatterDataKt.class);

View File

@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.capability;
import mekanism.api.energy.IStrictEnergyHandler;
import net.minecraftforge.common.capabilities.*;
import ru.dbotthepony.mc.otm.capability.android.IAndroidCapability;
import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive;
import ru.dbotthepony.mc.otm.capability.matter.IMatterHandler;
import ru.dbotthepony.mc.otm.capability.matter.IMatterTaskProvider;
@ -13,7 +12,7 @@ import ru.dbotthepony.mc.otm.storage.IStorageStack;
public class MatteryCapability {
public static final Capability<IMatteryEnergyStorage> ENERGY = CapabilityManager.get(new CapabilityToken<>() {});
public static final Capability<IAndroidCapability> ANDROID = CapabilityManager.get(new CapabilityToken<>() {});
public static final Capability<AndroidCapability> ANDROID = CapabilityManager.get(new CapabilityToken<>() {});
public static final Capability<IMatterHandler> MATTER = CapabilityManager.get(new CapabilityToken<>() {});
public static final Capability<IMatterGraphNode> MATTER_NODE = CapabilityManager.get(new CapabilityToken<>() {});
public static final Capability<IPatternStorage> PATTERN = CapabilityManager.get(new CapabilityToken<>() {});
@ -26,7 +25,7 @@ public class MatteryCapability {
@SuppressWarnings("unused")
public static void register(final RegisterCapabilitiesEvent event) {
event.register(IMatteryEnergyStorage.class);
event.register(IAndroidCapability.class);
event.register(AndroidCapability.class);
event.register(IMatterHandler.class);
event.register(IPatternStorage.class);
event.register(IMatterTaskProvider.class);

View File

@ -3,7 +3,7 @@ package ru.dbotthepony.mc.otm.android
import net.minecraft.nbt.CompoundTag
import net.minecraftforge.common.util.INBTSerializable
import net.minecraftforge.event.entity.living.LivingHurtEvent
import ru.dbotthepony.mc.otm.capability.android.AndroidCapability
import ru.dbotthepony.mc.otm.capability.AndroidCapability
import ru.dbotthepony.mc.otm.readNbt
import ru.dbotthepony.mc.otm.set
import ru.dbotthepony.mc.otm.writeNbt
@ -11,7 +11,7 @@ import java.io.InputStream
import java.io.OutputStream
abstract class AndroidFeature(val type: AndroidFeatureType<*>, val android: AndroidCapability) : INBTSerializable<CompoundTag> {
val entity get() = android.entity
val entity get() = android.ply
/**
* Whenever there are changes to network

View File

@ -4,7 +4,7 @@ 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 ru.dbotthepony.mc.otm.capability.android.AndroidCapability
import ru.dbotthepony.mc.otm.capability.AndroidCapability
import ru.dbotthepony.mc.otm.getKeyNullable
import ru.dbotthepony.mc.otm.registry.MRegistry

View File

@ -5,7 +5,7 @@ import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.world.item.ItemStack
import net.minecraftforge.common.util.INBTSerializable
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer
import ru.dbotthepony.mc.otm.capability.AndroidCapability
import ru.dbotthepony.mc.otm.client.render.SkinElement
import ru.dbotthepony.mc.otm.readNbt
import ru.dbotthepony.mc.otm.set
@ -13,7 +13,7 @@ import ru.dbotthepony.mc.otm.writeNbt
import java.io.InputStream
import java.io.OutputStream
abstract class AndroidResearch(val type: AndroidResearchType<*>, val capability: AndroidCapabilityPlayer) : INBTSerializable<CompoundTag> {
abstract class AndroidResearch(val type: AndroidResearchType<*>, val capability: AndroidCapability) : INBTSerializable<CompoundTag> {
var isResearched = false
protected set

View File

@ -7,12 +7,12 @@ import net.minecraft.network.chat.ComponentContents
import net.minecraft.network.chat.MutableComponent
import net.minecraft.network.chat.contents.TranslatableContents
import ru.dbotthepony.mc.otm.TranslatableComponent
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer
import ru.dbotthepony.mc.otm.capability.AndroidCapability
import ru.dbotthepony.mc.otm.getKeyNullable
import ru.dbotthepony.mc.otm.registry.MRegistry
fun interface AndroidResearchFactory<R : AndroidResearch> {
fun factory(type: AndroidResearchType<*>, capability: AndroidCapabilityPlayer): R
fun factory(type: AndroidResearchType<*>, capability: AndroidCapability): R
}
private fun findPrerequisites(
@ -173,7 +173,7 @@ open class AndroidResearchType<R : AndroidResearch>(
ImmutableList.copyOf(findAllChildren(flatUnlocks))
}
fun factory(capability: AndroidCapabilityPlayer) = factory.factory(this, capability)
fun factory(capability: AndroidCapability) = factory.factory(this, capability)
val registryName by lazy {
MRegistry.ANDROID_RESEARCH.getKeyNullable(this)

View File

@ -3,7 +3,7 @@ package ru.dbotthepony.mc.otm.android.feature
import net.minecraft.world.entity.ai.attributes.AttributeModifier
import net.minecraft.world.entity.ai.attributes.Attributes
import ru.dbotthepony.mc.otm.android.AndroidFeature
import ru.dbotthepony.mc.otm.capability.android.AndroidCapability
import ru.dbotthepony.mc.otm.capability.AndroidCapability
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import java.util.*

View File

@ -2,9 +2,8 @@ package ru.dbotthepony.mc.otm.android.feature
import net.minecraft.world.entity.ai.attributes.AttributeModifier
import net.minecraftforge.common.ForgeMod
import net.minecraftforge.event.entity.living.LivingHurtEvent
import ru.dbotthepony.mc.otm.android.AndroidFeature
import ru.dbotthepony.mc.otm.capability.android.AndroidCapability
import ru.dbotthepony.mc.otm.capability.AndroidCapability
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import java.util.*

View File

@ -3,7 +3,7 @@ package ru.dbotthepony.mc.otm.android.feature
import net.minecraft.world.entity.ai.attributes.AttributeModifier
import net.minecraft.world.entity.ai.attributes.Attributes
import ru.dbotthepony.mc.otm.android.AndroidFeature
import ru.dbotthepony.mc.otm.capability.android.AndroidCapability
import ru.dbotthepony.mc.otm.capability.AndroidCapability
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import java.util.*

View File

@ -4,7 +4,7 @@ import net.minecraft.nbt.CompoundTag
import net.minecraft.server.level.ServerPlayer
import net.minecraftforge.event.entity.living.LivingHurtEvent
import ru.dbotthepony.mc.otm.android.AndroidFeature
import ru.dbotthepony.mc.otm.capability.android.AndroidCapability
import ru.dbotthepony.mc.otm.capability.AndroidCapability
import ru.dbotthepony.mc.otm.capability.extractEnergyInnerExact
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.registry.AndroidFeatures

View File

@ -4,7 +4,7 @@ import net.minecraft.nbt.CompoundTag
import net.minecraft.server.level.ServerPlayer
import net.minecraftforge.event.entity.living.LivingHurtEvent
import ru.dbotthepony.mc.otm.android.AndroidFeature
import ru.dbotthepony.mc.otm.capability.android.AndroidCapability
import ru.dbotthepony.mc.otm.capability.AndroidCapability
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import ru.dbotthepony.mc.otm.registry.StatNames

View File

@ -3,7 +3,7 @@ package ru.dbotthepony.mc.otm.android.feature
import net.minecraft.world.entity.ai.attributes.AttributeModifier
import net.minecraftforge.common.ForgeMod
import ru.dbotthepony.mc.otm.android.AndroidFeature
import ru.dbotthepony.mc.otm.capability.android.AndroidCapability
import ru.dbotthepony.mc.otm.capability.AndroidCapability
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import java.util.*

View File

@ -0,0 +1,737 @@
package ru.dbotthepony.mc.otm.capability
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
import it.unimi.dsi.fastutil.objects.ObjectArraySet
import net.minecraft.ChatFormatting
import net.minecraft.core.Direction
import net.minecraftforge.common.capabilities.ICapabilityProvider
import net.minecraftforge.common.util.INBTSerializable
import net.minecraft.nbt.CompoundTag
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.android.AndroidFeatureType
import ru.dbotthepony.mc.otm.android.AndroidFeature
import java.lang.Runnable
import net.minecraft.server.level.ServerPlayer
import net.minecraftforge.event.entity.living.LivingHurtEvent
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.effect.MobEffect
import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent
import net.minecraft.world.effect.MobEffects
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.player.Player
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.event.AttachCapabilitiesEvent
import net.minecraftforge.eventbus.api.SubscribeEvent
import net.minecraftforge.event.entity.living.LivingEvent.LivingTickEvent
import net.minecraftforge.event.entity.player.PlayerEvent
import net.minecraftforge.eventbus.api.EventPriority
import ru.dbotthepony.mc.otm.*
import ru.dbotthepony.mc.otm.android.AndroidResearch
import ru.dbotthepony.mc.otm.android.AndroidResearchType
import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.extractEnergy
import ru.dbotthepony.mc.otm.capability.receiveEnergy
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.network.*
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import ru.dbotthepony.mc.otm.registry.MNames
import ru.dbotthepony.mc.otm.registry.MRegistry
import ru.dbotthepony.mc.otm.registry.StatNames
import java.util.*
@Suppress("unused")
class AndroidCapability(val ply: Player) : ICapabilityProvider, IMatteryEnergyStorage, INBTSerializable<CompoundTag> {
private var battery = ImpreciseFraction.ZERO
private var maxBattery = ImpreciseFraction(60000)
var batteryItemStack: ItemStack = ItemStack.EMPTY
private val features = Object2ObjectArrayMap<AndroidFeatureType<*>, AndroidFeature>()
private val networkQueue = ArrayList<Any>()
private val queuedTicks = ArrayList<Runnable>()
private var tickedOnce = false
private var willBecomeAndroidNetwork = false
private var shouldPlaySound = false
private val research = ArrayList<AndroidResearch>()
private var remoteBattery = ImpreciseFraction(-1)
private var remoteMaxBattery = ImpreciseFraction(-1)
private var remoteBatteryStack = ItemStack.EMPTY
private var invalidateNetworkIn = 10
private var remoteIsAndroid = false
var willBecomeAndroid = false
var isAndroid = false
var sleepTicks = 0
fun invalidateNetworkState() {
invalidateNetworkIn = 10
}
val isEverAndroid: Boolean get() = isAndroid || willBecomeAndroid
fun becomeAndroidSoft() {
if (isAndroid || willBecomeAndroid) return
willBecomeAndroid = true
(ply as? ServerPlayer)?.displayClientMessage(TranslatableComponent("otm.pill.message").withStyle(ChatFormatting.GRAY), false)
}
fun becomeAndroid() {
if (isAndroid) return
isAndroid = true
willBecomeAndroid = false
shouldPlaySound = false
battery = ImpreciseFraction(60000)
maxBattery = ImpreciseFraction(60000)
}
fun becomeAndroidAndKill() {
if (isAndroid) return
becomeAndroid()
ply.hurt(MRegistry.DAMAGE_BECOME_ANDROID, ply.maxHealth * 2)
}
fun becomeHumane() {
if (willBecomeAndroid) willBecomeAndroid = false
if (!isAndroid) return
isAndroid = false
shouldPlaySound = false
battery = ImpreciseFraction(0)
maxBattery = ImpreciseFraction(60000)
dropBattery()
}
fun becomeHumaneAndKill() {
if (willBecomeAndroid) willBecomeAndroid = false
if (!isAndroid) return
becomeHumane()
ply.hurt(MRegistry.DAMAGE_BECOME_HUMANE, ply.maxHealth * 2)
}
fun obliviate(refund: Boolean = true) {
if (refund) {
for (instance in research) {
if (instance.isResearched) {
instance.unResearch()
instance.refund(simulate = false)
}
}
}
val copy = java.util.List.copyOf(features.values)
for (feature in copy) {
removeFeature(feature.type)
}
}
fun <T : AndroidResearch> getResearch(type: AndroidResearchType<T>): T {
for (instance in research) {
if (instance.type === type) {
return instance as T
}
}
val instance = type.factory(this)
research.add(instance)
return instance
}
var lastJumpTicks = 14
private fun addFeature(feature: AndroidFeature): Boolean {
if (features.containsKey(feature.type)) return false
features[feature.type] = feature
if (!ply.level.isClientSide) {
queuedTicks.add(feature::applyModifiers)
}
if (ply is ServerPlayer) {
sendNetwork(AndroidFeatureSyncPacket(feature))
}
return true
}
@Suppress("unchecked_cast")
fun <T : AndroidFeature> addFeature(feature: AndroidFeatureType<T>): T {
val get = features[feature]
if (get != null) return get as T
val factory = feature.create(this)
features[feature] = factory
if (!ply.level.isClientSide) {
queuedTicks.add(factory::applyModifiers)
}
if (ply is ServerPlayer) {
sendNetwork(AndroidFeatureSyncPacket(factory))
}
return factory
}
fun removeFeature(feature: AndroidFeatureType<*>): Boolean {
val removed = features.remove(feature)
if (removed != null) {
if (!ply.level.isClientSide) {
queuedTicks.add {
removed.removeModifiers()
}
}
if (ply is ServerPlayer) {
sendNetwork(AndroidFeatureRemovePacket(removed.type))
}
}
return removed != null
}
fun hasFeature(feature: AndroidFeatureType<*>): Boolean {
return features.containsKey(feature)
}
fun hasFeatureLevel(feature: AndroidFeatureType<*>, level: Int): Boolean {
val get = features[feature] ?: return false
return get.level >= level
}
@Suppress("unchecked_cast")
fun <T : AndroidFeature> getFeature(feature: AndroidFeatureType<T>): T? {
return features[feature] as T?
}
fun onHurt(event: LivingHurtEvent) {
for (feature in features.values) {
feature.onHurt(event)
}
}
fun <T : AndroidFeature> computeIfAbsent(feature: AndroidFeatureType<T>): T {
return getFeature(feature) ?: addFeature(feature)
}
fun <T : AndroidFeature> getFeatureO(feature: AndroidFeatureType<T>): Optional<T> {
val get = getFeature(feature)
return if (get != null) Optional.of(get) else Optional.empty()
}
fun <T : AndroidFeature> ifFeature(feature: AndroidFeatureType<T>, consumer: (T) -> Unit) {
val get = getFeature(feature)
if (get != null) {
consumer(get)
}
}
override fun serializeNBT(): CompoundTag {
val tag = CompoundTag()
tag["energy_stored"] = battery.serializeNBT()
tag["energy_stored_max"] = maxBattery.serializeNBT()
tag["battery"] = batteryItemStack.serializeNBT()
val featureList = ListTag()
for (feature in features.values) {
val featureNbt = feature.serializeNBT()
featureNbt["id"] = feature.type.registryName!!.toString()
featureList.add(featureNbt)
}
tag["features"] = featureList
tag["is_android"] = isAndroid
tag["will_become_android"] = willBecomeAndroid
val list = ListTag()
for (instance in research) {
val researchTag = instance.serializeNBT()
researchTag["id"] = instance.type.registryName!!.toString()
list.add(researchTag)
}
tag["research"] = list
return tag
}
override fun deserializeNBT(compound: CompoundTag) {
compound.ifHas("energy_stored") {
battery = ImpreciseFraction.deserializeNBT(it)
}
compound.ifHas("energy_stored_max") {
maxBattery = ImpreciseFraction.deserializeNBT(it)
}
compound.ifHas("battery", CompoundTag::class.java) {
batteryItemStack = ItemStack.of(it)
}
features.clear()
val featuresNbt = compound.getList("features", Tag.TAG_COMPOUND.toInt())
for (tag in featuresNbt) {
if (tag is CompoundTag) {
val feature = MRegistry.ANDROID_FEATURES.getValue(ResourceLocation(tag.getString("id")))
if (feature?.isApplicable(this) == true) {
val instance = feature.create(this)
instance.deserializeNBT(tag)
addFeature(instance)
if (!ply.level.isClientSide) {
queuedTicks.add(instance::applyModifiers)
}
}
}
}
isAndroid = compound.getBoolean("is_android")
willBecomeAndroid = compound.getBoolean("will_become_android")
research.clear()
val list = compound.getList("research", Tag.TAG_COMPOUND.toInt())
for (tag in list) {
if (tag is CompoundTag) {
val research = MRegistry.ANDROID_RESEARCH.getValue(ResourceLocation(tag.getString("id")))
if (research != null) {
val instance = research.factory(this)
instance.deserializeNBT(tag)
this.research.add(instance)
}
}
}
}
fun dropBattery() {
if (batteryItemStack.isEmpty) return
ply.spawnAtLocation(batteryItemStack)
batteryItemStack = ItemStack.EMPTY
}
private fun sendNetwork(packet: Any) {
if (ply is ServerPlayer) {
if (tickedOnce) {
AndroidNetworkChannel.send(ply, packet)
} else {
networkQueue.add(packet)
}
}
}
fun tickClient() {
queuedTicks.clear()
if (!ply.isAlive) {
return
}
if (willBecomeAndroid) {
if (ply.isSleeping) {
sleepTicks++
} else {
sleepTicks = 0
}
}
if (isAndroid) {
for (feature in features.values) {
feature.tickClient()
}
}
}
fun tick() {
if (!ply.isAlive) return
if (willBecomeAndroid) {
if (ply.isSleeping) {
sleepTicks++
if (sleepTicks > SLEEP_TICKS_LIMIT) {
becomeAndroid()
shouldPlaySound = true
sleepTicks = 0
(ply as? ServerPlayer)?.displayClientMessage(TranslatableComponent("otm.pill.message_finish").withStyle(ChatFormatting.DARK_RED), false)
}
} else {
sleepTicks = 0
}
}
if (isAndroid) {
if (ply.airSupply < ply.maxAirSupply)
ply.airSupply = ply.maxAirSupply
for (effect in UNAFFECTED_EFFECTS)
if (ply.hasEffect(effect))
ply.removeEffect(effect)
if (!batteryItemStack.isEmpty && battery < maxBattery) {
batteryItemStack.getCapability(ForgeCapabilities.ENERGY).ifPresent {
if (it is IMatteryEnergyStorage) {
battery += it.extractEnergyInner(maxBattery - battery, false)
} else {
battery += it.extractEnergy(maxBattery - battery, false)
}
}
}
// TODO: Maybe passive drain?
// extractEnergyInner(BigDecimal.valueOf(new Random().nextDouble()), false);
if (ply.isSwimming && !hasFeature(AndroidFeatures.AIR_BAGS)) {
ply.isSwimming = false
}
val stats = ply.foodData
while (stats.foodLevel < 18 && extractEnergyInner(ENERGY_FOR_HUNGER_POINT, true) >= ENERGY_FOR_HUNGER_POINT) {
extractEnergyInner(ENERGY_FOR_HUNGER_POINT, false)
stats.foodLevel = stats.foodLevel + 1
}
// "block" quick regeneration
// also cause power to generate while in peaceful
while (stats.foodLevel > 18 && receiveEnergyInner(ENERGY_FOR_HUNGER_POINT, true) >= ENERGY_FOR_HUNGER_POINT) {
receiveEnergyInner(ENERGY_FOR_HUNGER_POINT, false)
stats.foodLevel = stats.foodLevel - 1
}
val foodLevel = stats.foodLevel.toFloat()
if (stats.saturationLevel < foodLevel) {
val extracted = extractEnergyInner(ENERGY_FOR_HUNGER_POINT * (foodLevel - stats.saturationLevel), false)
stats.setSaturation(stats.saturationLevel + (extracted / ENERGY_FOR_HUNGER_POINT).toFloat())
}
if (stats.exhaustionLevel > 0f) {
val extracted = extractEnergyInner(ENERGY_FOR_HUNGER_POINT * (stats.exhaustionLevel / 4f), false)
stats.setExhaustion(stats.exhaustionLevel - (extracted / ENERGY_FOR_HUNGER_POINT).toFloat() * 4f)
}
for (feature in features.values) {
feature.tickServer()
}
}
for (runnable in queuedTicks) {
runnable.run()
}
queuedTicks.clear()
if (invalidateNetworkIn > 0 && --invalidateNetworkIn == 0) {
remoteBattery = ImpreciseFraction.MINUS_ONE
remoteMaxBattery = ImpreciseFraction.MINUS_ONE
remoteBatteryStack = ItemStack.EMPTY
remoteIsAndroid = false
willBecomeAndroidNetwork = false
for (instance in research) {
instance.isDirty = true
instance.invalidateNetwork()
}
for (feature in features.values) {
feature.isDirty = true
feature.invalidateNetwork()
}
}
tickedOnce = true
if (battery != remoteBattery) {
remoteBattery = battery
sendNetwork(AndroidEnergyLevelPacket(level = battery, isMaxEnergy = false))
}
if (maxBattery != remoteMaxBattery) {
remoteMaxBattery = maxBattery
sendNetwork(AndroidEnergyLevelPacket(level = maxBattery, isMaxEnergy = true))
}
if (!remoteBatteryStack.equals(batteryItemStack, false)) {
remoteBatteryStack = batteryItemStack.copy()
sendNetwork(AndroidBatteryItemPacket(batteryItemStack))
}
if (networkQueue.size != 0) {
for (packet in networkQueue) {
AndroidNetworkChannel.send(ply, packet)
}
networkQueue.clear()
}
if (isAndroid != remoteIsAndroid) {
remoteIsAndroid = isAndroid
sendNetwork(IsAndroidPacket(isAndroid))
shouldPlaySound = false
}
if (willBecomeAndroid != willBecomeAndroidNetwork) {
willBecomeAndroidNetwork = willBecomeAndroid
sendNetwork(WillBecomeAndroidPacket(willBecomeAndroid))
}
for (instance in research) {
if (instance.isDirty) {
instance.isDirty = false
sendNetwork(AndroidResearchSyncPacket(instance))
}
}
for (instance in features.values) {
if (instance.isDirty) {
instance.isDirty = false
sendNetwork(AndroidFeatureSyncPacket(instance))
}
}
}
override fun extractEnergyOuter(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
return ImpreciseFraction.ZERO
}
override fun extractEnergyInner(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
@Suppress("name_shadowing")
var howMuch = howMuch
var drained = ImpreciseFraction.ZERO
if (!batteryItemStack.isEmpty) {
batteryItemStack.getCapability(ForgeCapabilities.ENERGY).ifPresentK {
if (it is IMatteryEnergyStorage) {
val extracted = it.extractEnergyOuter(howMuch, simulate)
drained += extracted
howMuch -= extracted
} else {
val extracted = it.extractEnergy(howMuch, simulate)
drained += extracted
howMuch -= extracted
}
}
if (howMuch.isZero) {
if (!simulate && ply is ServerPlayer) {
ply.awardStat(StatNames.POWER_CONSUMED, drained.toInt() * 10)
}
return drained
}
}
val new = (battery - howMuch).moreThanZero()
drained += battery - new
if (!simulate) {
battery = new
if (ply is ServerPlayer) {
ply.awardStat(StatNames.POWER_CONSUMED, drained.toInt() * 10)
}
}
return drained
}
override fun receiveEnergyOuter(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
@Suppress("name_shadowing")
var howMuch = howMuch
var received = ImpreciseFraction.ZERO
if (!batteryItemStack.isEmpty) {
batteryItemStack.getCapability(ForgeCapabilities.ENERGY).ifPresentK {
if (it is IMatteryEnergyStorage) {
val extracted = it.receiveEnergyOuter(howMuch, simulate)
received += extracted
howMuch -= extracted
} else {
val extracted = it.receiveEnergy(howMuch, simulate)
received += extracted
howMuch -= extracted
}
}
if (howMuch.isZero) {
return received
}
}
val new = (battery + howMuch).coerceAtMost(maxBattery)
received += new - battery
if (!simulate) {
battery = new
}
return received
}
override fun receiveEnergyInner(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
return receiveEnergyOuter(howMuch, simulate)
}
override var batteryLevel: ImpreciseFraction
get() {
if (!batteryItemStack.isEmpty) {
batteryItemStack.getCapability(ForgeCapabilities.ENERGY).ifPresentK {
if (it is IMatteryEnergyStorage) {
return battery + it.batteryLevel
} else {
return battery + it.energyStored
}
}
}
return battery
}
set(value) {
battery = value
}
override var maxBatteryLevel: ImpreciseFraction
get() {
if (batteryItemStack != ItemStack.EMPTY) {
batteryItemStack.getCapability(ForgeCapabilities.ENERGY).ifPresentK {
if (it is IMatteryEnergyStorage) {
return maxBattery + it.maxBatteryLevel
} else {
return maxBattery + it.maxEnergyStored
}
}
}
return maxBattery
}
set(value) {
maxBattery = value
}
private val resolver = LazyOptional.of { this }
override fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
return if (cap == MatteryCapability.ANDROID) {
resolver.cast()
} else LazyOptional.empty()
}
@Suppress("unused")
companion object {
@JvmField
val UNAFFECTED_EFFECTS = ObjectArraySet<MobEffect>()
@JvmStatic
fun registerEffects(event: FMLCommonSetupEvent) {
UNAFFECTED_EFFECTS.add(MobEffects.CONDUIT_POWER)
UNAFFECTED_EFFECTS.add(MobEffects.HEAL)
// maybe it makes things go haywire idk
// UNAFFECTED_EFFECTS.add(MobEffects.HARM);
UNAFFECTED_EFFECTS.add(MobEffects.REGENERATION)
UNAFFECTED_EFFECTS.add(MobEffects.WATER_BREATHING)
UNAFFECTED_EFFECTS.add(MobEffects.POISON)
// even skeletons can be withered
// UNAFFECTED_EFFECTS.add(MobEffects.WITHER);
UNAFFECTED_EFFECTS.add(MobEffects.HEALTH_BOOST)
UNAFFECTED_EFFECTS.add(MobEffects.ABSORPTION)
UNAFFECTED_EFFECTS.add(MobEffects.SATURATION)
UNAFFECTED_EFFECTS.add(MobEffects.DOLPHINS_GRACE)
}
@SubscribeEvent
fun onLivingTick(event: LivingTickEvent) {
val ent = event.entity
if (ent.level.isClientSide) {
ent.getCapability(MatteryCapability.ANDROID).ifPresentK {
it.tickClient()
}
} else {
ent.getCapability(MatteryCapability.ANDROID).ifPresentK {
it.tick()
}
}
}
@SubscribeEvent(priority = EventPriority.LOWEST)
fun onHurtEvent(event: LivingHurtEvent) {
if (event.isCanceled) return
event.entity.getCapability(MatteryCapability.ANDROID).ifPresentK { it.onHurt(event) }
}
@SubscribeEvent
fun onAttachCapabilityEvent(event: AttachCapabilitiesEvent<Entity?>) {
val ent = event.`object`
if (ent is Player) {
event.addCapability(MNames.ANDROID_CAPABILITY_RS, AndroidCapability(ent))
}
}
@SubscribeEvent
fun onPlayerChangeDimensionEvent(event: PlayerEvent.PlayerChangedDimensionEvent) {
event.entity.getCapability(MatteryCapability.ANDROID)
.ifPresentK { it.invalidateNetworkState() }
}
@SubscribeEvent
fun onPlayerCloneEvent(event: PlayerEvent.Clone) {
val it = event.entity.android ?: return
var resolver = event.original.getCapability(MatteryCapability.ANDROID)
if (!resolver.isPresent || resolver.resolve().isEmpty) {
event.original.reviveCaps()
resolver = event.original.getCapability(MatteryCapability.ANDROID)
}
if (!resolver.isPresent || resolver.resolve().isEmpty) {
event.original.invalidateCaps()
return
}
val original = resolver.resolve().get()
if (original.willBecomeAndroid && event.isWasDeath) {
original.becomeAndroid()
(event.entity as? ServerPlayer)?.displayClientMessage(TranslatableComponent("otm.pill.message_finish").withStyle(ChatFormatting.DARK_RED), false)
}
it.deserializeNBT(original.serializeNBT())
it.invalidateNetworkState()
event.original.invalidateCaps()
}
val ENERGY_FOR_HUNGER_POINT = ImpreciseFraction(1000)
const val SLEEP_TICKS_LIMIT = 80
}
}
val ICapabilityProvider.android get() = getCapability(MatteryCapability.ANDROID).orNull()

View File

@ -5,7 +5,6 @@ import net.minecraft.world.entity.LivingEntity
import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.capabilities.ICapabilityProvider
import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.energy.CapabilityEnergy
import net.minecraftforge.energy.IEnergyStorage
import net.minecraftforge.fml.ModList
import ru.dbotthepony.mc.otm.compat.mekanism.getMekanismEnergySided

View File

@ -1,436 +0,0 @@
package ru.dbotthepony.mc.otm.capability.android
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
import it.unimi.dsi.fastutil.objects.ObjectArraySet
import net.minecraft.core.Direction
import net.minecraft.world.entity.LivingEntity
import net.minecraftforge.common.capabilities.ICapabilityProvider
import net.minecraftforge.common.util.INBTSerializable
import net.minecraft.nbt.CompoundTag
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.android.AndroidFeatureType
import ru.dbotthepony.mc.otm.android.AndroidFeature
import java.lang.Runnable
import net.minecraft.server.level.ServerPlayer
import net.minecraftforge.event.entity.living.LivingHurtEvent
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import ru.dbotthepony.mc.otm.network.MatteryNetworking
import net.minecraftforge.network.PacketDistributor
import net.minecraft.world.effect.MobEffect
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import net.minecraftforge.energy.CapabilityEnergy
import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent
import net.minecraft.world.effect.MobEffects
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.eventbus.api.SubscribeEvent
import net.minecraftforge.event.entity.living.LivingEvent.LivingTickEvent
import net.minecraftforge.eventbus.api.EventPriority
import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.extractEnergy
import ru.dbotthepony.mc.otm.capability.receiveEnergy
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.ifHas
import ru.dbotthepony.mc.otm.ifPresentK
import ru.dbotthepony.mc.otm.network.AndroidFeatureRemovePacket
import ru.dbotthepony.mc.otm.network.AndroidFeatureSyncPacket
import ru.dbotthepony.mc.otm.network.AndroidNetworkChannel
import ru.dbotthepony.mc.otm.orNull
import ru.dbotthepony.mc.otm.registry.MRegistry
import ru.dbotthepony.mc.otm.registry.StatNames
import ru.dbotthepony.mc.otm.set
import java.util.*
open class AndroidCapability(final override val entity: LivingEntity) : ICapabilityProvider, IAndroidCapability, INBTSerializable<CompoundTag> {
protected var battery = ImpreciseFraction.ZERO
protected var maxBattery = ImpreciseFraction(60000)
override var batteryItemStack: ItemStack = ItemStack.EMPTY
protected val features = Object2ObjectArrayMap<AndroidFeatureType<*>, AndroidFeature>()
protected val networkQueue = ArrayList<Any>()
protected val queuedTicks = ArrayList<Runnable>()
protected var tickedOnce = false
protected fun addFeature(feature: AndroidFeature): Boolean {
if (features.containsKey(feature.type)) return false
features[feature.type] = feature
if (!entity.level.isClientSide) {
queuedTicks.add(feature::applyModifiers)
}
if (entity is ServerPlayer) {
sendNetwork(AndroidFeatureSyncPacket(feature))
}
return true
}
@Suppress("unchecked_cast")
override fun <T : AndroidFeature> addFeature(feature: AndroidFeatureType<T>): T {
val get = features[feature]
if (get != null) return get as T
val factory = feature.create(this)
features[feature] = factory
if (!entity.level.isClientSide) {
queuedTicks.add(factory::applyModifiers)
}
if (entity is ServerPlayer) {
sendNetwork(AndroidFeatureSyncPacket(factory))
}
return factory
}
override fun removeFeature(feature: AndroidFeatureType<*>): Boolean {
val removed = features.remove(feature)
if (removed != null) {
if (!entity.level.isClientSide) {
queuedTicks.add {
removed.removeModifiers()
}
}
if (entity is ServerPlayer) {
sendNetwork(AndroidFeatureRemovePacket(removed.type))
}
}
return removed != null
}
override fun hasFeature(feature: AndroidFeatureType<*>): Boolean {
return features.containsKey(feature)
}
override fun hasFeatureLevel(feature: AndroidFeatureType<*>, level: Int): Boolean {
val get = features[feature] ?: return false
return get.level >= level
}
@Suppress("unchecked_cast")
override fun <T : AndroidFeature> getFeature(feature: AndroidFeatureType<T>): T? {
return features[feature] as T?
}
override fun onHurt(event: LivingHurtEvent) {
for (feature in features.values) {
feature.onHurt(event)
}
}
override fun serializeNBT(): CompoundTag {
val tag = CompoundTag()
tag["energy_stored"] = battery.serializeNBT()
tag["energy_stored_max"] = maxBattery.serializeNBT()
tag["battery"] = batteryItemStack.serializeNBT()
val featureList = ListTag()
for (feature in features.values) {
val featureNbt = feature.serializeNBT()
featureNbt["id"] = feature.type.registryName!!.toString()
featureList.add(featureNbt)
}
tag["features"] = featureList
return tag
}
override fun deserializeNBT(compound: CompoundTag?) {
compound!!
compound.ifHas("energy_stored") {
battery = ImpreciseFraction.deserializeNBT(it)
}
compound.ifHas("energy_stored_max") {
maxBattery = ImpreciseFraction.deserializeNBT(it)
}
compound.ifHas("battery", CompoundTag::class.java) {
batteryItemStack = ItemStack.of(it)
}
features.clear()
val featuresNbt = compound.getList("features", Tag.TAG_COMPOUND.toInt())
for (tag in featuresNbt) {
if (tag is CompoundTag) {
val feature = MRegistry.ANDROID_FEATURES.getValue(ResourceLocation(tag.getString("id")))
if (feature?.isApplicable(this) == true) {
val instance = feature.create(this)
instance.deserializeNBT(tag)
addFeature(instance)
if (!entity.level.isClientSide) {
queuedTicks.add(instance::applyModifiers)
}
}
}
}
}
fun dropBattery() {
if (batteryItemStack.isEmpty) return
entity.spawnAtLocation(batteryItemStack)
batteryItemStack = ItemStack.EMPTY
}
protected fun sendNetwork(packet: Any) {
if (entity is ServerPlayer) {
if (tickedOnce) {
AndroidNetworkChannel.send(entity, packet)
} else {
networkQueue.add(packet)
}
}
}
protected open fun tickInnerClient() {}
protected open fun tickInnerClientAlways() {}
override fun tickClient() {
queuedTicks.clear()
if (!entity.isAlive)
return
tickInnerClientAlways()
if (isAndroid) {
tickInnerClient()
for (feature in features.values) {
feature.tickClient()
}
}
}
override fun tick() {
if (!entity.isAlive) return
tickServerAlways()
if (isAndroid) {
tickServer()
for (feature in features.values) {
feature.tickServer()
}
}
for (runnable in queuedTicks) {
runnable.run()
}
queuedTicks.clear()
}
protected open fun tickServerAlways() {}
protected open fun tickServer() {
if (entity.airSupply < entity.maxAirSupply)
entity.airSupply = entity.maxAirSupply
for (effect in UNAFFECTED_EFFECTS)
if (entity.hasEffect(effect))
entity.removeEffect(effect)
if (!batteryItemStack.isEmpty && battery < maxBattery) {
batteryItemStack.getCapability(CapabilityEnergy.ENERGY).ifPresent {
if (it is IMatteryEnergyStorage) {
battery += it.extractEnergyInner(maxBattery - battery, false)
} else {
battery += it.extractEnergy(maxBattery - battery, false)
}
}
}
}
override fun extractEnergyOuter(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
return ImpreciseFraction.ZERO
}
override fun extractEnergyInner(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
@Suppress("name_shadowing")
var howMuch = howMuch
var drained = ImpreciseFraction.ZERO
if (!batteryItemStack.isEmpty) {
batteryItemStack.getCapability(ForgeCapabilities.ENERGY).ifPresentK {
if (it is IMatteryEnergyStorage) {
val extracted = it.extractEnergyOuter(howMuch, simulate)
drained += extracted
howMuch -= extracted
} else {
val extracted = it.extractEnergy(howMuch, simulate)
drained += extracted
howMuch -= extracted
}
}
if (howMuch.isZero) {
if (!simulate && entity is ServerPlayer) {
entity.awardStat(StatNames.POWER_CONSUMED, drained.toInt() * 10)
}
return drained
}
}
val new = (battery - howMuch).moreThanZero()
drained += battery - new
if (!simulate) {
battery = new
if (entity is ServerPlayer) {
entity.awardStat(StatNames.POWER_CONSUMED, drained.toInt() * 10)
}
}
return drained
}
override fun receiveEnergyOuter(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
var howMuch = howMuch
var received = ImpreciseFraction.ZERO
if (!batteryItemStack.isEmpty) {
batteryItemStack.getCapability(ForgeCapabilities.ENERGY).ifPresentK {
if (it is IMatteryEnergyStorage) {
val extracted = it.receiveEnergyOuter(howMuch, simulate)
received += extracted
howMuch -= extracted
} else {
val extracted = it.receiveEnergy(howMuch, simulate)
received += extracted
howMuch -= extracted
}
}
if (howMuch.isZero) {
return received
}
}
val new = (battery + howMuch).coerceAtMost(maxBattery)
received += new - battery
if (!simulate) {
battery = new
}
return received
}
override fun receiveEnergyInner(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
return receiveEnergyOuter(howMuch, simulate)
}
override var batteryLevel: ImpreciseFraction
get() {
if (!batteryItemStack.isEmpty) {
batteryItemStack.getCapability(ForgeCapabilities.ENERGY).ifPresentK {
if (it is IMatteryEnergyStorage) {
return battery + it.batteryLevel
} else {
return battery + it.energyStored
}
}
}
return battery
}
set(value) {
battery = value
}
override var maxBatteryLevel: ImpreciseFraction
get() {
if (batteryItemStack != ItemStack.EMPTY) {
val resolver = batteryItemStack.getCapability(ForgeCapabilities.ENERGY).ifPresentK {
if (it is IMatteryEnergyStorage) {
return maxBattery + it.maxBatteryLevel
} else {
return maxBattery + it.maxEnergyStored
}
}
}
return maxBattery
}
set(value) {
maxBattery = value
}
private val resolver = LazyOptional.of { this }
override fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
return if (cap === MatteryCapability.ANDROID) {
resolver.cast()
} else LazyOptional.empty()
}
@Suppress("unused")
companion object {
@JvmField
val UNAFFECTED_EFFECTS = ObjectArraySet<MobEffect>()
@JvmStatic
fun registerEffects(event: FMLCommonSetupEvent) {
UNAFFECTED_EFFECTS.add(MobEffects.CONDUIT_POWER)
UNAFFECTED_EFFECTS.add(MobEffects.HEAL)
// maybe it makes things go haywire idk
// UNAFFECTED_EFFECTS.add(MobEffects.HARM);
UNAFFECTED_EFFECTS.add(MobEffects.REGENERATION)
UNAFFECTED_EFFECTS.add(MobEffects.WATER_BREATHING)
UNAFFECTED_EFFECTS.add(MobEffects.POISON)
// even skeletons can be withered
// UNAFFECTED_EFFECTS.add(MobEffects.WITHER);
UNAFFECTED_EFFECTS.add(MobEffects.HEALTH_BOOST)
UNAFFECTED_EFFECTS.add(MobEffects.ABSORPTION)
UNAFFECTED_EFFECTS.add(MobEffects.SATURATION)
UNAFFECTED_EFFECTS.add(MobEffects.DOLPHINS_GRACE)
}
@SubscribeEvent
fun onLivingTick(event: LivingTickEvent) {
val ent = event.entity
if (ent.level.isClientSide) {
ent.getCapability(MatteryCapability.ANDROID).ifPresentK {
it.tickClient()
}
} else {
ent.getCapability(MatteryCapability.ANDROID).ifPresentK {
it.tick()
}
}
}
@SubscribeEvent(priority = EventPriority.LOWEST)
fun onHurtEvent(event: LivingHurtEvent) {
if (event.isCanceled) return
event.entity.getCapability(MatteryCapability.ANDROID).ifPresentK { cap: IAndroidCapability -> cap.onHurt(event) }
}
}
}
val ICapabilityProvider.android get() = getCapability(MatteryCapability.ANDROID).orNull()

View File

@ -1,356 +0,0 @@
package ru.dbotthepony.mc.otm.capability.android
import net.minecraft.ChatFormatting
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraftforge.event.AttachCapabilitiesEvent
import net.minecraftforge.event.entity.player.PlayerEvent.Clone
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerChangedDimensionEvent
import net.minecraftforge.eventbus.api.SubscribeEvent
import net.minecraftforge.network.PacketDistributor
import ru.dbotthepony.mc.otm.TranslatableComponent
import ru.dbotthepony.mc.otm.android.AndroidResearch
import ru.dbotthepony.mc.otm.android.AndroidResearchType
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.ifPresentK
import ru.dbotthepony.mc.otm.network.*
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import ru.dbotthepony.mc.otm.registry.MNames
import ru.dbotthepony.mc.otm.registry.MRegistry
import ru.dbotthepony.mc.otm.set
import java.util.*
class AndroidCapabilityPlayer(val ply: Player) : AndroidCapability(ply) {
override var isAndroid = false
private var remoteIsAndroid = false
var willBecomeAndroid = false
private var willBecomeAndroidNetwork = false
private var shouldPlaySound = false
private val research = ArrayList<AndroidResearch>()
private var remoteBattery = ImpreciseFraction(-1)
private var remoteMaxBattery = ImpreciseFraction(-1)
private var remoteBatteryStack = ItemStack.EMPTY
private var invalidateNetworkIn = 10
fun invalidateNetworkState() {
invalidateNetworkIn = 10
}
fun isEverAndroid(): Boolean = isAndroid || willBecomeAndroid
fun becomeAndroidSoft() {
if (isAndroid || willBecomeAndroid) return
willBecomeAndroid = true
(ply as? ServerPlayer)?.displayClientMessage(TranslatableComponent("otm.pill.message").withStyle(ChatFormatting.GRAY), false)
}
fun becomeAndroid() {
if (isAndroid) return
isAndroid = true
willBecomeAndroid = false
shouldPlaySound = false
battery = ImpreciseFraction(60000)
maxBattery = ImpreciseFraction(60000)
}
fun becomeAndroidAndKill() {
if (isAndroid) return
becomeAndroid()
ply.hurt(MRegistry.DAMAGE_BECOME_ANDROID, ply.maxHealth * 2)
}
fun becomeHumane() {
if (willBecomeAndroid) willBecomeAndroid = false
if (!isAndroid) return
isAndroid = false
shouldPlaySound = false
battery = ImpreciseFraction(0)
maxBattery = ImpreciseFraction(60000)
dropBattery()
}
fun becomeHumaneAndKill() {
if (willBecomeAndroid) willBecomeAndroid = false
if (!isAndroid) return
becomeHumane()
ply.hurt(MRegistry.DAMAGE_BECOME_HUMANE, ply.maxHealth * 2)
}
override fun deserializeNBT(compound: CompoundTag?) {
super.deserializeNBT(compound!!)
isAndroid = compound.getBoolean("is_android")
willBecomeAndroid = compound.getBoolean("will_become_android")
research.clear()
val list = compound.getList("research", Tag.TAG_COMPOUND.toInt())
for (tag in list) {
if (tag is CompoundTag) {
val research = MRegistry.ANDROID_RESEARCH.getValue(ResourceLocation(tag.getString("id")))
if (research != null) {
val instance = research.factory(this)
instance.deserializeNBT(tag)
this.research.add(instance)
}
}
}
}
fun obliviate(refund: Boolean = true) {
if (refund) {
for (instance in research) {
if (instance.isResearched) {
instance.unResearch()
instance.refund(simulate = false)
}
}
}
val copy = java.util.List.copyOf(features.values)
for (feature in copy) {
removeFeature(feature.type)
}
}
override fun serializeNBT(): CompoundTag {
val tag = super.serializeNBT()
tag["is_android"] = isAndroid
tag["will_become_android"] = willBecomeAndroid
val list = ListTag()
for (instance in research) {
val researchTag = instance.serializeNBT()
researchTag["id"] = instance.type.registryName!!.toString()
list.add(researchTag)
}
tag["research"] = list
return tag
}
fun <T : AndroidResearch> getResearch(type: AndroidResearchType<T>): T {
for (instance in research) {
if (instance.type === type) {
return instance as T
}
}
val instance = type.factory(this)
research.add(instance)
return instance
}
var lastJumpTicks = 14
override fun tick() {
super.tick()
if (invalidateNetworkIn > 0 && --invalidateNetworkIn == 0) {
remoteBattery = ImpreciseFraction.MINUS_ONE
remoteMaxBattery = ImpreciseFraction.MINUS_ONE
remoteBatteryStack = ItemStack.EMPTY
remoteIsAndroid = false
willBecomeAndroidNetwork = false
for (instance in research) {
instance.isDirty = true
instance.invalidateNetwork()
}
for (feature in features.values) {
feature.isDirty = true
feature.invalidateNetwork()
}
}
tickedOnce = true
if (battery != remoteBattery) {
remoteBattery = battery
sendNetwork(AndroidEnergyLevelPacket(level = battery, isMaxEnergy = false))
}
if (maxBattery != remoteMaxBattery) {
remoteMaxBattery = maxBattery
sendNetwork(AndroidEnergyLevelPacket(level = maxBattery, isMaxEnergy = true))
}
if (!remoteBatteryStack.equals(batteryItemStack, false)) {
remoteBatteryStack = batteryItemStack.copy()
sendNetwork(AndroidBatteryItemPacket(batteryItemStack))
}
if (networkQueue.size != 0) {
for (packet in networkQueue) {
AndroidNetworkChannel.send(ply, packet)
}
networkQueue.clear()
}
if (isAndroid != remoteIsAndroid) {
remoteIsAndroid = isAndroid
sendNetwork(IsAndroidPacket(isAndroid))
shouldPlaySound = false
}
if (willBecomeAndroid != willBecomeAndroidNetwork) {
willBecomeAndroidNetwork = willBecomeAndroid
sendNetwork(WillBecomeAndroidPacket(willBecomeAndroid))
}
for (instance in research) {
if (instance.isDirty) {
instance.isDirty = false
sendNetwork(AndroidResearchSyncPacket(instance))
}
}
for (instance in features.values) {
if (instance.isDirty) {
instance.isDirty = false
sendNetwork(AndroidFeatureSyncPacket(instance))
}
}
}
var sleepTicks = 0
override fun tickInnerClientAlways() {
super.tickInnerClientAlways()
if (willBecomeAndroid) {
if (ply.isSleeping) {
sleepTicks++
} else {
sleepTicks = 0
}
}
}
override fun tickServerAlways() {
super.tickServerAlways()
if (willBecomeAndroid) {
if (ply.isSleeping) {
sleepTicks++
if (sleepTicks > SLEEP_TICKS_LIMIT) {
becomeAndroid()
shouldPlaySound = true
sleepTicks = 0
(ply as? ServerPlayer)?.displayClientMessage(TranslatableComponent("otm.pill.message_finish").withStyle(ChatFormatting.DARK_RED), false)
}
} else {
sleepTicks = 0
}
}
}
public override fun tickServer() {
super.tickServer()
// TODO: Maybe passive drain?
// extractEnergyInner(BigDecimal.valueOf(new Random().nextDouble()), false);
if (ply.isSwimming && !hasFeature(AndroidFeatures.AIR_BAGS)) {
ply.isSwimming = false
}
val stats = ply.foodData
while (stats.foodLevel < 18 && extractEnergyInner(ENERGY_FOR_HUNGER_POINT, true) >= ENERGY_FOR_HUNGER_POINT) {
extractEnergyInner(ENERGY_FOR_HUNGER_POINT, false)
stats.foodLevel = stats.foodLevel + 1
}
// "block" quick regeneration
// also cause power to generate while in peaceful
while (stats.foodLevel > 18 && receiveEnergyInner(ENERGY_FOR_HUNGER_POINT, true) >= ENERGY_FOR_HUNGER_POINT) {
receiveEnergyInner(ENERGY_FOR_HUNGER_POINT, false)
stats.foodLevel = stats.foodLevel - 1
}
val foodLevel = stats.foodLevel.toFloat()
if (stats.saturationLevel < foodLevel) {
val extracted = extractEnergyInner(ENERGY_FOR_HUNGER_POINT * (foodLevel - stats.saturationLevel), false)
stats.setSaturation(stats.saturationLevel + (extracted / ENERGY_FOR_HUNGER_POINT).toFloat())
}
if (stats.exhaustionLevel > 0f) {
val extracted = extractEnergyInner(ENERGY_FOR_HUNGER_POINT * (stats.exhaustionLevel / 4f), false)
stats.setExhaustion(stats.exhaustionLevel - (extracted / ENERGY_FOR_HUNGER_POINT).toFloat() * 4f)
}
}
@Suppress("unused")
companion object {
@SubscribeEvent
fun onAttachCapabilityEvent(event: AttachCapabilitiesEvent<Entity?>) {
val ent = event.`object`
if (ent is Player) {
event.addCapability(MNames.ANDROID_CAPABILITY_RS, AndroidCapabilityPlayer(ent))
}
}
@SubscribeEvent
fun onPlayerChangeDimensionEvent(event: PlayerChangedDimensionEvent) {
event.entity.getCapability(MatteryCapability.ANDROID)
.ifPresentK { (it as AndroidCapabilityPlayer).invalidateNetworkState() }
}
@SubscribeEvent
fun onPlayerCloneEvent(event: Clone) {
val it = event.entity.android as AndroidCapabilityPlayer? ?: return
var resolver = event.original.getCapability(MatteryCapability.ANDROID)
if (!resolver.isPresent || resolver.resolve().isEmpty) {
event.original.reviveCaps()
resolver = event.original.getCapability(MatteryCapability.ANDROID)
}
if (!resolver.isPresent || resolver.resolve().isEmpty) {
event.original.invalidateCaps()
return
}
val original = resolver.resolve().get() as AndroidCapabilityPlayer
if (original.willBecomeAndroid && event.isWasDeath) {
original.becomeAndroid()
(event.entity as? ServerPlayer)?.displayClientMessage(TranslatableComponent("otm.pill.message_finish").withStyle(ChatFormatting.DARK_RED), false)
}
it.deserializeNBT(original.serializeNBT())
it.invalidateNetworkState()
event.original.invalidateCaps()
}
val ENERGY_FOR_HUNGER_POINT = ImpreciseFraction(1000)
const val SLEEP_TICKS_LIMIT = 80
}
}

View File

@ -1,49 +0,0 @@
package ru.dbotthepony.mc.otm.capability.android
import net.minecraft.nbt.CompoundTag
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.item.ItemStack
import net.minecraftforge.common.util.INBTSerializable
import net.minecraftforge.event.entity.living.LivingHurtEvent
import ru.dbotthepony.mc.otm.android.AndroidFeature
import ru.dbotthepony.mc.otm.android.AndroidFeatureType
import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import java.util.*
interface IAndroidCapability : IMatteryEnergyStorage, INBTSerializable<CompoundTag> {
fun tick()
fun tickClient()
val entity: LivingEntity
fun <T : AndroidFeature> addFeature(feature: AndroidFeatureType<T>): T
fun removeFeature(feature: AndroidFeatureType<*>): Boolean
fun hasFeature(feature: AndroidFeatureType<*>): Boolean
fun hasFeatureLevel(feature: AndroidFeatureType<*>, level: Int): Boolean
fun <T : AndroidFeature> getFeature(feature: AndroidFeatureType<T>): T?
fun <T : AndroidFeature> computeIfAbsent(feature: AndroidFeatureType<T>): T {
return getFeature(feature) ?: addFeature(feature)
}
fun <T : AndroidFeature> getFeatureO(feature: AndroidFeatureType<T>): Optional<T> {
val get = getFeature(feature)
return if (get != null) Optional.of(get) else Optional.empty()
}
fun <T : AndroidFeature> ifFeature(feature: AndroidFeatureType<T>, consumer: (T) -> Unit) {
val get = getFeature(feature)
if (get != null) {
consumer(get)
}
}
val isAndroid: Boolean get() = true
fun onHurt(event: LivingHurtEvent) {}
var batteryItemStack: ItemStack
override var batteryLevel: ImpreciseFraction
override var maxBatteryLevel: ImpreciseFraction
}

View File

@ -19,7 +19,7 @@ import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.TranslatableComponent
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.android
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer
import ru.dbotthepony.mc.otm.capability.AndroidCapability
import ru.dbotthepony.mc.otm.ifPresentK
import java.util.*
@ -31,7 +31,7 @@ object AndroidGui {
private var knownButton: Button? = null
private var knownButtonScreen: InBedChatScreen? = null
private var lastState: AndroidCapabilityPlayer? = null
private var lastState: AndroidCapability? = null
private val button_shaker = Random()
@ -63,7 +63,7 @@ object AndroidGui {
}
minecraft.player?.getCapability(MatteryCapability.ANDROID)?.ifPresentK {
if (!(it as AndroidCapabilityPlayer).willBecomeAndroid) {
if (!it.willBecomeAndroid) {
knownButton!!.x = knownButtonX
knownButton!!.y = knownButtonY
knownButtonX = -1
@ -73,7 +73,7 @@ object AndroidGui {
return@ifPresentK
}
val dispersion = (10.0 * Math.max(0, it.sleepTicks - 20) / (AndroidCapabilityPlayer.SLEEP_TICKS_LIMIT - 20)).toInt()
val dispersion = (10.0 * Math.max(0, it.sleepTicks - 20) / (AndroidCapability.SLEEP_TICKS_LIMIT - 20)).toInt()
knownButton!!.x =
knownButtonX - dispersion / 2 + (button_shaker.nextDouble() * dispersion).toInt()
@ -103,7 +103,7 @@ object AndroidGui {
knownButtonScreen = screen
minecraft.player?.getCapability(MatteryCapability.ANDROID)?.ifPresentK {
if ((it as AndroidCapabilityPlayer).willBecomeAndroid) {
if (it.willBecomeAndroid) {
knownButtonScreen = screen
}
}
@ -134,12 +134,9 @@ object AndroidGui {
return
}
val lazy = ply.android
var android: AndroidCapabilityPlayer? = null
var android = ply.android
if (lazy != null) {
android = lazy as AndroidCapabilityPlayer
} else if (!ply.isAlive) {
if (!ply.isAlive && android == null) {
android = lastState
}

View File

@ -3,7 +3,6 @@ package ru.dbotthepony.mc.otm.client
import net.minecraftforge.client.event.MovementInputUpdateEvent
import net.minecraftforge.eventbus.api.SubscribeEvent
import ru.dbotthepony.mc.otm.capability.android
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
@SubscribeEvent
@ -12,7 +11,7 @@ fun inputEvent(event: MovementInputUpdateEvent) {
val ply = event.entity
val input = event.input
val cap = ply.android as? AndroidCapabilityPlayer ?: return
val cap = ply.android ?: return
if (cap.hasFeature(AndroidFeatures.AIR_BAGS))
return

View File

@ -14,8 +14,7 @@ import org.lwjgl.opengl.GL30
import ru.dbotthepony.mc.otm.TranslatableComponent
import ru.dbotthepony.mc.otm.block.entity.GravitationStabilizerBlockEntity
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleBlockEntity
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.android.android
import ru.dbotthepony.mc.otm.capability.android
import ru.dbotthepony.mc.otm.core.*
import ru.dbotthepony.mc.otm.registry.MItems
import kotlin.math.PI

View File

@ -11,13 +11,13 @@ import net.minecraft.world.entity.player.Inventory
import ru.dbotthepony.mc.otm.TranslatableComponent
import ru.dbotthepony.mc.otm.android.AndroidResearch
import ru.dbotthepony.mc.otm.android.AndroidResearchType
import ru.dbotthepony.mc.otm.capability.AndroidCapability
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer
import ru.dbotthepony.mc.otm.capability.android.IAndroidCapability
import ru.dbotthepony.mc.otm.client.render.RenderHelper
import ru.dbotthepony.mc.otm.client.screen.panels.*
import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel
import ru.dbotthepony.mc.otm.core.RGBAColor
import ru.dbotthepony.mc.otm.ifPresentK
import ru.dbotthepony.mc.otm.menu.AndroidStationMenu
import ru.dbotthepony.mc.otm.network.AndroidNetworkChannel
import ru.dbotthepony.mc.otm.network.AndroidResearchRequestPacket
@ -43,29 +43,27 @@ class AndroidStationScreen constructor(p_97741_: AndroidStationMenu, p_97742_: I
override fun innerRender(stack: PoseStack, mouse_x: Float, mouse_y: Float, flag: Float) {
ru.dbotthepony.mc.otm.client.minecraft.player!!.getCapability(MatteryCapability.ANDROID)
.ifPresent { _cap: IAndroidCapability? ->
if (_cap is AndroidCapabilityPlayer) {
if (node.isResearched) {
RESEARCHED.setSystemColor()
} else if (node.canResearch) {
CAN_BE_RESEARCHED.setSystemColor()
} else {
CAN_NOT_BE_RESEARCHED.setSystemColor()
}
.ifPresentK {
if (node.isResearched) {
RESEARCHED.setSystemColor()
} else if (node.canResearch) {
CAN_BE_RESEARCHED.setSystemColor()
} else {
CAN_NOT_BE_RESEARCHED.setSystemColor()
}
val icon = node.skinIcon
val icon = node.skinIcon
if (icon != null) {
icon.render(stack, 0f, 0f, width, height)
} else {
RenderHelper.drawRect(stack, 0f, 0f, width, height)
}
if (icon != null) {
icon.render(stack, 0f, 0f, width, height)
} else {
RenderHelper.drawRect(stack, 0f, 0f, width, height)
}
val text = node.iconText
val text = node.iconText
if (text != null) {
font.drawShadow(stack, text, width - font.width(text), height - font.lineHeight, -0x1)
}
if (text != null) {
font.drawShadow(stack, text, width - font.width(text), height - font.lineHeight, -0x1)
}
}
}
@ -108,7 +106,7 @@ class AndroidStationScreen constructor(p_97741_: AndroidStationMenu, p_97742_: I
private val createdButtons = Array(100) { arrayOfNulls<AndroidResearchButton>(1000) }
private val createdButtonsIdx = IntArray(100)
private fun dive(cap: AndroidCapabilityPlayer, research: AndroidResearchType<*>, level: Int) {
private fun dive(cap: AndroidCapability, research: AndroidResearchType<*>, level: Int) {
if (seen.contains(research)) return
seen.add(research)
@ -156,14 +154,11 @@ class AndroidStationScreen constructor(p_97741_: AndroidStationMenu, p_97742_: I
}
minecraft?.player?.getCapability(MatteryCapability.ANDROID)?.ifPresent {
if (it !is AndroidCapabilityPlayer)
return@ifPresent
Arrays.fill(rows, null)
nextX = 0f
for (research in MRegistry.ANDROID_RESEARCH.values) {
if (research.definedPrerequisites.size == 0) {
if (research.definedPrerequisites.isEmpty()) {
dive(it, research, 0)
var max = 0f

View File

@ -15,7 +15,7 @@ import net.minecraftforge.common.util.FakePlayer
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.TranslatableComponent
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer
import ru.dbotthepony.mc.otm.capability.AndroidCapability
enum class PillType {
BECOME_ANDROID, BECOME_HUMANE, OBLIVION
@ -98,14 +98,14 @@ class PillItem(val pillType: PillType) :
val resolver = ply.getCapability(MatteryCapability.ANDROID).resolve()
if (resolver.isEmpty || resolver.get() !is AndroidCapabilityPlayer)
if (resolver.isEmpty)
return super.use(level, ply, hand)
val cap = resolver.get() as AndroidCapabilityPlayer
val cap = resolver.get()
if (
pillType == PillType.BECOME_ANDROID && !cap.isEverAndroid() ||
pillType == PillType.BECOME_HUMANE && cap.isEverAndroid() ||
pillType == PillType.BECOME_ANDROID && !cap.isEverAndroid ||
pillType == PillType.BECOME_HUMANE && cap.isEverAndroid ||
pillType == PillType.OBLIVION && cap.isAndroid
) {
ply.startUsingItem(hand)
@ -122,10 +122,10 @@ class PillItem(val pillType: PillType) :
if (ent is Player) {
val resolver = ent.getCapability(MatteryCapability.ANDROID).resolve()
if (resolver.isEmpty || resolver.get() !is AndroidCapabilityPlayer)
if (resolver.isEmpty)
return super.finishUsingItem(stack, level, ent)
val cap = resolver.get() as AndroidCapabilityPlayer
val cap = resolver.get()
if (pillType == PillType.OBLIVION && cap.isAndroid) {
if (!ent.abilities.instabuild) {
@ -136,7 +136,7 @@ class PillItem(val pillType: PillType) :
cap.obliviate()
ent.displayClientMessage(TranslatableComponent("otm.pill.message_oblivion").withStyle(ChatFormatting.GRAY), false)
}
} else if (pillType == PillType.BECOME_ANDROID && !cap.isEverAndroid()) {
} else if (pillType == PillType.BECOME_ANDROID && !cap.isEverAndroid) {
if (ent.abilities.instabuild) {
if (ent is ServerPlayer) {
cap.becomeAndroid()
@ -148,7 +148,7 @@ class PillItem(val pillType: PillType) :
cap.becomeAndroidSoft()
}
}
} else if (pillType == PillType.BECOME_HUMANE && cap.isEverAndroid()) {
} else if (pillType == PillType.BECOME_HUMANE && cap.isEverAndroid) {
if (ent.abilities.instabuild) {
if (ent is ServerPlayer) {
cap.becomeHumane()

View File

@ -6,12 +6,12 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.block.entity.AndroidStationBlockEntity
import ru.dbotthepony.mc.otm.capability.AndroidCapability
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.android.IAndroidCapability
import ru.dbotthepony.mc.otm.registry.MMenus
private class AndroidStationContainer(val player: Player) : SimpleContainer(1) {
var android: IAndroidCapability? = null
private class AndroidStationContainer(player: Player) : SimpleContainer(1) {
var android: AndroidCapability? = null
init {
player.getCapability(MatteryCapability.ANDROID).ifPresent {
@ -22,7 +22,7 @@ private class AndroidStationContainer(val player: Player) : SimpleContainer(1) {
}
private class AndroidBatterySlot(container: AndroidStationContainer, index: Int) : BatterySlot(container, index) {
val android: IAndroidCapability? = container.android
val android: AndroidCapability? = container.android
override fun set(stack: ItemStack) {
super.set(stack)

View File

@ -10,6 +10,8 @@ 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.AndroidResearchType
import ru.dbotthepony.mc.otm.capability.AndroidCapability
import ru.dbotthepony.mc.otm.capability.android
import ru.dbotthepony.mc.otm.capability.android.*
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
@ -24,24 +26,24 @@ import java.util.function.Supplier
sealed class AndroidStatusPacket : MatteryPacket {
override fun write(buff: FriendlyByteBuf) {}
protected abstract fun play(capability: AndroidCapabilityPlayer)
protected abstract fun play(capability: AndroidCapability)
override fun play(context: Supplier<NetworkEvent.Context>) {
context.get().packetHandled = true
context.get().enqueueWork {
play(minecraft.player?.android as? AndroidCapabilityPlayer ?: return@enqueueWork)
play(minecraft.player?.android ?: return@enqueueWork)
}
}
}
object IsAndroidPacket : AndroidStatusPacket() {
override fun play(capability: AndroidCapabilityPlayer) {
override fun play(capability: AndroidCapability) {
capability.isAndroid = true
}
}
object IsNotAndroidPacket : AndroidStatusPacket() {
override fun play(capability: AndroidCapabilityPlayer) {
override fun play(capability: AndroidCapability) {
capability.isAndroid = false
}
}
@ -49,13 +51,13 @@ object IsNotAndroidPacket : AndroidStatusPacket() {
fun IsAndroidPacket(status: Boolean) = if (status) IsAndroidPacket else IsNotAndroidPacket
object WillBecomeAndroidPacket : AndroidStatusPacket() {
override fun play(capability: AndroidCapabilityPlayer) {
override fun play(capability: AndroidCapability) {
capability.willBecomeAndroid = true
}
}
object WillNotBecomeAndroidPacket : AndroidStatusPacket() {
override fun play(capability: AndroidCapabilityPlayer) {
override fun play(capability: AndroidCapability) {
capability.willBecomeAndroid = false
}
}
@ -71,7 +73,7 @@ class AndroidResearchRequestPacket(val type: AndroidResearchType<*>) : MatteryPa
context.get().packetHandled = true
context.get().enqueueWork {
val ply = context.get().sender ?: return@enqueueWork
val android = ply.android as? AndroidCapabilityPlayer ?: return@enqueueWork
val android = ply.android ?: return@enqueueWork
if (!android.isAndroid || ply.containerMenu !is AndroidStationMenu)
return@enqueueWork
@ -107,7 +109,7 @@ class AndroidResearchSyncPacket(val type: AndroidResearchType<*>, val dataList:
context.get().packetHandled = true
context.get().enqueueWork {
val android = minecraft.player?.android as? AndroidCapabilityPlayer ?: return@enqueueWork
val android = minecraft.player?.android ?: return@enqueueWork
android.getResearch(type).readNetwork(ByteArrayInputStream(dataBytes))
}
@ -137,7 +139,7 @@ class AndroidFeatureSyncPacket(val type: AndroidFeatureType<*>, val dataList: Fa
context.get().packetHandled = true
context.get().enqueueWork {
val android = minecraft.player?.android as? AndroidCapabilityPlayer ?: return@enqueueWork
val android = minecraft.player?.android ?: return@enqueueWork
android.computeIfAbsent(type).readNetwork(ByteArrayInputStream(dataBytes))
}
@ -161,7 +163,7 @@ class AndroidFeatureRemovePacket(val type: AndroidFeatureType<*>) : MatteryPacke
override fun play(context: Supplier<NetworkEvent.Context>) {
context.get().packetHandled = true
context.get().enqueueWork {
val android = minecraft.player?.android as? AndroidCapabilityPlayer ?: return@enqueueWork
val android = minecraft.player?.android ?: return@enqueueWork
android.removeFeature(type)
}
@ -182,7 +184,7 @@ class AndroidBatteryItemPacket(val item: ItemStack) : MatteryPacket {
override fun play(context: Supplier<NetworkEvent.Context>) {
context.get().packetHandled = true
context.get().enqueueWork {
val android = minecraft.player?.android as? AndroidCapabilityPlayer ?: return@enqueueWork
val android = minecraft.player?.android ?: return@enqueueWork
android.batteryItemStack = item
}
@ -204,7 +206,7 @@ class AndroidEnergyLevelPacket(val level: ImpreciseFraction, val isMaxEnergy: Bo
override fun play(context: Supplier<NetworkEvent.Context>) {
context.get().packetHandled = true
context.get().enqueueWork {
val android = minecraft.player?.android as? AndroidCapabilityPlayer ?: return@enqueueWork
val android = minecraft.player?.android ?: return@enqueueWork
if (isMaxEnergy) {
android.maxBatteryLevel = level