This commit is contained in:
GearShocky 2021-08-24 21:42:25 +06:00
commit 1650e63e20
18 changed files with 806 additions and 70 deletions

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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();

View File

@ -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);
}
}
}

View File

@ -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
*/

View File

@ -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");
}
}

View File

@ -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");
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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();

View File

@ -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) {

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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",