First stab at Living Armour reimplementation.
Added a lot of the upgrades, but there's still more testing/upgrades to be done/reimplemented.
This commit is contained in:
parent
06faa916c3
commit
2075fa5be3
42 changed files with 2352 additions and 2 deletions
|
@ -0,0 +1,182 @@
|
|||
package wayoftime.bloodmagic.core;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.io.Resources;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import net.minecraft.entity.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import wayoftime.bloodmagic.BloodMagic;
|
||||
import wayoftime.bloodmagic.common.registration.impl.LivingUpgradeDeferredRegister;
|
||||
import wayoftime.bloodmagic.common.registration.impl.LivingUpgradeRegistryObject;
|
||||
import wayoftime.bloodmagic.core.living.LivingUpgrade;
|
||||
import wayoftime.bloodmagic.gson.Serializers;
|
||||
|
||||
public class LivingArmorRegistrar
|
||||
{
|
||||
|
||||
public static final LivingUpgradeDeferredRegister UPGRADES = new LivingUpgradeDeferredRegister(BloodMagic.MODID);
|
||||
|
||||
public static final Map<ResourceLocation, LivingUpgrade> UPGRADE_MAP = new HashMap<>();
|
||||
|
||||
// public static final DefaultedRegistry<LivingUpgrade> UPGRADES = (DefaultedRegistry<LivingUpgrade>) createRegistry("livingarmor:upgrades", LivingUpgrade.DUMMY.getKey().toString(), () -> LivingUpgrade.DUMMY);
|
||||
// private static final Map<String, ResourceLocation> DEFINITIONS = ((Supplier<Map<String, ResourceLocation>>) () -> {
|
||||
// Map<String, ResourceLocation> def = new HashMap<>();
|
||||
// def.put("arrow_protect", Paths.get(MinecraftForge.getInstance().getConfigDirectory().getAbsolutePath(), "livingarmor", "arrow_protect.json"));
|
||||
// def.put("arrow_shot", Paths.get(FabricLoader.getInstance().getConfigDirectory().getAbsolutePath(), "livingarmor", "arrow_shot.json"));
|
||||
// def.put("critical_strike", Paths.get(FabricLoader.getInstance().getConfigDirectory().getAbsolutePath(), "livingarmor", "critical_strike.json"));
|
||||
// def.put("jump", Paths.get(FabricLoader.getInstance().getConfigDirectory().getAbsolutePath(), "livingarmor", "jump.json"));
|
||||
// return def;
|
||||
// }).get();
|
||||
private static final Map<String, ResourceLocation> DEFINITIONS = ((Supplier<Map<String, ResourceLocation>>) () -> {
|
||||
Map<String, ResourceLocation> def = new HashMap<>();
|
||||
def.put("arrow_protect", BloodMagic.rl("arrow_protect"));
|
||||
def.put("arrow_shot", BloodMagic.rl("arrow_shot"));
|
||||
def.put("critical_strike", BloodMagic.rl("critical_strike"));
|
||||
def.put("jump", BloodMagic.rl("jump"));
|
||||
def.put("health", BloodMagic.rl("health"));
|
||||
def.put("experience", BloodMagic.rl("experienced"));
|
||||
def.put("sprint_attack", BloodMagic.rl("sprint_attack"));
|
||||
def.put("self_sacrifice", BloodMagic.rl("self_sacrifice"));
|
||||
def.put("speed", BloodMagic.rl("speed"));
|
||||
return def;
|
||||
}).get();
|
||||
// private static final Map<String, Path> DEFINITIONS =
|
||||
// ResourceUtil.gatherResources("/data", "living_armor", p ->
|
||||
// FilenameUtils.getExtension(p.toFile().getName()).equals("json"))
|
||||
// .stream()
|
||||
// .collect(Collectors.toMap(key -> FilenameUtils.getBaseName(key.toFile().getName()), value -> value));
|
||||
private static final Gson GSON = new GsonBuilder().serializeNulls().create();
|
||||
|
||||
// public static final ItemLivingArmor LIVING_HELMET = new ItemLivingArmor(EquipmentSlotType.HEAD);
|
||||
// public static final ItemLivingArmor LIVING_CHESTPLATE = new ItemLivingArmor(EquipmentSlotType.CHEST);
|
||||
// public static final ItemLivingArmor LIVING_LEGGINGS = new ItemLivingArmor(EquipmentSlotType.LEGS);
|
||||
// public static final ItemLivingArmor LIVING_BOOTS = new ItemLivingArmor(EquipmentSlotType.FEET);
|
||||
// public static final ItemLivingTrainer TRAINER = new ItemLivingTrainer();
|
||||
// public static final ItemLivingTome TOME = new ItemLivingTome();
|
||||
|
||||
public static final LivingUpgradeRegistryObject<LivingUpgrade> UPGRADE_ARROW_PROTECT = UPGRADES.register("arrow_protect", () -> parseDefinition("arrow_protect").withArmorProvider((player, stats, source, upgrade, level) -> {
|
||||
if (source.isProjectile())
|
||||
{
|
||||
return upgrade.getBonusValue("protection", level).doubleValue();
|
||||
}
|
||||
return 0;
|
||||
}));
|
||||
|
||||
public static final LivingUpgradeRegistryObject<LivingUpgrade> UPGRADE_HEALTH = UPGRADES.register("health", () -> parseDefinition("health").withAttributeProvider((stats, attributeMap, uuid, upgrade, level) -> {
|
||||
attributeMap.put(Attributes.MAX_HEALTH, new AttributeModifier(uuid, "Health Modifier", upgrade.getBonusValue("hp", level).intValue(), AttributeModifier.Operation.ADDITION));
|
||||
}));
|
||||
|
||||
public static final LivingUpgradeRegistryObject<LivingUpgrade> UPGRADE_EXPERIENCE = UPGRADES.register("experienced", () -> parseDefinition("experienced"));
|
||||
public static final LivingUpgradeRegistryObject<LivingUpgrade> UPGRADE_SPRINT_ATTACK = UPGRADES.register("sprint_attack", () -> parseDefinition("sprint_attack").withDamageProvider((player, weapon, damage, stats, attackedEntity, upgrade, level) -> {
|
||||
if (player.isSprinting())
|
||||
{
|
||||
return damage * upgrade.getBonusValue("damage_boost", level).doubleValue();
|
||||
}
|
||||
return 0;
|
||||
}));
|
||||
public static final LivingUpgradeRegistryObject<LivingUpgrade> UPGRADE_SELF_SACRIFICE = UPGRADES.register("self_sacrifice", () -> parseDefinition("self_sacrifice"));
|
||||
public static final LivingUpgradeRegistryObject<LivingUpgrade> UPGRADE_SPEED = UPGRADES.register("speed", () -> parseDefinition("speed"));
|
||||
|
||||
// public static final LivingUpgrade UPGRADE_ARROW_PROTECT = parseDefinition("arrow_protect").withArmorProvider((player, stats, source, upgrade, level) -> {
|
||||
// if (source.isProjectile())
|
||||
// {
|
||||
// return upgrade.getBonusValue("protection", level).doubleValue();
|
||||
// }
|
||||
// return 0;
|
||||
// });
|
||||
// public static final LivingUpgrade UPGRADE_ARROW_SHOT = parseDefinition("arrow_shot");
|
||||
// public static final LivingUpgrade UPGRADE_CRITICAL_STRIKE = parseDefinition("critical_strike").withAttributeProvider((stats, attributeMap, uuid, upgrade, level) -> {
|
||||
// attributeMap.put(Attributes.ATTACK_DAMAGE, new AttributeModifier(uuid, "Weapon modifier", upgrade.getBonusValue("damage_boost", level).doubleValue(), AttributeModifier.Operation.ADDITION));
|
||||
//// attributeMap.put(EntityAttributes.ATTACK_DAMAGE.getId(), AttributeModifiers.create(upgrade, "damage_boost", upgrade.getBonusValue("damage_boost", level).doubleValue(), EntityAttributeModifier.Operation.ADDITION));
|
||||
//// attributeMap.put(EntityAttributes.ATTACK_DAMAGE.getId(), AttributeModifiers.create(upgrade, "damage_boost", level, EntityAttributeModifier.Operation.ADDITION));
|
||||
// });
|
||||
// public static final LivingUpgrade UPGRADE_JUMP = parseDefinition("jump");
|
||||
|
||||
public static void register()
|
||||
{
|
||||
registerUpgrade(UPGRADE_ARROW_PROTECT.get());
|
||||
registerUpgrade(UPGRADE_HEALTH.get());
|
||||
registerUpgrade(UPGRADE_EXPERIENCE.get());
|
||||
registerUpgrade(UPGRADE_SPRINT_ATTACK.get());
|
||||
registerUpgrade(UPGRADE_SELF_SACRIFICE.get());
|
||||
registerUpgrade(UPGRADE_SPEED.get());
|
||||
// Registry.register(UPGRADES, UPGRADE_ARROW_PROTECT.getKey(), UPGRADE_ARROW_PROTECT);
|
||||
// Registry.register(UPGRADES, UPGRADE_ARROW_SHOT.getKey(), UPGRADE_ARROW_SHOT);
|
||||
// Registry.register(UPGRADES, UPGRADE_CRITICAL_STRIKE.getKey(), UPGRADE_CRITICAL_STRIKE);
|
||||
// Registry.register(UPGRADES, UPGRADE_JUMP.getKey(), UPGRADE_JUMP);
|
||||
|
||||
// Registry.register(Registry.ITEM, new ResourceLocation("livingarmor", "living_helmet"), LIVING_HELMET);
|
||||
// Registry.register(Registry.ITEM, new Identifier("livingarmor", "living_chestplate"), LIVING_CHESTPLATE);
|
||||
// Registry.register(Registry.ITEM, new Identifier("livingarmor", "living_leggings"), LIVING_LEGGINGS);
|
||||
// Registry.register(Registry.ITEM, new Identifier("livingarmor", "living_boots"), LIVING_BOOTS);
|
||||
// Registry.register(Registry.ITEM, new Identifier("livingarmor", "trainer"), TRAINER);
|
||||
// Registry.register(Registry.ITEM, new Identifier("livingarmor", "tome"), TOME);
|
||||
}
|
||||
|
||||
public static void registerUpgrade(LivingUpgrade upgrade)
|
||||
{
|
||||
UPGRADE_MAP.put(upgrade.getKey(), upgrade);
|
||||
}
|
||||
|
||||
public static LivingUpgrade parseDefinition(String fileName)
|
||||
{
|
||||
ResourceLocation path = DEFINITIONS.get(fileName);
|
||||
if (path == null)
|
||||
return LivingUpgrade.DUMMY;
|
||||
|
||||
try
|
||||
{
|
||||
URL schematicURL = LivingUpgrade.class.getResource(resLocToResourcePath(path));
|
||||
System.out.println("Attempting to load Living Armour Upgrade: " + schematicURL + ", path: " + resLocToResourcePath(path));
|
||||
return Serializers.GSON.fromJson(Resources.toString(schematicURL, Charsets.UTF_8), LivingUpgrade.class);
|
||||
// return GSON.fromJson(IOUtils.toString(path.toUri(), StandardCharsets.UTF_8), LivingUpgrade.class);
|
||||
} catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
return LivingUpgrade.DUMMY;
|
||||
}
|
||||
// Path path = DEFINITIONS.get(fileName);
|
||||
// if (path == null)
|
||||
// return LivingUpgrade.DUMMY;
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// return GSON.fromJson(IOUtils.toString(path.toUri(), StandardCharsets.UTF_8), LivingUpgrade.class);
|
||||
// } catch (Exception e)
|
||||
// {
|
||||
// e.printStackTrace();
|
||||
// return LivingUpgrade.DUMMY;
|
||||
// }
|
||||
}
|
||||
|
||||
public static String resLocToResourcePath(ResourceLocation resourceLocation)
|
||||
{
|
||||
return "/data/" + resourceLocation.getNamespace() + "/living_armor/" + resourceLocation.getPath() + ".json";
|
||||
}
|
||||
|
||||
// private static <T> Registry<T> createRegistry(String registryId, String defaultId, Supplier<T> defaultProvider)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// Method _createRegistry = Registry.class.getDeclaredMethod("create", String.class, String.class, Supplier.class); // FIXME
|
||||
// // yarn
|
||||
// // name
|
||||
// _createRegistry.setAccessible(true);
|
||||
// return (Registry<T>) _createRegistry.invoke(null, registryId, defaultId, defaultProvider);
|
||||
// } catch (Exception e)
|
||||
// {
|
||||
// e.printStackTrace();
|
||||
// MutableRegistry<T> registry = new DefaultedRegistry(defaultId, null, null);
|
||||
// registry.add(new ResourceLocation(defaultId), defaultProvider.get());
|
||||
// return registry;
|
||||
// }
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package wayoftime.bloodmagic.core.living;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
import net.minecraft.util.text.TranslationTextComponent;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
|
||||
public interface ILivingContainer
|
||||
{
|
||||
default LivingStats getLivingStats(ItemStack stack)
|
||||
{
|
||||
if (!stack.hasTag() || !stack.getTag().contains("livingStats"))
|
||||
return null;
|
||||
|
||||
return LivingStats.fromNBT(stack.getTag().getCompound("livingStats"));
|
||||
}
|
||||
|
||||
default void updateLivingStats(ItemStack stack, LivingStats stats)
|
||||
{
|
||||
if (stats == null)
|
||||
{
|
||||
if (stack.hasTag())
|
||||
stack.getTag().remove("livingStats");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!stack.hasTag())
|
||||
stack.setTag(new CompoundNBT());
|
||||
|
||||
stack.getTag().put("livingStats", stats.serialize());
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
static void appendLivingTooltip(LivingStats stats, List<ITextComponent> tooltip, boolean trainable)
|
||||
{
|
||||
if (stats != null)
|
||||
{
|
||||
if (trainable)
|
||||
tooltip.add(new TranslationTextComponent("tooltip.bloodmagic.livingarmour.upgrade.points", stats.getUsedPoints(), stats.getMaxPoints()).mergeStyle(TextFormatting.GOLD));
|
||||
|
||||
stats.getUpgrades().forEach((k, v) -> {
|
||||
if (k.getLevel(v.intValue()) <= 0)
|
||||
return;
|
||||
|
||||
boolean sneaking = Screen.hasShiftDown();
|
||||
// if (!InputUtil.isKeyPressed(MinecraftClient.getInstance().getWindow().getHandle(), 340) || k.getNextRequirement(v) == 0)
|
||||
if (!sneaking || k.getNextRequirement(v.intValue()) == 0)
|
||||
tooltip.add(new TranslationTextComponent("%s %s", new TranslationTextComponent(k.getTranslationKey()), new TranslationTextComponent("enchantment.level." + k.getLevel(v.intValue()))));
|
||||
else
|
||||
tooltip.add(new TranslationTextComponent("%s %s", new TranslationTextComponent(k.getTranslationKey()), (": " + v.intValue() + "/" + k.getNextRequirement(v.intValue()))));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
150
src/main/java/wayoftime/bloodmagic/core/living/LivingStats.java
Normal file
150
src/main/java/wayoftime/bloodmagic/core/living/LivingStats.java
Normal file
|
@ -0,0 +1,150 @@
|
|||
package wayoftime.bloodmagic.core.living;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.inventory.EquipmentSlotType;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.nbt.ListNBT;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import wayoftime.bloodmagic.core.LivingArmorRegistrar;
|
||||
|
||||
public class LivingStats
|
||||
{
|
||||
|
||||
public static final int DEFAULT_UPGRADE_POINTS = 100;
|
||||
|
||||
private final Map<LivingUpgrade, Double> upgrades;
|
||||
private int maxPoints = DEFAULT_UPGRADE_POINTS;
|
||||
|
||||
public LivingStats(Map<LivingUpgrade, Double> upgrades)
|
||||
{
|
||||
this.upgrades = upgrades;
|
||||
}
|
||||
|
||||
public LivingStats()
|
||||
{
|
||||
this(Maps.newHashMap());
|
||||
}
|
||||
|
||||
public Map<LivingUpgrade, Double> getUpgrades()
|
||||
{
|
||||
return ImmutableMap.copyOf(upgrades);
|
||||
}
|
||||
|
||||
public LivingStats addExperience(ResourceLocation key, double experience)
|
||||
{
|
||||
// LivingUpgrade upgrade = LivingArmorRegistrar.UPGRADES.getOrDefault(key);
|
||||
LivingUpgrade upgrade = LivingArmorRegistrar.UPGRADE_MAP.getOrDefault(key, LivingUpgrade.DUMMY);
|
||||
double current = upgrades.getOrDefault(upgrade, 0d);
|
||||
|
||||
System.out.println("Upgrade: " + upgrade);
|
||||
|
||||
if (upgrade.getNextRequirement((int) current) == 0)
|
||||
return this;
|
||||
|
||||
upgrades.put(upgrade, current + experience);
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getLevel(ResourceLocation key)
|
||||
{
|
||||
LivingUpgrade upgrade = LivingArmorRegistrar.UPGRADE_MAP.getOrDefault(key, LivingUpgrade.DUMMY);
|
||||
// LivingUpgrade upgrade = LivingArmorRegistrar.UPGRADES.getOrDefault(key);
|
||||
return upgrade.getLevel(upgrades.getOrDefault(upgrade, 0d).intValue());
|
||||
}
|
||||
|
||||
public int getUsedPoints()
|
||||
{
|
||||
int total = 0;
|
||||
for (Map.Entry<LivingUpgrade, Double> applied : upgrades.entrySet())
|
||||
{
|
||||
double experience = applied.getValue();
|
||||
int level = applied.getKey().getLevel((int) experience);
|
||||
int cost = applied.getKey().getLevelCost(level);
|
||||
total += cost;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
public int getMaxPoints()
|
||||
{
|
||||
return maxPoints;
|
||||
}
|
||||
|
||||
public LivingStats setMaxPoints(int maxPoints)
|
||||
{
|
||||
this.maxPoints = maxPoints;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CompoundNBT serialize()
|
||||
{
|
||||
CompoundNBT compound = new CompoundNBT();
|
||||
ListNBT statList = new ListNBT();
|
||||
upgrades.forEach((k, v) -> {
|
||||
CompoundNBT upgrade = new CompoundNBT();
|
||||
upgrade.putString("key", k.getKey().toString());
|
||||
upgrade.putDouble("exp", v);
|
||||
statList.add(upgrade);
|
||||
});
|
||||
compound.put("upgrades", statList);
|
||||
|
||||
compound.putInt("maxPoints", maxPoints);
|
||||
|
||||
return compound;
|
||||
}
|
||||
|
||||
public void deserialize(CompoundNBT nbt)
|
||||
{
|
||||
ListNBT statList = nbt.getList("upgrades", 10);
|
||||
statList.forEach(tag -> {
|
||||
if (!(tag instanceof CompoundNBT))
|
||||
return;
|
||||
|
||||
LivingUpgrade upgrade = LivingArmorRegistrar.UPGRADE_MAP.getOrDefault(new ResourceLocation(((CompoundNBT) tag).getString("key")), LivingUpgrade.DUMMY);
|
||||
if (upgrade == LivingUpgrade.DUMMY)
|
||||
return;
|
||||
double experience = ((CompoundNBT) tag).getDouble("exp");
|
||||
upgrades.put(upgrade, experience);
|
||||
});
|
||||
|
||||
maxPoints = nbt.getInt("maxPoints");
|
||||
}
|
||||
|
||||
public static LivingStats fromNBT(CompoundNBT statTag)
|
||||
{
|
||||
LivingStats stats = new LivingStats();
|
||||
stats.deserialize(statTag);
|
||||
return stats;
|
||||
}
|
||||
|
||||
public static LivingStats fromPlayer(PlayerEntity player)
|
||||
{
|
||||
return fromPlayer(player, false);
|
||||
}
|
||||
|
||||
public static LivingStats fromPlayer(PlayerEntity player, boolean createNew)
|
||||
{
|
||||
if (!LivingUtil.hasFullSet(player))
|
||||
return null;
|
||||
|
||||
ItemStack chest = player.getItemStackFromSlot(EquipmentSlotType.CHEST);
|
||||
LivingStats stats = ((ILivingContainer) chest.getItem()).getLivingStats(chest);
|
||||
return stats == null && createNew ? new LivingStats() : stats;
|
||||
}
|
||||
|
||||
public static void toPlayer(PlayerEntity player, LivingStats stats)
|
||||
{
|
||||
if (!LivingUtil.hasFullSet(player))
|
||||
return;
|
||||
|
||||
ItemStack chest = player.getItemStackFromSlot(EquipmentSlotType.CHEST);
|
||||
((ILivingContainer) chest.getItem()).updateLivingStats(chest, stats);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,267 @@
|
|||
package wayoftime.bloodmagic.core.living;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.ai.attributes.Attribute;
|
||||
import net.minecraft.entity.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraftforge.registries.ForgeRegistryEntry;
|
||||
|
||||
@JsonAdapter(LivingUpgrade.Deserializer.class)
|
||||
public class LivingUpgrade extends ForgeRegistryEntry<LivingUpgrade>
|
||||
{
|
||||
public static final LivingUpgrade DUMMY = new LivingUpgrade(new ResourceLocation("dummy"), levels -> levels.add(new Level(0, 0)));
|
||||
|
||||
private final ResourceLocation key;
|
||||
private final Set<ResourceLocation> incompatible;
|
||||
private final TreeMap<Integer, Integer> experienceToLevel;
|
||||
private final Map<Integer, Integer> levelToCost;
|
||||
private final Map<String, Bonus> bonuses;
|
||||
private boolean isNegative;
|
||||
private String translationKey = null;
|
||||
private IAttributeProvider attributeProvider;
|
||||
private IArmorProvider armorProvider;
|
||||
private IDamageProvider damageProvider;
|
||||
|
||||
public LivingUpgrade(ResourceLocation key, Consumer<List<Level>> experienceMapper)
|
||||
{
|
||||
this.key = key;
|
||||
this.incompatible = Sets.newHashSet();
|
||||
this.experienceToLevel = Maps.newTreeMap();
|
||||
this.levelToCost = Maps.newHashMap();
|
||||
this.bonuses = Maps.newHashMap();
|
||||
|
||||
List<Level> levels = Lists.newArrayList();
|
||||
experienceMapper.accept(levels);
|
||||
|
||||
for (int i = 0; i < levels.size(); i++)
|
||||
{
|
||||
Level level = levels.get(i);
|
||||
experienceToLevel.put(level.experienceNeeded, i + 1);
|
||||
levelToCost.put(i + 1, level.upgradeCost);
|
||||
}
|
||||
}
|
||||
|
||||
public LivingUpgrade withBonusSet(String id, Consumer<List<Number>> modifiers)
|
||||
{
|
||||
// List<Number> values = DefaultedList.of();
|
||||
List<Number> values = new ArrayList<Number>();
|
||||
modifiers.accept(values);
|
||||
if (values.size() != levelToCost.size())
|
||||
throw new RuntimeException("Bonus size and level size must be the same.");
|
||||
|
||||
bonuses.put(id, new Bonus(id, values));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Number getBonusValue(String id, int level)
|
||||
{
|
||||
List<Number> modifiers = bonuses.getOrDefault(id, Bonus.DEFAULT).modifiers;
|
||||
if (modifiers.isEmpty() || level == 0)
|
||||
return 0;
|
||||
|
||||
return modifiers.get(level - 1);
|
||||
}
|
||||
|
||||
public LivingUpgrade withAttributeProvider(IAttributeProvider attributeProvider)
|
||||
{
|
||||
this.attributeProvider = attributeProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IAttributeProvider getAttributeProvider()
|
||||
{
|
||||
return attributeProvider;
|
||||
}
|
||||
|
||||
public LivingUpgrade withArmorProvider(IArmorProvider armorProvider)
|
||||
{
|
||||
this.armorProvider = armorProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IArmorProvider getArmorProvider()
|
||||
{
|
||||
return armorProvider;
|
||||
}
|
||||
|
||||
public LivingUpgrade withDamageProvider(IDamageProvider damageProvider)
|
||||
{
|
||||
this.damageProvider = damageProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IDamageProvider getDamageProvider()
|
||||
{
|
||||
return damageProvider;
|
||||
}
|
||||
|
||||
public String getTranslationKey()
|
||||
{
|
||||
return translationKey == null ? translationKey = Util.makeTranslationKey("living_upgrade", key)
|
||||
: translationKey;
|
||||
}
|
||||
|
||||
public boolean isNegative()
|
||||
{
|
||||
return isNegative;
|
||||
}
|
||||
|
||||
public boolean isCompatible(ResourceLocation otherUpgrade)
|
||||
{
|
||||
return !incompatible.contains(otherUpgrade);
|
||||
}
|
||||
|
||||
public LivingUpgrade addIncompatibility(ResourceLocation key, ResourceLocation... otherKeys)
|
||||
{
|
||||
incompatible.add(key);
|
||||
Collections.addAll(incompatible, otherKeys);
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getLevel(int experience)
|
||||
{
|
||||
Map.Entry<Integer, Integer> floor = experienceToLevel.floorEntry(experience);
|
||||
return floor == null ? 0 : floor.getValue();
|
||||
}
|
||||
|
||||
public int getNextRequirement(int experience)
|
||||
{
|
||||
Integer ret = experienceToLevel.ceilingKey(experience + 1);
|
||||
return ret == null ? 0 : ret;
|
||||
}
|
||||
|
||||
public int getLevelCost(int level)
|
||||
{
|
||||
return levelToCost.getOrDefault(level, 0);
|
||||
}
|
||||
|
||||
public ResourceLocation getKey()
|
||||
{
|
||||
return key;
|
||||
}
|
||||
|
||||
public LivingUpgrade asDowngrade()
|
||||
{
|
||||
this.isNegative = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return key.toString();
|
||||
}
|
||||
|
||||
public interface IAttributeProvider
|
||||
{
|
||||
void handleAttributes(LivingStats stats, Multimap<Attribute, AttributeModifier> modifiers, UUID uuid, LivingUpgrade upgrade, int level);
|
||||
}
|
||||
|
||||
public interface IArmorProvider
|
||||
{
|
||||
double getProtection(PlayerEntity player, LivingStats stats, DamageSource source, LivingUpgrade upgrade, int level);
|
||||
}
|
||||
|
||||
public interface IDamageProvider
|
||||
{
|
||||
double getAdditionalDamage(PlayerEntity player, ItemStack weapon, double damage, LivingStats stats, LivingEntity attacked, LivingUpgrade upgrade, int level);
|
||||
}
|
||||
|
||||
public static class Level
|
||||
{
|
||||
@SerializedName("xp")
|
||||
private final int experienceNeeded;
|
||||
@SerializedName("cost")
|
||||
private final int upgradeCost;
|
||||
|
||||
public Level(int experienceNeeded, int upgradeCost)
|
||||
{
|
||||
this.experienceNeeded = experienceNeeded;
|
||||
this.upgradeCost = upgradeCost;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Bonus
|
||||
{
|
||||
|
||||
private static final Bonus DEFAULT = new Bonus("null", Collections.emptyList());
|
||||
|
||||
private final String id;
|
||||
private final List<Number> modifiers;
|
||||
|
||||
public Bonus(String id, List<Number> modifiers)
|
||||
{
|
||||
this.id = id;
|
||||
this.modifiers = modifiers;
|
||||
}
|
||||
|
||||
public String getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Deserializer implements JsonDeserializer<LivingUpgrade>
|
||||
{
|
||||
@Override
|
||||
public LivingUpgrade deserialize(JsonElement element, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException
|
||||
{
|
||||
JsonObject json = element.getAsJsonObject();
|
||||
ResourceLocation id = new ResourceLocation(json.getAsJsonPrimitive("id").getAsString());
|
||||
List<Level> levels = context.deserialize(json.getAsJsonArray("levels"), new TypeToken<List<Level>>()
|
||||
{
|
||||
}.getType());
|
||||
boolean negative = json.has("negative") && json.getAsJsonPrimitive("negative").getAsBoolean();
|
||||
|
||||
LivingUpgrade upgrade = new LivingUpgrade(id, upgradeLevels -> upgradeLevels.addAll(levels));
|
||||
if (negative)
|
||||
upgrade.asDowngrade();
|
||||
|
||||
if (json.has("incompatibilities"))
|
||||
{
|
||||
String[] incompatibilities = context.deserialize(json.getAsJsonArray("incompatibilities"), String[].class);
|
||||
for (String incompatible : incompatibilities)
|
||||
upgrade.addIncompatibility(new ResourceLocation(incompatible));
|
||||
}
|
||||
|
||||
if (json.has("bonuses"))
|
||||
{
|
||||
Map<String, Number[]> bonuses = context.deserialize(json.getAsJsonObject("bonuses"), new TypeToken<Map<String, Number[]>>()
|
||||
{
|
||||
}.getType());
|
||||
bonuses.forEach((k, v) -> upgrade.withBonusSet(k, numbers -> Collections.addAll(numbers, v)));
|
||||
}
|
||||
|
||||
return upgrade;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
176
src/main/java/wayoftime/bloodmagic/core/living/LivingUtil.java
Normal file
176
src/main/java/wayoftime/bloodmagic/core/living/LivingUtil.java
Normal file
|
@ -0,0 +1,176 @@
|
|||
package wayoftime.bloodmagic.core.living;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.ai.attributes.Attribute;
|
||||
import net.minecraft.entity.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.inventory.EquipmentSlotType;
|
||||
import net.minecraft.item.ArmorItem;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.text.TranslationTextComponent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import wayoftime.bloodmagic.common.item.ItemLivingTrainer;
|
||||
import wayoftime.bloodmagic.core.util.PlayerUtil;
|
||||
import wayoftime.bloodmagic.event.LivingEquipmentEvent;
|
||||
|
||||
public class LivingUtil
|
||||
{
|
||||
public static LivingStats applyNewExperience(PlayerEntity player, LivingUpgrade upgrade, double experience)
|
||||
{
|
||||
LivingStats stats = LivingStats.fromPlayer(player, true);
|
||||
if (stats == null)
|
||||
return null;
|
||||
|
||||
if (!canTrain(player, upgrade, upgrade.getLevel((int) experience)))
|
||||
return stats;
|
||||
|
||||
LivingEquipmentEvent.GainExperience event = new LivingEquipmentEvent.GainExperience(player, stats, upgrade, experience);
|
||||
// EventResult result = LivingEquipmentEvent.EXPERIENCE_GAIN.invoker().gainExperience(event);
|
||||
MinecraftForge.EVENT_BUS.post(event);
|
||||
if (event.isCanceled())
|
||||
return stats;
|
||||
|
||||
experience = event.getExperience();
|
||||
|
||||
double currentExperience = stats.getUpgrades().getOrDefault(upgrade, 0d);
|
||||
double requiredForLevel = upgrade.getNextRequirement((int) currentExperience) - currentExperience;
|
||||
|
||||
// If we're going to level up from this, check points
|
||||
if (requiredForLevel <= experience)
|
||||
{
|
||||
int currentPoints = stats.getUsedPoints();
|
||||
// If we're already capped or somehow over the cap, we don't want to add
|
||||
// experience
|
||||
if (currentPoints >= stats.getMaxPoints())
|
||||
return stats;
|
||||
|
||||
int nextPointCost = upgrade.getLevelCost(upgrade.getLevel((int) currentExperience) + 1);
|
||||
// If there's no more levels in this upgrade, we don't want to add experience
|
||||
if (nextPointCost == -1)
|
||||
return stats;
|
||||
|
||||
// If applying this new level will go over our cap, we don't want to add
|
||||
// experience
|
||||
if (currentPoints + nextPointCost > stats.getMaxPoints())
|
||||
return stats;
|
||||
}
|
||||
|
||||
int newLevel = upgrade.getLevel((int) (currentExperience + experience));
|
||||
if (upgrade.getLevel((int) currentExperience) != newLevel)
|
||||
{
|
||||
LivingEquipmentEvent.LevelUp levelUpEvent = new LivingEquipmentEvent.LevelUp(player, stats, upgrade);
|
||||
// LivingEquipmentEvent.LEVEL_UP.invoker().levelUp(levelUpEvent);
|
||||
MinecraftForge.EVENT_BUS.post(levelUpEvent);
|
||||
|
||||
player.sendStatusMessage(new TranslationTextComponent("chat.bloodmagic.living_upgrade_level_increase", new TranslationTextComponent(upgrade.getTranslationKey()), newLevel), true);
|
||||
}
|
||||
|
||||
System.out.println("Adding experience!");
|
||||
|
||||
stats.addExperience(upgrade.getKey(), experience);
|
||||
LivingStats.toPlayer(player, stats);
|
||||
return stats;
|
||||
}
|
||||
|
||||
public static double getDamageReceivedForArmour(PlayerEntity player, DamageSource source, double damage)
|
||||
{
|
||||
// System.out.println("Initial damage from " + source + ": " + damage);
|
||||
LivingStats stats = LivingStats.fromPlayer(player, true);
|
||||
if (stats == null)
|
||||
return damage;
|
||||
|
||||
Map<LivingUpgrade, Double> upgrades = stats.getUpgrades();
|
||||
for (Entry<LivingUpgrade, Double> entry : upgrades.entrySet())
|
||||
{
|
||||
LivingUpgrade upgrade = entry.getKey();
|
||||
if (upgrade.getArmorProvider() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int level = upgrade.getLevel(entry.getValue().intValue());
|
||||
damage *= 1 - upgrade.getArmorProvider().getProtection(player, stats, source, upgrade, level);
|
||||
}
|
||||
|
||||
// System.out.println("Final damage: " + damage);
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
public static double getAdditionalDamage(PlayerEntity player, ItemStack weapon, LivingEntity attackedEntity, double damage)
|
||||
{
|
||||
// System.out.println("Initial damage from " + source + ": " + damage);
|
||||
LivingStats stats = LivingStats.fromPlayer(player, true);
|
||||
if (stats == null)
|
||||
return 0;
|
||||
|
||||
double additionalDamage = 0;
|
||||
|
||||
Map<LivingUpgrade, Double> upgrades = stats.getUpgrades();
|
||||
for (Entry<LivingUpgrade, Double> entry : upgrades.entrySet())
|
||||
{
|
||||
LivingUpgrade upgrade = entry.getKey();
|
||||
if (upgrade.getArmorProvider() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int level = upgrade.getLevel(entry.getValue().intValue());
|
||||
if (upgrade.getDamageProvider() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
additionalDamage += upgrade.getDamageProvider().getAdditionalDamage(player, weapon, damage, stats, attackedEntity, upgrade, level);
|
||||
}
|
||||
|
||||
// System.out.println("Final damage: " + damage);
|
||||
|
||||
return additionalDamage;
|
||||
}
|
||||
|
||||
public static boolean canTrain(PlayerEntity player, LivingUpgrade upgrade, int currentLevel)
|
||||
{
|
||||
ItemStack trainer = PlayerUtil.findItem(player, stack -> stack.getItem() instanceof ItemLivingTrainer && stack.hasTag() && stack.getTag().contains("livingStats"));
|
||||
if (trainer.isEmpty())
|
||||
return true;
|
||||
|
||||
String mode = trainer.getTag().getString("livingLock");
|
||||
LivingStats stats = ((ILivingContainer) trainer.getItem()).getLivingStats(trainer);
|
||||
|
||||
int levelLimit = stats.getLevel(upgrade.getKey());
|
||||
if (mode.equalsIgnoreCase("whitelist"))
|
||||
{
|
||||
return levelLimit != 0 && levelLimit > currentLevel;
|
||||
} else if (mode.equalsIgnoreCase("blacklist"))
|
||||
{
|
||||
return levelLimit == 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean hasFullSet(PlayerEntity player)
|
||||
{
|
||||
for (ItemStack stack : player.inventory.armorInventory)
|
||||
if (stack.isEmpty() || !(stack.getItem() instanceof ILivingContainer))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void applyAttributes(Multimap<Attribute, AttributeModifier> attributes, ItemStack stack, PlayerEntity player, EquipmentSlotType slot)
|
||||
{
|
||||
if (player == null || !hasFullSet(player))
|
||||
return;
|
||||
|
||||
Multimap<Attribute, AttributeModifier> newAttributes = ((ArmorItem) stack.getItem()).getAttributeModifiers(slot, stack);
|
||||
// newAttributes.values().forEach(e -> e.setSerialize(false));
|
||||
attributes.putAll(newAttributes);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package wayoftime.bloodmagic.core.living;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.util.DamageSource;
|
||||
|
||||
public class ProjectileArmorProvider implements LivingUpgrade.IArmorProvider
|
||||
{
|
||||
@Override
|
||||
public double getProtection(PlayerEntity player, LivingStats stats, DamageSource source, LivingUpgrade upgrade, int level)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
57
src/main/java/wayoftime/bloodmagic/core/util/PlayerUtil.java
Normal file
57
src/main/java/wayoftime/bloodmagic/core/util/PlayerUtil.java
Normal file
|
@ -0,0 +1,57 @@
|
|||
package wayoftime.bloodmagic.core.util;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import net.minecraft.entity.ai.attributes.Attribute;
|
||||
import net.minecraft.entity.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.inventory.EquipmentSlotType;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import wayoftime.bloodmagic.common.item.ExpandedArmor;
|
||||
import wayoftime.bloodmagic.core.living.LivingUtil;
|
||||
|
||||
public class PlayerUtil
|
||||
{
|
||||
|
||||
public static ItemStack findItem(PlayerEntity player, Predicate<ItemStack> requirements)
|
||||
{
|
||||
|
||||
// Check offhand first
|
||||
ItemStack offHand = player.getHeldItemOffhand();
|
||||
if (requirements.test(offHand))
|
||||
return offHand;
|
||||
|
||||
// Check inventory next
|
||||
for (int slot = 0; slot < player.inventory.getSizeInventory(); slot++)
|
||||
{
|
||||
ItemStack foundStack = player.inventory.getStackInSlot(slot);
|
||||
if (!foundStack.isEmpty() && requirements.test(foundStack))
|
||||
return foundStack;
|
||||
}
|
||||
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
public static Multimap<Attribute, AttributeModifier> handle(PlayerEntity player, Multimap<Attribute, AttributeModifier> existing)
|
||||
{
|
||||
|
||||
ItemStack chest = player.getItemStackFromSlot(EquipmentSlotType.CHEST);
|
||||
boolean hasFullSet = LivingUtil.hasFullSet(player);
|
||||
|
||||
if (hasFullSet && existing == null)
|
||||
{
|
||||
existing = ((ExpandedArmor) chest.getItem()).getAttributeModifiers(EquipmentSlotType.CHEST, chest);
|
||||
player.getAttributeManager().reapplyModifiers(existing);
|
||||
}
|
||||
|
||||
if (!hasFullSet && existing != null)
|
||||
{
|
||||
player.getAttributeManager().removeModifiers(existing);
|
||||
existing = null;
|
||||
}
|
||||
|
||||
return existing;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package wayoftime.bloodmagic.core.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystemAlreadyExistsException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import wayoftime.bloodmagic.BloodMagic;
|
||||
|
||||
public class ResourceUtil
|
||||
{
|
||||
public static Set<Path> gatherResources(String home, String following, Predicate<Path> predicate)
|
||||
{
|
||||
FileSystem fileSystem = null;
|
||||
try
|
||||
{
|
||||
URL url = ResourceUtil.class.getResource(home);
|
||||
if (url != null)
|
||||
{
|
||||
URI uri = url.toURI();
|
||||
Path path;
|
||||
if (uri.getScheme().equals("file"))
|
||||
{
|
||||
path = Paths.get(ResourceUtil.class.getResource(home + "/" + following).toURI());
|
||||
} else
|
||||
{
|
||||
if (!uri.getScheme().equals("jar"))
|
||||
{
|
||||
BloodMagic.LOGGER.error("Unsupported URI scheme {}", uri.getScheme());
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap());
|
||||
} catch (FileSystemAlreadyExistsException e)
|
||||
{
|
||||
fileSystem = FileSystems.getFileSystem(uri);
|
||||
}
|
||||
path = fileSystem.getPath(home + "/" + following);
|
||||
}
|
||||
|
||||
return Files.walk(path).filter(predicate).collect(Collectors.toSet());
|
||||
}
|
||||
} catch (IOException | URISyntaxException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
} finally
|
||||
{
|
||||
IOUtils.closeQuietly(fileSystem);
|
||||
}
|
||||
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
public static Set<Path> gatherResources(String home, String following)
|
||||
{
|
||||
return gatherResources(home, following, p -> true);
|
||||
}
|
||||
|
||||
public static ResourceLocation addContext(ResourceLocation rl, String context)
|
||||
{
|
||||
return new ResourceLocation(rl.getNamespace(), context + rl.getPath());
|
||||
}
|
||||
}
|
27
src/main/java/wayoftime/bloodmagic/core/util/Value.java
Normal file
27
src/main/java/wayoftime/bloodmagic/core/util/Value.java
Normal file
|
@ -0,0 +1,27 @@
|
|||
package wayoftime.bloodmagic.core.util;
|
||||
|
||||
public final class Value<T>
|
||||
{
|
||||
private T value;
|
||||
|
||||
private Value(T t)
|
||||
{
|
||||
this.value = t;
|
||||
}
|
||||
|
||||
public T get()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
public Value<T> set(T t)
|
||||
{
|
||||
this.value = t;
|
||||
return this;
|
||||
}
|
||||
|
||||
public static <T> Value<T> of(T t)
|
||||
{
|
||||
return new Value<>(t);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue