Move Android capability to kotlin and fix issues along the way

This commit is contained in:
DBotThePony 2021-12-31 18:09:16 +07:00
parent 0acbdc8c69
commit 9efb4d6176
Signed by: DBot
GPG Key ID: DCC23B5715498507
6 changed files with 548 additions and 706 deletions

View File

@ -126,8 +126,8 @@ public class OverdriveThatMatters {
// Register ourselves for server and other game events we are interested in
MinecraftForge.EVENT_BUS.register(this);
MinecraftForge.EVENT_BUS.register(AndroidCapabilityPlayer.class);
MinecraftForge.EVENT_BUS.register(AndroidCapability.class);
MinecraftForge.EVENT_BUS.register(AndroidCapabilityPlayer.Companion);
MinecraftForge.EVENT_BUS.register(AndroidCapability.Companion);
MinecraftForge.EVENT_BUS.register(MatterRegistry.class);
MinecraftForge.EVENT_BUS.register(BlockEntityBlackHole.BlackHoleExplosionQueue.class);

View File

@ -1,586 +1,479 @@
package ru.dbotthepony.mc.otm.capability.android;
package ru.dbotthepony.mc.otm.capability.android
import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.core.Direction;
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.effect.MobEffect;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.INBTSerializable;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import net.minecraftforge.event.entity.living.LivingEvent;
import net.minecraftforge.event.entity.living.LivingHurtEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.network.PacketDistributor;
import ru.dbotthepony.mc.otm.Registry;
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.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.core.Fraction;
import ru.dbotthepony.mc.otm.network.MatteryNetworking;
import ru.dbotthepony.mc.otm.network.android.AndroidBatteryPacket;
import ru.dbotthepony.mc.otm.network.android.AndroidEnergyPacket;
import ru.dbotthepony.mc.otm.network.android.AndroidFeaturePacket;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.math.BigDecimal;
import java.util.*;
import net.minecraft.MethodsReturnNonnullByDefault
import net.minecraft.core.Direction
import javax.annotation.ParametersAreNonnullByDefault
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 ru.dbotthepony.mc.otm.network.android.AndroidFeaturePacket
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 ru.dbotthepony.mc.otm.network.android.AndroidEnergyPacket
import ru.dbotthepony.mc.otm.network.android.AndroidBatteryPacket
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.eventbus.api.SubscribeEvent
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent
import net.minecraftforge.eventbus.api.EventPriority
import ru.dbotthepony.mc.otm.Registry
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.Fraction
import ru.dbotthepony.mc.otm.ifHas
import ru.dbotthepony.mc.otm.set
import java.util.*
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public class AndroidCapability implements ICapabilityProvider, IAndroidCapability, INBTSerializable<CompoundTag> {
protected final LivingEntity ent;
protected Fraction energy_stored = Fraction.ZERO;
protected Fraction energy_stored_max = new Fraction(60_000);
protected ItemStack battery = ItemStack.EMPTY;
open class AndroidCapability(@JvmField protected val ent: LivingEntity) : ICapabilityProvider, IAndroidCapability, INBTSerializable<CompoundTag?> {
@JvmField
protected var energy_stored = Fraction.ZERO
@JvmField
protected var energy_stored_max = Fraction(60000)
private Fraction network_energy = new Fraction(-1);
private Fraction network_energy_max = new Fraction(-1);
private ItemStack network_battery = ItemStack.EMPTY;
override var batteryItemStack = ItemStack.EMPTY
protected final Map<AndroidFeatureType<?>, AndroidFeature> features = new HashMap<>();
protected final ArrayList<Object> network_queue = new ArrayList<>();
private var network_energy = Fraction(-1)
private var network_energy_max = Fraction(-1)
private var network_battery = ItemStack.EMPTY
protected final ArrayList<Runnable> delayed_tick_server = new ArrayList<>();
protected boolean network_first = false;
protected boolean addFeature(AndroidFeature feature) {
if (features.containsKey(feature.type))
return false;
features.put(feature.type, feature);
@JvmField protected val features: MutableMap<AndroidFeatureType<*>?, AndroidFeature> = HashMap()
@JvmField protected val network_queue = ArrayList<Any>()
@JvmField protected val delayed_tick_server = ArrayList<Runnable>()
@JvmField protected var network_first = false
protected fun addFeature(feature: AndroidFeature): Boolean {
if (features.containsKey(feature.type)) return false
features[feature.type] = feature
if (!ent.level.isClientSide) {
delayed_tick_server.add(feature::applyModifiers);
delayed_tick_server.add(Runnable { feature.applyModifiers() })
}
if (ent instanceof ServerPlayer ply) {
sendNetwork(new AndroidFeaturePacket(true, feature));
if (ent is ServerPlayer) {
sendNetwork(AndroidFeaturePacket(true, feature))
}
return true;
return true
}
@Override
@SuppressWarnings("unchecked")
public <T extends AndroidFeature> T addFeature(@Nonnull AndroidFeatureType<T> feature) {
var get = features.get(feature);
override fun <T : AndroidFeature> addFeature(feature: AndroidFeatureType<T>): T {
val get = features[feature]
if (get != null) return get as T
if (get != null)
return (T) get;
val factory = feature.factory(this)
var factory = feature.factory(this);
features.put(feature, factory);
features[feature] = factory
if (!ent.level.isClientSide) {
delayed_tick_server.add(factory::applyModifiers);
delayed_tick_server.add(Runnable { factory!!.applyModifiers() })
}
if (ent instanceof ServerPlayer ply) {
sendNetwork(new AndroidFeaturePacket(true, factory));
if (ent is ServerPlayer) {
sendNetwork(AndroidFeaturePacket(true, factory))
}
return factory;
return factory
}
@Override
public boolean removeFeature(@Nonnull AndroidFeatureType<?> feature) {
var removed = features.remove(feature);
override fun removeFeature(feature: AndroidFeatureType<*>): Boolean {
val removed = features.remove(feature)
if (removed != null) {
if (!ent.level.isClientSide) {
delayed_tick_server.add(removed::removeModifiers);
delayed_tick_server.add { removed.removeModifiers() }
}
if (ent instanceof ServerPlayer ply) {
sendNetwork(new AndroidFeaturePacket(false, removed));
if (ent is ServerPlayer) {
sendNetwork(AndroidFeaturePacket(false, removed))
}
return true;
return true
}
return false;
return false
}
@Override
public boolean hasFeature(@Nullable AndroidFeatureType<?> feature) {
return features.containsKey(feature);
override fun hasFeature(feature: AndroidFeatureType<*>): Boolean {
return features.containsKey(feature)
}
@Override
public boolean hasFeatureLevel(@Nullable AndroidFeatureType<?> feature, int level) {
var get = features.get(feature);
if (get == null) {
return false;
}
return get.getLevel() >= level;
override fun hasFeatureLevel(feature: AndroidFeatureType<*>, level: Int): Boolean {
val get = features[feature] ?: return false
return get.level >= level
}
@Nullable
@SuppressWarnings("unchecked")
public <T extends AndroidFeature> T getFeature(AndroidFeatureType<T> feature) {
return (T) features.get(feature);
override fun <T : AndroidFeature> getFeature(feature: AndroidFeatureType<T>): T? {
return features[feature] as T?
}
public static final Set<MobEffect> UNAFFECTED_EFFECTS = new HashSet<>();
override fun invalidateNetworkState() {
network_energy = Fraction.MINUS_ONE
network_energy_max = Fraction.MINUS_ONE
network_battery = ItemStack.EMPTY
public static void registerEffects(final FMLCommonSetupEvent event) {
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);
}
@Override
public void invalidateNetworkState() {
network_energy = Fraction.MINUS_ONE;
network_energy_max = Fraction.MINUS_ONE;
network_battery = ItemStack.EMPTY;
if (ent instanceof ServerPlayer ply) {
for (var feature : features.values()) {
sendNetwork(new AndroidFeaturePacket(true, feature));
feature.invalidateNetwork();
if (ent is ServerPlayer) {
for (feature in features.values) {
sendNetwork(AndroidFeaturePacket(true, feature))
feature.invalidateNetwork()
}
}
}
@Override
public void setEnergy(Fraction value) {
energy_stored = value;
override fun setEnergy(value: Fraction?) {
energy_stored = value!!
}
@Override
public void setMaxEnergy(Fraction value) {
energy_stored_max = value;
override fun setMaxEnergy(value: Fraction?) {
energy_stored_max = value!!
}
public AndroidCapability(LivingEntity ent) {
this.ent = ent;
}
@SubscribeEvent
@SuppressWarnings("unused")
public static void onLivingTick(LivingEvent.LivingUpdateEvent event) {
var ent = event.getEntity();
ent.getCapability(MatteryCapability.ANDROID).ifPresent(ent.level.isClientSide ? IAndroidCapability::tickClient : IAndroidCapability::tick);
}
@SubscribeEvent(priority = EventPriority.LOWEST)
@SuppressWarnings("unused")
public static void onHurtEvent(LivingHurtEvent event) {
if (event.isCanceled())
return;
var ent = event.getEntity();
ent.getCapability(MatteryCapability.ANDROID).ifPresent((cap) -> cap.onHurt(event));
}
public void onHurt(LivingHurtEvent event) {
for (var feature : features.values()) {
feature.onHurt(event);
override fun onHurt(event: LivingHurtEvent?) {
for (feature in features.values) {
feature.onHurt(event)
}
}
@Override
@Nonnull
public CompoundTag serializeNBT() {
CompoundTag tag = new CompoundTag();
tag.putString("energy_stored", energy_stored.toString());
tag.putString("energy_stored_max", energy_stored_max.toString());
tag.put("battery", battery.serializeNBT());
override fun serializeNBT(): CompoundTag {
val tag = CompoundTag()
var features_nbt = new ListTag();
tag["energy_stored"] = energy_stored.serializeNBT()
tag["energy_stored_max"] = energy_stored_max.serializeNBT()
tag["battery"] = batteryItemStack.serializeNBT()
for (var feature : features.values()) {
var compound = new CompoundTag();
feature.serializeNBT(compound);
compound.putString("id", Objects.requireNonNull(feature.type.getRegistryName()).toString());
features_nbt.add(compound);
val featureList = ListTag()
for (feature in features.values) {
val featureNbt = CompoundTag()
feature.serializeNBT(featureNbt)
featureNbt["id"] = feature.type.registryName!!.toString()
featureList.add(featureNbt)
}
tag.put("features", features_nbt);
return tag;
tag["features"] = featureList
return tag
}
@Override
public void deserializeNBT(CompoundTag compound) {
if (compound.contains("energy_stored"))
energy_stored = Fraction.deserializeNBT(compound.get("energy_stored"));
override fun deserializeNBT(compound: CompoundTag?) {
compound!!
if (compound.contains("energy_stored_max"))
energy_stored_max = Fraction.deserializeNBT(compound.get("energy_stored_max"));
compound.ifHas("energy_stored") {
energy_stored = Fraction.deserializeNBT(it)
}
if (compound.contains("battery"))
battery = ItemStack.of(compound.getCompound("battery"));
compound.ifHas("energy_stored_max") {
energy_stored_max = Fraction.deserializeNBT(it)
}
features.clear();
compound.ifHas("battery", CompoundTag::class.java) {
batteryItemStack = ItemStack.of(it)
}
var features_nbt = compound.getList("features", Tag.TAG_COMPOUND);
features.clear()
for (var _tag : features_nbt) {
if (_tag instanceof CompoundTag tag) {
var get_feature = Registry.ANDROID_FEATURES.getValue(new ResourceLocation(tag.getString("id")));
val featuresNbt = compound.getList("features", Tag.TAG_COMPOUND.toInt())
if (get_feature != null && get_feature.isApplicable(this)) {
var feature = get_feature.factory(this);
feature.deserializeNBT(tag);
for (tag in featuresNbt) {
if (tag is CompoundTag) {
val feature = Registry.ANDROID_FEATURES.getValue(ResourceLocation(tag.getString("id")))
addFeature(feature);
if (feature != null && feature.isApplicable(this)) {
val feature = feature.factory(this)
if (ent.level == null || !ent.level.isClientSide) {
delayed_tick_server.add(feature::applyModifiers);
feature.deserializeNBT(tag)
addFeature(feature)
if (!ent.level.isClientSide) {
delayed_tick_server.add(Runnable { feature.applyModifiers() })
}
}
}
}
}
public void dropBattery() {
if (battery.isEmpty())
return;
ent.spawnAtLocation(battery);
battery = ItemStack.EMPTY;
fun dropBattery() {
if (batteryItemStack.isEmpty) return
ent.spawnAtLocation(batteryItemStack)
batteryItemStack = ItemStack.EMPTY
}
protected void sendNetwork(Object packet) {
if (ent instanceof ServerPlayer ply) {
protected fun sendNetwork(packet: Any) {
if (ent is ServerPlayer) {
if (network_first) {
MatteryNetworking.CHANNEL.send(PacketDistributor.PLAYER.with(() -> ply), packet);
MatteryNetworking.CHANNEL.send(PacketDistributor.PLAYER.with { ent }, packet)
} else {
network_queue.add(packet);
network_queue.add(packet)
}
}
}
protected void tickNetwork() {
network_first = true;
protected open fun tickNetwork() {
network_first = true
if (ent instanceof ServerPlayer ply) {
if (!energy_stored.equals(network_energy)) {
network_energy = energy_stored;
sendNetwork(new AndroidEnergyPacket(false, energy_stored));
if (ent is ServerPlayer) {
if (energy_stored != network_energy) {
network_energy = energy_stored
sendNetwork(AndroidEnergyPacket(false, energy_stored))
}
if (!energy_stored_max.equals(network_energy_max)) {
network_energy_max = energy_stored_max;
sendNetwork(new AndroidEnergyPacket(true, energy_stored_max));
if (energy_stored_max != network_energy_max) {
network_energy_max = energy_stored_max
sendNetwork(AndroidEnergyPacket(true, energy_stored_max))
}
if (!network_battery.equals(battery, false)) {
network_battery = battery.copy();
sendNetwork(new AndroidBatteryPacket(battery));
if (!network_battery.equals(batteryItemStack, false)) {
network_battery = batteryItemStack.copy()
sendNetwork(AndroidBatteryPacket(batteryItemStack))
}
if (network_queue.size() != 0) {
for (var packet : network_queue) {
MatteryNetworking.CHANNEL.send(PacketDistributor.PLAYER.with(() -> ply), packet);
if (network_queue.size != 0) {
for (packet in network_queue) {
MatteryNetworking.CHANNEL.send(PacketDistributor.PLAYER.with {ent}, packet)
}
network_queue.clear();
network_queue.clear()
}
}
}
protected void tickInnerClient() {
}
protected void tickInnerClientAlways() {
}
@Override
public void tickClient() {
delayed_tick_server.clear();
if (!ent.isAlive())
return;
tickInnerClientAlways();
protected open fun tickInnerClient() {}
protected open fun tickInnerClientAlways() {}
override fun tickClient() {
delayed_tick_server.clear()
if (!ent.isAlive) return
tickInnerClientAlways()
if (isAndroid()) {
tickInnerClient();
for (var feature : features.values()) {
feature.tickClient();
tickInnerClient()
for (feature in features.values) {
feature.tickClient()
}
}
}
@Override
public void tick() {
if (!ent.isAlive())
return;
tickServerAlways();
override fun tick() {
if (!ent.isAlive) return
tickServerAlways()
if (isAndroid()) {
tickServer();
for (var feature : features.values()) {
feature.tickServer();
tickServer()
for (feature in features.values) {
feature.tickServer()
}
}
for (var runnable : delayed_tick_server) {
runnable.run();
for (runnable in delayed_tick_server) {
runnable.run()
}
delayed_tick_server.clear();
tickNetwork();
delayed_tick_server.clear()
tickNetwork()
}
protected void tickServerAlways() {
protected open fun tickServerAlways() {}
}
protected open fun tickServer() {
if (ent.airSupply < ent.maxAirSupply)
ent.airSupply = ent.maxAirSupply
protected void tickServer() {
if (ent.getAirSupply() < ent.getMaxAirSupply())
ent.setAirSupply(ent.getMaxAirSupply());
for (var effect : UNAFFECTED_EFFECTS)
for (effect in UNAFFECTED_EFFECTS)
if (ent.hasEffect(effect))
ent.removeEffect(effect);
ent.removeEffect(effect)
if (!battery.isEmpty()) {
Fraction demand = energy_stored_max.minus(energy_stored);
if (demand.compareTo(Fraction.ZERO) > 0) {
Optional<IMatteryEnergyStorage> get_mattery = battery.getCapability(MatteryCapability.ENERGY).resolve();
if (get_mattery.isPresent()) {
demand = demand.minus(get_mattery.get().extractEnergyInner(demand, false));
if (!batteryItemStack.isEmpty && energy_stored < energy_stored_max) {
batteryItemStack.getCapability(CapabilityEnergy.ENERGY).ifPresent {
if (it is IMatteryEnergyStorage) {
energy_stored += it.extractEnergyInner(energy_stored_max - energy_stored, false)
} else {
energy_stored += it.extractEnergy(energy_stored_max - energy_stored, false)
}
if (demand.compareTo(Fraction.ONE) >= 0) {
Optional<IEnergyStorage> get_energy = battery.getCapability(CapabilityEnergy.ENERGY).resolve();
if (get_energy.isPresent()) {
demand = demand.minus(MatteryCapability.drainFE(get_energy.get(), demand, false));
}
}
energy_stored = energy_stored_max.minus(demand);
}
}
}
@Nonnull
@Override
public Fraction extractEnergyOuter(Fraction howMuch, boolean simulate) {
return Fraction.ZERO;
override fun extractEnergyOuter(howMuch: Fraction, simulate: Boolean): Fraction {
return Fraction.ZERO
}
@Nonnull
@Override
public Fraction extractEnergyInner(Fraction howMuch, boolean simulate) {
Fraction drained = Fraction.ZERO;
override fun extractEnergyInner(howMuch: Fraction, simulate: Boolean): Fraction {
var howMuch = howMuch
var drained = Fraction.ZERO
if (battery != ItemStack.EMPTY) {
Optional<IMatteryEnergyStorage> get_mattery = battery.getCapability(MatteryCapability.ENERGY).resolve();
if (get_mattery.isPresent()) {
Fraction changed = get_mattery.get().extractEnergyOuter(howMuch, simulate);
if (changed.compareTo(Fraction.ZERO) > 0) {
drained = drained.plus(changed);
howMuch = howMuch.minus(changed);
if (howMuch.compareTo(Fraction.ZERO) <= 0) {
if (!simulate && ent instanceof ServerPlayer ply) {
ply.awardStat(Registry.Names.POWER_CONSUMED, drained.toInt() * 10);
}
return drained;
}
if (!batteryItemStack.isEmpty) {
batteryItemStack.getCapability(CapabilityEnergy.ENERGY).ifPresent {
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.compareTo(Fraction.ONE) >= 0) {
Optional<IEnergyStorage> get_energy = battery.getCapability(CapabilityEnergy.ENERGY).resolve();
if (get_energy.isPresent()) {
Fraction changed = MatteryCapability.drainFE(get_energy.get(), howMuch, simulate);
if (changed.compareTo(Fraction.ZERO) > 0) {
drained = drained.plus(changed);
howMuch = howMuch.minus(changed);
if (howMuch.compareTo(Fraction.ZERO) <= 0) {
if (!simulate && ent instanceof ServerPlayer ply) {
ply.awardStat(Registry.Names.POWER_CONSUMED, drained.toInt() * 10);
}
return drained;
}
}
if (howMuch.isZero()) {
if (!simulate && ent is ServerPlayer) {
ent.awardStat(Registry.Names.POWER_CONSUMED, drained.toInt() * 10)
}
return drained
}
}
Fraction new_energy = energy_stored.minus(howMuch).max(Fraction.ZERO);
drained = drained.plus(energy_stored.minus(new_energy));
val new = (energy_stored - howMuch).moreThanZero()
drained += energy_stored - new
if (!simulate) {
energy_stored = new_energy;
energy_stored = new
if (ent instanceof ServerPlayer ply) {
ply.awardStat(Registry.Names.POWER_CONSUMED, drained.toInt() * 10);
if (ent is ServerPlayer) {
ent.awardStat(Registry.Names.POWER_CONSUMED, drained.toInt() * 10)
}
}
return drained;
return drained
}
@Nonnull
@Override
public Fraction receiveEnergyOuter(Fraction howMuch, boolean simulate) {
Fraction received = Fraction.ZERO;
override fun receiveEnergyOuter(howMuch: Fraction, simulate: Boolean): Fraction {
var howMuch = howMuch
var received = Fraction.ZERO
if (battery != ItemStack.EMPTY) {
Optional<IMatteryEnergyStorage> get_mattery = battery.getCapability(MatteryCapability.ENERGY).resolve();
if (get_mattery.isPresent()) {
Fraction changed = get_mattery.get().receiveEnergyOuter(howMuch, simulate);
if (changed.compareTo(Fraction.ZERO) > 0) {
received = received.plus(changed);
howMuch = howMuch.minus(changed);
if (howMuch.compareTo(Fraction.ZERO) <= 0) {
return received;
}
if (!batteryItemStack.isEmpty) {
batteryItemStack.getCapability(CapabilityEnergy.ENERGY).ifPresent {
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.compareTo(Fraction.ONE) >= 0) {
Optional<IEnergyStorage> get_energy = battery.getCapability(CapabilityEnergy.ENERGY).resolve();
if (get_energy.isPresent()) {
Fraction changed = MatteryCapability.floodFE(get_energy.get(), howMuch, simulate);
if (changed.compareTo(Fraction.ZERO) > 0) {
received = received.plus(changed);
howMuch = howMuch.minus(changed);
if (howMuch.compareTo(Fraction.ZERO) <= 0) {
return received;
}
}
}
if (howMuch.isZero()) {
return received
}
}
Fraction new_energy = energy_stored.plus(howMuch).min(energy_stored_max);
received = received.plus(new_energy.minus(energy_stored));
val new = (energy_stored + howMuch).min(energy_stored_max)
received += new - energy_stored
if (!simulate) {
energy_stored = new_energy;
energy_stored = new
}
return received;
return received
}
@Nonnull
@Override
public Fraction receiveEnergyInner(Fraction howMuch, boolean simulate) {
return receiveEnergyOuter(howMuch, simulate);
override fun receiveEnergyInner(howMuch: Fraction, simulate: Boolean): Fraction {
return receiveEnergyOuter(howMuch, simulate)
}
@Override
public LivingEntity getEntity() {
return this.ent;
override fun getEntity(): LivingEntity {
return ent
}
@Nonnull
@Override
public ItemStack getBatteryItemStack() {
return battery;
}
override fun getBatteryLevel(): Fraction {
if (!batteryItemStack.isEmpty) {
val resolver = batteryItemStack.getCapability(CapabilityEnergy.ENERGY).resolve()
@Override
public void setBatteryItemStack(@Nonnull ItemStack stack) {
battery = stack;
}
if (resolver.isPresent) {
val it = resolver.get()
@Override
@Nonnull
public Fraction getBatteryLevel() {
if (battery != ItemStack.EMPTY) {
Optional<IMatteryEnergyStorage> get_mattery = battery.getCapability(MatteryCapability.ENERGY).resolve();
if (get_mattery.isPresent()) {
return get_mattery.get().getBatteryLevel().plus(energy_stored);
}
Optional<IEnergyStorage> get_energy = battery.getCapability(CapabilityEnergy.ENERGY).resolve();
if (get_energy.isPresent()) {
return energy_stored.plus(get_energy.get().getEnergyStored());
if (it is IMatteryEnergyStorage) {
return energy_stored + it.batteryLevel
} else {
return energy_stored + it.energyStored
}
}
}
return energy_stored;
return energy_stored
}
@Override
@Nonnull
public Fraction getMaxBatteryLevel() {
if (battery != ItemStack.EMPTY) {
Optional<IMatteryEnergyStorage> get_mattery = battery.getCapability(MatteryCapability.ENERGY).resolve();
override fun getMaxBatteryLevel(): Fraction {
if (batteryItemStack != ItemStack.EMPTY) {
val resolver = batteryItemStack.getCapability(CapabilityEnergy.ENERGY).resolve()
if (get_mattery.isPresent()) {
return get_mattery.get().getMaxBatteryLevel().plus(energy_stored_max);
}
if (resolver.isPresent) {
val it = resolver.get()
Optional<IEnergyStorage> get_energy = battery.getCapability(CapabilityEnergy.ENERGY).resolve();
if (get_energy.isPresent()) {
return energy_stored_max.plus(get_energy.get().getMaxEnergyStored());
if (it is IMatteryEnergyStorage) {
return energy_stored_max + it.maxBatteryLevel
} else {
return energy_stored_max + it.maxEnergyStored
}
}
}
return energy_stored_max;
return energy_stored_max
}
private final LazyOptional<IAndroidCapability> resolver = LazyOptional.of(() -> this).cast();
private val resolver = LazyOptional.of { this }
@Nonnull
@Override
public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {
if (cap == MatteryCapability.ANDROID) {
return resolver.cast();
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: MutableSet<MobEffect> = HashSet()
@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)
}
return LazyOptional.empty();
@SubscribeEvent
fun onLivingTick(event: LivingUpdateEvent) {
val ent = event.entity
if (ent.level.isClientSide) {
ent.getCapability(MatteryCapability.ANDROID).ifPresent {
it.tickClient()
}
} else {
ent.getCapability(MatteryCapability.ANDROID).ifPresent {
it.tick()
}
}
}
@SubscribeEvent(priority = EventPriority.LOWEST)
fun onHurtEvent(event: LivingHurtEvent) {
if (event.isCanceled) return
val ent = event.entity
ent.getCapability(MatteryCapability.ANDROID).ifPresent { cap: IAndroidCapability -> cap.onHurt(event) }
}
}
}
}

View File

@ -1,328 +1,285 @@
package ru.dbotthepony.mc.otm.capability.android;
package ru.dbotthepony.mc.otm.capability.android
import net.minecraft.ChatFormatting;
import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.TranslatableComponent;
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.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import ru.dbotthepony.mc.otm.Registry;
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.Fraction;
import ru.dbotthepony.mc.otm.network.android.AndroidResearchPacket;
import ru.dbotthepony.mc.otm.network.android.AndroidStatusPacket;
import net.minecraft.ChatFormatting
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.Tag
import net.minecraft.network.chat.TranslatableComponent
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.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 ru.dbotthepony.mc.otm.Registry
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.Fraction
import ru.dbotthepony.mc.otm.network.android.AndroidResearchPacket
import ru.dbotthepony.mc.otm.network.android.AndroidStatusPacket
import ru.dbotthepony.mc.otm.set
import java.util.*
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Objects;
class AndroidCapabilityPlayer(@JvmField val ply: Player) : AndroidCapability(ply) {
@JvmField
var is_android = false
private var network_is_android = false
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public class AndroidCapabilityPlayer extends AndroidCapability {
public boolean is_android = false;
private boolean network_is_android = false;
@JvmField
var will_become_android = false
private var network_will_become_android = false
private val research = ArrayList<AndroidResearch>()
public boolean will_become_android = false;
private boolean network_will_become_android = false;
override fun invalidateNetworkState() {
super.invalidateNetworkState()
private final ArrayList<AndroidResearch> research_list = new ArrayList<>();
network_is_android = false
network_will_become_android = false
@Override
public void invalidateNetworkState() {
super.invalidateNetworkState();
network_is_android = false;
network_will_become_android = false;
for (var instance : research_list) {
instance.dirty = true;
for (instance in research) {
instance.dirty = true
}
}
@Override
public boolean isAndroid() {
return is_android;
override fun isAndroid(): Boolean = is_android
fun isEverAndroid(): Boolean = is_android || will_become_android
fun becomeAndroidSoft() {
if (is_android || will_become_android) return
will_become_android = true
(ply as? ServerPlayer)?.displayClientMessage(TranslatableComponent("otm.pill.message").withStyle(ChatFormatting.GRAY), false)
}
public boolean isEverAndroid() {
return is_android || will_become_android;
fun becomeAndroid() {
if (is_android) return
is_android = true
will_become_android = false
energy_stored = Fraction(60000)
energy_stored_max = Fraction(60000)
}
public void becomeAndroidSoft() {
if (is_android || will_become_android)
return;
fun becomeAndroidAndKill() {
if (is_android) return
will_become_android = true;
if (ent instanceof ServerPlayer ply) {
ply.displayClientMessage(new TranslatableComponent("otm.pill.message").withStyle(ChatFormatting.GRAY), false);
}
becomeAndroid()
ply.hurt(Registry.DAMAGE_BECOME_ANDROID, ply.maxHealth * 2)
}
public void becomeAndroid() {
if (is_android)
return;
fun becomeHumane() {
if (will_become_android) will_become_android = false
if (!is_android) return
is_android = true;
will_become_android = false;
energy_stored = new Fraction(60_000);
energy_stored_max = new Fraction(60_000);
is_android = false
energy_stored = Fraction(0)
energy_stored_max = Fraction(60000)
dropBattery()
}
public void becomeAndroidAndKill() {
if (is_android)
return;
fun becomeHumaneAndKill() {
if (will_become_android) will_become_android = false
if (!is_android) return
becomeAndroid();
ply.hurt(Registry.DAMAGE_BECOME_ANDROID, ply.getMaxHealth() * 2);
becomeHumane()
ply.hurt(Registry.DAMAGE_BECOME_HUMANE, ply.maxHealth * 2)
}
public void becomeHumane() {
if (will_become_android)
will_become_android = false;
override fun deserializeNBT(compound: CompoundTag?) {
super.deserializeNBT(compound!!)
if (!is_android)
return;
is_android = compound.getBoolean("is_android")
will_become_android = compound.getBoolean("will_become_android")
research.clear()
is_android = false;
val list = compound.getList("research", Tag.TAG_COMPOUND.toInt())
energy_stored = new Fraction(0);
energy_stored_max = new Fraction(60_000);
dropBattery();
}
public void becomeHumaneAndKill() {
if (will_become_android)
will_become_android = false;
if (!is_android)
return;
becomeHumane();
ply.hurt(Registry.DAMAGE_BECOME_HUMANE, ply.getMaxHealth() * 2);
}
@Override
public void deserializeNBT(CompoundTag compound) {
super.deserializeNBT(compound);
is_android = compound.getBoolean("is_android");
will_become_android = compound.getBoolean("will_become_android");
research_list.clear();
var list = compound.getList("research", Tag.TAG_COMPOUND);
for (var _tag : list) {
if (_tag instanceof CompoundTag tag) {
var research = Registry.ANDROID_RESEARCH.getValue(new ResourceLocation(tag.getString("id")));
for (tag in list) {
if (tag is CompoundTag) {
val research = Registry.ANDROID_RESEARCH.getValue(ResourceLocation(tag.getString("id")))
if (research != null) {
var instance = research.factory(this);
instance.deserializeNBT(tag);
research_list.add(instance);
val instance = research.factory(this)
instance.deserializeNBT(tag)
this.research.add(instance)
}
}
}
}
@Nonnull
@Override
public CompoundTag serializeNBT() {
CompoundTag tag = super.serializeNBT();
tag.putBoolean("is_android", is_android);
tag.putBoolean("will_become_android", will_become_android);
override fun serializeNBT(): CompoundTag {
val tag = super.serializeNBT()
var list = new ListTag();
tag["is_android"] = is_android
tag["will_become_android"] = will_become_android
for (var instance : research_list) {
var _tag = new CompoundTag();
instance.serializeNBT(_tag);
_tag.putString("id", Objects.requireNonNull(instance.type.getRegistryName()).toString());
list.add(_tag);
val list = ListTag()
for (instance in research) {
val researchTag = CompoundTag()
instance.serializeNBT(researchTag)
researchTag["id"] = instance.type.registryName!!.toString()
list.add(researchTag)
}
tag.put("research", list);
return tag;
tag["research"] = list
return tag
}
@SuppressWarnings("unchecked")
public <T extends AndroidResearch> T getResearch(AndroidResearchType<T> type) {
for (var instance : research_list) {
if (instance.type == type) {
return (T) instance;
fun <T : AndroidResearch> getResearch(type: AndroidResearchType<T>): T {
for (instance in research) {
if (instance.type === type) {
return instance as T
}
}
var instance = type.factory(this);
research_list.add(instance);
return instance;
val instance = type.factory(this)
research.add(instance)
return instance
}
@SubscribeEvent
@SuppressWarnings("unused")
public static void onAttachCapabilityEvent(AttachCapabilitiesEvent<Entity> event) {
if (event.getObject() instanceof Player ply) {
event.addCapability(Registry.Names.ANDROID_CAPABILITY, new AndroidCapabilityPlayer(ply));
}
}
@SubscribeEvent
@SuppressWarnings("unused")
public static void onPlayerChangeDimensionEvent(PlayerEvent.PlayerChangedDimensionEvent event) {
event.getPlayer().getCapability(MatteryCapability.ANDROID).ifPresent(IAndroidCapability::invalidateNetworkState);
}
@SubscribeEvent
@SuppressWarnings("unused")
public static void onPlayerCloneEvent(PlayerEvent.Clone event) {
event.getPlayer().getCapability(MatteryCapability.ANDROID).ifPresent((cap) -> {
LazyOptional<IAndroidCapability> resolver = event.getOriginal().getCapability(MatteryCapability.ANDROID);
if (!resolver.isPresent() || resolver.resolve().isEmpty()) {
event.getOriginal().reviveCaps();
resolver = event.getOriginal().getCapability(MatteryCapability.ANDROID);
}
if (!resolver.isPresent() || resolver.resolve().isEmpty()) {
event.getOriginal().invalidateCaps();
return;
}
var original = (AndroidCapabilityPlayer) resolver.resolve().get();
if (original.will_become_android && event.isWasDeath()) {
original.becomeAndroid();
if (event.getPlayer() instanceof ServerPlayer ply) {
ply.displayClientMessage(new TranslatableComponent("otm.pill.message_finish").withStyle(ChatFormatting.DARK_RED), false);
}
}
cap.deserializeNBT(original.serializeNBT());
cap.invalidateNetworkState();
event.getOriginal().invalidateCaps();
});
}
public int last_jump_ticks = 14;
public final Player ply;
public AndroidCapabilityPlayer(Player ent) {
super(ent);
ply = ent;
}
@Override
protected void tickNetwork() {
super.tickNetwork();
@JvmField
var last_jump_ticks = 14
override fun tickNetwork() {
super.tickNetwork()
if (is_android != network_is_android) {
network_is_android = is_android;
sendNetwork(new AndroidStatusPacket(AndroidStatusPacket.Type.IS_ANDROID, is_android));
network_is_android = is_android
sendNetwork(AndroidStatusPacket(AndroidStatusPacket.Type.IS_ANDROID, is_android))
}
if (will_become_android != network_will_become_android) {
network_will_become_android = will_become_android;
sendNetwork(new AndroidStatusPacket(AndroidStatusPacket.Type.WILL_BECOME_ANDROID, will_become_android));
network_will_become_android = will_become_android
sendNetwork(AndroidStatusPacket(AndroidStatusPacket.Type.WILL_BECOME_ANDROID, will_become_android))
}
for (var instance : research_list) {
for (instance in research) {
if (instance.dirty) {
instance.dirty = false;
sendNetwork(new AndroidResearchPacket(instance));
instance.dirty = false
sendNetwork(AndroidResearchPacket(instance))
}
}
}
public static final Fraction ENERGY_FOR_HUNGER_POINT = new Fraction(1000);
@JvmField
var sleep_ticks = 0
public int sleep_ticks = 0;
public static final int SLEEP_TICKS_LIMIT = 80;
@Override
protected void tickInnerClientAlways() {
super.tickInnerClientAlways();
override fun tickInnerClientAlways() {
super.tickInnerClientAlways()
if (will_become_android) {
if (ent.isSleeping()) {
sleep_ticks++;
if (ply.isSleeping) {
sleep_ticks++
} else {
sleep_ticks = 0;
sleep_ticks = 0
}
}
}
@Override
protected void tickServerAlways() {
super.tickServerAlways();
override fun tickServerAlways() {
super.tickServerAlways()
if (will_become_android) {
if (ent.isSleeping()) {
sleep_ticks++;
if (ply.isSleeping) {
sleep_ticks++
if (sleep_ticks > SLEEP_TICKS_LIMIT) {
becomeAndroid();
sleep_ticks = 0;
becomeAndroid()
sleep_ticks = 0
if (ent instanceof ServerPlayer ply) {
ply.displayClientMessage(new TranslatableComponent("otm.pill.message_finish").withStyle(ChatFormatting.DARK_RED), false);
}
(ply as? ServerPlayer)?.displayClientMessage(TranslatableComponent("otm.pill.message_finish").withStyle(ChatFormatting.DARK_RED), false)
}
} else {
sleep_ticks = 0;
sleep_ticks = 0
}
}
}
@Override
public void tickServer() {
super.tickServer();
public override fun tickServer() {
super.tickServer()
// TODO: Maybe passive drain?
// extractEnergyInner(BigDecimal.valueOf(new Random().nextDouble()), false);
if (ply.isSwimming() && !hasFeature(Registry.AndroidFeatures.AIR_BAGS)) {
ply.setSwimming(false);
if (ply.isSwimming && !hasFeature(Registry.AndroidFeatures.AIR_BAGS)) {
ply.isSwimming = false
}
var stats = ply.getFoodData();
val stats = ply.foodData
while (stats.getFoodLevel() < 18 && this.extractEnergyInner(ENERGY_FOR_HUNGER_POINT, true).compareTo(ENERGY_FOR_HUNGER_POINT) == 0) {
this.extractEnergyInner(ENERGY_FOR_HUNGER_POINT, false);
stats.setFoodLevel(stats.getFoodLevel() + 1);
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.getFoodLevel() > 18 && this.receiveEnergyInner(ENERGY_FOR_HUNGER_POINT, true).compareTo(ENERGY_FOR_HUNGER_POINT) == 0) {
this.receiveEnergyInner(ENERGY_FOR_HUNGER_POINT, false);
stats.setFoodLevel(stats.getFoodLevel() - 1);
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
}
var food_level = (float) stats.getFoodLevel();
val foodLevel = stats.foodLevel.toFloat()
if (stats.getSaturationLevel() < food_level) {
Fraction extracted = this.extractEnergyInner(ENERGY_FOR_HUNGER_POINT.times(food_level - stats.getSaturationLevel()), false);
stats.setSaturation(stats.getSaturationLevel() + extracted.div(ENERGY_FOR_HUNGER_POINT).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.getExhaustionLevel() > 0f) {
Fraction extracted = this.extractEnergyInner(ENERGY_FOR_HUNGER_POINT.times(stats.getExhaustionLevel() / 4f), false);
stats.setExhaustion(stats.getExhaustionLevel() - extracted.div(ENERGY_FOR_HUNGER_POINT).toFloat() * 4f);
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(Registry.Names.ANDROID_CAPABILITY, AndroidCapabilityPlayer(ent))
}
}
@SubscribeEvent
fun onPlayerChangeDimensionEvent(event: PlayerChangedDimensionEvent) {
event.player.getCapability(MatteryCapability.ANDROID)
.ifPresent { it.invalidateNetworkState() }
}
@SubscribeEvent
fun onPlayerCloneEvent(event: Clone) {
event.player.getCapability(MatteryCapability.ANDROID).ifPresent {
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@ifPresent
}
val original = resolver.resolve().get() as AndroidCapabilityPlayer
if (original.will_become_android && event.isWasDeath) {
original.becomeAndroid()
(event.player 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 = Fraction(1000)
const val SLEEP_TICKS_LIMIT = 80
}
}

View File

@ -1,51 +1,45 @@
package ru.dbotthepony.mc.otm.capability.android;
package ru.dbotthepony.mc.otm.capability.android
import net.minecraft.MethodsReturnNonnullByDefault;
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.Fraction;
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.Fraction
import java.util.*
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.math.BigDecimal;
import java.util.Optional;
interface IAndroidCapability : IMatteryEnergyStorage, INBTSerializable<CompoundTag?> {
fun tick()
fun tickClient()
fun getEntity(): 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
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public interface IAndroidCapability extends IMatteryEnergyStorage, INBTSerializable<CompoundTag> {
void tick();
void tickClient();
LivingEntity getEntity();
fun <T : AndroidFeature> getFeature(feature: AndroidFeatureType<T>): T?
<T extends AndroidFeature> T addFeature(AndroidFeatureType<T> feature);
boolean removeFeature(AndroidFeatureType<?> feature);
boolean hasFeature(@Nullable AndroidFeatureType<?> feature);
boolean hasFeatureLevel(@Nullable AndroidFeatureType<?> feature, int level);
@Nullable
<T extends AndroidFeature> T getFeature(AndroidFeatureType<T> feature);
default <T extends AndroidFeature> Optional<T> getFeatureO(AndroidFeatureType<T> feature) {
var get = getFeature(feature);
return get != null ? Optional.of(get) : Optional.empty();
fun <T : AndroidFeature> getFeatureO(feature: AndroidFeatureType<T>): Optional<T> {
val get = getFeature(feature)
return if (get != null) Optional.of(get) else Optional.empty()
}
default boolean isAndroid() {
return true;
fun <T : AndroidFeature> ifFeature(feature: AndroidFeatureType<T>, consumer: (T) -> Unit) {
val get = getFeature(feature)
if (get != null) {
consumer(get)
}
}
default void onHurt(LivingHurtEvent event) {}
ItemStack getBatteryItemStack();
void setBatteryItemStack(ItemStack stack);
fun isAndroid(): Boolean = true
void invalidateNetworkState(); // tell capability that player forgot everything, and everything needs to be re-networked
void setEnergy(Fraction value);
void setMaxEnergy(Fraction value);
}
fun onHurt(event: LivingHurtEvent?) {}
var batteryItemStack: ItemStack
fun invalidateNetworkState() // tell capability that player forgot everything, and everything needs to be re-networked
fun setEnergy(value: Fraction?)
fun setMaxEnergy(value: Fraction?)
}

View File

@ -19,9 +19,7 @@ import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel
import ru.dbotthepony.mc.otm.menu.AndroidStationMenu
import java.util.*
class AndroidStationScreen(p_97741_: AndroidStationMenu, p_97742_: Inventory, p_97743_: Component) :
class AndroidStationScreen constructor(p_97741_: AndroidStationMenu, p_97742_: Inventory, p_97743_: Component) :
MatteryScreen<AndroidStationMenu>(p_97741_, p_97742_, p_97743_) {
internal inner class AndroidResearchButton(parent: EditablePanel?, private val node: AndroidResearch) :

View File

@ -53,7 +53,7 @@ class ItemPill(val pillType: PillType) :
val cap = resolver.get() as AndroidCapabilityPlayer
if (pillType == PillType.BECOME_ANDROID && !cap.isEverAndroid || pillType == PillType.BECOME_HUMANE && cap.isEverAndroid) {
if (pillType == PillType.BECOME_ANDROID && !cap.isEverAndroid() || pillType == PillType.BECOME_HUMANE && cap.isEverAndroid()) {
ply.startUsingItem(hand)
return InteractionResultHolder.consume(ply.getItemInHand(hand))
}
@ -73,7 +73,7 @@ class ItemPill(val pillType: PillType) :
val cap = resolver.get() as AndroidCapabilityPlayer
if (pillType == PillType.BECOME_ANDROID && !cap.isEverAndroid) {
if (pillType == PillType.BECOME_ANDROID && !cap.isEverAndroid()) {
if (ent.abilities.instabuild) {
if (ent is ServerPlayer) {
cap.becomeAndroid()
@ -85,7 +85,7 @@ class ItemPill(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()