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 // Register ourselves for server and other game events we are interested in
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
MinecraftForge.EVENT_BUS.register(AndroidCapabilityPlayer.class); MinecraftForge.EVENT_BUS.register(AndroidCapabilityPlayer.Companion);
MinecraftForge.EVENT_BUS.register(AndroidCapability.class); MinecraftForge.EVENT_BUS.register(AndroidCapability.Companion);
MinecraftForge.EVENT_BUS.register(MatterRegistry.class); MinecraftForge.EVENT_BUS.register(MatterRegistry.class);
MinecraftForge.EVENT_BUS.register(BlockEntityBlackHole.BlackHoleExplosionQueue.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.MethodsReturnNonnullByDefault
import net.minecraft.core.Direction; import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag; import javax.annotation.ParametersAreNonnullByDefault
import net.minecraft.nbt.ListTag; import net.minecraft.world.entity.LivingEntity
import net.minecraft.nbt.Tag; import net.minecraftforge.common.capabilities.ICapabilityProvider
import net.minecraft.resources.ResourceLocation; import net.minecraftforge.common.util.INBTSerializable
import net.minecraft.server.level.ServerPlayer; import net.minecraft.nbt.CompoundTag
import net.minecraft.world.effect.MobEffect; import net.minecraft.world.item.ItemStack
import net.minecraft.world.effect.MobEffects; import ru.dbotthepony.mc.otm.android.AndroidFeatureType
import net.minecraft.world.entity.LivingEntity; import ru.dbotthepony.mc.otm.android.AndroidFeature
import net.minecraft.world.item.ItemStack; import java.lang.Runnable
import net.minecraftforge.common.capabilities.Capability; import net.minecraft.server.level.ServerPlayer
import net.minecraftforge.common.capabilities.ICapabilityProvider; import ru.dbotthepony.mc.otm.network.android.AndroidFeaturePacket
import net.minecraftforge.common.util.INBTSerializable; import net.minecraftforge.event.entity.living.LivingHurtEvent
import net.minecraftforge.common.util.LazyOptional; import net.minecraft.nbt.ListTag
import net.minecraftforge.energy.CapabilityEnergy; import net.minecraft.nbt.Tag
import net.minecraftforge.energy.IEnergyStorage; import net.minecraft.resources.ResourceLocation
import net.minecraftforge.event.entity.living.LivingEvent; import ru.dbotthepony.mc.otm.network.MatteryNetworking
import net.minecraftforge.event.entity.living.LivingHurtEvent; import net.minecraftforge.network.PacketDistributor
import net.minecraftforge.eventbus.api.EventPriority; import ru.dbotthepony.mc.otm.network.android.AndroidEnergyPacket
import net.minecraftforge.eventbus.api.SubscribeEvent; import ru.dbotthepony.mc.otm.network.android.AndroidBatteryPacket
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraft.world.effect.MobEffect
import net.minecraftforge.network.PacketDistributor; import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.Registry; import net.minecraftforge.energy.CapabilityEnergy
import ru.dbotthepony.mc.otm.android.AndroidFeature; import net.minecraftforge.common.util.LazyOptional
import ru.dbotthepony.mc.otm.android.AndroidFeatureType; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent
import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage; import net.minecraft.world.effect.MobEffects
import ru.dbotthepony.mc.otm.capability.MatteryCapability; import net.minecraftforge.common.capabilities.Capability
import ru.dbotthepony.mc.otm.core.Fraction; import net.minecraftforge.eventbus.api.SubscribeEvent
import ru.dbotthepony.mc.otm.network.MatteryNetworking; import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent
import ru.dbotthepony.mc.otm.network.android.AndroidBatteryPacket; import net.minecraftforge.eventbus.api.EventPriority
import ru.dbotthepony.mc.otm.network.android.AndroidEnergyPacket; import ru.dbotthepony.mc.otm.Registry
import ru.dbotthepony.mc.otm.network.android.AndroidFeaturePacket; import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.extractEnergy
import javax.annotation.Nonnull; import ru.dbotthepony.mc.otm.capability.receiveEnergy
import javax.annotation.Nullable; import ru.dbotthepony.mc.otm.core.Fraction
import javax.annotation.ParametersAreNonnullByDefault; import ru.dbotthepony.mc.otm.ifHas
import java.math.BigDecimal; import ru.dbotthepony.mc.otm.set
import java.util.*; import java.util.*
@MethodsReturnNonnullByDefault @MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault @ParametersAreNonnullByDefault
public class AndroidCapability implements ICapabilityProvider, IAndroidCapability, INBTSerializable<CompoundTag> { open class AndroidCapability(@JvmField protected val ent: LivingEntity) : ICapabilityProvider, IAndroidCapability, INBTSerializable<CompoundTag?> {
protected final LivingEntity ent; @JvmField
protected Fraction energy_stored = Fraction.ZERO; protected var energy_stored = Fraction.ZERO
protected Fraction energy_stored_max = new Fraction(60_000); @JvmField
protected ItemStack battery = ItemStack.EMPTY; protected var energy_stored_max = Fraction(60000)
private Fraction network_energy = new Fraction(-1); override var batteryItemStack = ItemStack.EMPTY
private Fraction network_energy_max = new Fraction(-1);
private ItemStack network_battery = ItemStack.EMPTY;
protected final Map<AndroidFeatureType<?>, AndroidFeature> features = new HashMap<>(); private var network_energy = Fraction(-1)
protected final ArrayList<Object> network_queue = new ArrayList<>(); private var network_energy_max = Fraction(-1)
private var network_battery = ItemStack.EMPTY
protected final ArrayList<Runnable> delayed_tick_server = new ArrayList<>(); @JvmField protected val features: MutableMap<AndroidFeatureType<*>?, AndroidFeature> = HashMap()
@JvmField protected val network_queue = ArrayList<Any>()
protected boolean network_first = false; @JvmField protected val delayed_tick_server = ArrayList<Runnable>()
@JvmField protected var network_first = false
protected boolean addFeature(AndroidFeature feature) {
if (features.containsKey(feature.type))
return false;
features.put(feature.type, feature);
protected fun addFeature(feature: AndroidFeature): Boolean {
if (features.containsKey(feature.type)) return false
features[feature.type] = feature
if (!ent.level.isClientSide) { if (!ent.level.isClientSide) {
delayed_tick_server.add(feature::applyModifiers); delayed_tick_server.add(Runnable { feature.applyModifiers() })
} }
if (ent is ServerPlayer) {
if (ent instanceof ServerPlayer ply) { sendNetwork(AndroidFeaturePacket(true, feature))
sendNetwork(new AndroidFeaturePacket(true, feature));
} }
return true
return true;
} }
@Override override fun <T : AndroidFeature> addFeature(feature: AndroidFeatureType<T>): T {
@SuppressWarnings("unchecked") val get = features[feature]
public <T extends AndroidFeature> T addFeature(@Nonnull AndroidFeatureType<T> feature) { if (get != null) return get as T
var get = features.get(feature);
if (get != null) val factory = feature.factory(this)
return (T) get;
var factory = feature.factory(this); features[feature] = factory
features.put(feature, factory);
if (!ent.level.isClientSide) { if (!ent.level.isClientSide) {
delayed_tick_server.add(factory::applyModifiers); delayed_tick_server.add(Runnable { factory!!.applyModifiers() })
} }
if (ent instanceof ServerPlayer ply) { if (ent is ServerPlayer) {
sendNetwork(new AndroidFeaturePacket(true, factory)); sendNetwork(AndroidFeaturePacket(true, factory))
} }
return factory; return factory
} }
@Override override fun removeFeature(feature: AndroidFeatureType<*>): Boolean {
public boolean removeFeature(@Nonnull AndroidFeatureType<?> feature) { val removed = features.remove(feature)
var removed = features.remove(feature);
if (removed != null) { if (removed != null) {
if (!ent.level.isClientSide) { if (!ent.level.isClientSide) {
delayed_tick_server.add(removed::removeModifiers); delayed_tick_server.add { removed.removeModifiers() }
} }
if (ent instanceof ServerPlayer ply) { if (ent is ServerPlayer) {
sendNetwork(new AndroidFeaturePacket(false, removed)); sendNetwork(AndroidFeaturePacket(false, removed))
} }
return true; return true
} }
return false; return false
} }
@Override override fun hasFeature(feature: AndroidFeatureType<*>): Boolean {
public boolean hasFeature(@Nullable AndroidFeatureType<?> feature) { return features.containsKey(feature)
return features.containsKey(feature);
} }
@Override override fun hasFeatureLevel(feature: AndroidFeatureType<*>, level: Int): Boolean {
public boolean hasFeatureLevel(@Nullable AndroidFeatureType<?> feature, int level) { val get = features[feature] ?: return false
var get = features.get(feature); return get.level >= level
if (get == null) {
return false;
}
return get.getLevel() >= level;
} }
@Nullable override fun <T : AndroidFeature> getFeature(feature: AndroidFeatureType<T>): T? {
@SuppressWarnings("unchecked") return features[feature] as T?
public <T extends AndroidFeature> T getFeature(AndroidFeatureType<T> feature) {
return (T) features.get(feature);
} }
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) { if (ent is ServerPlayer) {
UNAFFECTED_EFFECTS.add(MobEffects.CONDUIT_POWER); for (feature in features.values) {
UNAFFECTED_EFFECTS.add(MobEffects.HEAL); sendNetwork(AndroidFeaturePacket(true, feature))
// maybe it makes things go haywire idk feature.invalidateNetwork()
// 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();
} }
} }
} }
@Override override fun setEnergy(value: Fraction?) {
public void setEnergy(Fraction value) { energy_stored = value!!
energy_stored = value;
} }
@Override override fun setMaxEnergy(value: Fraction?) {
public void setMaxEnergy(Fraction value) { energy_stored_max = value!!
energy_stored_max = value;
} }
public AndroidCapability(LivingEntity ent) { override fun onHurt(event: LivingHurtEvent?) {
this.ent = ent; for (feature in features.values) {
} feature.onHurt(event)
@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 override fun serializeNBT(): CompoundTag {
@Nonnull val tag = CompoundTag()
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());
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()) { val featureList = ListTag()
var compound = new CompoundTag();
feature.serializeNBT(compound); for (feature in features.values) {
compound.putString("id", Objects.requireNonNull(feature.type.getRegistryName()).toString()); val featureNbt = CompoundTag()
features_nbt.add(compound); feature.serializeNBT(featureNbt)
featureNbt["id"] = feature.type.registryName!!.toString()
featureList.add(featureNbt)
} }
tag.put("features", features_nbt); tag["features"] = featureList
return tag
return tag;
} }
@Override override fun deserializeNBT(compound: CompoundTag?) {
public void deserializeNBT(CompoundTag compound) { compound!!
if (compound.contains("energy_stored"))
energy_stored = Fraction.deserializeNBT(compound.get("energy_stored"));
if (compound.contains("energy_stored_max")) compound.ifHas("energy_stored") {
energy_stored_max = Fraction.deserializeNBT(compound.get("energy_stored_max")); energy_stored = Fraction.deserializeNBT(it)
}
if (compound.contains("battery")) compound.ifHas("energy_stored_max") {
battery = ItemStack.of(compound.getCompound("battery")); 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) { val featuresNbt = compound.getList("features", Tag.TAG_COMPOUND.toInt())
if (_tag instanceof CompoundTag tag) {
var get_feature = Registry.ANDROID_FEATURES.getValue(new ResourceLocation(tag.getString("id")));
if (get_feature != null && get_feature.isApplicable(this)) { for (tag in featuresNbt) {
var feature = get_feature.factory(this); if (tag is CompoundTag) {
feature.deserializeNBT(tag); 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) { feature.deserializeNBT(tag)
delayed_tick_server.add(feature::applyModifiers); addFeature(feature)
if (!ent.level.isClientSide) {
delayed_tick_server.add(Runnable { feature.applyModifiers() })
} }
} }
} }
} }
} }
public void dropBattery() { fun dropBattery() {
if (battery.isEmpty()) if (batteryItemStack.isEmpty) return
return; ent.spawnAtLocation(batteryItemStack)
batteryItemStack = ItemStack.EMPTY
ent.spawnAtLocation(battery);
battery = ItemStack.EMPTY;
} }
protected void sendNetwork(Object packet) { protected fun sendNetwork(packet: Any) {
if (ent instanceof ServerPlayer ply) { if (ent is ServerPlayer) {
if (network_first) { if (network_first) {
MatteryNetworking.CHANNEL.send(PacketDistributor.PLAYER.with(() -> ply), packet); MatteryNetworking.CHANNEL.send(PacketDistributor.PLAYER.with { ent }, packet)
} else { } else {
network_queue.add(packet); network_queue.add(packet)
} }
} }
} }
protected void tickNetwork() { protected open fun tickNetwork() {
network_first = true; network_first = true
if (ent instanceof ServerPlayer ply) { if (ent is ServerPlayer) {
if (!energy_stored.equals(network_energy)) { if (energy_stored != network_energy) {
network_energy = energy_stored; network_energy = energy_stored
sendNetwork(new AndroidEnergyPacket(false, energy_stored)); sendNetwork(AndroidEnergyPacket(false, energy_stored))
} }
if (!energy_stored_max.equals(network_energy_max)) { if (energy_stored_max != network_energy_max) {
network_energy_max = energy_stored_max; network_energy_max = energy_stored_max
sendNetwork(new AndroidEnergyPacket(true, energy_stored_max)); sendNetwork(AndroidEnergyPacket(true, energy_stored_max))
} }
if (!network_battery.equals(battery, false)) { if (!network_battery.equals(batteryItemStack, false)) {
network_battery = battery.copy(); network_battery = batteryItemStack.copy()
sendNetwork(new AndroidBatteryPacket(battery)); sendNetwork(AndroidBatteryPacket(batteryItemStack))
} }
if (network_queue.size() != 0) { if (network_queue.size != 0) {
for (var packet : network_queue) { for (packet in network_queue) {
MatteryNetworking.CHANNEL.send(PacketDistributor.PLAYER.with(() -> ply), packet); MatteryNetworking.CHANNEL.send(PacketDistributor.PLAYER.with {ent}, packet)
} }
network_queue.clear(); network_queue.clear()
} }
} }
} }
protected void tickInnerClient() { protected open fun tickInnerClient() {}
protected open fun tickInnerClientAlways() {}
}
protected void tickInnerClientAlways() {
}
@Override
public void tickClient() {
delayed_tick_server.clear();
if (!ent.isAlive())
return;
tickInnerClientAlways();
override fun tickClient() {
delayed_tick_server.clear()
if (!ent.isAlive) return
tickInnerClientAlways()
if (isAndroid()) { if (isAndroid()) {
tickInnerClient(); tickInnerClient()
for (feature in features.values) {
for (var feature : features.values()) { feature.tickClient()
feature.tickClient();
} }
} }
} }
@Override override fun tick() {
public void tick() { if (!ent.isAlive) return
if (!ent.isAlive()) tickServerAlways()
return;
tickServerAlways();
if (isAndroid()) { if (isAndroid()) {
tickServer(); tickServer()
for (feature in features.values) {
for (var feature : features.values()) { feature.tickServer()
feature.tickServer();
} }
} }
for (runnable in delayed_tick_server) {
for (var runnable : delayed_tick_server) { runnable.run()
runnable.run();
} }
delayed_tick_server.clear()
delayed_tick_server.clear(); tickNetwork()
tickNetwork();
} }
protected void tickServerAlways() { protected open fun tickServerAlways() {}
} protected open fun tickServer() {
if (ent.airSupply < ent.maxAirSupply)
ent.airSupply = ent.maxAirSupply
protected void tickServer() { for (effect in UNAFFECTED_EFFECTS)
if (ent.getAirSupply() < ent.getMaxAirSupply())
ent.setAirSupply(ent.getMaxAirSupply());
for (var effect : UNAFFECTED_EFFECTS)
if (ent.hasEffect(effect)) if (ent.hasEffect(effect))
ent.removeEffect(effect); ent.removeEffect(effect)
if (!battery.isEmpty()) { if (!batteryItemStack.isEmpty && energy_stored < energy_stored_max) {
Fraction demand = energy_stored_max.minus(energy_stored); batteryItemStack.getCapability(CapabilityEnergy.ENERGY).ifPresent {
if (it is IMatteryEnergyStorage) {
if (demand.compareTo(Fraction.ZERO) > 0) { energy_stored += it.extractEnergyInner(energy_stored_max - energy_stored, false)
Optional<IMatteryEnergyStorage> get_mattery = battery.getCapability(MatteryCapability.ENERGY).resolve(); } else {
energy_stored += it.extractEnergy(energy_stored_max - energy_stored, false)
if (get_mattery.isPresent()) {
demand = demand.minus(get_mattery.get().extractEnergyInner(demand, 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 fun extractEnergyOuter(howMuch: Fraction, simulate: Boolean): Fraction {
@Override return Fraction.ZERO
public Fraction extractEnergyOuter(Fraction howMuch, boolean simulate) {
return Fraction.ZERO;
} }
@Nonnull override fun extractEnergyInner(howMuch: Fraction, simulate: Boolean): Fraction {
@Override var howMuch = howMuch
public Fraction extractEnergyInner(Fraction howMuch, boolean simulate) { var drained = Fraction.ZERO
Fraction drained = Fraction.ZERO;
if (battery != ItemStack.EMPTY) { if (!batteryItemStack.isEmpty) {
Optional<IMatteryEnergyStorage> get_mattery = battery.getCapability(MatteryCapability.ENERGY).resolve(); batteryItemStack.getCapability(CapabilityEnergy.ENERGY).ifPresent {
if (it is IMatteryEnergyStorage) {
if (get_mattery.isPresent()) { val extracted = it.extractEnergyOuter(howMuch, simulate)
Fraction changed = get_mattery.get().extractEnergyOuter(howMuch, simulate); drained += extracted
howMuch -= extracted
if (changed.compareTo(Fraction.ZERO) > 0) { } else {
drained = drained.plus(changed); val extracted = it.extractEnergy(howMuch, simulate)
howMuch = howMuch.minus(changed); drained += extracted
howMuch -= extracted
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.compareTo(Fraction.ONE) >= 0) { if (howMuch.isZero()) {
Optional<IEnergyStorage> get_energy = battery.getCapability(CapabilityEnergy.ENERGY).resolve(); if (!simulate && ent is ServerPlayer) {
ent.awardStat(Registry.Names.POWER_CONSUMED, drained.toInt() * 10)
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;
}
}
} }
return drained
} }
} }
Fraction new_energy = energy_stored.minus(howMuch).max(Fraction.ZERO); val new = (energy_stored - howMuch).moreThanZero()
drained = drained.plus(energy_stored.minus(new_energy)); drained += energy_stored - new
if (!simulate) { if (!simulate) {
energy_stored = new_energy; energy_stored = new
if (ent instanceof ServerPlayer ply) { if (ent is ServerPlayer) {
ply.awardStat(Registry.Names.POWER_CONSUMED, drained.toInt() * 10); ent.awardStat(Registry.Names.POWER_CONSUMED, drained.toInt() * 10)
} }
} }
return drained; return drained
} }
@Nonnull override fun receiveEnergyOuter(howMuch: Fraction, simulate: Boolean): Fraction {
@Override var howMuch = howMuch
public Fraction receiveEnergyOuter(Fraction howMuch, boolean simulate) { var received = Fraction.ZERO
Fraction received = Fraction.ZERO;
if (battery != ItemStack.EMPTY) { if (!batteryItemStack.isEmpty) {
Optional<IMatteryEnergyStorage> get_mattery = battery.getCapability(MatteryCapability.ENERGY).resolve(); batteryItemStack.getCapability(CapabilityEnergy.ENERGY).ifPresent {
if (it is IMatteryEnergyStorage) {
if (get_mattery.isPresent()) { val extracted = it.receiveEnergyOuter(howMuch, simulate)
Fraction changed = get_mattery.get().receiveEnergyOuter(howMuch, simulate); received += extracted
howMuch -= extracted
if (changed.compareTo(Fraction.ZERO) > 0) { } else {
received = received.plus(changed); val extracted = it.receiveEnergy(howMuch, simulate)
howMuch = howMuch.minus(changed); received += extracted
howMuch -= extracted
if (howMuch.compareTo(Fraction.ZERO) <= 0) {
return received;
}
} }
} }
if (howMuch.compareTo(Fraction.ONE) >= 0) { if (howMuch.isZero()) {
Optional<IEnergyStorage> get_energy = battery.getCapability(CapabilityEnergy.ENERGY).resolve(); return received
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;
}
}
}
} }
} }
Fraction new_energy = energy_stored.plus(howMuch).min(energy_stored_max); val new = (energy_stored + howMuch).min(energy_stored_max)
received = received.plus(new_energy.minus(energy_stored)); received += new - energy_stored
if (!simulate) { if (!simulate) {
energy_stored = new_energy; energy_stored = new
} }
return received; return received
} }
@Nonnull override fun receiveEnergyInner(howMuch: Fraction, simulate: Boolean): Fraction {
@Override return receiveEnergyOuter(howMuch, simulate)
public Fraction receiveEnergyInner(Fraction howMuch, boolean simulate) {
return receiveEnergyOuter(howMuch, simulate);
} }
@Override override fun getEntity(): LivingEntity {
public LivingEntity getEntity() { return ent
return this.ent;
} }
@Nonnull override fun getBatteryLevel(): Fraction {
@Override if (!batteryItemStack.isEmpty) {
public ItemStack getBatteryItemStack() { val resolver = batteryItemStack.getCapability(CapabilityEnergy.ENERGY).resolve()
return battery;
}
@Override if (resolver.isPresent) {
public void setBatteryItemStack(@Nonnull ItemStack stack) { val it = resolver.get()
battery = stack;
}
@Override if (it is IMatteryEnergyStorage) {
@Nonnull return energy_stored + it.batteryLevel
public Fraction getBatteryLevel() { } else {
if (battery != ItemStack.EMPTY) { return energy_stored + it.energyStored
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());
} }
} }
return energy_stored; return energy_stored
} }
@Override override fun getMaxBatteryLevel(): Fraction {
@Nonnull if (batteryItemStack != ItemStack.EMPTY) {
public Fraction getMaxBatteryLevel() { val resolver = batteryItemStack.getCapability(CapabilityEnergy.ENERGY).resolve()
if (battery != ItemStack.EMPTY) {
Optional<IMatteryEnergyStorage> get_mattery = battery.getCapability(MatteryCapability.ENERGY).resolve();
if (get_mattery.isPresent()) { if (resolver.isPresent) {
return get_mattery.get().getMaxBatteryLevel().plus(energy_stored_max); val it = resolver.get()
}
Optional<IEnergyStorage> get_energy = battery.getCapability(CapabilityEnergy.ENERGY).resolve(); if (it is IMatteryEnergyStorage) {
return energy_stored_max + it.maxBatteryLevel
if (get_energy.isPresent()) { } else {
return energy_stored_max.plus(get_energy.get().getMaxEnergyStored()); 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 fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
@Override return if (cap === MatteryCapability.ANDROID) {
public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) { resolver.cast()
if (cap == MatteryCapability.ANDROID) { } else LazyOptional.empty()
return resolver.cast(); }
@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.ChatFormatting
import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag
import net.minecraft.nbt.ListTag; import net.minecraft.nbt.Tag
import net.minecraft.nbt.Tag; import net.minecraft.network.chat.TranslatableComponent
import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.resources.ResourceLocation
import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player
import net.minecraft.world.entity.player.Player; import net.minecraftforge.event.AttachCapabilitiesEvent
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.event.entity.player.PlayerEvent.Clone
import net.minecraftforge.event.AttachCapabilitiesEvent; import net.minecraftforge.event.entity.player.PlayerEvent.PlayerChangedDimensionEvent
import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.eventbus.api.SubscribeEvent
import net.minecraftforge.eventbus.api.SubscribeEvent; import ru.dbotthepony.mc.otm.Registry
import ru.dbotthepony.mc.otm.Registry; import ru.dbotthepony.mc.otm.android.AndroidResearch
import ru.dbotthepony.mc.otm.android.AndroidResearch; import ru.dbotthepony.mc.otm.android.AndroidResearchType
import ru.dbotthepony.mc.otm.android.AndroidResearchType; import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.MatteryCapability; import ru.dbotthepony.mc.otm.core.Fraction
import ru.dbotthepony.mc.otm.core.Fraction; import ru.dbotthepony.mc.otm.network.android.AndroidResearchPacket
import ru.dbotthepony.mc.otm.network.android.AndroidResearchPacket; import ru.dbotthepony.mc.otm.network.android.AndroidStatusPacket
import ru.dbotthepony.mc.otm.network.android.AndroidStatusPacket; import ru.dbotthepony.mc.otm.set
import java.util.*
import javax.annotation.Nonnull; class AndroidCapabilityPlayer(@JvmField val ply: Player) : AndroidCapability(ply) {
import javax.annotation.ParametersAreNonnullByDefault; @JvmField
import java.math.BigDecimal; var is_android = false
import java.util.ArrayList; private var network_is_android = false
import java.util.Objects;
@MethodsReturnNonnullByDefault @JvmField
@ParametersAreNonnullByDefault var will_become_android = false
public class AndroidCapabilityPlayer extends AndroidCapability { private var network_will_become_android = false
public boolean is_android = false; private val research = ArrayList<AndroidResearch>()
private boolean network_is_android = false;
public boolean will_become_android = false; override fun invalidateNetworkState() {
private boolean network_will_become_android = false; super.invalidateNetworkState()
private final ArrayList<AndroidResearch> research_list = new ArrayList<>(); network_is_android = false
network_will_become_android = false
@Override for (instance in research) {
public void invalidateNetworkState() { instance.dirty = true
super.invalidateNetworkState();
network_is_android = false;
network_will_become_android = false;
for (var instance : research_list) {
instance.dirty = true;
} }
} }
@Override override fun isAndroid(): Boolean = is_android
public boolean isAndroid() { fun isEverAndroid(): Boolean = is_android || will_become_android
return is_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() { fun becomeAndroid() {
return is_android || will_become_android; if (is_android) return
is_android = true
will_become_android = false
energy_stored = Fraction(60000)
energy_stored_max = Fraction(60000)
} }
public void becomeAndroidSoft() { fun becomeAndroidAndKill() {
if (is_android || will_become_android) if (is_android) return
return;
will_become_android = true; becomeAndroid()
ply.hurt(Registry.DAMAGE_BECOME_ANDROID, ply.maxHealth * 2)
if (ent instanceof ServerPlayer ply) {
ply.displayClientMessage(new TranslatableComponent("otm.pill.message").withStyle(ChatFormatting.GRAY), false);
}
} }
public void becomeAndroid() { fun becomeHumane() {
if (is_android) if (will_become_android) will_become_android = false
return; if (!is_android) return
is_android = true; is_android = false
will_become_android = false; energy_stored = Fraction(0)
energy_stored = new Fraction(60_000); energy_stored_max = Fraction(60000)
energy_stored_max = new Fraction(60_000); dropBattery()
} }
public void becomeAndroidAndKill() { fun becomeHumaneAndKill() {
if (is_android) if (will_become_android) will_become_android = false
return; if (!is_android) return
becomeAndroid(); becomeHumane()
ply.hurt(Registry.DAMAGE_BECOME_ANDROID, ply.getMaxHealth() * 2); ply.hurt(Registry.DAMAGE_BECOME_HUMANE, ply.maxHealth * 2)
} }
public void becomeHumane() { override fun deserializeNBT(compound: CompoundTag?) {
if (will_become_android) super.deserializeNBT(compound!!)
will_become_android = false;
if (!is_android) is_android = compound.getBoolean("is_android")
return; 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); for (tag in list) {
energy_stored_max = new Fraction(60_000); if (tag is CompoundTag) {
val research = Registry.ANDROID_RESEARCH.getValue(ResourceLocation(tag.getString("id")))
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")));
if (research != null) { if (research != null) {
var instance = research.factory(this); val instance = research.factory(this)
instance.deserializeNBT(tag); instance.deserializeNBT(tag)
research_list.add(instance); this.research.add(instance)
} }
} }
} }
} }
@Nonnull override fun serializeNBT(): CompoundTag {
@Override val tag = super.serializeNBT()
public CompoundTag serializeNBT() {
CompoundTag tag = super.serializeNBT();
tag.putBoolean("is_android", is_android);
tag.putBoolean("will_become_android", will_become_android);
var list = new ListTag(); tag["is_android"] = is_android
tag["will_become_android"] = will_become_android
for (var instance : research_list) { val list = ListTag()
var _tag = new CompoundTag();
instance.serializeNBT(_tag); for (instance in research) {
_tag.putString("id", Objects.requireNonNull(instance.type.getRegistryName()).toString()); val researchTag = CompoundTag()
list.add(_tag); instance.serializeNBT(researchTag)
researchTag["id"] = instance.type.registryName!!.toString()
list.add(researchTag)
} }
tag.put("research", list); tag["research"] = list
return tag
return tag;
} }
@SuppressWarnings("unchecked") fun <T : AndroidResearch> getResearch(type: AndroidResearchType<T>): T {
public <T extends AndroidResearch> T getResearch(AndroidResearchType<T> type) { for (instance in research) {
for (var instance : research_list) { if (instance.type === type) {
if (instance.type == type) { return instance as T
return (T) instance;
} }
} }
var instance = type.factory(this); val instance = type.factory(this)
research_list.add(instance); research.add(instance)
return instance; return instance
} }
@SubscribeEvent @JvmField
@SuppressWarnings("unused") var last_jump_ticks = 14
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();
override fun tickNetwork() {
super.tickNetwork()
if (is_android != network_is_android) { if (is_android != network_is_android) {
network_is_android = is_android; network_is_android = is_android
sendNetwork(new AndroidStatusPacket(AndroidStatusPacket.Type.IS_ANDROID, is_android)); sendNetwork(AndroidStatusPacket(AndroidStatusPacket.Type.IS_ANDROID, is_android))
} }
if (will_become_android != network_will_become_android) { if (will_become_android != network_will_become_android) {
network_will_become_android = will_become_android; network_will_become_android = will_become_android
sendNetwork(new AndroidStatusPacket(AndroidStatusPacket.Type.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) { if (instance.dirty) {
instance.dirty = false; instance.dirty = false
sendNetwork(new AndroidResearchPacket(instance)); sendNetwork(AndroidResearchPacket(instance))
} }
} }
} }
public static final Fraction ENERGY_FOR_HUNGER_POINT = new Fraction(1000); @JvmField
var sleep_ticks = 0
public int sleep_ticks = 0; override fun tickInnerClientAlways() {
public static final int SLEEP_TICKS_LIMIT = 80; super.tickInnerClientAlways()
@Override
protected void tickInnerClientAlways() {
super.tickInnerClientAlways();
if (will_become_android) { if (will_become_android) {
if (ent.isSleeping()) { if (ply.isSleeping) {
sleep_ticks++; sleep_ticks++
} else { } else {
sleep_ticks = 0; sleep_ticks = 0
} }
} }
} }
@Override override fun tickServerAlways() {
protected void tickServerAlways() { super.tickServerAlways()
super.tickServerAlways();
if (will_become_android) { if (will_become_android) {
if (ent.isSleeping()) { if (ply.isSleeping) {
sleep_ticks++; sleep_ticks++
if (sleep_ticks > SLEEP_TICKS_LIMIT) { if (sleep_ticks > SLEEP_TICKS_LIMIT) {
becomeAndroid(); becomeAndroid()
sleep_ticks = 0; sleep_ticks = 0
if (ent instanceof ServerPlayer ply) { (ply as? ServerPlayer)?.displayClientMessage(TranslatableComponent("otm.pill.message_finish").withStyle(ChatFormatting.DARK_RED), false)
ply.displayClientMessage(new TranslatableComponent("otm.pill.message_finish").withStyle(ChatFormatting.DARK_RED), false);
}
} }
} else { } else {
sleep_ticks = 0; sleep_ticks = 0
} }
} }
} }
@Override public override fun tickServer() {
public void tickServer() { super.tickServer()
super.tickServer();
// TODO: Maybe passive drain? // TODO: Maybe passive drain?
// extractEnergyInner(BigDecimal.valueOf(new Random().nextDouble()), false); // extractEnergyInner(BigDecimal.valueOf(new Random().nextDouble()), false);
if (ply.isSwimming && !hasFeature(Registry.AndroidFeatures.AIR_BAGS)) {
if (ply.isSwimming() && !hasFeature(Registry.AndroidFeatures.AIR_BAGS)) { ply.isSwimming = false
ply.setSwimming(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) { while (stats.foodLevel < 18 && extractEnergyInner(ENERGY_FOR_HUNGER_POINT, true) >= ENERGY_FOR_HUNGER_POINT) {
this.extractEnergyInner(ENERGY_FOR_HUNGER_POINT, false); extractEnergyInner(ENERGY_FOR_HUNGER_POINT, false)
stats.setFoodLevel(stats.getFoodLevel() + 1); stats.foodLevel = stats.foodLevel + 1
} }
// "block" quick regeneration // "block" quick regeneration
// also cause power to generate while in peaceful // 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) { while (stats.foodLevel > 18 && receiveEnergyInner(ENERGY_FOR_HUNGER_POINT, true) >= ENERGY_FOR_HUNGER_POINT) {
this.receiveEnergyInner(ENERGY_FOR_HUNGER_POINT, false); receiveEnergyInner(ENERGY_FOR_HUNGER_POINT, false)
stats.setFoodLevel(stats.getFoodLevel() - 1); stats.foodLevel = stats.foodLevel - 1
} }
var food_level = (float) stats.getFoodLevel(); val foodLevel = stats.foodLevel.toFloat()
if (stats.getSaturationLevel() < food_level) { if (stats.saturationLevel < foodLevel) {
Fraction extracted = this.extractEnergyInner(ENERGY_FOR_HUNGER_POINT.times(food_level - stats.getSaturationLevel()), false); val extracted = extractEnergyInner(ENERGY_FOR_HUNGER_POINT * (foodLevel - stats.saturationLevel), false)
stats.setSaturation(stats.getSaturationLevel() + extracted.div(ENERGY_FOR_HUNGER_POINT).toFloat()); stats.setSaturation(stats.saturationLevel + (extracted / ENERGY_FOR_HUNGER_POINT).toFloat())
} }
if (stats.getExhaustionLevel() > 0f) { if (stats.exhaustionLevel > 0f) {
Fraction extracted = this.extractEnergyInner(ENERGY_FOR_HUNGER_POINT.times(stats.getExhaustionLevel() / 4f), false); val extracted = extractEnergyInner(ENERGY_FOR_HUNGER_POINT * (stats.exhaustionLevel / 4f), false)
stats.setExhaustion(stats.getExhaustionLevel() - extracted.div(ENERGY_FOR_HUNGER_POINT).toFloat() * 4f); 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.nbt.CompoundTag; import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.ItemStack; import net.minecraftforge.common.util.INBTSerializable
import net.minecraftforge.common.util.INBTSerializable; import net.minecraftforge.event.entity.living.LivingHurtEvent
import net.minecraftforge.event.entity.living.LivingHurtEvent; import ru.dbotthepony.mc.otm.android.AndroidFeature
import ru.dbotthepony.mc.otm.android.AndroidFeature; import ru.dbotthepony.mc.otm.android.AndroidFeatureType
import ru.dbotthepony.mc.otm.android.AndroidFeatureType; import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage; import ru.dbotthepony.mc.otm.core.Fraction
import ru.dbotthepony.mc.otm.core.Fraction; import java.util.*
import javax.annotation.Nonnull; interface IAndroidCapability : IMatteryEnergyStorage, INBTSerializable<CompoundTag?> {
import javax.annotation.Nullable; fun tick()
import javax.annotation.ParametersAreNonnullByDefault; fun tickClient()
import java.math.BigDecimal; fun getEntity(): LivingEntity
import java.util.Optional; 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 fun <T : AndroidFeature> getFeature(feature: AndroidFeatureType<T>): T?
@ParametersAreNonnullByDefault
public interface IAndroidCapability extends IMatteryEnergyStorage, INBTSerializable<CompoundTag> {
void tick();
void tickClient();
LivingEntity getEntity();
<T extends AndroidFeature> T addFeature(AndroidFeatureType<T> feature); fun <T : AndroidFeature> getFeatureO(feature: AndroidFeatureType<T>): Optional<T> {
boolean removeFeature(AndroidFeatureType<?> feature); val get = getFeature(feature)
boolean hasFeature(@Nullable AndroidFeatureType<?> feature); return if (get != null) Optional.of(get) else Optional.empty()
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();
} }
default boolean isAndroid() { fun <T : AndroidFeature> ifFeature(feature: AndroidFeatureType<T>, consumer: (T) -> Unit) {
return true; val get = getFeature(feature)
if (get != null) {
consumer(get)
}
} }
default void onHurt(LivingHurtEvent event) {}
ItemStack getBatteryItemStack(); fun isAndroid(): Boolean = true
void setBatteryItemStack(ItemStack stack);
void invalidateNetworkState(); // tell capability that player forgot everything, and everything needs to be re-networked fun onHurt(event: LivingHurtEvent?) {}
var batteryItemStack: ItemStack
void setEnergy(Fraction value); fun invalidateNetworkState() // tell capability that player forgot everything, and everything needs to be re-networked
void setMaxEnergy(Fraction value); 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 ru.dbotthepony.mc.otm.menu.AndroidStationMenu
import java.util.* import java.util.*
class AndroidStationScreen constructor(p_97741_: AndroidStationMenu, p_97742_: Inventory, p_97743_: Component) :
class AndroidStationScreen(p_97741_: AndroidStationMenu, p_97742_: Inventory, p_97743_: Component) :
MatteryScreen<AndroidStationMenu>(p_97741_, p_97742_, p_97743_) { MatteryScreen<AndroidStationMenu>(p_97741_, p_97742_, p_97743_) {
internal inner class AndroidResearchButton(parent: EditablePanel?, private val node: AndroidResearch) : 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 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) ply.startUsingItem(hand)
return InteractionResultHolder.consume(ply.getItemInHand(hand)) return InteractionResultHolder.consume(ply.getItemInHand(hand))
} }
@ -73,7 +73,7 @@ class ItemPill(val pillType: PillType) :
val cap = resolver.get() as AndroidCapabilityPlayer 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.abilities.instabuild) {
if (ent is ServerPlayer) { if (ent is ServerPlayer) {
cap.becomeAndroid() cap.becomeAndroid()
@ -85,7 +85,7 @@ class ItemPill(val pillType: PillType) :
cap.becomeAndroidSoft() cap.becomeAndroidSoft()
} }
} }
} else if (pillType == PillType.BECOME_HUMANE && cap.isEverAndroid) { } else if (pillType == PillType.BECOME_HUMANE && cap.isEverAndroid()) {
if (ent.abilities.instabuild) { if (ent.abilities.instabuild) {
if (ent is ServerPlayer) { if (ent is ServerPlayer) {
cap.becomeHumane() cap.becomeHumane()