Revisit android features structure

This commit is contained in:
DBotThePony 2022-02-26 20:03:29 +07:00
parent 5ba79f20e5
commit ddf2c178e6
Signed by: DBot
GPG Key ID: DCC23B5715498507
16 changed files with 388 additions and 476 deletions

View File

@ -1,80 +0,0 @@
package ru.dbotthepony.mc.otm.android;
import net.minecraft.nbt.CompoundTag;
import net.minecraftforge.common.util.INBTSerializable;
import net.minecraftforge.event.entity.living.LivingHurtEvent;
import ru.dbotthepony.mc.otm.capability.android.IAndroidCapability;
public class AndroidFeature implements INBTSerializable<CompoundTag> {
public final AndroidFeatureType<? extends AndroidFeature> type;
public final IAndroidCapability capability;
protected int level = 0;
public AndroidFeature(AndroidFeatureType<?> type, IAndroidCapability capability) {
this.type = type;
this.capability = capability;
}
public void setLevel(int level) {
if (this.level != level) {
this.level = level;
applyModifiers();
}
}
public int getLevel() {
return level;
}
@Override
public boolean equals(Object obj) {
if (super.equals(obj))
return true;
if (obj instanceof AndroidFeature feature)
return feature.type.equals(type);
return false;
}
public void invalidateNetwork() {
// when player forgets everything
}
@Override
public int hashCode() {
return type.hashCode();
}
public void tickClient() {
// override
}
public void tickServer() {
// override
}
public void applyModifiers() {}
public void removeModifiers() {}
public void serializeNBT(CompoundTag tag) {
tag.putInt("level", level);
}
public void onHurt(LivingHurtEvent event) {
// override
}
@Override
public CompoundTag serializeNBT() {
var tag = new CompoundTag();
serializeNBT(tag);
return tag;
}
@Override
public void deserializeNBT(CompoundTag tag) {
level = tag.getInt("level");
}
}

View File

@ -1,41 +0,0 @@
package ru.dbotthepony.mc.otm.android;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraftforge.registries.*;
import ru.dbotthepony.mc.otm.capability.android.IAndroidCapability;
public class AndroidFeatureType<T extends AndroidFeature> extends ForgeRegistryEntry<AndroidFeatureType<?>> {
public interface AndroidFeatureFactory<T extends AndroidFeature> {
T factory(IAndroidCapability capability);
}
public interface AndroidFullFeatureFactory<T extends AndroidFeature> {
T factory(AndroidFeatureType<T> type, IAndroidCapability capability);
}
protected final AndroidFeatureFactory<T> factory;
public AndroidFeatureType(AndroidFeatureFactory<T> factory) {
this.factory = factory;
}
public AndroidFeatureType(AndroidFullFeatureFactory<T> factory) {
this.factory = (c) -> factory.factory(this, c);
}
public T factory(IAndroidCapability capability) {
return factory.factory(capability);
}
public boolean isApplicable(IAndroidCapability capability) {
return true;
}
public Component getDisplayName() {
if (getRegistryName() == null)
return new TranslatableComponent("android_feature.null.null");
return new TranslatableComponent("android_feature." + getRegistryName().getNamespace() + "." + getRegistryName().getPath());
}
}

View File

@ -1,36 +0,0 @@
package ru.dbotthepony.mc.otm.android.feature;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraftforge.common.ForgeMod;
import ru.dbotthepony.mc.otm.android.AndroidFeature;
import ru.dbotthepony.mc.otm.capability.android.IAndroidCapability;
import ru.dbotthepony.mc.otm.registry.AndroidFeatures;
import java.util.UUID;
public class AndroidExtendedReach extends AndroidFeature {
public static final UUID MODIFIER_ID = UUID.fromString("4a3fae46-47a8-a03f-857d-f5c2b2c8f2f2");
public AndroidExtendedReach(IAndroidCapability capability) {
super(AndroidFeatures.INSTANCE.getEXTENDED_REACH(), capability);
}
@Override
public void applyModifiers() {
var reach = capability.getEntity().getAttribute(ForgeMod.REACH_DISTANCE.get());
if (reach != null) {
reach.removePermanentModifier(MODIFIER_ID);
reach.addPermanentModifier(new AttributeModifier(MODIFIER_ID, type.getDisplayName().toString(), level + 1, AttributeModifier.Operation.ADDITION));
}
}
@Override
public void removeModifiers() {
var reach = capability.getEntity().getAttribute(ForgeMod.REACH_DISTANCE.get());
if (reach != null) {
reach.removePermanentModifier(MODIFIER_ID);
}
}
}

View File

@ -1,49 +0,0 @@
package ru.dbotthepony.mc.otm.android.feature;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import ru.dbotthepony.mc.otm.android.AndroidFeature;
import ru.dbotthepony.mc.otm.capability.android.IAndroidCapability;
import ru.dbotthepony.mc.otm.registry.AndroidFeatures;
import java.util.UUID;
public class AndroidLimbOverclocking extends AndroidFeature {
public static final UUID MODIFIER_ID = UUID.fromString("4a3fae46-e57b-4e20-857d-f5c2b2c8f2f2");
public AndroidLimbOverclocking(IAndroidCapability capability) {
super(AndroidFeatures.INSTANCE.getLIMB_OVERCLOCKING(), capability);
}
@Override
public void applyModifiers() {
var speed = capability.getEntity().getAttribute(Attributes.MOVEMENT_SPEED);
if (speed != null) {
speed.removePermanentModifier(MODIFIER_ID);
speed.addPermanentModifier(new AttributeModifier(MODIFIER_ID, type.getDisplayName().toString(), 0.08d * (level + 1), AttributeModifier.Operation.MULTIPLY_TOTAL));
}
var attack_speed = capability.getEntity().getAttribute(Attributes.ATTACK_SPEED);
if (attack_speed != null) {
attack_speed.removePermanentModifier(MODIFIER_ID);
attack_speed.addPermanentModifier(new AttributeModifier(MODIFIER_ID, type.getDisplayName().toString(), 0.06d * (level + 1), AttributeModifier.Operation.MULTIPLY_TOTAL));
}
}
@Override
public void removeModifiers() {
var speed = capability.getEntity().getAttribute(Attributes.MOVEMENT_SPEED);
if (speed != null) {
speed.removePermanentModifier(MODIFIER_ID);
}
var attack_speed = capability.getEntity().getAttribute(Attributes.ATTACK_SPEED);
if (attack_speed != null) {
attack_speed.removePermanentModifier(MODIFIER_ID);
}
}
}

View File

@ -1,111 +0,0 @@
package ru.dbotthepony.mc.otm.android.feature;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.event.entity.living.LivingHurtEvent;
import ru.dbotthepony.mc.otm.android.AndroidFeature;
import ru.dbotthepony.mc.otm.capability.android.IAndroidCapability;
import ru.dbotthepony.mc.otm.core.ImpreciseFraction;
import ru.dbotthepony.mc.otm.registry.AndroidFeatures;
import ru.dbotthepony.mc.otm.registry.MNames;
import ru.dbotthepony.mc.otm.registry.StatNames;
public class AndroidNanobotsArmor extends AndroidFeature {
public AndroidNanobotsArmor(IAndroidCapability capability) {
super(AndroidFeatures.INSTANCE.getNANOBOTS_ARMOR(), capability);
}
public int getStrength() {
return strength;
}
public void setStrength(int strength) {
this.strength = Math.max(0, Math.min(2, strength));
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = Math.max(0, Math.min(2, speed));
}
protected int strength = 0;
protected int speed = 0;
protected int ticks_passed = 0;
protected int layers = 0;
protected static final ImpreciseFraction ENERGY_PER_BUILT = new ImpreciseFraction(200);
protected static final ImpreciseFraction ENERGY_PER_HITPOINT = new ImpreciseFraction(500);
public static final int[] TICKS = new int[] {
80, // 4 seconds to build a layer
70, // 3.5 seconds to build a layer
60, // 3 seconds to build a layer
50, // 2.5 seconds to build a layer
};
public static final float[] SHIELD_STRENGTH = new float[] {
0.1f,
0.2f,
0.3f,
0.4f,
};
@Override
public void tickServer() {
if (layers < strength + 1 && capability.extractEnergyInner(ENERGY_PER_BUILT, true).compareTo(ENERGY_PER_BUILT) == 0) {
ticks_passed++;
if (ticks_passed >= TICKS[speed]) {
layers++;
capability.extractEnergyInner(ENERGY_PER_BUILT, false);
}
} else {
ticks_passed = 0;
}
}
@Override
public void onHurt(LivingHurtEvent event) {
ticks_passed = 0;
if (!event.getSource().isBypassArmor() && layers > 0) {
var absorbed = event.getAmount() * SHIELD_STRENGTH[layers];
if (absorbed > 0.1f) {
var required = ENERGY_PER_HITPOINT.times(absorbed);
var extracted = capability.extractEnergyInner(required, false);
var real_absorbed = absorbed * extracted.div(required).toFloat();
event.setAmount(event.getAmount() - real_absorbed);
if (capability.getEntity() instanceof ServerPlayer ply) {
ply.awardStat(StatNames.INSTANCE.getDAMAGE_ABSORBED(), Math.round(real_absorbed * 10f));
}
layers--;
}
}
}
@Override
public void serializeNBT(CompoundTag tag) {
super.serializeNBT(tag);
tag.putInt("ticks_passed", ticks_passed);
tag.putInt("layers", layers);
tag.putInt("strength", strength);
tag.putInt("speed", speed);
}
@Override
public void deserializeNBT(CompoundTag tag) {
super.deserializeNBT(tag);
ticks_passed = tag.getInt("ticks_passed");
layers = tag.getInt("layers");
strength = tag.getInt("strength");
speed = tag.getInt("speed");
}
}

View File

@ -1,80 +0,0 @@
package ru.dbotthepony.mc.otm.android.feature;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.event.entity.living.LivingHurtEvent;
import ru.dbotthepony.mc.otm.android.AndroidFeature;
import ru.dbotthepony.mc.otm.capability.android.IAndroidCapability;
import ru.dbotthepony.mc.otm.core.ImpreciseFraction;
import ru.dbotthepony.mc.otm.registry.AndroidFeatures;
import ru.dbotthepony.mc.otm.registry.MNames;
import ru.dbotthepony.mc.otm.registry.StatNames;
public class AndroidNanobotsRegeneration extends AndroidFeature {
public AndroidNanobotsRegeneration(IAndroidCapability capability) {
super(AndroidFeatures.INSTANCE.getNANOBOTS_REGENERATION(), capability);
}
protected int ticks_passed = 0;
protected int heal_ticks = 0;
protected static final ImpreciseFraction ENERGY_PER_HITPOINT = new ImpreciseFraction(800);
protected static final int[] TICKS_BETWEEN_HEAL = new int[] {
100, // 5 seconds
80, // 4 seconds
60, // 3 seconds
40, // 2 seconds
};
@Override
public void tickServer() {
var ent = capability.getEntity();
if (ent.getHealth() > 0 && ent.getHealth() < ent.getMaxHealth()) {
ticks_passed++;
var wait_time = heal_ticks >= TICKS_BETWEEN_HEAL.length ? TICKS_BETWEEN_HEAL[TICKS_BETWEEN_HEAL.length - 1] : TICKS_BETWEEN_HEAL[heal_ticks];
if (ticks_passed > wait_time) {
var missing = Math.min(1, ent.getMaxHealth() - ent.getHealth());
var extract = capability.extractEnergyInner(ENERGY_PER_HITPOINT.times(missing), false);
if (extract.compareTo(ImpreciseFraction.ZERO) > 0) {
heal_ticks = Math.min(heal_ticks + 1, level);
var heal = missing * extract.div(ENERGY_PER_HITPOINT).toFloat();
ent.heal(heal);
if (capability.getEntity() instanceof ServerPlayer ply) {
ply.awardStat(StatNames.INSTANCE.getHEALTH_REGENERATED(), Math.round(heal * 10f));
}
ticks_passed = 0;
}
}
} else {
heal_ticks = 0;
ticks_passed = 0;
}
}
@Override
public void onHurt(LivingHurtEvent event) {
heal_ticks = 0;
ticks_passed = 0;
}
@Override
public void serializeNBT(CompoundTag tag) {
super.serializeNBT(tag);
tag.putInt("heal_ticks", heal_ticks);
tag.putInt("ticks_passed", ticks_passed);
}
@Override
public void deserializeNBT(CompoundTag tag) {
super.deserializeNBT(tag);
heal_ticks = tag.getInt("heal_ticks");
ticks_passed = tag.getInt("ticks_passed");
}
}

View File

@ -15,13 +15,8 @@ import java.util.Objects;
import java.util.function.Supplier;
public record AndroidFeaturePacket(boolean is_added, AndroidFeatureType<?> feature) {
public AndroidFeaturePacket(boolean is_added, AndroidFeatureType<?> feature) {
this.is_added = is_added;
this.feature = feature;
}
public AndroidFeaturePacket(boolean is_added, AndroidFeature feature) {
this(is_added, feature.type);
this(is_added, feature.getType());
}
public void write(FriendlyByteBuf buffer) {

View File

@ -0,0 +1,76 @@
package ru.dbotthepony.mc.otm.android
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.TranslatableComponent
import net.minecraftforge.common.util.INBTSerializable
import net.minecraftforge.event.entity.living.LivingHurtEvent
import net.minecraftforge.registries.ForgeRegistryEntry
import ru.dbotthepony.mc.otm.capability.android.AndroidCapability
import ru.dbotthepony.mc.otm.set
open class AndroidFeatureType<T : AndroidFeature> : ForgeRegistryEntry<AndroidFeatureType<*>> {
private val factory: (AndroidFeatureType<T>, AndroidCapability) -> T
constructor(factory: (AndroidCapability) -> T) : super() {
this.factory = { _, capability -> factory.invoke(capability) }
}
constructor(factory: (AndroidFeatureType<T>, AndroidCapability) -> T) : super() {
this.factory = factory
}
fun create(android: AndroidCapability): T {
return factory.invoke(this, android)
}
open fun isApplicable(android: AndroidCapability) = true
open val displayName: Component get() = registryName?.let { TranslatableComponent("android_feature.${it.namespace}.${it.path}") } ?: TranslatableComponent("android_feature.null.null")
}
abstract class AndroidFeature(val type: AndroidFeatureType<*>, val android: AndroidCapability) : INBTSerializable<CompoundTag> {
val entity get() = android.entity
open var level = 0
set(value) {
if (value != field) {
field = value
applyModifiers()
}
}
open fun tickClient() {}
open fun tickServer() {}
/**
* Called when it is required to network everything again
*/
open fun invalidateNetwork() {}
open fun applyModifiers() {}
open fun removeModifiers() {}
open fun onHurt(event: LivingHurtEvent) {}
override fun serializeNBT(): CompoundTag {
return CompoundTag().also {
it["level"] = level
}
}
override fun deserializeNBT(nbt: CompoundTag) {
level = nbt.getInt("level")
}
}
class DummyAndroidFeature(type: AndroidFeatureType<*>, android: AndroidCapability) : AndroidFeature(type, android) {
override fun serializeNBT(): CompoundTag {
return CompoundTag()
}
override fun deserializeNBT(nbt: CompoundTag) {
}
}

View File

@ -0,0 +1,32 @@
package ru.dbotthepony.mc.otm.android.feature
import net.minecraft.world.entity.ai.attributes.AttributeModifier
import net.minecraftforge.common.ForgeMod
import net.minecraftforge.event.entity.living.LivingHurtEvent
import ru.dbotthepony.mc.otm.android.AndroidFeature
import ru.dbotthepony.mc.otm.capability.android.AndroidCapability
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import java.util.*
class ExtendedReach(android: AndroidCapability) : AndroidFeature(AndroidFeatures.EXTENDED_REACH, android) {
override fun applyModifiers() {
if (!ForgeMod.REACH_DISTANCE.isPresent)
return
val reach = entity.getAttribute(ForgeMod.REACH_DISTANCE.get()) ?: return
reach.removePermanentModifier(MODIFIER_ID)
reach.addPermanentModifier(AttributeModifier(MODIFIER_ID, type.displayName.toString(), level + 1.0, AttributeModifier.Operation.ADDITION))
}
override fun removeModifiers() {
if (!ForgeMod.REACH_DISTANCE.isPresent)
return
entity.getAttribute(ForgeMod.REACH_DISTANCE.get())?.removePermanentModifier(MODIFIER_ID)
}
companion object {
private val MODIFIER_ID = UUID.fromString("4a3fae46-47a8-a03f-857d-f5c2b2c8f2f2")
}
}

View File

@ -0,0 +1,35 @@
package ru.dbotthepony.mc.otm.android.feature
import net.minecraft.world.entity.ai.attributes.AttributeModifier
import net.minecraft.world.entity.ai.attributes.Attributes
import ru.dbotthepony.mc.otm.android.AndroidFeature
import ru.dbotthepony.mc.otm.capability.android.AndroidCapability
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import java.util.*
class LimbOverclocking(android: AndroidCapability) : AndroidFeature(AndroidFeatures.LIMB_OVERCLOCKING, android) {
override fun applyModifiers() {
val speed = entity.getAttribute(Attributes.MOVEMENT_SPEED)
if (speed != null) {
speed.removePermanentModifier(MODIFIER_ID)
speed.addPermanentModifier(AttributeModifier(MODIFIER_ID, type.displayName.toString(), (level + 1) * 0.08, AttributeModifier.Operation.MULTIPLY_TOTAL))
}
val attackSpeed = entity.getAttribute(Attributes.ATTACK_SPEED)
if (attackSpeed != null) {
attackSpeed.removePermanentModifier(MODIFIER_ID)
attackSpeed.addPermanentModifier(AttributeModifier(MODIFIER_ID, type.displayName.toString(), (level + 1) * 0.06, AttributeModifier.Operation.MULTIPLY_TOTAL))
}
}
override fun removeModifiers() {
entity.getAttribute(Attributes.MOVEMENT_SPEED)?.removePermanentModifier(MODIFIER_ID)
entity.getAttribute(Attributes.ATTACK_SPEED)?.removePermanentModifier(MODIFIER_ID)
}
companion object {
private val MODIFIER_ID = UUID.fromString("4a3fae46-e57b-4e20-857d-f5c2b2c8f2f2")
}
}

View File

@ -0,0 +1,89 @@
package ru.dbotthepony.mc.otm.android.feature
import net.minecraft.nbt.CompoundTag
import net.minecraft.server.level.ServerPlayer
import net.minecraftforge.event.entity.living.LivingHurtEvent
import ru.dbotthepony.mc.otm.android.AndroidFeature
import ru.dbotthepony.mc.otm.capability.android.AndroidCapability
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import ru.dbotthepony.mc.otm.registry.StatNames
import ru.dbotthepony.mc.otm.set
import kotlin.math.roundToInt
class NanobotsArmor(android: AndroidCapability) : AndroidFeature(AndroidFeatures.NANOBOTS_ARMOR, android) {
var strength: Int = 0
set(value) { field = value.coerceIn(0 .. 2) }
var speed: Int = 0
set(value) { field = value.coerceIn(0 .. 2) }
private var ticksPassed = 0
private var layers = 0
override fun tickServer() {
if (layers < strength + 1 && android.extractEnergyInnerExact(ENERGY_PER_LAYER, true).isPositive) {
ticksPassed++
if (ticksPassed >= TICKS[speed]) {
layers++
android.extractEnergyInner(ENERGY_PER_LAYER, false)
}
} else {
ticksPassed = 0
}
}
override fun onHurt(event: LivingHurtEvent) {
ticksPassed = 0
if (!event.source.isBypassArmor && layers > 0) {
val absorbed = event.amount * STRENGTH[layers]
if (absorbed > 0.1f) {
val powerRequired = ENERGY_PER_HITPOINT * absorbed
val powerExtracted = android.extractEnergyInner(powerRequired, false)
val realAbsorbed = (powerExtracted / ENERGY_PER_HITPOINT).toFloat()
event.amount = event.amount - realAbsorbed
(entity as ServerPlayer?)?.awardStat(StatNames.DAMAGE_ABSORBED, (realAbsorbed * 10f).roundToInt())
layers--
}
}
}
override fun serializeNBT(): CompoundTag {
return super.serializeNBT().also {
it["ticksPassed"] = ticksPassed
it["layers"] = layers
it["strength"] = strength
it["speed"] = speed
}
}
override fun deserializeNBT(nbt: CompoundTag) {
super.deserializeNBT(nbt)
ticksPassed = nbt.getInt("ticksPassed")
layers = nbt.getInt("layers")
strength = nbt.getInt("strength")
speed = nbt.getInt("speed")
}
companion object {
private val ENERGY_PER_LAYER = ImpreciseFraction(200)
private val ENERGY_PER_HITPOINT = ImpreciseFraction(500)
private val TICKS = listOf(
80, // 4 seconds to build a layer
70, // 3.5 seconds to build a layer
60, // 3 seconds to build a layer
50, // 2.5 seconds to build a layer
)
private val STRENGTH = listOf(
0.1f,
0.2f,
0.3f,
0.4f,
)
}
}

View File

@ -0,0 +1,71 @@
package ru.dbotthepony.mc.otm.android.feature
import net.minecraft.nbt.CompoundTag
import net.minecraft.server.level.ServerPlayer
import net.minecraftforge.event.entity.living.LivingHurtEvent
import ru.dbotthepony.mc.otm.android.AndroidFeature
import ru.dbotthepony.mc.otm.capability.android.AndroidCapability
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import ru.dbotthepony.mc.otm.registry.StatNames
import ru.dbotthepony.mc.otm.set
import kotlin.math.roundToInt
class NanobotsRegeneration(android: AndroidCapability) : AndroidFeature(AndroidFeatures.NANOBOTS_REGENERATION, android) {
private var ticksPassed = 0
private var healTicks = 0
override fun tickServer() {
if (entity.health > 0f && entity.health < entity.maxHealth) {
ticksPassed++
val waitTime = TICKS_BETWEEN_HEAL.getOrElse(healTicks) { TICKS_BETWEEN_HEAL.last() }
if (ticksPassed > waitTime) {
val missingHealth = entity.maxHealth - entity.health
val power = ENERGY_PER_HITPOINT * missingHealth
val extracted = android.extractEnergyInner(power, false)
if (extracted.isPositive) {
healTicks = (healTicks + 1).coerceAtMost(level)
val healed = (extracted / ENERGY_PER_HITPOINT).toFloat()
entity.heal(healed)
(entity as ServerPlayer?)?.awardStat(StatNames.HEALTH_REGENERATED, (healed * 10f).roundToInt())
ticksPassed = 0
}
}
} else {
ticksPassed = 0
healTicks = 0
}
}
override fun onHurt(event: LivingHurtEvent) {
ticksPassed = 0
healTicks = 0
}
override fun serializeNBT(): CompoundTag {
return super.serializeNBT().also {
it["ticksPassed"] = ticksPassed
it["healTicks"] = healTicks
}
}
override fun deserializeNBT(nbt: CompoundTag) {
super.deserializeNBT(nbt)
ticksPassed = nbt.getInt("ticksPassed")
healTicks = nbt.getInt("healTicks")
}
companion object {
private val ENERGY_PER_HITPOINT = ImpreciseFraction(800)
private val TICKS_BETWEEN_HEAL = listOf(
100, // 5 seconds
80, // 4 seconds
60, // 3 seconds
40, // 2 seconds
)
}
}

View File

@ -1,8 +1,8 @@
package ru.dbotthepony.mc.otm.capability.android
import net.minecraft.MethodsReturnNonnullByDefault
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
import it.unimi.dsi.fastutil.objects.ObjectArraySet
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
@ -36,55 +36,55 @@ import ru.dbotthepony.mc.otm.capability.extractEnergy
import ru.dbotthepony.mc.otm.capability.receiveEnergy
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.ifHas
import ru.dbotthepony.mc.otm.registry.MNames
import ru.dbotthepony.mc.otm.registry.MRegistry
import ru.dbotthepony.mc.otm.registry.StatNames
import ru.dbotthepony.mc.otm.set
import java.util.*
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
open class AndroidCapability(@JvmField protected val ent: LivingEntity) : ICapabilityProvider, IAndroidCapability, INBTSerializable<CompoundTag> {
@JvmField protected var battery = ImpreciseFraction.ZERO
@JvmField protected var maxBattery = ImpreciseFraction(60000)
open class AndroidCapability(final override val entity: LivingEntity) : ICapabilityProvider, IAndroidCapability, INBTSerializable<CompoundTag> {
protected var battery = ImpreciseFraction.ZERO
protected var maxBattery = ImpreciseFraction(60000)
override var batteryItemStack = ItemStack.EMPTY
override var batteryItemStack: ItemStack = ItemStack.EMPTY
private var remoteBattery = ImpreciseFraction(-1)
private var remoteMaxBattery = ImpreciseFraction(-1)
private var remoteBatteryStack = ItemStack.EMPTY
@JvmField protected val features: MutableMap<AndroidFeatureType<*>, AndroidFeature> = HashMap()
@JvmField protected val networkQueue = ArrayList<Any>()
@JvmField protected val queuedTicks = ArrayList<Runnable>()
@JvmField protected var networkFirst = false
protected val features = Object2ObjectArrayMap<AndroidFeatureType<*>, AndroidFeature>()
protected val networkQueue = ArrayList<Any>()
protected val queuedTicks = ArrayList<Runnable>()
protected var networkTickedOnce = false
protected fun addFeature(feature: AndroidFeature): Boolean {
if (features.containsKey(feature.type)) return false
features[feature.type] = feature
if (!ent.level.isClientSide) {
queuedTicks.add(Runnable { feature.applyModifiers() })
if (!entity.level.isClientSide) {
queuedTicks.add(feature::applyModifiers)
}
if (ent is ServerPlayer) {
if (entity is ServerPlayer) {
sendNetwork(AndroidFeaturePacket(true, feature))
}
return true
}
@Suppress("unchecked_cast")
override fun <T : AndroidFeature> addFeature(feature: AndroidFeatureType<T>): T {
val get = features[feature]
if (get != null) return get as T
val factory = feature.factory(this)
val factory = feature.create(this)
features[feature] = factory
if (!ent.level.isClientSide) {
queuedTicks.add(Runnable { factory.applyModifiers() })
if (!entity.level.isClientSide) {
queuedTicks.add(factory::applyModifiers)
}
if (ent is ServerPlayer) {
if (entity is ServerPlayer) {
sendNetwork(AndroidFeaturePacket(true, factory))
}
@ -95,11 +95,13 @@ open class AndroidCapability(@JvmField protected val ent: LivingEntity) : ICapab
val removed = features.remove(feature)
if (removed != null) {
if (!ent.level.isClientSide) {
queuedTicks.add { removed.removeModifiers() }
if (!entity.level.isClientSide) {
queuedTicks.add {
removed.removeModifiers()
}
}
if (ent is ServerPlayer) {
if (entity is ServerPlayer) {
sendNetwork(AndroidFeaturePacket(false, removed))
}
}
@ -116,6 +118,7 @@ open class AndroidCapability(@JvmField protected val ent: LivingEntity) : ICapab
return get.level >= level
}
@Suppress("unchecked_cast")
override fun <T : AndroidFeature> getFeature(feature: AndroidFeatureType<T>): T? {
return features[feature] as T?
}
@ -125,7 +128,7 @@ open class AndroidCapability(@JvmField protected val ent: LivingEntity) : ICapab
remoteMaxBattery = ImpreciseFraction.MINUS_ONE
remoteBatteryStack = ItemStack.EMPTY
if (ent is ServerPlayer) {
if (entity is ServerPlayer) {
for (feature in features.values) {
sendNetwork(AndroidFeaturePacket(true, feature))
feature.invalidateNetwork()
@ -157,8 +160,7 @@ open class AndroidCapability(@JvmField protected val ent: LivingEntity) : ICapab
val featureList = ListTag()
for (feature in features.values) {
val featureNbt = CompoundTag()
feature.serializeNBT(featureNbt)
val featureNbt = feature.serializeNBT()
featureNbt["id"] = feature.type.registryName!!.toString()
featureList.add(featureNbt)
@ -191,14 +193,14 @@ open class AndroidCapability(@JvmField protected val ent: LivingEntity) : ICapab
if (tag is CompoundTag) {
val feature = MRegistry.ANDROID_FEATURES.getValue(ResourceLocation(tag.getString("id")))
if (feature != null && feature.isApplicable(this)) {
val feature = feature.factory(this)
if (feature?.isApplicable(this) == true) {
val instance = feature.create(this)
feature.deserializeNBT(tag)
addFeature(feature)
instance.deserializeNBT(tag)
addFeature(instance)
if (!ent.level.isClientSide) {
queuedTicks.add(Runnable { feature.applyModifiers() })
if (!entity.level.isClientSide) {
queuedTicks.add(instance::applyModifiers)
}
}
}
@ -207,14 +209,14 @@ open class AndroidCapability(@JvmField protected val ent: LivingEntity) : ICapab
fun dropBattery() {
if (batteryItemStack.isEmpty) return
ent.spawnAtLocation(batteryItemStack)
entity.spawnAtLocation(batteryItemStack)
batteryItemStack = ItemStack.EMPTY
}
protected fun sendNetwork(packet: Any) {
if (ent is ServerPlayer) {
if (networkFirst) {
MatteryNetworking.CHANNEL.send(PacketDistributor.PLAYER.with { ent }, packet)
if (entity is ServerPlayer) {
if (networkTickedOnce) {
MatteryNetworking.CHANNEL.send(PacketDistributor.PLAYER.with { entity }, packet)
} else {
networkQueue.add(packet)
}
@ -222,9 +224,9 @@ open class AndroidCapability(@JvmField protected val ent: LivingEntity) : ICapab
}
protected open fun tickNetwork() {
networkFirst = true
networkTickedOnce = true
if (ent is ServerPlayer) {
if (entity is ServerPlayer) {
if (battery != remoteBattery) {
remoteBattery = battery
sendNetwork(AndroidEnergyPacket(false, battery))
@ -242,7 +244,7 @@ open class AndroidCapability(@JvmField protected val ent: LivingEntity) : ICapab
if (networkQueue.size != 0) {
for (packet in networkQueue) {
MatteryNetworking.CHANNEL.send(PacketDistributor.PLAYER.with {ent}, packet)
MatteryNetworking.CHANNEL.send(PacketDistributor.PLAYER.with { entity }, packet)
}
networkQueue.clear()
@ -255,10 +257,15 @@ open class AndroidCapability(@JvmField protected val ent: LivingEntity) : ICapab
override fun tickClient() {
queuedTicks.clear()
if (!ent.isAlive) return
if (!entity.isAlive)
return
tickInnerClientAlways()
if (isAndroid()) {
tickInnerClient()
for (feature in features.values) {
feature.tickClient()
}
@ -266,17 +273,22 @@ open class AndroidCapability(@JvmField protected val ent: LivingEntity) : ICapab
}
override fun tick() {
if (!ent.isAlive) return
if (!entity.isAlive) return
tickServerAlways()
if (isAndroid()) {
tickServer()
for (feature in features.values) {
feature.tickServer()
}
}
for (runnable in queuedTicks) {
runnable.run()
}
queuedTicks.clear()
tickNetwork()
}
@ -284,12 +296,12 @@ open class AndroidCapability(@JvmField protected val ent: LivingEntity) : ICapab
protected open fun tickServerAlways() {}
protected open fun tickServer() {
if (ent.airSupply < ent.maxAirSupply)
ent.airSupply = ent.maxAirSupply
if (entity.airSupply < entity.maxAirSupply)
entity.airSupply = entity.maxAirSupply
for (effect in UNAFFECTED_EFFECTS)
if (ent.hasEffect(effect))
ent.removeEffect(effect)
if (entity.hasEffect(effect))
entity.removeEffect(effect)
if (!batteryItemStack.isEmpty && battery < maxBattery) {
batteryItemStack.getCapability(CapabilityEnergy.ENERGY).ifPresent {
@ -325,8 +337,8 @@ open class AndroidCapability(@JvmField protected val ent: LivingEntity) : ICapab
}
if (howMuch.isZero) {
if (!simulate && ent is ServerPlayer) {
ent.awardStat(StatNames.POWER_CONSUMED, drained.toInt() * 10)
if (!simulate && entity is ServerPlayer) {
entity.awardStat(StatNames.POWER_CONSUMED, drained.toInt() * 10)
}
return drained
@ -339,8 +351,8 @@ open class AndroidCapability(@JvmField protected val ent: LivingEntity) : ICapab
if (!simulate) {
battery = new
if (ent is ServerPlayer) {
ent.awardStat(StatNames.POWER_CONSUMED, drained.toInt() * 10)
if (entity is ServerPlayer) {
entity.awardStat(StatNames.POWER_CONSUMED, drained.toInt() * 10)
}
}
@ -383,10 +395,6 @@ open class AndroidCapability(@JvmField protected val ent: LivingEntity) : ICapab
return receiveEnergyOuter(howMuch, simulate)
}
override fun getEntity(): LivingEntity {
return ent
}
override val batteryLevel: ImpreciseFraction get() {
if (!batteryItemStack.isEmpty) {
val resolver = batteryItemStack.getCapability(CapabilityEnergy.ENERGY).resolve()
@ -434,10 +442,10 @@ open class AndroidCapability(@JvmField protected val ent: LivingEntity) : ICapab
@Suppress("unused")
companion object {
@JvmField
val UNAFFECTED_EFFECTS: MutableSet<MobEffect> = HashSet()
val UNAFFECTED_EFFECTS = ObjectArraySet<MobEffect>()
@JvmStatic
fun registerEffects(event: FMLCommonSetupEvent?) {
fun registerEffects(event: FMLCommonSetupEvent) {
UNAFFECTED_EFFECTS.add(MobEffects.CONDUIT_POWER)
UNAFFECTED_EFFECTS.add(MobEffects.HEAL)
// maybe it makes things go haywire idk

View File

@ -14,7 +14,7 @@ import java.util.*
interface IAndroidCapability : IMatteryEnergyStorage, INBTSerializable<CompoundTag> {
fun tick()
fun tickClient()
fun getEntity(): LivingEntity
val entity: LivingEntity
fun <T : AndroidFeature> addFeature(feature: AndroidFeatureType<T>): T
fun removeFeature(feature: AndroidFeatureType<*>): Boolean
fun hasFeature(feature: AndroidFeatureType<*>): Boolean

View File

@ -4,20 +4,20 @@ import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext
import net.minecraftforge.registries.DeferredRegister
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.android.AndroidFeatureType
import ru.dbotthepony.mc.otm.android.AndroidFeature
import ru.dbotthepony.mc.otm.android.feature.AndroidExtendedReach
import ru.dbotthepony.mc.otm.android.feature.AndroidLimbOverclocking
import ru.dbotthepony.mc.otm.android.feature.AndroidNanobotsArmor
import ru.dbotthepony.mc.otm.android.feature.AndroidNanobotsRegeneration
import ru.dbotthepony.mc.otm.android.DummyAndroidFeature
import ru.dbotthepony.mc.otm.android.feature.ExtendedReach
import ru.dbotthepony.mc.otm.android.feature.LimbOverclocking
import ru.dbotthepony.mc.otm.android.feature.NanobotsArmor
import ru.dbotthepony.mc.otm.android.feature.NanobotsRegeneration
object AndroidFeatures {
private val registry = DeferredRegister.create(AndroidFeatureType::class.java, OverdriveThatMatters.MOD_ID)
val AIR_BAGS: AndroidFeatureType<*> by registry.register(MNames.AIR_BAGS) { AndroidFeatureType(::AndroidFeature) }
val LIMB_OVERCLOCKING: AndroidFeatureType<*> by registry.register(MNames.LIMB_OVERCLOCKING) { AndroidFeatureType(::AndroidLimbOverclocking) }
val NANOBOTS_REGENERATION: AndroidFeatureType<*> by registry.register(MNames.NANOBOTS_REGENERATION) { AndroidFeatureType(::AndroidNanobotsRegeneration) }
val NANOBOTS_ARMOR: AndroidFeatureType<*> by registry.register(MNames.NANOBOTS_ARMOR) { AndroidFeatureType(::AndroidNanobotsArmor) }
val EXTENDED_REACH: AndroidFeatureType<*> by registry.register(MNames.EXTENDED_REACH) { AndroidFeatureType(::AndroidExtendedReach) }
val AIR_BAGS: AndroidFeatureType<*> by registry.register(MNames.AIR_BAGS) { AndroidFeatureType(::DummyAndroidFeature) }
val LIMB_OVERCLOCKING: AndroidFeatureType<*> by registry.register(MNames.LIMB_OVERCLOCKING) { AndroidFeatureType(::LimbOverclocking) }
val NANOBOTS_REGENERATION: AndroidFeatureType<*> by registry.register(MNames.NANOBOTS_REGENERATION) { AndroidFeatureType(::NanobotsRegeneration) }
val NANOBOTS_ARMOR: AndroidFeatureType<*> by registry.register(MNames.NANOBOTS_ARMOR) { AndroidFeatureType(::NanobotsArmor) }
val EXTENDED_REACH: AndroidFeatureType<*> by registry.register(MNames.EXTENDED_REACH) { AndroidFeatureType(::ExtendedReach) }
internal fun register() {
registry.register(FMLJavaModLoadingContext.get().modEventBus)

View File

@ -1,3 +1,6 @@
@file:Suppress("unused")
package ru.dbotthepony.mc.otm.registry
import net.minecraft.network.chat.TextComponent
@ -9,7 +12,7 @@ import net.minecraftforge.registries.RegistryObject
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.android.AndroidResearchBuilder
import ru.dbotthepony.mc.otm.android.AndroidResearchType
import ru.dbotthepony.mc.otm.android.feature.AndroidNanobotsArmor
import ru.dbotthepony.mc.otm.android.feature.NanobotsArmor
import ru.dbotthepony.mc.otm.client.render.SkinElement
object AndroidResearch {
@ -105,7 +108,7 @@ object AndroidResearch {
registry.register(FMLJavaModLoadingContext.get().modEventBus)
}
val AIR_BAGS by registry.register(MNames.AIR_BAGS) {
val AIR_BAGS: AndroidResearchType<*> by registry.register(MNames.AIR_BAGS) {
AndroidResearchBuilder()
.setExperienceCost(18)
.addFeatureResult(AndroidFeatures.AIR_BAGS)
@ -114,7 +117,7 @@ object AndroidResearch {
.build()
}
val EXTENDED_REACH by registry.register(MNames.EXTENDED_REACH) {
val EXTENDED_REACH: AndroidResearchType<*> by registry.register(MNames.EXTENDED_REACH) {
AndroidResearchBuilder()
.setExperienceCost(40)
.addFeatureResult(AndroidFeatures.EXTENDED_REACH)
@ -123,7 +126,7 @@ object AndroidResearch {
.build()
}
val NANOBOTS by registry.register(MNames.NANOBOTS) {
val NANOBOTS: AndroidResearchType<*> by registry.register(MNames.NANOBOTS) {
AndroidResearchBuilder()
.setExperienceCost(15)
.withDescription()
@ -131,7 +134,7 @@ object AndroidResearch {
.build()
}
val NANOBOTS_ARMOR by registry.register(MNames.NANOBOTS_ARMOR) {
val NANOBOTS_ARMOR: AndroidResearchType<*> by registry.register(MNames.NANOBOTS_ARMOR) {
AndroidResearchBuilder()
.setExperienceCost(25)
.withDescription()
@ -222,7 +225,7 @@ object AndroidResearch {
)
)
.addFeatureResult(OverdriveThatMatters.loc(MNames.NANOBOTS_ARMOR), 0) { feature ->
if ((feature as AndroidNanobotsArmor).strength < level) feature.strength = level
if ((feature as NanobotsArmor).strength < level) feature.strength = level
}
.build()
})
@ -247,7 +250,7 @@ object AndroidResearch {
)
)
.addFeatureResult(OverdriveThatMatters.loc(MNames.NANOBOTS_ARMOR), 0) { feature ->
if ((feature as AndroidNanobotsArmor).speed < level) feature.speed = level
if ((feature as NanobotsArmor).speed < level) feature.speed = level
}
.build()
})