Merge branch 'master' of https://gitlab.com/DBotThePony/overdrive-that-matters
This commit is contained in:
commit
1650e63e20
@ -56,6 +56,7 @@ public class OverdriveThatMatters {
|
||||
FMLJavaModLoadingContext.get().getModEventBus().register(Registry.Menus.class);
|
||||
FMLJavaModLoadingContext.get().getModEventBus().register(Registry.AndroidFeatures.class);
|
||||
FMLJavaModLoadingContext.get().getModEventBus().register(Registry.AndroidResearch.class);
|
||||
FMLJavaModLoadingContext.get().getModEventBus().register(Registry.Stats.class);
|
||||
|
||||
FMLJavaModLoadingContext.get().getModEventBus().addListener(AndroidCapability::registerEffects);
|
||||
|
||||
|
@ -4,6 +4,9 @@ import net.minecraft.client.gui.screens.MenuScreens;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.stats.StatFormatter;
|
||||
import net.minecraft.stats.StatType;
|
||||
import net.minecraft.stats.Stats;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.food.FoodProperties;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
@ -14,12 +17,15 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraftforge.event.RegistryEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import net.minecraftforge.registries.ForgeRegistry;
|
||||
import net.minecraftforge.registries.RegistryBuilder;
|
||||
import ru.dbotthepony.mc.otm.android.AndroidFeature;
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearchBuilder;
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearchType;
|
||||
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.block.*;
|
||||
import ru.dbotthepony.mc.otm.block.entity.*;
|
||||
import ru.dbotthepony.mc.otm.android.AndroidFeatureType;
|
||||
@ -109,6 +115,7 @@ public class Registry {
|
||||
|
||||
// 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");
|
||||
@ -116,6 +123,45 @@ public class Registry {
|
||||
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 final ResourceLocation NANOBOTS = new ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots");
|
||||
public static final ResourceLocation NANOBOTS_REGENERATION = new ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_regeneration");
|
||||
public static final ResourceLocation NANOBOTS_REGENERATION_1 = new ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_regeneration_1");
|
||||
public static final ResourceLocation NANOBOTS_REGENERATION_2 = new ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_regeneration_2");
|
||||
public static final ResourceLocation NANOBOTS_REGENERATION_3 = new ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_regeneration_3");
|
||||
public static final ResourceLocation NANOBOTS_REGENERATION_4 = new ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_regeneration_4");
|
||||
|
||||
public static final ResourceLocation[] NANOBOTS_REGENERATION_LIST = new ResourceLocation[] { NANOBOTS_REGENERATION_1, NANOBOTS_REGENERATION_2, NANOBOTS_REGENERATION_3, NANOBOTS_REGENERATION_4 };
|
||||
|
||||
public static final ResourceLocation NANOBOTS_ARMOR = new ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_armor");
|
||||
|
||||
public static final ResourceLocation NANOBOTS_ARMOR_STRENGTH_1 = new ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_armor_strength_1");
|
||||
public static final ResourceLocation NANOBOTS_ARMOR_STRENGTH_2 = new ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_armor_strength_2");
|
||||
public static final ResourceLocation NANOBOTS_ARMOR_STRENGTH_3 = new ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_armor_strength_3");
|
||||
public static final ResourceLocation[] NANOBOTS_ARMOR_STRENGTH_LIST = new ResourceLocation[] {
|
||||
NANOBOTS_ARMOR_STRENGTH_1,
|
||||
NANOBOTS_ARMOR_STRENGTH_2,
|
||||
NANOBOTS_ARMOR_STRENGTH_3,
|
||||
};
|
||||
|
||||
public static final ResourceLocation NANOBOTS_ARMOR_SPEED_1 = new ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_armor_speed_1");
|
||||
public static final ResourceLocation NANOBOTS_ARMOR_SPEED_2 = new ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_armor_speed_2");
|
||||
public static final ResourceLocation NANOBOTS_ARMOR_SPEED_3 = new ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_armor_speed_3");
|
||||
public static final ResourceLocation[] NANOBOTS_ARMOR_SPEED_LIST = new ResourceLocation[] {
|
||||
NANOBOTS_ARMOR_SPEED_1,
|
||||
NANOBOTS_ARMOR_SPEED_2,
|
||||
NANOBOTS_ARMOR_SPEED_3,
|
||||
};
|
||||
|
||||
public static final ResourceLocation HYDRAULICS_OVERLOAD = new ResourceLocation(OverdriveThatMatters.MOD_ID, "hydraulics_overload");
|
||||
public static final ResourceLocation HYDRAULICS_OVERLOAD_1 = new ResourceLocation(OverdriveThatMatters.MOD_ID, "hydraulics_overload_1");
|
||||
public static final ResourceLocation HYDRAULICS_OVERLOAD_2 = new ResourceLocation(OverdriveThatMatters.MOD_ID, "hydraulics_overload_2");
|
||||
public static final ResourceLocation HYDRAULICS_OVERLOAD_3 = new ResourceLocation(OverdriveThatMatters.MOD_ID, "hydraulics_overload_3");
|
||||
|
||||
// stats
|
||||
public static final ResourceLocation DAMAGE_ABSORBED = new ResourceLocation(OverdriveThatMatters.MOD_ID, "damage_absorbed");
|
||||
public static final ResourceLocation HEALTH_REGENERATED = new ResourceLocation(OverdriveThatMatters.MOD_ID, "health_regenerated");
|
||||
public static final ResourceLocation POWER_CONSUMED = new ResourceLocation(OverdriveThatMatters.MOD_ID, "power_consumed");
|
||||
}
|
||||
|
||||
public static class Blocks {
|
||||
@ -299,16 +345,22 @@ public class Registry {
|
||||
public static class AndroidFeatures {
|
||||
public static final AndroidFeatureType<AndroidFeature> AIR_BAGS = new AndroidFeatureType<>(AndroidFeature::new);
|
||||
public static final AndroidFeatureType<AndroidLimbOverclocking> LIMB_OVERCLOCKING = new AndroidFeatureType<>(AndroidLimbOverclocking::new);
|
||||
public static final AndroidFeatureType<AndroidNanobotsRegeneration> NANOBOTS_REGENERATION = new AndroidFeatureType<>(AndroidNanobotsRegeneration::new);
|
||||
public static final AndroidFeatureType<AndroidNanobotsArmor> NANOBOTS_ARMOR = new AndroidFeatureType<>(AndroidNanobotsArmor::new);
|
||||
|
||||
static {
|
||||
AIR_BAGS.setRegistryName(Names.AIR_BAGS);
|
||||
LIMB_OVERCLOCKING.setRegistryName(Names.LIMB_OVERCLOCKING);
|
||||
NANOBOTS_REGENERATION.setRegistryName(Names.NANOBOTS_REGENERATION);
|
||||
NANOBOTS_ARMOR.setRegistryName(Names.NANOBOTS_ARMOR);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void register(final RegistryEvent.Register<AndroidFeatureType<?>> event) {
|
||||
event.getRegistry().register(AIR_BAGS);
|
||||
event.getRegistry().register(LIMB_OVERCLOCKING);
|
||||
event.getRegistry().register(NANOBOTS_REGENERATION);
|
||||
event.getRegistry().register(NANOBOTS_ARMOR);
|
||||
}
|
||||
}
|
||||
|
||||
@ -326,7 +378,7 @@ public class Registry {
|
||||
public static final SkinElement ICON_ARC;
|
||||
public static final SkinElement ICON_ARROW;
|
||||
public static final SkinElement ICON_ARMOR;
|
||||
public static final SkinElement ICON_REGENERATIVE;
|
||||
public static final SkinElement ICON_NANOBOTS;
|
||||
public static final SkinElement ICON_NIGHT_VISION;
|
||||
public static final SkinElement ICON_OXYGEN_SUPPLY;
|
||||
|
||||
@ -357,7 +409,7 @@ public class Registry {
|
||||
ICON_ARC = new SkinElement(ICONS, x, y, 18, 18, 126, 126); x += 18;
|
||||
ICON_ARROW = new SkinElement(ICONS, x, y, 18, 18, 126, 126); x += 18;
|
||||
ICON_ARMOR = new SkinElement(ICONS, x, y, 18, 18, 126, 126); x += 18;
|
||||
ICON_REGENERATIVE = new SkinElement(ICONS, x, y, 18, 18, 126, 126); x += 18;
|
||||
ICON_NANOBOTS = new SkinElement(ICONS, x, y, 18, 18, 126, 126); x += 18;
|
||||
ICON_NIGHT_VISION = new SkinElement(ICONS, x, y, 18, 18, 126, 126); x += 18;
|
||||
ICON_OXYGEN_SUPPLY = new SkinElement(ICONS, x, y, 18, 18, 126, 126);
|
||||
|
||||
@ -382,40 +434,131 @@ public class Registry {
|
||||
.build();
|
||||
|
||||
public static final AndroidResearchType<ru.dbotthepony.mc.otm.android.AndroidResearch>[] LIMB_OVERCLOCKING = new AndroidResearchType[4];
|
||||
public static final AndroidResearchType<ru.dbotthepony.mc.otm.android.AndroidResearch>[] NANOBOTS_REGENERATION = new AndroidResearchType[4];
|
||||
public static final AndroidResearchType<ru.dbotthepony.mc.otm.android.AndroidResearch>[] NANOBOTS_ARMOR_STRENGTH = new AndroidResearchType[3];
|
||||
public static final AndroidResearchType<ru.dbotthepony.mc.otm.android.AndroidResearch>[] NANOBOTS_ARMOR_SPEED = new AndroidResearchType[3];
|
||||
|
||||
public static final AndroidResearchType<ru.dbotthepony.mc.otm.android.AndroidResearch> NANOBOTS =
|
||||
new AndroidResearchBuilder()
|
||||
.setExperienceCost(15)
|
||||
.withDescription()
|
||||
.withIcon(ICON_NANOBOTS)
|
||||
.build();
|
||||
|
||||
public static final AndroidResearchType<ru.dbotthepony.mc.otm.android.AndroidResearch> NANOBOTS_ARMOR =
|
||||
new AndroidResearchBuilder()
|
||||
.setExperienceCost(25)
|
||||
.withDescription()
|
||||
.addPrerequisite(Names.NANOBOTS)
|
||||
.addFeatureResult(Names.NANOBOTS_ARMOR)
|
||||
.withIcon(ICON_ARMOR)
|
||||
.build();
|
||||
|
||||
static {
|
||||
AIR_BAGS.setRegistryName(Names.AIR_BAGS);
|
||||
NANOBOTS.setRegistryName(Names.NANOBOTS);
|
||||
NANOBOTS_ARMOR.setRegistryName(Names.NANOBOTS_ARMOR);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
var builder = new AndroidResearchBuilder()
|
||||
.withDescription()
|
||||
.setExperienceCost(24 + i * 8)
|
||||
var limbs = new AndroidResearchBuilder()
|
||||
.setExperienceCost(18 + i * 8)
|
||||
.withIconText(new TextComponent(String.valueOf(i + 1)))
|
||||
.withIcon(ICON_LIMB_OVERCLOCKING)
|
||||
.withName(new TranslatableComponent("android_research.overdrive_that_matters.limb_overclocking", i + 1))
|
||||
.withDescription(new TranslatableComponent("android_research.overdrive_that_matters.limb_overclocking.description", (i + 1) * 8, (i + 1) * 6))
|
||||
.addFeatureResult(Names.LIMB_OVERCLOCKING, i);
|
||||
|
||||
var regeneration = new AndroidResearchBuilder()
|
||||
.setExperienceCost(20 + i * 6)
|
||||
.withIconText(new TextComponent(String.valueOf(i + 1)))
|
||||
.withIcon(ICON_NANOBOTS)
|
||||
.withName(new TranslatableComponent("android_research.overdrive_that_matters.nanobots_regeneration", i + 1))
|
||||
.withDescription(
|
||||
i > 0 ? new TranslatableComponent("android_research.overdrive_that_matters.nanobots_regeneration.description_improve") : new TranslatableComponent("android_research.overdrive_that_matters.nanobots_regeneration.description"))
|
||||
.addFeatureResult(Names.NANOBOTS_REGENERATION, i);
|
||||
|
||||
if (i > 0) {
|
||||
builder.addPrerequisite(Names.LIMB_OVERCLOCKING_LIST[i - 1]);
|
||||
limbs.addPrerequisite(Names.LIMB_OVERCLOCKING_LIST[i - 1]);
|
||||
regeneration.addPrerequisite(Names.NANOBOTS_REGENERATION_LIST[i - 1]);
|
||||
} else {
|
||||
regeneration.addPrerequisite(Names.NANOBOTS);
|
||||
}
|
||||
|
||||
LIMB_OVERCLOCKING[i] = builder.build();
|
||||
LIMB_OVERCLOCKING[i] = limbs.build();
|
||||
NANOBOTS_REGENERATION[i] = regeneration.build();
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
final int level = i + 1;
|
||||
|
||||
NANOBOTS_ARMOR_STRENGTH[i] = new AndroidResearchBuilder()
|
||||
.setExperienceCost(20 + i * 8)
|
||||
.withIconText(new TextComponent(String.valueOf(i + 1)))
|
||||
.withIcon(ICON_ARMOR)
|
||||
.addPrerequisite(i > 0 ? Names.NANOBOTS_ARMOR_STRENGTH_LIST[i - 1] : Names.NANOBOTS_ARMOR)
|
||||
.withName(new TranslatableComponent("android_research.overdrive_that_matters.nanobots_armor_strength", i + 1))
|
||||
.withDescription(new TranslatableComponent("android_research.overdrive_that_matters.nanobots_armor_strength.description", (i + 1) * 8, (i + 1) * 6))
|
||||
.addFeatureResult(Names.NANOBOTS_ARMOR, 0, (feature) -> {
|
||||
if (((AndroidNanobotsArmor) feature).getStrength() < level)
|
||||
((AndroidNanobotsArmor) feature).setStrength(level);
|
||||
})
|
||||
.build();
|
||||
|
||||
NANOBOTS_ARMOR_SPEED[i] = new AndroidResearchBuilder()
|
||||
.setExperienceCost(20 + i * 8)
|
||||
.withIconText(new TextComponent(String.valueOf(i + 1)))
|
||||
.withIcon(ICON_ARMOR)
|
||||
.addPrerequisite(i > 0 ? Names.NANOBOTS_ARMOR_SPEED_LIST[i - 1] : Names.NANOBOTS_ARMOR)
|
||||
.withName(new TranslatableComponent("android_research.overdrive_that_matters.nanobots_armor_speed", i + 1))
|
||||
.withDescription(new TranslatableComponent("android_research.overdrive_that_matters.nanobots_armor_speed.description", (i + 1) * 8, (i + 1) * 6))
|
||||
.addFeatureResult(Names.NANOBOTS_ARMOR, 0, (feature) -> {
|
||||
if (((AndroidNanobotsArmor) feature).getSpeed() < level)
|
||||
((AndroidNanobotsArmor) feature).setSpeed(level);
|
||||
})
|
||||
.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);
|
||||
|
||||
NANOBOTS_ARMOR_SPEED[0].setRegistryName(Names.NANOBOTS_ARMOR_SPEED_1);
|
||||
NANOBOTS_ARMOR_SPEED[1].setRegistryName(Names.NANOBOTS_ARMOR_SPEED_2);
|
||||
NANOBOTS_ARMOR_SPEED[2].setRegistryName(Names.NANOBOTS_ARMOR_SPEED_3);
|
||||
|
||||
NANOBOTS_ARMOR_STRENGTH[0].setRegistryName(Names.NANOBOTS_ARMOR_STRENGTH_1);
|
||||
NANOBOTS_ARMOR_STRENGTH[1].setRegistryName(Names.NANOBOTS_ARMOR_STRENGTH_2);
|
||||
NANOBOTS_ARMOR_STRENGTH[2].setRegistryName(Names.NANOBOTS_ARMOR_STRENGTH_3);
|
||||
|
||||
NANOBOTS_REGENERATION[0].setRegistryName(Names.NANOBOTS_REGENERATION_1);
|
||||
NANOBOTS_REGENERATION[1].setRegistryName(Names.NANOBOTS_REGENERATION_2);
|
||||
NANOBOTS_REGENERATION[2].setRegistryName(Names.NANOBOTS_REGENERATION_3);
|
||||
NANOBOTS_REGENERATION[3].setRegistryName(Names.NANOBOTS_REGENERATION_4);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void register(final RegistryEvent.Register<AndroidResearchType<?>> event) {
|
||||
event.getRegistry().register(AIR_BAGS);
|
||||
event.getRegistry().register(NANOBOTS);
|
||||
event.getRegistry().register(NANOBOTS_ARMOR);
|
||||
|
||||
event.getRegistry().register(LIMB_OVERCLOCKING[0]);
|
||||
event.getRegistry().register(LIMB_OVERCLOCKING[1]);
|
||||
event.getRegistry().register(LIMB_OVERCLOCKING[2]);
|
||||
event.getRegistry().register(LIMB_OVERCLOCKING[3]);
|
||||
|
||||
event.getRegistry().register(NANOBOTS_ARMOR_SPEED[0]);
|
||||
event.getRegistry().register(NANOBOTS_ARMOR_SPEED[1]);
|
||||
event.getRegistry().register(NANOBOTS_ARMOR_SPEED[2]);
|
||||
event.getRegistry().register(NANOBOTS_ARMOR_STRENGTH[0]);
|
||||
event.getRegistry().register(NANOBOTS_ARMOR_STRENGTH[1]);
|
||||
event.getRegistry().register(NANOBOTS_ARMOR_STRENGTH[2]);
|
||||
|
||||
event.getRegistry().register(NANOBOTS_REGENERATION[0]);
|
||||
event.getRegistry().register(NANOBOTS_REGENERATION[1]);
|
||||
event.getRegistry().register(NANOBOTS_REGENERATION[2]);
|
||||
event.getRegistry().register(NANOBOTS_REGENERATION[3]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -472,4 +615,17 @@ public class Registry {
|
||||
// OverdriveThatMatters.LOGGER.info("Registered screens");
|
||||
}
|
||||
}
|
||||
|
||||
public static class Stats {
|
||||
@SubscribeEvent
|
||||
public static void registerVanilla(final FMLCommonSetupEvent event) {
|
||||
net.minecraft.core.Registry.register(net.minecraft.core.Registry.CUSTOM_STAT, Names.DAMAGE_ABSORBED, Names.DAMAGE_ABSORBED);
|
||||
net.minecraft.core.Registry.register(net.minecraft.core.Registry.CUSTOM_STAT, Names.HEALTH_REGENERATED, Names.HEALTH_REGENERATED);
|
||||
net.minecraft.core.Registry.register(net.minecraft.core.Registry.CUSTOM_STAT, Names.POWER_CONSUMED, Names.POWER_CONSUMED);
|
||||
|
||||
net.minecraft.stats.Stats.CUSTOM.get(Names.DAMAGE_ABSORBED, StatFormatter.DIVIDE_BY_TEN);
|
||||
net.minecraft.stats.Stats.CUSTOM.get(Names.HEALTH_REGENERATED, StatFormatter.DIVIDE_BY_TEN);
|
||||
net.minecraft.stats.Stats.CUSTOM.get(Names.POWER_CONSUMED, StatFormatter.DIVIDE_BY_TEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package ru.dbotthepony.mc.otm.android;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraftforge.common.util.INBTSerializable;
|
||||
import net.minecraftforge.event.entity.living.LivingHurtEvent;
|
||||
import ru.dbotthepony.mc.otm.capability.IAndroidCapability;
|
||||
|
||||
public class AndroidFeature implements INBTSerializable<CompoundTag> {
|
||||
@ -47,11 +48,11 @@ public class AndroidFeature implements INBTSerializable<CompoundTag> {
|
||||
}
|
||||
|
||||
public void tickClient() {
|
||||
|
||||
// override
|
||||
}
|
||||
|
||||
public void tickServer() {
|
||||
|
||||
// override
|
||||
}
|
||||
|
||||
public void applyModifiers() {}
|
||||
@ -61,6 +62,10 @@ public class AndroidFeature implements INBTSerializable<CompoundTag> {
|
||||
tag.putInt("level", level);
|
||||
}
|
||||
|
||||
public void onHurt(LivingHurtEvent event) {
|
||||
// override
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag serializeNBT() {
|
||||
var tag = new CompoundTag();
|
||||
|
@ -15,19 +15,29 @@ import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class AndroidResearchBuilder {
|
||||
record DeferredItemStack(ResourceLocation id, int amount) {
|
||||
}
|
||||
|
||||
record DeferredFeature(ResourceLocation id, int level) {
|
||||
DeferredFeature(ResourceLocation id, int level) {
|
||||
record DeferredFeature(ResourceLocation id, int level, Consumer<AndroidFeature> callback) {
|
||||
DeferredFeature(ResourceLocation id, int level, Consumer<AndroidFeature> callback) {
|
||||
this.id = id;
|
||||
this.level = level;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
DeferredFeature(ResourceLocation id, int level) {
|
||||
this(id, level, (f) -> {});
|
||||
}
|
||||
|
||||
DeferredFeature(ResourceLocation id, Consumer<AndroidFeature> callback) {
|
||||
this(id, 0, (f) -> {});
|
||||
}
|
||||
|
||||
DeferredFeature(ResourceLocation id) {
|
||||
this(id, 0);
|
||||
this(id, 0, (f) -> {});
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,6 +71,16 @@ public class AndroidResearchBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public AndroidResearchBuilder addFeatureResult(ResourceLocation location, Consumer<AndroidFeature> callback) {
|
||||
feature_results.add(new DeferredFeature(location, callback));
|
||||
return this;
|
||||
}
|
||||
|
||||
public AndroidResearchBuilder addFeatureResult(AndroidFeatureType<?> location, Consumer<AndroidFeature> callback) {
|
||||
feature_results.add(new DeferredFeature(Objects.requireNonNull(location.getRegistryName()), callback));
|
||||
return this;
|
||||
}
|
||||
|
||||
public AndroidResearchBuilder addFeatureResult(ResourceLocation location, int level) {
|
||||
feature_results.add(new DeferredFeature(location, level));
|
||||
return this;
|
||||
@ -71,6 +91,16 @@ public class AndroidResearchBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public AndroidResearchBuilder addFeatureResult(ResourceLocation location, int level, Consumer<AndroidFeature> callback) {
|
||||
feature_results.add(new DeferredFeature(location, level, callback));
|
||||
return this;
|
||||
}
|
||||
|
||||
public AndroidResearchBuilder addFeatureResult(AndroidFeatureType<?> location, int level, Consumer<AndroidFeature> callback) {
|
||||
feature_results.add(new DeferredFeature(Objects.requireNonNull(location.getRegistryName()), level, callback));
|
||||
return this;
|
||||
}
|
||||
|
||||
public AndroidResearchBuilder addItem(ResourceLocation location) {
|
||||
items.add(new DeferredItemStack(location, 1));
|
||||
return this;
|
||||
@ -115,7 +145,7 @@ public class AndroidResearchBuilder {
|
||||
private final ArrayList<ItemStack> resolved_stacks = new ArrayList<>();
|
||||
private List<AndroidResearchType<?>> resolved_preq;
|
||||
|
||||
record ResolvedFeature(AndroidFeatureType<?> type, int level) {
|
||||
record ResolvedFeature(AndroidFeatureType<?> type, int level, Consumer<AndroidFeature> callback) {
|
||||
}
|
||||
|
||||
private final ArrayList<ResolvedFeature> resolved_features = new ArrayList<>();
|
||||
@ -152,7 +182,7 @@ public class AndroidResearchBuilder {
|
||||
var get = Registry.ANDROID_FEATURES().getValue(entry.id);
|
||||
|
||||
if (get != null) {
|
||||
resolved_features.add(new ResolvedFeature(get, entry.level));
|
||||
resolved_features.add(new ResolvedFeature(get, entry.level, entry.callback));
|
||||
} else {
|
||||
throw new IllegalArgumentException("Can not find android feature " + entry);
|
||||
}
|
||||
@ -246,8 +276,12 @@ public class AndroidResearchBuilder {
|
||||
if (get == null) {
|
||||
get = capability.addFeature(feature.type);
|
||||
get.setLevel(feature.level);
|
||||
} else if (get.getLevel() < feature.level) {
|
||||
get.setLevel(feature.level);
|
||||
feature.callback.accept(get);
|
||||
} else {
|
||||
if (get.getLevel() < feature.level)
|
||||
get.setLevel(feature.level);
|
||||
|
||||
feature.callback.accept(get);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ public class AndroidResearchType<T extends AndroidResearch> extends ForgeRegistr
|
||||
}
|
||||
|
||||
private List<AndroidResearchType<?>> blocking;
|
||||
private List<AndroidResearchType<?>> unlocks;
|
||||
|
||||
/**
|
||||
* @return List of research blocked once this research is unlocked
|
||||
@ -38,6 +39,25 @@ public class AndroidResearchType<T extends AndroidResearch> extends ForgeRegistr
|
||||
return blocking;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return List of research unlocked once this research is unlocked
|
||||
*/
|
||||
public List<AndroidResearchType<?>> getUnlocks() {
|
||||
if (unlocks == null) {
|
||||
var list = new ArrayList<AndroidResearchType<?>>();
|
||||
|
||||
for (var type : Registry.ANDROID_RESEARCH()) {
|
||||
if (type.getPrerequisites().contains(this)) {
|
||||
list.add(type);
|
||||
}
|
||||
}
|
||||
|
||||
unlocks = List.copyOf(list);
|
||||
}
|
||||
|
||||
return unlocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return List of research once unlocked blocks this research
|
||||
*/
|
||||
|
@ -0,0 +1,113 @@
|
||||
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.OverdriveThatMatters;
|
||||
import ru.dbotthepony.mc.otm.Registry;
|
||||
import ru.dbotthepony.mc.otm.android.AndroidFeature;
|
||||
import ru.dbotthepony.mc.otm.android.AndroidFeatureType;
|
||||
import ru.dbotthepony.mc.otm.capability.IAndroidCapability;
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class AndroidNanobotsArmor extends AndroidFeature {
|
||||
public AndroidNanobotsArmor(AndroidFeatureType<?> type, IAndroidCapability capability) {
|
||||
super(type, 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 BigDecimal ENERGY_PER_BUILT = new BigDecimal(200);
|
||||
protected static final BigDecimal ENERGY_PER_HITPOINT = new BigDecimal(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.multiply(new BigDecimal(Float.toString(absorbed)));
|
||||
var extracted = capability.extractEnergyInner(required, false);
|
||||
var real_absorbed = absorbed * extracted.divide(required, MatteryCapability.ROUND_RULES).floatValue();
|
||||
event.setAmount(event.getAmount() - real_absorbed);
|
||||
|
||||
if (capability.getEntity() instanceof ServerPlayer ply) {
|
||||
ply.awardStat(Registry.Names.DAMAGE_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");
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
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.Registry;
|
||||
import ru.dbotthepony.mc.otm.android.AndroidFeature;
|
||||
import ru.dbotthepony.mc.otm.android.AndroidFeatureType;
|
||||
import ru.dbotthepony.mc.otm.capability.IAndroidCapability;
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class AndroidNanobotsRegeneration extends AndroidFeature {
|
||||
public AndroidNanobotsRegeneration(AndroidFeatureType<?> type, IAndroidCapability capability) {
|
||||
super(type, capability);
|
||||
}
|
||||
|
||||
protected int ticks_passed = 0;
|
||||
protected int heal_ticks = 0;
|
||||
|
||||
protected static final BigDecimal ENERGY_PER_HITPOINT = new BigDecimal(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.multiply(new BigDecimal(Float.toString(missing))), false);
|
||||
|
||||
if (extract.compareTo(BigDecimal.ZERO) > 0) {
|
||||
heal_ticks = Math.min(heal_ticks + 1, level);
|
||||
var heal = missing * extract.divide(ENERGY_PER_HITPOINT, MatteryCapability.ROUND_RULES).floatValue();
|
||||
ent.heal(heal);
|
||||
|
||||
if (capability.getEntity() instanceof ServerPlayer ply) {
|
||||
ply.awardStat(Registry.Names.HEALTH_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");
|
||||
}
|
||||
}
|
@ -17,6 +17,8 @@ import net.minecraftforge.common.util.LazyOptional;
|
||||
import net.minecraftforge.energy.CapabilityEnergy;
|
||||
import net.minecraftforge.energy.IEnergyStorage;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingHurtEvent;
|
||||
import net.minecraftforge.eventbus.api.EventPriority;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import net.minecraftforge.fmllegacy.network.PacketDistributor;
|
||||
@ -69,11 +71,11 @@ public class AndroidCapability implements ICapabilityProvider, IAndroidCapabilit
|
||||
}
|
||||
|
||||
@Override
|
||||
public AndroidFeature addFeature(@Nonnull AndroidFeatureType<?> feature) {
|
||||
public <T extends AndroidFeature> T addFeature(@Nonnull AndroidFeatureType<T> feature) {
|
||||
var get = features.get(feature);
|
||||
|
||||
if (get != null)
|
||||
return get;
|
||||
return (T) get;
|
||||
|
||||
var factory = feature.factory(this);
|
||||
features.put(feature, factory);
|
||||
@ -125,8 +127,8 @@ public class AndroidCapability implements ICapabilityProvider, IAndroidCapabilit
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AndroidFeature getFeature(AndroidFeatureType<?> feature) {
|
||||
return features.get(feature);
|
||||
public <T extends AndroidFeature> T getFeature(AndroidFeatureType<T> feature) {
|
||||
return (T) features.get(feature);
|
||||
}
|
||||
|
||||
public static final Set<MobEffect> UNAFFECTED_EFFECTS = new HashSet<>();
|
||||
@ -181,6 +183,21 @@ public class AndroidCapability implements ICapabilityProvider, IAndroidCapabilit
|
||||
ent.getCapability(MatteryCapability.ANDROID).ifPresent(ent.level.isClientSide ? IAndroidCapability::tickClient : IAndroidCapability::tick);
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||
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
|
||||
@Nonnull
|
||||
public CompoundTag serializeNBT() {
|
||||
@ -372,6 +389,10 @@ public class AndroidCapability implements ICapabilityProvider, IAndroidCapabilit
|
||||
howMuch = howMuch.subtract(changed, MatteryCapability.ROUND_RULES);
|
||||
|
||||
if (howMuch.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
if (!simulate && ent instanceof ServerPlayer ply) {
|
||||
ply.awardStat(Registry.Names.POWER_CONSUMED, drained.intValue() * 10);
|
||||
}
|
||||
|
||||
return drained;
|
||||
}
|
||||
}
|
||||
@ -388,6 +409,10 @@ public class AndroidCapability implements ICapabilityProvider, IAndroidCapabilit
|
||||
howMuch = howMuch.subtract(changed, MatteryCapability.ROUND_RULES);
|
||||
|
||||
if (howMuch.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
if (!simulate && ent instanceof ServerPlayer ply) {
|
||||
ply.awardStat(Registry.Names.POWER_CONSUMED, drained.intValue() * 10);
|
||||
}
|
||||
|
||||
return drained;
|
||||
}
|
||||
}
|
||||
@ -400,6 +425,10 @@ public class AndroidCapability implements ICapabilityProvider, IAndroidCapabilit
|
||||
|
||||
if (!simulate) {
|
||||
energy_stored = new_energy;
|
||||
|
||||
if (ent instanceof ServerPlayer ply) {
|
||||
ply.awardStat(Registry.Names.POWER_CONSUMED, drained.intValue() * 10);
|
||||
}
|
||||
}
|
||||
|
||||
return drained;
|
||||
|
@ -115,10 +115,10 @@ public class AndroidCapabilityPlayer extends AndroidCapability {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public AndroidResearch getResearch(AndroidResearchType<?> type) {
|
||||
public <T extends AndroidResearch> T getResearch(AndroidResearchType<T> type) {
|
||||
for (var instance : research_list) {
|
||||
if (instance.type == type) {
|
||||
return instance;
|
||||
return (T) instance;
|
||||
}
|
||||
}
|
||||
|
||||
@ -200,14 +200,14 @@ public class AndroidCapabilityPlayer extends AndroidCapability {
|
||||
|
||||
var stats = ply.getFoodData();
|
||||
|
||||
while (stats.getFoodLevel() < 17 && this.extractEnergyInner(ENERGY_FOR_HUNGER_POINT, true).compareTo(ENERGY_FOR_HUNGER_POINT) == 0) {
|
||||
while (stats.getFoodLevel() < 18 && this.extractEnergyInner(ENERGY_FOR_HUNGER_POINT, true).compareTo(ENERGY_FOR_HUNGER_POINT) == 0) {
|
||||
this.extractEnergyInner(ENERGY_FOR_HUNGER_POINT, false);
|
||||
stats.setFoodLevel(stats.getFoodLevel() + 1);
|
||||
}
|
||||
|
||||
// "block" quick regeneration
|
||||
// also cause power to generate while in peaceful
|
||||
while (stats.getFoodLevel() > 17 && this.receiveEnergyInner(ENERGY_FOR_HUNGER_POINT, true).compareTo(ENERGY_FOR_HUNGER_POINT) == 0) {
|
||||
while (stats.getFoodLevel() > 18 && this.receiveEnergyInner(ENERGY_FOR_HUNGER_POINT, true).compareTo(ENERGY_FOR_HUNGER_POINT) == 0) {
|
||||
this.receiveEnergyInner(ENERGY_FOR_HUNGER_POINT, false);
|
||||
stats.setFoodLevel(stats.getFoodLevel() - 1);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.common.util.INBTSerializable;
|
||||
import net.minecraftforge.event.entity.living.LivingHurtEvent;
|
||||
import ru.dbotthepony.mc.otm.android.AndroidFeature;
|
||||
import ru.dbotthepony.mc.otm.android.AndroidFeatureType;
|
||||
import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage;
|
||||
@ -18,14 +19,14 @@ public interface IAndroidCapability extends IMatteryEnergyStorage, INBTSerializa
|
||||
void tickClient();
|
||||
LivingEntity getEntity();
|
||||
|
||||
AndroidFeature addFeature(@Nonnull AndroidFeatureType<?> feature);
|
||||
<T extends AndroidFeature> T addFeature(@Nonnull AndroidFeatureType<T> feature);
|
||||
boolean removeFeature(@Nonnull AndroidFeatureType<?> feature);
|
||||
boolean hasFeature(@Nullable AndroidFeatureType<?> feature);
|
||||
boolean hasFeatureLevel(@Nullable AndroidFeatureType<?> feature, int level);
|
||||
AndroidFeature getFeature(AndroidFeatureType<?> feature);
|
||||
<T extends AndroidFeature> T getFeature(AndroidFeatureType<T> feature);
|
||||
|
||||
@Nonnull
|
||||
default Optional<AndroidFeature> getFeatureO(AndroidFeatureType<?> feature) {
|
||||
default <T extends AndroidFeature> Optional<T> getFeatureO(AndroidFeatureType<T> feature) {
|
||||
var get = getFeature(feature);
|
||||
return get != null ? Optional.of(get) : Optional.empty();
|
||||
}
|
||||
@ -33,6 +34,7 @@ public interface IAndroidCapability extends IMatteryEnergyStorage, INBTSerializa
|
||||
default boolean isAndroid() {
|
||||
return true;
|
||||
}
|
||||
default void onHurt(LivingHurtEvent event) {}
|
||||
|
||||
@Nonnull
|
||||
ItemStack getBatteryItemStack();
|
||||
|
@ -8,6 +8,7 @@ import ru.dbotthepony.mc.otm.android.AndroidResearch;
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearchType;
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
|
||||
import ru.dbotthepony.mc.otm.capability.AndroidCapabilityPlayer;
|
||||
import ru.dbotthepony.mc.otm.menu.AndroidStationMenu;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
@ -34,7 +35,8 @@ public record AndroidResearchRequestPacket(AndroidResearchType<?> research) {
|
||||
if (!cap.isAndroid())
|
||||
return;
|
||||
|
||||
cap.getResearch(research).research();
|
||||
if (ply.containerMenu instanceof AndroidStationMenu)
|
||||
cap.getResearch(research).research();
|
||||
}
|
||||
|
||||
public static AndroidResearchRequestPacket read(FriendlyByteBuf buffer) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package ru.dbotthepony.mc.otm.screen;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@ -8,6 +9,7 @@ import net.minecraft.network.chat.TranslatableComponent;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import ru.dbotthepony.mc.otm.Registry;
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearch;
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearchType;
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
|
||||
import ru.dbotthepony.mc.otm.capability.AndroidCapabilityPlayer;
|
||||
import ru.dbotthepony.mc.otm.menu.AndroidStationMenu;
|
||||
@ -16,8 +18,7 @@ import ru.dbotthepony.mc.otm.menu.widget.GaugeWidget;
|
||||
import ru.dbotthepony.mc.otm.screen.panels.*;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
public class AndroidStationScreen extends MatteryScreen<AndroidStationMenu> implements MatteryScreen.IMatteryScreenGaugeGetter, MatteryScreen.IMatteryScreenBatteryGetter {
|
||||
class AndroidResearchButton extends EditablePanel {
|
||||
@ -31,7 +32,7 @@ public class AndroidStationScreen extends MatteryScreen<AndroidStationMenu> impl
|
||||
public AndroidResearchButton(@Nullable EditablePanel parent, AndroidResearch node) {
|
||||
super(AndroidStationScreen.this, parent, 0, 0, BUTTON_SIZE, BUTTON_SIZE);
|
||||
this.node = node;
|
||||
setDockMargin(2, 2, 0, 0);
|
||||
setDockMargin(2, 2, 2, 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -90,8 +91,7 @@ public class AndroidStationScreen extends MatteryScreen<AndroidStationMenu> impl
|
||||
}
|
||||
|
||||
public static final int FRAME_WIDTH = 210;
|
||||
public static final int GRID_WIDTH = 6;
|
||||
public static final int GRID_HEIGHT = 5;
|
||||
public static final int GRID_WIDTH = 5;
|
||||
public static final int FRAME_HEIGHT = 120;
|
||||
|
||||
public AndroidStationScreen(AndroidStationMenu p_97741_, Inventory p_97742_, Component p_97743_) {
|
||||
@ -108,6 +108,144 @@ public class AndroidStationScreen extends MatteryScreen<AndroidStationMenu> impl
|
||||
return List.of(menu.battery_slot);
|
||||
}
|
||||
|
||||
private final EditablePanel[] rows = new EditablePanel[100];
|
||||
private final Set<AndroidResearchType<?>> seen = new HashSet<>();
|
||||
private float next_x = 0;
|
||||
private float[] rows_width = new float[100];
|
||||
private final AndroidResearchButton[][] created_buttons = new AndroidResearchButton[100][1000];
|
||||
private final int[] created_buttons_idx = new int[100];
|
||||
|
||||
private void dive(AndroidCapabilityPlayer cap, AndroidResearchType<?> research, int level) {
|
||||
if (seen.contains(research))
|
||||
return;
|
||||
|
||||
seen.add(research);
|
||||
|
||||
if (rows[level] == null) {
|
||||
rows[level] = new EditablePanel(this, canvas, 0, level * 22, 10000, 22) {
|
||||
@Override
|
||||
protected boolean mouseClickedInner(double mouse_x, double mouse_y, int flag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean mouseReleasedInner(double mouse_x, double mouse_y, int flag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean mouseDraggedInner(double mouse_x, double mouse_y, int flag, double drag_x, double drag_y) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var row = rows[level];
|
||||
var button = new AndroidResearchButton(row, cap.getResearch(research));
|
||||
button.setPos(next_x + rows_width[level], 2);
|
||||
created_buttons[level][created_buttons_idx[level]] = button;
|
||||
created_buttons_idx[level]++;
|
||||
|
||||
rows_width[level] += 22;
|
||||
|
||||
for (var _research : research.getUnlocks()) {
|
||||
dive(cap, _research, level + 1);
|
||||
}
|
||||
|
||||
if (level > 0) {
|
||||
for (var _research : research.getPrerequisites()) {
|
||||
dive(cap, _research, level - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DraggableCanvasPanel canvas;
|
||||
private FramePanel research;
|
||||
|
||||
private void openResearchTree() {
|
||||
var window = this.minecraft.getWindow();
|
||||
research = new FramePanel(this, null, 0, 0, window.getGuiScaledWidth() * 0.8f, window.getGuiScaledHeight() * 0.8f, new TranslatableComponent("otm.gui.android_research"));
|
||||
|
||||
canvas = new DraggableCanvasPanel(this, research, 0, 0, GRID_WIDTH * 22, 0) {
|
||||
@Override
|
||||
protected void innerRender(PoseStack stack, float mouse_x, float mouse_y, float flag) {
|
||||
RenderHelper.setDrawColor(RGBAColor.BLACK);
|
||||
RenderHelper.drawRect(stack, 0, 0, width, height);
|
||||
}
|
||||
};
|
||||
|
||||
minecraft.player.getCapability(MatteryCapability.ANDROID).ifPresent(_cap -> {
|
||||
if (_cap instanceof AndroidCapabilityPlayer cap) {
|
||||
Arrays.fill(rows, null);
|
||||
next_x = 0;
|
||||
|
||||
for (var research : Registry.ANDROID_RESEARCH().getValues()) {
|
||||
if (research.getPrerequisites().size() == 0) {
|
||||
dive(cap, research, 0);
|
||||
float max = 0;
|
||||
|
||||
for (float v : rows_width)
|
||||
max = Math.max(max, v);
|
||||
|
||||
for (var button_list : created_buttons) {
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < button_list.length; i++) {
|
||||
if (button_list[i] == null) {
|
||||
count = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
float this_x = next_x + max / 2f - (count * 22f) / 2f;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
button_list[i].setPos(this_x, 2);
|
||||
this_x += 22;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var v : created_buttons)
|
||||
Arrays.fill(v, null);
|
||||
|
||||
next_x += max;
|
||||
Arrays.fill(rows_width, 0);
|
||||
Arrays.fill(created_buttons_idx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
seen.clear();
|
||||
}
|
||||
});
|
||||
|
||||
canvas.setDock(Dock.FILL);
|
||||
|
||||
var bottom = new EditablePanel(this, research, 0, 0, 0, 20);
|
||||
var close = new ButtonPanel(this, bottom, 0, 0, 90, 20, new TranslatableComponent("otm.container.matter_panel.close"));
|
||||
bottom.setDock(Dock.BOTTOM);
|
||||
close.setDock(Dock.RIGHT);
|
||||
close.bindOnPress(research::remove);
|
||||
bottom.setDockMargin(0, 0, 4, 4);
|
||||
|
||||
canvas.setDockMargin(4, 4, 4, 4);
|
||||
|
||||
research.toScreenCenter();
|
||||
addPanel(research);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(Minecraft minecraft, int p_96576_, int p_96577_) {
|
||||
super.resize(minecraft, p_96576_, p_96577_);
|
||||
|
||||
if (research != null) {
|
||||
var window = minecraft.getWindow();
|
||||
research.setSize(window.getGuiScaledWidth() * 0.8f, window.getGuiScaledHeight() * 0.8f);
|
||||
research.toScreenCenter();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected FramePanel makeMainFrame() {
|
||||
@ -115,21 +253,10 @@ public class AndroidStationScreen extends MatteryScreen<AndroidStationMenu> impl
|
||||
|
||||
autoAttachToFrame(frame);
|
||||
|
||||
var grid = new GridPanel(this, frame, 0, 0, GRID_WIDTH * 18, 0, GRID_WIDTH, GRID_HEIGHT);
|
||||
|
||||
minecraft.player.getCapability(MatteryCapability.ANDROID).ifPresent(_cap -> {
|
||||
if (_cap instanceof AndroidCapabilityPlayer cap) {
|
||||
for (var feature : Registry.ANDROID_RESEARCH().getValues()) {
|
||||
var node = cap.getResearch(feature);
|
||||
|
||||
if (node != null) {
|
||||
new AndroidResearchButton(grid, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grid.setDock(Dock.RIGHT);
|
||||
var button = new ButtonPanel(this, frame, 0, 0, 90, 20, new TranslatableComponent("otm.gui.android_research"));
|
||||
button.setDock(Dock.BOTTOM);
|
||||
button.bindOnPress(this::openResearchTree);
|
||||
button.setDockMargin(10, 0, 10, 0);
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
@ -10,6 +10,9 @@ public record RGBAColor(float r, float g, float b, float a) {
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public static final RGBAColor BLACK = new RGBAColor(0, 0, 0, 1f);
|
||||
public static final RGBAColor WHITE = new RGBAColor(1f, 1f, 1f, 1f);
|
||||
|
||||
public RGBAColor(int r, int g, int b) {
|
||||
this(r / 255F, g / 255F, b / 255F, 1F);
|
||||
}
|
||||
|
@ -3,10 +3,13 @@ package ru.dbotthepony.mc.otm.screen;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.*;
|
||||
import com.mojang.math.Matrix4f;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters;
|
||||
|
||||
import java.util.Stack;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.GL_ALWAYS;
|
||||
import static org.lwjgl.opengl.GL11.GL_LESS;
|
||||
|
||||
@ -553,4 +556,49 @@ public class RenderHelper {
|
||||
) {
|
||||
drawRect(stack.last().pose(), x, y, width, height);
|
||||
}
|
||||
|
||||
record ScissorRect(int x, int y, int width, int height) {}
|
||||
|
||||
private static final Stack<ScissorRect> SCISSOR = new Stack<>();
|
||||
|
||||
public static void ensureScissorStackEmpty() {
|
||||
if (!SCISSOR.empty()) {
|
||||
throw new IllegalStateException("Unbalanced scissor rects: Popping less than pushing");
|
||||
}
|
||||
}
|
||||
|
||||
public static void pushScissorRect(int x, int y, int width, int height) {
|
||||
if (!SCISSOR.empty()) {
|
||||
final var peek = SCISSOR.peek();
|
||||
x = Math.max(x, peek.x);
|
||||
y = Math.max(y, peek.y);
|
||||
width = Math.min(width, peek.width);
|
||||
height = Math.min(height, peek.height);
|
||||
}
|
||||
|
||||
SCISSOR.push(new ScissorRect(x, y, width, height));
|
||||
|
||||
final var window = Minecraft.getInstance().getWindow();
|
||||
y = window.getHeight() - y - height;
|
||||
|
||||
RenderSystem.enableScissor(x, y, width, height);
|
||||
}
|
||||
|
||||
public static void popScissorRect() {
|
||||
if (SCISSOR.empty()) {
|
||||
throw new IllegalStateException("Unbalanced scissor rects: Popping more than pushing");
|
||||
}
|
||||
|
||||
SCISSOR.pop();
|
||||
|
||||
if (SCISSOR.empty()) {
|
||||
RenderSystem.disableScissor();
|
||||
return;
|
||||
}
|
||||
|
||||
final var value = SCISSOR.peek();
|
||||
final var window = Minecraft.getInstance().getWindow();
|
||||
final var y = window.getHeight() - value.y - value.height;
|
||||
RenderSystem.enableScissor(value.x, y, value.width, value.height);
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +69,8 @@ public abstract class AbstractSlotPanel extends EditablePanel {
|
||||
RenderSystem.depthFunc(GL_ALWAYS);
|
||||
screen.getItemRenderer().renderGuiItemDecorations(screen.getFont(), itemstack, 0, 0, count_override);
|
||||
|
||||
screen.getItemRenderer().blitOffset = 0;
|
||||
|
||||
// too big accumulations can lead to Z near clipping issues
|
||||
accumulate3DHeight(101);
|
||||
height = 101;
|
||||
|
@ -0,0 +1,51 @@
|
||||
package ru.dbotthepony.mc.otm.screen.panels;
|
||||
|
||||
import ru.dbotthepony.mc.otm.screen.MatteryScreen;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class DraggableCanvasPanel extends EditablePanel {
|
||||
public DraggableCanvasPanel(@Nonnull MatteryScreen<?> screen, @Nullable EditablePanel parent, float x, float y, float width, float height) {
|
||||
super(screen, parent, x, y, width, height);
|
||||
scissor = true;
|
||||
}
|
||||
|
||||
public DraggableCanvasPanel(@Nonnull MatteryScreen<?> screen, @Nullable EditablePanel parent) {
|
||||
super(screen, parent);
|
||||
scissor = true;
|
||||
}
|
||||
|
||||
protected boolean dragging = false;
|
||||
protected double last_mouse_x;
|
||||
protected double last_mouse_y;
|
||||
|
||||
@Override
|
||||
protected boolean mouseClickedInner(double mouse_x, double mouse_y, int flag) {
|
||||
dragging = true;
|
||||
last_mouse_x = mouse_x;
|
||||
last_mouse_y = mouse_y;
|
||||
setIgnoreMouseEventBoundaries(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean mouseReleasedInner(double mouse_x, double mouse_y, int flag) {
|
||||
dragging = false;
|
||||
setIgnoreMouseEventBoundaries(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean mouseDraggedInner(double mouse_x, double mouse_y, int flag, double drag_x, double drag_y) {
|
||||
if (dragging) {
|
||||
x_offset -= last_mouse_x - mouse_x;
|
||||
y_offset -= last_mouse_y - mouse_y;
|
||||
|
||||
last_mouse_x = mouse_x;
|
||||
last_mouse_y = mouse_y;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -2,8 +2,12 @@ package ru.dbotthepony.mc.otm.screen.panels;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.math.Vector3f;
|
||||
import com.mojang.math.Vector4f;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.components.events.GuiEventListener;
|
||||
import ru.dbotthepony.mc.otm.screen.MatteryScreen;
|
||||
import ru.dbotthepony.mc.otm.screen.RenderHelper;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@ -56,6 +60,37 @@ public class EditablePanel implements GuiEventListener {
|
||||
protected float parent_x;
|
||||
protected float parent_y;
|
||||
|
||||
// allows to offset children by given amount of pixels
|
||||
protected float x_offset;
|
||||
protected float y_offset;
|
||||
|
||||
// whenever is to scissor render bounds
|
||||
protected boolean scissor = false;
|
||||
|
||||
public float getXOffset() {
|
||||
return x_offset;
|
||||
}
|
||||
|
||||
public void setXOffset(float x_offset) {
|
||||
this.x_offset = x_offset;
|
||||
}
|
||||
|
||||
public float getYOffset() {
|
||||
return y_offset;
|
||||
}
|
||||
|
||||
public void setYOffset(float y_offset) {
|
||||
this.y_offset = y_offset;
|
||||
}
|
||||
|
||||
public boolean getEnableScissor() {
|
||||
return scissor;
|
||||
}
|
||||
|
||||
public void setEnableScissor(boolean scissor) {
|
||||
this.scissor = scissor;
|
||||
}
|
||||
|
||||
protected float accumulated_depth = 0;
|
||||
|
||||
public float getMost3DHeight() {
|
||||
@ -151,6 +186,14 @@ public class EditablePanel implements GuiEventListener {
|
||||
is_hovered = parent.is_hovered && mouse_x >= parent_x && mouse_x < parent_x + width && mouse_y >= parent_y && mouse_y < parent_y + height;
|
||||
}
|
||||
|
||||
var scissor = this.scissor;
|
||||
|
||||
if (scissor) {
|
||||
var window = Minecraft.getInstance().getWindow();
|
||||
var scale = window.getGuiScale();
|
||||
RenderHelper.pushScissorRect((int) (parent_x * scale), (int) (parent_y * scale), (int) (width * scale), (int) (height * scale));
|
||||
}
|
||||
|
||||
stack.pushPose();
|
||||
stack.translate(parent_x, parent_y, accumulated_depth);
|
||||
innerRender(stack, mouse_x, mouse_y, flag);
|
||||
@ -161,14 +204,18 @@ public class EditablePanel implements GuiEventListener {
|
||||
for (var child : children) {
|
||||
if (child.getVisible()) {
|
||||
child.accumulated_depth = accumulated_depth + 1;
|
||||
child.parent_x = parent_x + child.x;
|
||||
child.parent_y = parent_y + child.y;
|
||||
child.parent_x = parent_x + child.x + getXOffset();
|
||||
child.parent_y = parent_y + child.y + getYOffset();
|
||||
|
||||
RenderSystem.setShaderColor(1F, 1F, 1F, 1F);
|
||||
most_depth = Math.max(most_depth, child.render(stack, mouse_x, mouse_y, flag));
|
||||
}
|
||||
}
|
||||
|
||||
if (scissor) {
|
||||
RenderHelper.popScissorRect();
|
||||
}
|
||||
|
||||
return most_depth;
|
||||
}
|
||||
|
||||
@ -278,8 +325,8 @@ public class EditablePanel implements GuiEventListener {
|
||||
var get_parent = getParent();
|
||||
|
||||
while (get_parent != null) {
|
||||
x += get_parent.getX();
|
||||
y += get_parent.getY();
|
||||
x += get_parent.getX() + get_parent.getXOffset();
|
||||
y += get_parent.getY() + get_parent.getYOffset();
|
||||
get_parent = get_parent.getParent();
|
||||
}
|
||||
|
||||
@ -741,7 +788,7 @@ public class EditablePanel implements GuiEventListener {
|
||||
}
|
||||
|
||||
protected boolean mouseClickedInner(double mouse_x, double mouse_y, int flag) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean withinBounds(double mouse_x, double mouse_y) {
|
||||
@ -781,8 +828,7 @@ public class EditablePanel implements GuiEventListener {
|
||||
if (getMouseInputEnabled() && parent == null)
|
||||
screen.popup(this);
|
||||
|
||||
mouseClicked(mouse_x, mouse_y, flag);
|
||||
return true;
|
||||
return mouseClicked(mouse_x, mouse_y, flag);
|
||||
} else if (withinExtendedBounds(mouse_x, mouse_y)) {
|
||||
for (var child : children) {
|
||||
if (child.mouseClickedChecked(mouse_x, mouse_y, flag)) {
|
||||
@ -802,7 +848,7 @@ public class EditablePanel implements GuiEventListener {
|
||||
}
|
||||
|
||||
protected boolean mouseReleasedInner(double mouse_x, double mouse_y, int flag) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -827,8 +873,7 @@ public class EditablePanel implements GuiEventListener {
|
||||
return false;
|
||||
|
||||
if (getIgnoreMouseEventBoundaries() || withinBounds(mouse_x, mouse_y)) {
|
||||
mouseReleased(mouse_x, mouse_y, flag);
|
||||
return true;
|
||||
return mouseReleased(mouse_x, mouse_y, flag);
|
||||
} else if (withinExtendedBounds(mouse_x, mouse_y)) {
|
||||
for (var child : children) {
|
||||
if (child.mouseReleasedChecked(mouse_x, mouse_y, flag)) {
|
||||
@ -841,7 +886,7 @@ public class EditablePanel implements GuiEventListener {
|
||||
}
|
||||
|
||||
protected boolean mouseDraggedInner(double mouse_x, double mouse_y, int flag, double drag_x, double drag_y) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -866,8 +911,7 @@ public class EditablePanel implements GuiEventListener {
|
||||
return false;
|
||||
|
||||
if (getIgnoreMouseEventBoundaries() || withinBounds(mouse_x, mouse_y)) {
|
||||
mouseDragged(mouse_x, mouse_y, flag, drag_x, drag_y);
|
||||
return true;
|
||||
return mouseDragged(mouse_x, mouse_y, flag, drag_x, drag_y);
|
||||
} else if (withinExtendedBounds(mouse_x, mouse_y)) {
|
||||
for (var child : children) {
|
||||
if (child.mouseDraggedChecked(mouse_x, mouse_y, flag, drag_x, drag_y)) {
|
||||
@ -880,7 +924,7 @@ public class EditablePanel implements GuiEventListener {
|
||||
}
|
||||
|
||||
protected boolean mouseScrolledInner(double mouse_x, double mouse_y, double scroll) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -905,8 +949,7 @@ public class EditablePanel implements GuiEventListener {
|
||||
return false;
|
||||
|
||||
if (getIgnoreMouseEventBoundaries() || withinBounds(mouse_x, mouse_y)) {
|
||||
mouseScrolled(mouse_x, mouse_y, scroll);
|
||||
return true;
|
||||
return mouseScrolled(mouse_x, mouse_y, scroll);
|
||||
} else if (withinExtendedBounds(mouse_x, mouse_y)) {
|
||||
for (var child : children) {
|
||||
if (child.mouseScrolledChecked(mouse_x, mouse_y, scroll)) {
|
||||
@ -1011,7 +1054,9 @@ public class EditablePanel implements GuiEventListener {
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
|
||||
for (var child : children) {
|
||||
child.tick();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean is_removed = false;
|
||||
|
@ -15,6 +15,8 @@
|
||||
"otm.gui.matter.format": "Matter: %s",
|
||||
"otm.gui.matter.name": "MtU",
|
||||
|
||||
"otm.gui.android_research": "Android research tree",
|
||||
|
||||
"otm.gui.pattern.percentage_level": "Fill level: %s%%",
|
||||
"otm.gui.pattern.format": "Stored patterns: %s / %s",
|
||||
|
||||
@ -49,21 +51,36 @@
|
||||
"otm.matter_bottler.switch_mode": "Switch work mode",
|
||||
|
||||
"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": "Limb overclocking %s",
|
||||
|
||||
"android_research.overdrive_that_matters.limb_overclocking.description": "Boosts unit mobility by %s%% and attack speed by %s%%",
|
||||
"android_research.overdrive_that_matters.limb_overclocking.description": "Boosts unit's mobility by %s%% and attack speed by %s%%",
|
||||
|
||||
"android_research.status.requires": "Requires %s to be researched",
|
||||
"android_research.status.blocks": "Locks %s",
|
||||
"android_research.status.blocked_by": "Locked by %s",
|
||||
|
||||
"android_research.overdrive_that_matters.air_bags": "Air bags",
|
||||
"android_research.overdrive_that_matters.nanobots": "Nanobots",
|
||||
"android_research.overdrive_that_matters.air_bags.description": "Allows unit to swim in water",
|
||||
"android_research.overdrive_that_matters.nanobots.description": "Various useful nanobots for doing various tasks",
|
||||
|
||||
"android_research.overdrive_that_matters.nanobots_regeneration": "Regeneration %s",
|
||||
"android_research.overdrive_that_matters.nanobots_regeneration.description": "Nanobots get ability to repair unit's internal systems on the move",
|
||||
"android_research.overdrive_that_matters.nanobots_regeneration.description_improve": "Improves regeneration speed",
|
||||
|
||||
"android_research.overdrive_that_matters.nanobots_armor": "Nanobots armor",
|
||||
"android_research.overdrive_that_matters.nanobots_armor.description": "Allows nanobots to align themselves in cell shape, reducing incoming damage by a %% by absorbing impacts",
|
||||
|
||||
"android_research.overdrive_that_matters.nanobots_armor_speed": "Nanobots armor build speed %s",
|
||||
"android_research.overdrive_that_matters.nanobots_armor_speed.description": "Reduces time required for nanobots to form protection layer",
|
||||
|
||||
"android_research.overdrive_that_matters.nanobots_armor_strength": "Nanobots armor strength %s",
|
||||
"android_research.overdrive_that_matters.nanobots_armor_strength.description": "Increases impact absorption strength of nanobots",
|
||||
|
||||
"stat.overdrive_that_matters.health_regenerated": "Damage Regenerated by Nanobots",
|
||||
"stat.overdrive_that_matters.damage_absorbed": "Damage Absorbed by Nanobots",
|
||||
"stat.overdrive_that_matters.power_consumed": "MtE Burnt as Android",
|
||||
|
||||
"otm.suffix.merge": "%s %s",
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user