diff --git a/src/main/java/ru/dbotthepony/mc/otm/Registry.java b/src/main/java/ru/dbotthepony/mc/otm/Registry.java index b7b8286de..628348225 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/Registry.java +++ b/src/main/java/ru/dbotthepony/mc/otm/Registry.java @@ -1,6 +1,7 @@ package ru.dbotthepony.mc.otm; import net.minecraft.client.gui.screens.MenuScreens; +import net.minecraft.network.chat.TextComponent; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.food.FoodProperties; @@ -16,6 +17,7 @@ import net.minecraftforge.registries.ForgeRegistry; import net.minecraftforge.registries.RegistryBuilder; import ru.dbotthepony.mc.otm.android.AndroidFeature; import ru.dbotthepony.mc.otm.android.AndroidResearchType; +import ru.dbotthepony.mc.otm.android.feature.AndroidLimbOverclocking; import ru.dbotthepony.mc.otm.block.*; import ru.dbotthepony.mc.otm.block.entity.*; import ru.dbotthepony.mc.otm.android.AndroidFeatureType; @@ -102,8 +104,15 @@ public class Registry { public static final ResourceLocation NUTRIENT_PASTE = new ResourceLocation(OverdriveThatMatters.MOD_ID, "nutrient_paste"); - // android features + // android features and research public static final ResourceLocation AIR_BAGS = new ResourceLocation(OverdriveThatMatters.MOD_ID, "air_bags"); + public static final ResourceLocation LIMB_OVERCLOCKING = new ResourceLocation(OverdriveThatMatters.MOD_ID, "limb_overclocking"); + public static final ResourceLocation LIMB_OVERCLOCKING_1 = new ResourceLocation(OverdriveThatMatters.MOD_ID, "limb_overclocking_1"); + public static final ResourceLocation LIMB_OVERCLOCKING_2 = new ResourceLocation(OverdriveThatMatters.MOD_ID, "limb_overclocking_2"); + public static final ResourceLocation LIMB_OVERCLOCKING_3 = new ResourceLocation(OverdriveThatMatters.MOD_ID, "limb_overclocking_3"); + public static final ResourceLocation LIMB_OVERCLOCKING_4 = new ResourceLocation(OverdriveThatMatters.MOD_ID, "limb_overclocking_4"); + + public static final ResourceLocation[] LIMB_OVERCLOCKING_LIST = new ResourceLocation[] { LIMB_OVERCLOCKING_1, LIMB_OVERCLOCKING_2, LIMB_OVERCLOCKING_3, LIMB_OVERCLOCKING_4 }; } public static class Blocks { @@ -277,14 +286,17 @@ public class Registry { public static class AndroidFeatures { public static final AndroidFeatureType AIR_BAGS = new AndroidFeatureType<>(AndroidFeature::new); + public static final AndroidFeatureType LIMB_OVERCLOCKING = new AndroidFeatureType<>(AndroidLimbOverclocking::new); static { AIR_BAGS.setRegistryName(Names.AIR_BAGS); + LIMB_OVERCLOCKING.setRegistryName(Names.LIMB_OVERCLOCKING); } @SubscribeEvent public static void register(final RegistryEvent.Register> event) { event.getRegistry().register(AIR_BAGS); + event.getRegistry().register(LIMB_OVERCLOCKING); } } @@ -358,13 +370,39 @@ public class Registry { .build() ); + public static final AndroidResearchType[] LIMB_OVERCLOCKING = new AndroidResearchType[4]; + static { AIR_BAGS.setRegistryName(Names.AIR_BAGS); + + for (int i = 0; i < 4; i++) { + var builder = new ru.dbotthepony.mc.otm.android.AndroidResearch.Builder() + .withDescription() + .setExperienceCost(24 + i * 8) + .withIconText(new TextComponent(String.valueOf(i + 1))) + .withIcon(ICON_LIMB_OVERCLOCKING) + .addFeatureResult(Names.LIMB_OVERCLOCKING, i); + + if (i > 0) { + builder.addPrerequisite(Names.LIMB_OVERCLOCKING_LIST[i - 1]); + } + + LIMB_OVERCLOCKING[i] = new AndroidResearchType<>(builder.build()); + } + + LIMB_OVERCLOCKING[0].setRegistryName(Names.LIMB_OVERCLOCKING_1); + LIMB_OVERCLOCKING[1].setRegistryName(Names.LIMB_OVERCLOCKING_2); + LIMB_OVERCLOCKING[2].setRegistryName(Names.LIMB_OVERCLOCKING_3); + LIMB_OVERCLOCKING[3].setRegistryName(Names.LIMB_OVERCLOCKING_4); } @SubscribeEvent public static void register(final RegistryEvent.Register> event) { event.getRegistry().register(AIR_BAGS); + event.getRegistry().register(LIMB_OVERCLOCKING[0]); + event.getRegistry().register(LIMB_OVERCLOCKING[1]); + event.getRegistry().register(LIMB_OVERCLOCKING[2]); + event.getRegistry().register(LIMB_OVERCLOCKING[3]); } } diff --git a/src/main/java/ru/dbotthepony/mc/otm/android/AndroidFeature.java b/src/main/java/ru/dbotthepony/mc/otm/android/AndroidFeature.java index dbedd63da..4490dba80 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/android/AndroidFeature.java +++ b/src/main/java/ru/dbotthepony/mc/otm/android/AndroidFeature.java @@ -8,11 +8,24 @@ public class AndroidFeature implements INBTSerializable { public final AndroidFeatureType 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)) @@ -28,10 +41,6 @@ public class AndroidFeature implements INBTSerializable { // when player forgets everything } - public void attachFinish() { - // override - } - @Override public int hashCode() { return type.hashCode(); @@ -45,8 +54,11 @@ public class AndroidFeature implements INBTSerializable { } - public void serializeNBT(CompoundTag tag) { + public void applyModifiers() {} + public void removeModifiers() {} + public void serializeNBT(CompoundTag tag) { + tag.putInt("level", level); } @Override @@ -58,6 +70,6 @@ public class AndroidFeature implements INBTSerializable { @Override public void deserializeNBT(CompoundTag tag) { - + level = tag.getInt("level"); } } diff --git a/src/main/java/ru/dbotthepony/mc/otm/android/AndroidResearch.java b/src/main/java/ru/dbotthepony/mc/otm/android/AndroidResearch.java index 5fc5fba51..594029d1f 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/android/AndroidResearch.java +++ b/src/main/java/ru/dbotthepony/mc/otm/android/AndroidResearch.java @@ -76,6 +76,11 @@ public abstract class AndroidResearch implements INBTSerializable { return true; } + @Nullable + public Component getIconText() { + return null; + } + public boolean research() { return research(false); } @@ -102,12 +107,23 @@ public abstract class AndroidResearch implements INBTSerializable { public static class Builder { record DeferredItemStack(ResourceLocation id, int amount) {} + record DeferredFeature(ResourceLocation id, int level) { + DeferredFeature(ResourceLocation id, int level) { + this.id = id; + this.level = level; + } + + DeferredFeature(ResourceLocation id) { + this(id, 0); + } + } public int experience = 0; + public Component text; public boolean has_description = false; public final ArrayList items = new ArrayList<>(); public final ArrayList prerequisites = new ArrayList<>(); - public final ArrayList feature_results = new ArrayList<>(); + public final ArrayList feature_results = new ArrayList<>(); public SkinElement icon; public Builder addPrerequisite(ResourceLocation location) { @@ -115,13 +131,28 @@ public abstract class AndroidResearch implements INBTSerializable { return this; } + public Builder addPrerequisite(AndroidResearchType location) { + prerequisites.add(Objects.requireNonNull(location.getRegistryName())); + return this; + } + public Builder addFeatureResult(ResourceLocation location) { - feature_results.add(location); + feature_results.add(new DeferredFeature(location)); return this; } public Builder addFeatureResult(AndroidFeatureType location) { - feature_results.add(Objects.requireNonNull(location.getRegistryName())); + feature_results.add(new DeferredFeature(Objects.requireNonNull(location.getRegistryName()))); + return this; + } + + public Builder addFeatureResult(ResourceLocation location, int level) { + feature_results.add(new DeferredFeature(location, level)); + return this; + } + + public Builder addFeatureResult(AndroidFeatureType location, int level) { + feature_results.add(new DeferredFeature(Objects.requireNonNull(location.getRegistryName()), level)); return this; } @@ -150,9 +181,17 @@ public abstract class AndroidResearch implements INBTSerializable { return this; } + public Builder withIconText(Component text) { + this.text = text; + return this; + } + private final ArrayList resolved_stacks = new ArrayList<>(); private final ArrayList> resolved_preq = new ArrayList<>(); - private final ArrayList> resolved_features = new ArrayList<>(); + + record ResolvedFeature(AndroidFeatureType type, int level) {} + + private final ArrayList resolved_features = new ArrayList<>(); private boolean resolved = false; @@ -179,10 +218,10 @@ public abstract class AndroidResearch implements INBTSerializable { } for (var entry : feature_results) { - var get = Registry.ANDROID_FEATURES().getValue(entry); + var get = Registry.ANDROID_FEATURES().getValue(entry.id); if (get != null) { - resolved_features.add(get); + resolved_features.add(new ResolvedFeature(get, entry.level)); } else { throw new IllegalArgumentException("Can not find android feature " + entry); } @@ -201,6 +240,12 @@ public abstract class AndroidResearch implements INBTSerializable { return icon; } + @Nullable + @Override + public Component getIconText() { + return text; + } + @Override public List getTooltip() { var list = new ArrayList<>(super.getTooltip()); @@ -256,12 +301,25 @@ public abstract class AndroidResearch implements INBTSerializable { @Override public void onResearched() { for (var feature : resolved_features) { - capability.addFeature(feature); + var get = capability.getFeature(feature.type); + + if (get == null) { + get = capability.addFeature(feature.type); + get.setLevel(feature.level); + } else if (get.getLevel() < feature.level) { + get.setLevel(feature.level); + } } } @Override public boolean canAfford() { + for (var preq : resolved_preq) { + if (!capability.getResearch(preq).isResearched()) { + return false; + } + } + if (capability.ply.experienceLevel < experience) return false; diff --git a/src/main/java/ru/dbotthepony/mc/otm/android/feature/AndroidLimbOverclocking.java b/src/main/java/ru/dbotthepony/mc/otm/android/feature/AndroidLimbOverclocking.java new file mode 100644 index 000000000..38c52a936 --- /dev/null +++ b/src/main/java/ru/dbotthepony/mc/otm/android/feature/AndroidLimbOverclocking.java @@ -0,0 +1,43 @@ +package ru.dbotthepony.mc.otm.android.feature; + +import net.minecraft.network.chat.TextComponent; +import net.minecraft.world.entity.ai.attributes.AttributeModifier; +import net.minecraft.world.entity.ai.attributes.Attributes; +import ru.dbotthepony.mc.otm.OverdriveThatMatters; +import ru.dbotthepony.mc.otm.android.AndroidFeature; +import ru.dbotthepony.mc.otm.android.AndroidFeatureType; +import ru.dbotthepony.mc.otm.android.AndroidResearch; +import ru.dbotthepony.mc.otm.capability.IAndroidCapability; + +import java.util.UUID; + +public class AndroidLimbOverclocking extends AndroidFeature { + public final UUID MODIFIED_NAME = UUID.fromString("4a3fae46-e57b-4e20-857d-f5c2b2c8f2f2"); + + public AndroidLimbOverclocking(AndroidFeatureType type, IAndroidCapability capability) { + super(type, capability); + } + + @Override + public void applyModifiers() { + var speed = capability.getEntity().getAttribute(Attributes.MOVEMENT_SPEED); + + OverdriveThatMatters.LOGGER.info("Get modifier {} {}", this, speed); + + if (speed != null) { + speed.removePermanentModifier(MODIFIED_NAME); + speed.addPermanentModifier(new AttributeModifier(MODIFIED_NAME, type.getDisplayName().toString(), 0.08d * (level + 1), AttributeModifier.Operation.MULTIPLY_TOTAL)); + OverdriveThatMatters.LOGGER.info("Add modifier {} {}", this, capability.getEntity()); + } + } + + @Override + public void removeModifiers() { + var speed = capability.getEntity().getAttribute(Attributes.MOVEMENT_SPEED); + + if (speed != null) { + speed.removePermanentModifier(MODIFIED_NAME); + OverdriveThatMatters.LOGGER.info("Remove modifier {} {}", this, capability.getEntity()); + } + } +} diff --git a/src/main/java/ru/dbotthepony/mc/otm/capability/AndroidCapability.java b/src/main/java/ru/dbotthepony/mc/otm/capability/AndroidCapability.java index 76cbfc08f..bc7af82e4 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/capability/AndroidCapability.java +++ b/src/main/java/ru/dbotthepony/mc/otm/capability/AndroidCapability.java @@ -20,6 +20,7 @@ import net.minecraftforge.event.entity.living.LivingEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fmllegacy.network.PacketDistributor; +import ru.dbotthepony.mc.otm.OverdriveThatMatters; import ru.dbotthepony.mc.otm.Registry; import ru.dbotthepony.mc.otm.android.AndroidFeature; import ru.dbotthepony.mc.otm.android.AndroidFeatureType; @@ -46,6 +47,8 @@ public class AndroidCapability implements ICapabilityProvider, IAndroidCapabilit protected final Map, AndroidFeature> features = new HashMap<>(); protected final ArrayList network_queue = new ArrayList<>(); + protected final ArrayList delayed_tick_server = new ArrayList<>(); + protected boolean network_first = false; protected boolean addFeature(@Nonnull AndroidFeature feature) { @@ -54,6 +57,10 @@ public class AndroidCapability implements ICapabilityProvider, IAndroidCapabilit features.put(feature.type, feature); + if (!ent.level.isClientSide) { + delayed_tick_server.add(feature::applyModifiers); + } + if (ent instanceof ServerPlayer ply) { sendNetwork(new AndroidFeaturePacket(true, feature)); } @@ -62,18 +69,24 @@ public class AndroidCapability implements ICapabilityProvider, IAndroidCapabilit } @Override - public boolean addFeature(@Nonnull AndroidFeatureType feature) { - if (features.containsKey(feature)) - return false; + public AndroidFeature addFeature(@Nonnull AndroidFeatureType feature) { + var get = features.get(feature); + + if (get != null) + return get; var factory = feature.factory(this); features.put(feature, factory); + if (!ent.level.isClientSide) { + delayed_tick_server.add(factory::applyModifiers); + } + if (ent instanceof ServerPlayer ply) { sendNetwork(new AndroidFeaturePacket(true, factory)); } - return true; + return factory; } @Override @@ -81,6 +94,10 @@ public class AndroidCapability implements ICapabilityProvider, IAndroidCapabilit var removed = features.remove(feature); if (removed != null) { + if (!ent.level.isClientSide) { + delayed_tick_server.add(removed::removeModifiers); + } + if (ent instanceof ServerPlayer ply) { sendNetwork(new AndroidFeaturePacket(false, removed)); } @@ -96,6 +113,17 @@ public class AndroidCapability implements ICapabilityProvider, IAndroidCapabilit 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; + } + @Nullable public AndroidFeature getFeature(AndroidFeatureType feature) { return features.get(feature); @@ -199,7 +227,10 @@ public class AndroidCapability implements ICapabilityProvider, IAndroidCapabilit feature.deserializeNBT(tag); addFeature(feature); - feature.attachFinish(); + + if (ent.level == null || !ent.level.isClientSide) { + delayed_tick_server.add(feature::applyModifiers); + } } } } @@ -265,6 +296,8 @@ public class AndroidCapability implements ICapabilityProvider, IAndroidCapabilit feature.tickClient(); } } + + delayed_tick_server.clear(); } @Override @@ -277,6 +310,12 @@ public class AndroidCapability implements ICapabilityProvider, IAndroidCapabilit } } + for (var runnable : delayed_tick_server) { + runnable.run(); + } + + delayed_tick_server.clear(); + tickNetwork(); } diff --git a/src/main/java/ru/dbotthepony/mc/otm/capability/IAndroidCapability.java b/src/main/java/ru/dbotthepony/mc/otm/capability/IAndroidCapability.java index 8b8e23f32..283f47e01 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/capability/IAndroidCapability.java +++ b/src/main/java/ru/dbotthepony/mc/otm/capability/IAndroidCapability.java @@ -18,9 +18,10 @@ public interface IAndroidCapability extends IMatteryEnergyStorage, INBTSerializa void tickClient(); LivingEntity getEntity(); - boolean addFeature(@Nonnull AndroidFeatureType feature); + AndroidFeature addFeature(@Nonnull AndroidFeatureType feature); boolean removeFeature(@Nonnull AndroidFeatureType feature); boolean hasFeature(@Nullable AndroidFeatureType feature); + boolean hasFeatureLevel(@Nullable AndroidFeatureType feature, int level); AndroidFeature getFeature(AndroidFeatureType feature); @Nonnull diff --git a/src/main/java/ru/dbotthepony/mc/otm/screen/AndroidStationScreen.java b/src/main/java/ru/dbotthepony/mc/otm/screen/AndroidStationScreen.java index ec4c54b69..74ad9f5aa 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/screen/AndroidStationScreen.java +++ b/src/main/java/ru/dbotthepony/mc/otm/screen/AndroidStationScreen.java @@ -50,6 +50,12 @@ public class AndroidStationScreen extends MatteryScreen { } else { RenderHelper.drawRect(stack, 0, 0, getWidth(), getHeight()); } + + var text = node.getIconText(); + + if (text != null) { + font.drawShadow(stack, text, getWidth() - font.width(text), getHeight() - font.lineHeight, 0xFFFFFFFF); + } } }); } diff --git a/src/main/resources/assets/overdrive_that_matters/lang/en_us.json b/src/main/resources/assets/overdrive_that_matters/lang/en_us.json index dbf4d12aa..3ae205a93 100644 --- a/src/main/resources/assets/overdrive_that_matters/lang/en_us.json +++ b/src/main/resources/assets/overdrive_that_matters/lang/en_us.json @@ -44,6 +44,21 @@ "otm.android_station.research.missing_predecessors": "%s needs to be researched first", "android_feature.overdrive_that_matters.air_bags": "Air bags", + "android_feature.overdrive_that_matters.limb_overclocking_1": "Limb overclocking 1", + "android_feature.overdrive_that_matters.limb_overclocking_2": "Limb overclocking 2", + "android_feature.overdrive_that_matters.limb_overclocking_3": "Limb overclocking 3", + "android_feature.overdrive_that_matters.limb_overclocking_4": "Limb overclocking 4", + + "android_research.overdrive_that_matters.limb_overclocking_1": "Limb overclocking 1", + "android_research.overdrive_that_matters.limb_overclocking_2": "Limb overclocking 2", + "android_research.overdrive_that_matters.limb_overclocking_3": "Limb overclocking 3", + "android_research.overdrive_that_matters.limb_overclocking_4": "Limb overclocking 4", + + "android_research.overdrive_that_matters.limb_overclocking_1.description": "Boosts unit mobility by 8%% and attack speed by 4%%", + "android_research.overdrive_that_matters.limb_overclocking_2.description": "Boosts unit mobility by 16%% and attack speed by 8%%", + "android_research.overdrive_that_matters.limb_overclocking_3.description": "Boosts unit mobility by 24%% and attack speed by 12%%", + "android_research.overdrive_that_matters.limb_overclocking_4.description": "Boosts unit mobility by 32%% and attack speed by 16%%", + "android_research.overdrive_that_matters.air_bags": "Air bags", "android_research.overdrive_that_matters.air_bags.description": "Allows unit to swim in water",