More abstract and straightforward android research system

This commit is contained in:
DBotThePony 2021-08-22 22:45:36 +07:00
parent 6769af52f5
commit bba2a1d9e0
Signed by: DBot
GPG Key ID: DCC23B5715498507
26 changed files with 572 additions and 385 deletions

View File

@ -12,8 +12,8 @@ import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import ru.dbotthepony.mc.otm.capability.android.AndroidCapability;
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer;
import ru.dbotthepony.mc.otm.capability.AndroidCapability;
import ru.dbotthepony.mc.otm.capability.AndroidCapabilityPlayer;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.client.AndroidGui;
import ru.dbotthepony.mc.otm.matter.MatterGrid;
@ -54,6 +54,7 @@ public class OverdriveThatMatters {
FMLJavaModLoadingContext.get().getModEventBus().register(Registry.BlockEntities.class);
FMLJavaModLoadingContext.get().getModEventBus().register(Registry.Menus.class);
FMLJavaModLoadingContext.get().getModEventBus().register(Registry.AndroidFeatures.class);
FMLJavaModLoadingContext.get().getModEventBus().register(Registry.AndroidResearch.class);
FMLJavaModLoadingContext.get().getModEventBus().addListener(AndroidCapability::registerEffects);

View File

@ -13,13 +13,13 @@ import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.registries.ForgeRegistry;
import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.RegistryBuilder;
import ru.dbotthepony.mc.otm.android.AndroidFeature;
import ru.dbotthepony.mc.otm.android.AndroidResearch;
import ru.dbotthepony.mc.otm.android.AndroidResearchType;
import ru.dbotthepony.mc.otm.block.*;
import ru.dbotthepony.mc.otm.block.entity.*;
import ru.dbotthepony.mc.otm.capability.android.AndroidAirBags;
import ru.dbotthepony.mc.otm.capability.android.AndroidAirBagsType;
import ru.dbotthepony.mc.otm.capability.android.AndroidFeatureType;
import ru.dbotthepony.mc.otm.android.AndroidFeatureType;
import ru.dbotthepony.mc.otm.item.ItemBattery;
import ru.dbotthepony.mc.otm.item.ItemMatterCapacitor;
import ru.dbotthepony.mc.otm.item.ItemPatternStorage;
@ -39,10 +39,14 @@ public class Registry {
}
private static ForgeRegistry<AndroidFeatureType<?>> ANDROID_FEATURES;
private static ForgeRegistry<AndroidResearchType<?>> ANDROID_RESEARCH;
public static ForgeRegistry<AndroidFeatureType<?>> ANDROID_FEATURES() {
return ANDROID_FEATURES;
}
public static ForgeRegistry<AndroidResearchType<?>> ANDROID_RESEARCH() {
return ANDROID_RESEARCH;
}
public static void createRegistry() {
if (ANDROID_FEATURES != null)
@ -54,6 +58,13 @@ public class Registry {
// make it shut up
builder.setType(c(AndroidFeatureType.class));
ANDROID_FEATURES = (ForgeRegistry<AndroidFeatureType<?>>) builder.create();
var builder2 = new RegistryBuilder<AndroidResearchType<?>>();
builder2.setName(new ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research"));
// make it shut up
builder2.setType(c(AndroidResearchType.class));
ANDROID_RESEARCH = (ForgeRegistry<AndroidResearchType<?>>) builder2.create();
}
private static <T> Class<T> c(Class<?> cls) { return (Class<T>) cls; }
@ -266,7 +277,7 @@ public class Registry {
}
public static class AndroidFeatures {
public static final AndroidAirBagsType AIR_BAGS = new AndroidAirBagsType();
public static final AndroidFeatureType<AndroidFeature> AIR_BAGS = new AndroidFeatureType<>(AndroidFeature::new);
static {
AIR_BAGS.setRegistryName(Names.AIR_BAGS);
@ -278,6 +289,25 @@ public class Registry {
}
}
public static class AndroidResearch {
public static final AndroidResearchType<ru.dbotthepony.mc.otm.android.AndroidResearch> AIR_BAGS = new AndroidResearchType<>(
new ru.dbotthepony.mc.otm.android.AndroidResearch.Builder()
.setExperienceCost(18)
.addFeatureResult(AndroidFeatures.AIR_BAGS)
.withDescription()
.build()
);
static {
AIR_BAGS.setRegistryName(Names.AIR_BAGS);
}
@SubscribeEvent
public static void register(final RegistryEvent.Register<AndroidResearchType<?>> event) {
event.getRegistry().register(AIR_BAGS);
}
}
public static class Menus {
public static final MenuType<AndroidStationMenu> ANDROID_STATION = new MenuType<>(AndroidStationMenu::new);
public static final MenuType<BatteryBankMenu> BATTERY_BANK = new MenuType<>(BatteryBankMenu::new);

View File

@ -1,6 +1,10 @@
package ru.dbotthepony.mc.otm.capability.android;
package ru.dbotthepony.mc.otm.android;
public class AndroidFeature {
import net.minecraft.nbt.CompoundTag;
import net.minecraftforge.common.util.INBTSerializable;
import ru.dbotthepony.mc.otm.capability.IAndroidCapability;
public class AndroidFeature implements INBTSerializable<CompoundTag> {
public final AndroidFeatureType<? extends AndroidFeature> type;
public final IAndroidCapability capability;
@ -40,4 +44,20 @@ public class AndroidFeature {
public void tickServer() {
}
public void serializeNBT(CompoundTag tag) {
}
@Override
public CompoundTag serializeNBT() {
var tag = new CompoundTag();
serializeNBT(tag);
return tag;
}
@Override
public void deserializeNBT(CompoundTag tag) {
}
}

View File

@ -1,9 +1,9 @@
package ru.dbotthepony.mc.otm.capability.android;
package ru.dbotthepony.mc.otm.android;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraftforge.registries.*;
import ru.dbotthepony.mc.otm.capability.IAndroidCapability;
import javax.annotation.Nullable;
@ -26,25 +26,6 @@ public class AndroidFeatureType<T extends AndroidFeature> extends ForgeRegistryE
return true;
}
public boolean isResearchable() {
return false;
}
@Nullable
protected AndroidFeatureResearchNode getResearchNodeInner() {
return null;
}
private AndroidFeatureResearchNode cache;
@Nullable
public AndroidFeatureResearchNode getResearchNode() {
if (cache != null)
return cache;
return cache = getResearchNodeInner();
}
public Component getDisplayName() {
if (getRegistryName() == null)
return new TranslatableComponent("android_feature.null.null");

View File

@ -0,0 +1,303 @@
package ru.dbotthepony.mc.otm.android;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraftforge.common.util.INBTSerializable;
import net.minecraftforge.registries.ForgeRegistry;
import net.minecraftforge.registries.RegistryManager;
import ru.dbotthepony.mc.otm.Registry;
import ru.dbotthepony.mc.otm.capability.AndroidCapabilityPlayer;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.network.MatteryNetworking;
import ru.dbotthepony.mc.otm.network.android.AndroidResearchRequestPacket;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
public abstract class AndroidResearch implements INBTSerializable<CompoundTag> {
public final AndroidResearchType<?> type;
public final AndroidCapabilityPlayer capability;
protected boolean researched = false;
public boolean dirty = true;
public void markDirty() {
dirty = true;
}
public boolean isResearched() {
return researched;
}
public AndroidResearch(AndroidResearchType<?> type, AndroidCapabilityPlayer capability) {
this.capability = capability;
this.type = type;
}
abstract public List<AndroidResearchType<?>> getPrerequisites();
abstract public void onResearched();
abstract public void consumeCost();
abstract public boolean canAfford();
public List<Component> getTooltip() {
return List.of(type.getDisplayName());
}
public boolean research(boolean force) {
if (researched)
return false;
if (!force && !canAfford())
return false;
consumeCost();
researched = true;
onResearched();
markDirty();
return true;
}
public boolean research() {
return research(false);
}
public void researchClient() {
if (!researched && canAfford() && capability.isAndroid())
MatteryNetworking.CHANNEL.sendToServer(new AndroidResearchRequestPacket(type));
}
@Override
public CompoundTag serializeNBT() {
var nbt = new CompoundTag();
serializeNBT(nbt);
return nbt;
}
public void serializeNBT(CompoundTag tag) {
tag.putByte("researched", researched ? (byte) 1 : 0);
}
public void deserializeNBT(CompoundTag nbt) {
researched = nbt.getByte("researched") > 0;
}
public static class Builder {
record DeferredItemStack(ResourceLocation id, int amount) {}
public int experience = 0;
public boolean has_description = false;
public final ArrayList<DeferredItemStack> items = new ArrayList<>();
public final ArrayList<ResourceLocation> prerequisites = new ArrayList<>();
public final ArrayList<ResourceLocation> feature_results = new ArrayList<>();
public Builder addPrerequisite(ResourceLocation location) {
prerequisites.add(location);
return this;
}
public Builder addFeatureResult(ResourceLocation location) {
feature_results.add(location);
return this;
}
public Builder addFeatureResult(AndroidFeatureType<?> location) {
feature_results.add(Objects.requireNonNull(location.getRegistryName()));
return this;
}
public Builder addItem(ResourceLocation location) {
items.add(new DeferredItemStack(location, 1));
return this;
}
public Builder addItem(ResourceLocation location, int amount) {
items.add(new DeferredItemStack(location, amount));
return this;
}
public Builder setExperienceCost(int experience) {
this.experience = experience;
return this;
}
public Builder withDescription() {
this.has_description = true;
return this;
}
private final ArrayList<ItemStack> resolved_stacks = new ArrayList<>();
private final ArrayList<AndroidResearchType<?>> resolved_preq = new ArrayList<>();
private final ArrayList<AndroidFeatureType<?>> resolved_features = new ArrayList<>();
private boolean resolved = false;
private void resolve() {
if (!resolved) {
resolved = true;
for (var item : items) {
var get_item = RegistryManager.ACTIVE.getRegistry(Item.class).getValue(item.id);
if (get_item != null && get_item != Items.AIR) {
resolved_stacks.add(new ItemStack(get_item, item.amount));
}
}
for (var entry : prerequisites) {
var get = Registry.ANDROID_RESEARCH().getValue(entry);
if (get != null) {
resolved_preq.add(get);
} else {
throw new IllegalArgumentException("Can not find android research " + entry);
}
}
for (var entry : feature_results) {
var get = Registry.ANDROID_FEATURES().getValue(entry);
if (get != null) {
resolved_features.add(get);
} else {
throw new IllegalArgumentException("Can not find android feature " + entry);
}
}
}
}
public AndroidResearchType.AndroidResearchFactory<AndroidResearch> build() {
return (type, capability) -> {
resolve();
return new AndroidResearch(type, capability) {
@Override
public List<Component> getTooltip() {
var list = new ArrayList<>(super.getTooltip());
if (has_description) {
list.add(new TranslatableComponent("android_research." + type.getRegistryName().getNamespace() + "." + type.getRegistryName().getPath() + ".description"));
}
if (!researched) {
if (experience > 0) {
if (capability.ply.experienceLevel >= experience) {
list.add(new TranslatableComponent("otm.android_station.research.xp_cost", experience).withStyle(ChatFormatting.DARK_GREEN));
} else {
list.add(new TranslatableComponent("otm.android_station.research.xp_cost", experience).withStyle(ChatFormatting.DARK_RED));
}
}
for (var stack : resolved_stacks) {
boolean hit = false;
if (stack.getTag() == null) {
for (var inv_stack : capability.ply.getInventory().items) {
if (inv_stack.is(stack.getItem()) && inv_stack.getCount() >= stack.getCount()) {
hit = true;
break;
}
}
} else {
for (var inv_stack : capability.ply.getInventory().items) {
if (inv_stack.is(stack.getItem()) && inv_stack.getCount() >= stack.getCount() && inv_stack.getTag() != null && inv_stack.getTag().equals(stack.getTag())) {
hit = true;
break;
}
}
}
if (hit) {
list.add(new TranslatableComponent("otm.android_station.research.item", stack.getDisplayName(), stack.getCount()).withStyle(ChatFormatting.DARK_GREEN));
} else {
list.add(new TranslatableComponent("otm.android_station.research.item", stack.getDisplayName(), stack.getCount()).withStyle(ChatFormatting.DARK_RED));
}
}
}
return list;
}
@Override
public List<AndroidResearchType<?>> getPrerequisites() {
return List.copyOf(resolved_preq);
}
@Override
public void onResearched() {
for (var feature : resolved_features) {
capability.addFeature(feature);
}
}
@Override
public boolean canAfford() {
if (capability.ply.experienceLevel < experience)
return false;
for (var stack : resolved_stacks) {
boolean hit = false;
if (stack.getTag() == null) {
for (var inv_stack : capability.ply.getInventory().items) {
if (inv_stack.is(stack.getItem()) && inv_stack.getCount() >= stack.getCount()) {
hit = true;
break;
}
}
} else {
for (var inv_stack : capability.ply.getInventory().items) {
if (inv_stack.is(stack.getItem()) && inv_stack.getCount() >= stack.getCount() && inv_stack.getTag() != null && inv_stack.getTag().equals(stack.getTag())) {
hit = true;
break;
}
}
}
if (!hit)
return false;
}
return true;
}
@Override
public void consumeCost() {
if (experience > 0)
capability.ply.giveExperienceLevels(-experience);
for (var stack : resolved_stacks) {
if (stack.getTag() == null) {
for (var inv_stack : capability.ply.getInventory().items) {
if (inv_stack.is(stack.getItem()) && inv_stack.getCount() >= stack.getCount()) {
inv_stack.shrink(stack.getCount());
break;
}
}
} else {
for (var inv_stack : capability.ply.getInventory().items) {
if (inv_stack.is(stack.getItem()) && inv_stack.getCount() >= stack.getCount() && inv_stack.getTag() != null && inv_stack.getTag().equals(stack.getTag())) {
inv_stack.shrink(stack.getCount());
break;
}
}
}
}
capability.ply.getInventory().setChanged();
}
};
};
}
}
}

View File

@ -0,0 +1,29 @@
package ru.dbotthepony.mc.otm.android;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraftforge.registries.ForgeRegistryEntry;
import ru.dbotthepony.mc.otm.capability.AndroidCapabilityPlayer;
public class AndroidResearchType<T extends AndroidResearch> extends ForgeRegistryEntry<AndroidResearchType<?>> {
public interface AndroidResearchFactory<T extends AndroidResearch> {
T factory(AndroidResearchType<T> entry, AndroidCapabilityPlayer capability);
}
protected final AndroidResearchFactory<T> factory;
public AndroidResearchType(AndroidResearchFactory<T> factory) {
this.factory = factory;
}
public T factory(AndroidCapabilityPlayer capability) {
return factory.factory(this, capability);
}
public Component getDisplayName() {
if (getRegistryName() == null)
return new TranslatableComponent("android_research.null.null");
return new TranslatableComponent("android_research." + getRegistryName().getNamespace() + "." + getRegistryName().getPath());
}
}

View File

@ -17,7 +17,7 @@ import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.common.util.LazyOptional;
import ru.dbotthepony.mc.otm.Registry;
import ru.dbotthepony.mc.otm.block.entity.BlockEntityAndroidStation;
import ru.dbotthepony.mc.otm.capability.android.IAndroidCapability;
import ru.dbotthepony.mc.otm.capability.IAndroidCapability;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.shapes.BlockShapes;

View File

@ -13,7 +13,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import ru.dbotthepony.mc.otm.Registry;
import ru.dbotthepony.mc.otm.capability.android.IAndroidCapability;
import ru.dbotthepony.mc.otm.capability.IAndroidCapability;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.capability.MatteryMachineEnergyStorage;
import ru.dbotthepony.mc.otm.menu.AndroidStationMenu;

View File

@ -1,4 +1,4 @@
package ru.dbotthepony.mc.otm.capability.android;
package ru.dbotthepony.mc.otm.capability;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
@ -20,11 +20,9 @@ import net.minecraftforge.event.entity.living.LivingEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fmllegacy.network.PacketDistributor;
import org.apache.logging.log4j.core.jmx.Server;
import ru.dbotthepony.mc.otm.OverdriveThatMatters;
import ru.dbotthepony.mc.otm.Registry;
import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.android.AndroidFeature;
import ru.dbotthepony.mc.otm.android.AndroidFeatureType;
import ru.dbotthepony.mc.otm.network.MatteryNetworking;
import ru.dbotthepony.mc.otm.network.android.AndroidBatteryPacket;
import ru.dbotthepony.mc.otm.network.android.AndroidEnergyPacket;
@ -167,12 +165,8 @@ public class AndroidCapability implements ICapabilityProvider, IAndroidCapabilit
for (var feature : features.values()) {
var compound = new CompoundTag();
feature.serializeNBT(compound);
compound.putString("id", Objects.requireNonNull(feature.type.getRegistryName()).toString());
if (feature instanceof INBTSerializable serializable) {
compound.put("data", serializable.serializeNBT());
}
features_nbt.add(compound);
}
@ -202,10 +196,7 @@ public class AndroidCapability implements ICapabilityProvider, IAndroidCapabilit
if (get_feature != null && get_feature.isApplicable(this)) {
var feature = get_feature.factory(this);
if (feature instanceof INBTSerializable serializable) {
serializable.deserializeNBT(tag.getCompound("data"));
}
feature.deserializeNBT(tag);
addFeature(feature);
feature.attachFinish();

View File

@ -1,6 +1,9 @@
package ru.dbotthepony.mc.otm.capability.android;
package ru.dbotthepony.mc.otm.capability;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraftforge.client.event.InputUpdateEvent;
@ -9,20 +12,30 @@ import net.minecraftforge.event.AttachCapabilitiesEvent;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import ru.dbotthepony.mc.otm.Registry;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.android.AndroidResearch;
import ru.dbotthepony.mc.otm.android.AndroidResearchType;
import ru.dbotthepony.mc.otm.network.android.AndroidResearchPacket;
import ru.dbotthepony.mc.otm.network.android.AndroidStatusPacket;
import javax.annotation.Nonnull;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Objects;
public class AndroidCapabilityPlayer extends AndroidCapability {
public boolean is_android = false;
private boolean network_is_android = false;
private final ArrayList<AndroidResearch> research_list = new ArrayList<>();
@Override
public void invalidateNetworkState() {
super.invalidateNetworkState();
network_is_android = false;
for (var instance : research_list) {
instance.dirty = true;
}
}
@Override
@ -63,16 +76,56 @@ public class AndroidCapabilityPlayer extends AndroidCapability {
if (compound.contains("is_android"))
is_android = compound.getByte("is_android") > 0;
research_list.clear();
var list = compound.getList("research", Tag.TAG_COMPOUND);
for (var _tag : list) {
if (_tag instanceof CompoundTag tag) {
var research = Registry.ANDROID_RESEARCH().getValue(new ResourceLocation(tag.getString("id")));
if (research != null) {
var instance = research.factory(this);
instance.deserializeNBT(tag);
research_list.add(instance);
}
}
}
}
@Nonnull
@Override
public CompoundTag serializeNBT() {
CompoundTag tag = super.serializeNBT();
tag.putByte("is_android", is_android ? (byte) 1 : (byte) 0);
tag.putByte("is_android", is_android ? (byte) 1 : 0);
var list = new ListTag();
for (var instance : research_list) {
var _tag = new CompoundTag();
instance.serializeNBT(_tag);
_tag.putString("id", Objects.requireNonNull(instance.type.getRegistryName()).toString());
list.add(_tag);
}
tag.put("research", list);
return tag;
}
public AndroidResearch getResearch(AndroidResearchType<?> type) {
for (var instance : research_list) {
if (instance.type == type) {
return instance;
}
}
var instance = type.factory(this);
research_list.add(instance);
return instance;
}
@SubscribeEvent
public static void onAttachCapabilityEvent(AttachCapabilitiesEvent<Entity> event) {
if (event.getObject() instanceof Player ply) {
@ -143,7 +196,7 @@ public class AndroidCapabilityPlayer extends AndroidCapability {
});
}
private final Player ply;
public final Player ply;
public AndroidCapabilityPlayer(Player ent) {
super(ent);
@ -158,6 +211,13 @@ public class AndroidCapabilityPlayer extends AndroidCapability {
network_is_android = is_android;
sendNetwork(new AndroidStatusPacket(is_android));
}
for (var instance : research_list) {
if (instance.dirty) {
instance.dirty = false;
sendNetwork(new AndroidResearchPacket(instance));
}
}
}
public static final BigDecimal ENERGY_FOR_HUNGER_POINT = new BigDecimal(1000);

View File

@ -1,10 +1,11 @@
package ru.dbotthepony.mc.otm.capability.android;
package ru.dbotthepony.mc.otm.capability;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.INBTSerializable;
import ru.dbotthepony.mc.otm.android.AndroidFeature;
import ru.dbotthepony.mc.otm.android.AndroidFeatureType;
import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage;
import javax.annotation.Nonnull;

View File

@ -4,7 +4,6 @@ import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.CapabilityInject;
import net.minecraftforge.common.capabilities.CapabilityManager;
import net.minecraftforge.energy.IEnergyStorage;
import ru.dbotthepony.mc.otm.capability.android.IAndroidCapability;
import java.math.BigDecimal;
import java.math.MathContext;

View File

@ -1,7 +0,0 @@
package ru.dbotthepony.mc.otm.capability.android;
public class AndroidAirBags extends AndroidFeature {
public AndroidAirBags(AndroidFeatureType<?> type, IAndroidCapability capability) {
super(type, capability);
}
}

View File

@ -1,16 +0,0 @@
package ru.dbotthepony.mc.otm.capability.android;
import javax.annotation.Nullable;
public class AndroidAirBagsType extends AndroidFeatureType<AndroidAirBags> {
public AndroidAirBagsType() {
super(AndroidAirBags::new);
}
@Nullable
@Override
public AndroidFeatureResearchNode getResearchNodeInner() {
return new AndroidFeatureResearchNode(this, new AndroidFeatureResearchCost(18))
.withDescription();
}
}

View File

@ -1,124 +0,0 @@
package ru.dbotthepony.mc.otm.capability.android;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import java.util.ArrayList;
import java.util.List;
public record AndroidFeatureResearchCost(int experience, ItemStack ...items) {
public boolean matches(Player ply) {
if (ply.experienceLevel < experience)
return false;
for (var stack : items) {
boolean hit = false;
if (stack.getTag() == null) {
for (var inv_stack : ply.getInventory().items) {
if (inv_stack.is(stack.getItem()) && inv_stack.getCount() >= stack.getCount()) {
hit = true;
break;
}
}
} else {
for (var inv_stack : ply.getInventory().items) {
if (inv_stack.is(stack.getItem()) && inv_stack.getCount() >= stack.getCount() && inv_stack.getTag() != null && inv_stack.getTag().equals(stack.getTag())) {
hit = true;
break;
}
}
}
if (!hit)
return false;
}
return true;
}
public List<Component> getTooltip() {
var list = new ArrayList<Component>();
if (experience > 0)
list.add(new TranslatableComponent("otm.android_station.research.xp_cost", experience));
for (var itemstack : items)
list.add(new TranslatableComponent("otm.android_station.research.item", itemstack.getDisplayName(), itemstack.getCount()));
return list;
}
public List<Component> getTooltip(Player ply) {
var list = new ArrayList<Component>();
if (experience > 0) {
if (ply.experienceLevel >= experience) {
list.add(new TranslatableComponent("otm.android_station.research.xp_cost", experience).withStyle(ChatFormatting.DARK_GREEN));
} else {
list.add(new TranslatableComponent("otm.android_station.research.xp_cost", experience).withStyle(ChatFormatting.DARK_RED));
}
}
for (var stack : items) {
boolean hit = false;
if (stack.getTag() == null) {
for (var inv_stack : ply.getInventory().items) {
if (inv_stack.is(stack.getItem()) && inv_stack.getCount() >= stack.getCount()) {
hit = true;
break;
}
}
} else {
for (var inv_stack : ply.getInventory().items) {
if (inv_stack.is(stack.getItem()) && inv_stack.getCount() >= stack.getCount() && inv_stack.getTag() != null && inv_stack.getTag().equals(stack.getTag())) {
hit = true;
break;
}
}
}
if (hit) {
list.add(new TranslatableComponent("otm.android_station.research.item", stack.getDisplayName(), stack.getCount()).withStyle(ChatFormatting.DARK_GREEN));
} else {
list.add(new TranslatableComponent("otm.android_station.research.item", stack.getDisplayName(), stack.getCount()).withStyle(ChatFormatting.DARK_RED));
}
}
return list;
}
public boolean research(Player ply) {
if (!matches(ply))
return false;
if (experience > 0)
ply.giveExperienceLevels(-experience);
for (var stack : items) {
if (stack.getTag() == null) {
for (var inv_stack : ply.getInventory().items) {
if (inv_stack.is(stack.getItem()) && inv_stack.getCount() >= stack.getCount()) {
inv_stack.shrink(stack.getCount());
break;
}
}
} else {
for (var inv_stack : ply.getInventory().items) {
if (inv_stack.is(stack.getItem()) && inv_stack.getCount() >= stack.getCount() && inv_stack.getTag() != null && inv_stack.getTag().equals(stack.getTag())) {
inv_stack.shrink(stack.getCount());
break;
}
}
}
}
ply.getInventory().setChanged();
return true;
}
}

View File

@ -1,120 +0,0 @@
package ru.dbotthepony.mc.otm.capability.android;
import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.world.entity.player.Player;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.network.MatteryNetworking;
import ru.dbotthepony.mc.otm.network.android.AndroidResearchRequestPacket;
import ru.dbotthepony.mc.otm.screen.SkinElement;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class AndroidFeatureResearchNode {
public final AndroidFeatureType<?> type;
public final AndroidFeatureResearchCost cost;
public final Set<AndroidFeatureType<?>> predecessors = new HashSet<>();
protected final Component description;
public boolean with_description = false;
public SkinElement icon;
public AndroidFeatureResearchNode(AndroidFeatureType<?> type, AndroidFeatureResearchCost cost) {
this.type = type;
this.cost = cost;
description = new TranslatableComponent("android_feature." + type.getRegistryName().getNamespace() + "." + type.getRegistryName().getPath() + ".description");
}
public AndroidFeatureResearchNode(AndroidFeatureType<?> type, AndroidFeatureResearchCost cost, SkinElement icon) {
this(type, cost);
this.icon = icon;
}
public AndroidFeatureResearchNode addPredecessor(AndroidFeatureType<?> value) {
predecessors.add(value);
return this;
}
public AndroidFeatureResearchNode withDescription() {
with_description = true;
return this;
}
public boolean canResearch(Player ply, AndroidCapabilityPlayer cap) {
for (var prec : predecessors) {
if (!cap.hasFeature(prec))
return false;
}
return cost.matches(ply);
}
public void research(Player ply, AndroidCapabilityPlayer cap) {
if (cap.hasFeature(type))
return;
if (!canResearch(ply, cap))
return;
cost.research(ply);
cap.addFeature(type);
}
public void researchClient() {
var ply = Minecraft.getInstance().player;
ply.getCapability(MatteryCapability.ANDROID).ifPresent(cap -> {
if (cap.hasFeature(type))
return;
if (!canResearch(ply, (AndroidCapabilityPlayer) cap))
return;
MatteryNetworking.CHANNEL.sendToServer(new AndroidResearchRequestPacket(type));
});
}
public Component getDescription() {
return description;
}
public List<Component> getTooltip() {
var list = new ArrayList<Component>();
list.add(type.getDisplayName());
if (with_description) {
list.add(getDescription());
}
list.addAll(cost.getTooltip());
return list;
}
public List<Component> getTooltip(Player ply) {
var list = new ArrayList<Component>();
list.add(type.getDisplayName());
if (with_description) {
list.add(getDescription());
}
list.addAll(cost.getTooltip(ply));
ply.getCapability(MatteryCapability.ANDROID).ifPresent(cap -> {
for (var prec : predecessors) {
if (!cap.hasFeature(prec)) {
list.add(new TranslatableComponent("otm.android_station.research.missing_predecessors", prec.getDisplayName()));
}
}
});
return list;
}
}

View File

@ -14,7 +14,7 @@ import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.client.gui.ForgeIngameGui;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import ru.dbotthepony.mc.otm.OverdriveThatMatters;
import ru.dbotthepony.mc.otm.capability.android.IAndroidCapability;
import ru.dbotthepony.mc.otm.capability.IAndroidCapability;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import java.math.BigDecimal;

View File

@ -12,8 +12,8 @@ import net.minecraft.world.item.*;
import net.minecraft.world.level.Level;
import net.minecraftforge.common.util.FakePlayer;
import ru.dbotthepony.mc.otm.OverdriveThatMatters;
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer;
import ru.dbotthepony.mc.otm.capability.android.IAndroidCapability;
import ru.dbotthepony.mc.otm.capability.AndroidCapabilityPlayer;
import ru.dbotthepony.mc.otm.capability.IAndroidCapability;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import javax.annotation.Nullable;

View File

@ -7,7 +7,7 @@ import net.minecraft.world.item.ItemStack;
import net.minecraftforge.common.util.LazyOptional;
import ru.dbotthepony.mc.otm.Registry;
import ru.dbotthepony.mc.otm.block.entity.BlockEntityAndroidStation;
import ru.dbotthepony.mc.otm.capability.android.IAndroidCapability;
import ru.dbotthepony.mc.otm.capability.IAndroidCapability;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.menu.slot.BatterySlot;

View File

@ -111,5 +111,14 @@ public class MatteryNetworking {
AndroidResearchRequestPacket::play,
Optional.of(NetworkDirection.PLAY_TO_SERVER)
);
CHANNEL.registerMessage(
next_network_id++,
AndroidResearchPacket.class,
AndroidResearchPacket::write,
AndroidResearchPacket::read,
AndroidResearchPacket::play,
Optional.of(NetworkDirection.PLAY_TO_CLIENT)
);
}
}

View File

@ -2,15 +2,14 @@ package ru.dbotthepony.mc.otm.network.android;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import ru.dbotthepony.mc.otm.Registry;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer;
import ru.dbotthepony.mc.otm.capability.android.AndroidFeature;
import ru.dbotthepony.mc.otm.capability.android.AndroidFeatureType;
import ru.dbotthepony.mc.otm.capability.AndroidCapabilityPlayer;
import ru.dbotthepony.mc.otm.android.AndroidFeature;
import ru.dbotthepony.mc.otm.android.AndroidFeatureType;
import java.util.Objects;
import java.util.function.Supplier;

View File

@ -0,0 +1,49 @@
package ru.dbotthepony.mc.otm.network.android;
import net.minecraft.client.Minecraft;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
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.AndroidCapabilityPlayer;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import java.util.function.Supplier;
public record AndroidResearchPacket(AndroidResearchType<?> type, CompoundTag payload) {
public AndroidResearchPacket(AndroidResearch research) {
this(research.type, research.serializeNBT());
}
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(Registry.ANDROID_RESEARCH().getID(type));
buffer.writeNbt(payload);
}
public static AndroidResearchPacket read(FriendlyByteBuf buffer) {
return new AndroidResearchPacket(Registry.ANDROID_RESEARCH().getValue(buffer.readInt()), buffer.readNbt());
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> {
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> this::playClient);
});
}
public void playClient() {
var ply = Minecraft.getInstance().player;
if (ply != null) {
ply.getCapability(MatteryCapability.ANDROID).ifPresent(cap -> {
if (cap instanceof AndroidCapabilityPlayer pcap) {
pcap.getResearch(type).deserializeNBT(payload);
}
});
}
}
}

View File

@ -1,52 +1,43 @@
package ru.dbotthepony.mc.otm.network.android;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
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.android.AndroidCapabilityPlayer;
import ru.dbotthepony.mc.otm.capability.android.AndroidFeature;
import ru.dbotthepony.mc.otm.capability.android.AndroidFeatureType;
import ru.dbotthepony.mc.otm.capability.android.IAndroidCapability;
import ru.dbotthepony.mc.otm.capability.AndroidCapabilityPlayer;
import java.util.Objects;
import java.util.function.Supplier;
public record AndroidResearchRequestPacket(AndroidFeatureType<?> feature) {
public AndroidResearchRequestPacket(AndroidFeatureType<?> feature) {
this.feature = feature;
public record AndroidResearchRequestPacket(AndroidResearchType<?> research) {
public AndroidResearchRequestPacket(AndroidResearchType<?> research) {
this.research = research;
}
public AndroidResearchRequestPacket(AndroidFeature feature) {
public AndroidResearchRequestPacket(AndroidResearch feature) {
this(feature.type);
}
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(Registry.ANDROID_FEATURES().getID(feature));
buffer.writeInt(Registry.ANDROID_RESEARCH().getID(research));
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> context.get().getSender().getCapability(MatteryCapability.ANDROID).ifPresent(cap -> this.playServer((AndroidCapabilityPlayer) cap, (ServerPlayer) context.get().getSender())));
context.get().enqueueWork(() -> context.get().getSender().getCapability(MatteryCapability.ANDROID).ifPresent(cap -> this.playServer((AndroidCapabilityPlayer) cap, context.get().getSender())));
}
public void playServer(AndroidCapabilityPlayer cap, ServerPlayer ply) {
if (cap.hasFeature(feature))
if (!cap.isAndroid())
return;
var node = feature.getResearchNode();
if (node == null)
return;
node.research(ply, cap);
cap.getResearch(research).research();
}
public static AndroidResearchRequestPacket read(FriendlyByteBuf buffer) {
return new AndroidResearchRequestPacket(Objects.requireNonNull(Registry.ANDROID_FEATURES().getValue(buffer.readInt())));
return new AndroidResearchRequestPacket(Objects.requireNonNull(Registry.ANDROID_RESEARCH().getValue(buffer.readInt())));
}
}

View File

@ -2,12 +2,11 @@ package ru.dbotthepony.mc.otm.network.android;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer;
import ru.dbotthepony.mc.otm.capability.AndroidCapabilityPlayer;
import java.util.function.Supplier;

View File

@ -1,39 +1,31 @@
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;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Inventory;
import ru.dbotthepony.mc.otm.OverdriveThatMatters;
import ru.dbotthepony.mc.otm.Registry;
import ru.dbotthepony.mc.otm.android.AndroidResearch;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer;
import ru.dbotthepony.mc.otm.capability.android.AndroidFeatureResearchNode;
import ru.dbotthepony.mc.otm.capability.AndroidCapabilityPlayer;
import ru.dbotthepony.mc.otm.menu.AndroidStationMenu;
import ru.dbotthepony.mc.otm.screen.panels.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
public class AndroidStationScreen extends MatteryScreen<AndroidStationMenu> {
class AndroidFeatureButton extends EditablePanel {
class AndroidResearchButton extends EditablePanel {
public static final int BUTTON_SIZE = 16;
private final AndroidFeatureResearchNode node;
private final AndroidResearch node;
public static final RGBAColor RESEARCHED = new RGBAColor(150, 150, 200);
public static final RGBAColor CAN_BE_RESEARCHED = new RGBAColor(150, 200, 150);
public static final RGBAColor CAN_NOT_BE_RESEARCHED = new RGBAColor(200, 150, 150);
public AndroidFeatureButton(@Nullable EditablePanel parent, AndroidFeatureResearchNode node) {
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);
@ -43,9 +35,9 @@ public class AndroidStationScreen extends MatteryScreen<AndroidStationMenu> {
protected void innerRender(PoseStack stack, float mouse_x, float mouse_y, float flag) {
Minecraft.getInstance().player.getCapability(MatteryCapability.ANDROID).ifPresent(_cap -> {
if (_cap instanceof AndroidCapabilityPlayer cap) {
if (cap.hasFeature(node.type)) {
if (node.isResearched()) {
RenderHelper.setDrawColor(RESEARCHED);
} else if (node.canResearch(Minecraft.getInstance().player, cap)) {
} else if (node.canAfford()) {
RenderHelper.setDrawColor(CAN_BE_RESEARCHED);
} else {
RenderHelper.setDrawColor(CAN_NOT_BE_RESEARCHED);
@ -65,19 +57,15 @@ public class AndroidStationScreen extends MatteryScreen<AndroidStationMenu> {
@Override
protected boolean innerRenderTooltips(PoseStack stack, float mouse_x, float mouse_y, float flag) {
if (is_hovered) {
var list = new ArrayList<>(node.getTooltip(Minecraft.getInstance().player));
var list = new ArrayList<>(node.getTooltip());
Minecraft.getInstance().player.getCapability(MatteryCapability.ANDROID).ifPresent(_cap -> {
if (_cap instanceof AndroidCapabilityPlayer cap) {
if (cap.hasFeature(node.type)) {
list.add(new TranslatableComponent("otm.android_station.research.researched").withStyle(ChatFormatting.DARK_AQUA));
} else if (node.canResearch(Minecraft.getInstance().player, cap)) {
list.add(new TranslatableComponent("otm.android_station.research.can_be_researched").withStyle(ChatFormatting.DARK_GREEN));
} else {
list.add(new TranslatableComponent("otm.android_station.research.can_not_be_researched").withStyle(ChatFormatting.DARK_RED));
}
}
});
if (node.isResearched()) {
list.add(new TranslatableComponent("otm.android_station.research.researched").withStyle(ChatFormatting.DARK_AQUA));
} else if (node.canAfford()) {
list.add(new TranslatableComponent("otm.android_station.research.can_be_researched").withStyle(ChatFormatting.DARK_GREEN));
} else {
list.add(new TranslatableComponent("otm.android_station.research.can_not_be_researched").withStyle(ChatFormatting.DARK_RED));
}
renderComponentTooltip(stack, list, (int) mouse_x, (int) mouse_y);
}
@ -99,16 +87,19 @@ public class AndroidStationScreen extends MatteryScreen<AndroidStationMenu> {
@Override
protected FramePanel makeMainFrame() {
var frame = new FramePanel(this, null, 0, 0, FRAME_WIDTH, FRAME_HEIGHT, getTitle());
var grid = new GridPanel(this, frame, 0, 0, GRID_WIDTH * 18, 0, GRID_WIDTH, GRID_HEIGHT);
for (var feature : Registry.ANDROID_FEATURES().getValues()) {
var node = feature.getResearchNode();
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 AndroidFeatureButton(grid, node);
if (node != null) {
new AndroidResearchButton(grid, node);
}
}
}
}
});
grid.setDocking(Dock.RIGHT);

View File

@ -44,7 +44,8 @@
"otm.android_station.research.missing_predecessors": "%s needs to be researched first",
"android_feature.overdrive_that_matters.air_bags": "Air bags",
"android_feature.overdrive_that_matters.air_bags.description": "Allows to swim in water",
"android_research.overdrive_that_matters.air_bags": "Air bags",
"android_research.overdrive_that_matters.air_bags.description": "Allows unit to swim in water",
"otm.suffix.merge": "%s %s",