JSON-based living upgrade definitions
Since they're just data, it's okay for them to be data driven. Also makes it easier for outside sources (ie: a wiki) to parse the data to keep it up to date.
This commit is contained in:
parent
2ca458aaea
commit
59142c2a9c
8 changed files with 258 additions and 21 deletions
|
@ -1,22 +1,31 @@
|
||||||
package com.wayoftime.bloodmagic.core;
|
package com.wayoftime.bloodmagic.core;
|
||||||
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
import com.wayoftime.bloodmagic.BloodMagic;
|
import com.wayoftime.bloodmagic.BloodMagic;
|
||||||
import com.wayoftime.bloodmagic.core.living.LivingUpgrade;
|
import com.wayoftime.bloodmagic.core.living.LivingUpgrade;
|
||||||
|
import com.wayoftime.bloodmagic.core.util.ResourceUtil;
|
||||||
import net.minecraft.item.Item;
|
import net.minecraft.item.Item;
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
import net.minecraftforge.event.RegistryEvent;
|
import net.minecraftforge.event.RegistryEvent;
|
||||||
import net.minecraftforge.fml.common.Mod;
|
import net.minecraftforge.fml.common.Mod;
|
||||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO - See checklist
|
* TODO - See checklist
|
||||||
* - [-] Upgrades (Names pulled from 2.0 class names)
|
* - [-] Upgrades (Names pulled from 2.0 class names)
|
||||||
* - [ ] Arrow Protect
|
* - [-] Arrow Protect
|
||||||
* - [ ] Arrow Shot
|
* - [-] Arrow Shot
|
||||||
* - [ ] Critical Strike
|
* - [-] Critical Strike
|
||||||
* - [ ] Digging
|
* - [ ] Digging
|
||||||
* - [ ] Elytra
|
* - [ ] Elytra
|
||||||
* - This will wait for Forge to add the ability to make them properly. I'm not adding that hacky shit back in.
|
* - This will wait for Forge to add the ability to make them properly. I'm not adding that hacky shit back in.
|
||||||
|
@ -63,20 +72,37 @@ import java.util.Map;
|
||||||
@Mod.EventBusSubscriber(modid = BloodMagic.MODID)
|
@Mod.EventBusSubscriber(modid = BloodMagic.MODID)
|
||||||
public class RegistrarBloodMagicLivingArmor {
|
public class RegistrarBloodMagicLivingArmor {
|
||||||
|
|
||||||
|
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 Map<ResourceLocation, LivingUpgrade> UPGRADES = Maps.newHashMap();
|
public static final Map<ResourceLocation, LivingUpgrade> UPGRADES = Maps.newHashMap();
|
||||||
public static final LivingUpgrade JUMP = new LivingUpgrade(new ResourceLocation(BloodMagic.MODID, "jump"), levels -> {
|
public static final LivingUpgrade UPGRADE_ARROW_PROTECT = parseDefinition("arrow_protect");
|
||||||
levels.add(new LivingUpgrade.UpgradeLevel(10, 1));
|
public static final LivingUpgrade UPGRADE_ARROW_SHOT = parseDefinition("arrow_shot");
|
||||||
levels.add(new LivingUpgrade.UpgradeLevel(20, 5));
|
public static final LivingUpgrade UPGRADE_CRITICAL_STRIKE = parseDefinition("critical_strike");
|
||||||
levels.add(new LivingUpgrade.UpgradeLevel(30, 25));
|
public static final LivingUpgrade UPGRADE_JUMP = parseDefinition("jump");
|
||||||
levels.add(new LivingUpgrade.UpgradeLevel(40, 125));
|
|
||||||
});
|
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void registerUpgrades(RegistryEvent.Register<Item> event) {
|
public static void registerUpgrades(RegistryEvent.Register<Item> event) {
|
||||||
addUpgrade(JUMP);
|
addUpgrade(UPGRADE_ARROW_PROTECT);
|
||||||
|
addUpgrade(UPGRADE_ARROW_SHOT);
|
||||||
|
addUpgrade(UPGRADE_CRITICAL_STRIKE);
|
||||||
|
addUpgrade(UPGRADE_JUMP);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addUpgrade(LivingUpgrade upgrade) {
|
private static void addUpgrade(LivingUpgrade upgrade) {
|
||||||
UPGRADES.put(upgrade.getKey(), upgrade);
|
UPGRADES.put(upgrade.getKey(), upgrade);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static LivingUpgrade parseDefinition(String fileName) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,11 @@ public class LivingStatusWatcher {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
EntityPlayer player = (EntityPlayer) event.getEntity();
|
EntityPlayer player = (EntityPlayer) event.getEntity();
|
||||||
LivingStats stats = LivingUtil.applyNewExperience(player, RegistrarBloodMagicLivingArmor.JUMP, 1);
|
LivingStats stats = LivingUtil.applyNewExperience(player, RegistrarBloodMagicLivingArmor.UPGRADE_JUMP, 1);
|
||||||
if (stats == null)
|
if (stats == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int level = stats.getLevel(RegistrarBloodMagicLivingArmor.JUMP.getKey());
|
int level = stats.getLevel(RegistrarBloodMagicLivingArmor.UPGRADE_JUMP.getKey());
|
||||||
player.motionY += 0.05 * level;
|
player.motionY += 0.05 * level;
|
||||||
|
|
||||||
if (level >= 3) {
|
if (level >= 3) {
|
||||||
|
|
|
@ -4,45 +4,67 @@ import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
import com.google.gson.*;
|
||||||
|
import com.google.gson.annotations.JsonAdapter;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
import net.minecraft.entity.ai.attributes.AttributeModifier;
|
import net.minecraft.entity.ai.attributes.AttributeModifier;
|
||||||
import net.minecraft.entity.player.EntityPlayer;
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
import net.minecraft.util.DamageSource;
|
import net.minecraft.util.DamageSource;
|
||||||
|
import net.minecraft.util.NonNullList;
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@JsonAdapter(LivingUpgrade.Deserializer.class)
|
||||||
public class LivingUpgrade {
|
public class LivingUpgrade {
|
||||||
|
|
||||||
|
public static final LivingUpgrade DUMMY = new LivingUpgrade(new ResourceLocation("dummy"), levels -> levels.add(new Level(0, 0)));
|
||||||
|
|
||||||
private final ResourceLocation key;
|
private final ResourceLocation key;
|
||||||
private final Set<ResourceLocation> incompatible;
|
private final Set<ResourceLocation> incompatible;
|
||||||
private final TreeMap<Integer, Integer> experienceToLevel;
|
private final TreeMap<Integer, Integer> experienceToLevel;
|
||||||
private final Map<Integer, Integer> levelToCost;
|
private final Map<Integer, Integer> levelToCost;
|
||||||
private final boolean isNegative;
|
private final Map<String, Bonus> bonuses;
|
||||||
|
private boolean isNegative;
|
||||||
private String unlocalizedName = null;
|
private String unlocalizedName = null;
|
||||||
private IAttributeProvider attributeProvider;
|
private IAttributeProvider attributeProvider;
|
||||||
private IArmorProvider armorProvider;
|
private IArmorProvider armorProvider;
|
||||||
|
|
||||||
public LivingUpgrade(ResourceLocation key, Consumer<List<UpgradeLevel>> experienceMapper /* xp needed -> level */, boolean isNegative) {
|
public LivingUpgrade(ResourceLocation key, Consumer<List<Level>> experienceMapper) {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.incompatible = Sets.newHashSet();
|
this.incompatible = Sets.newHashSet();
|
||||||
this.experienceToLevel = Maps.newTreeMap();
|
this.experienceToLevel = Maps.newTreeMap();
|
||||||
this.levelToCost = Maps.newHashMap();
|
this.levelToCost = Maps.newHashMap();
|
||||||
this.isNegative = isNegative;
|
this.bonuses = Maps.newHashMap();
|
||||||
|
|
||||||
List<UpgradeLevel> levels = Lists.newArrayList();
|
List<Level> levels = Lists.newArrayList();
|
||||||
experienceMapper.accept(levels);
|
experienceMapper.accept(levels);
|
||||||
|
|
||||||
for (int i = 0; i < levels.size(); i++) {
|
for (int i = 0; i < levels.size(); i++) {
|
||||||
UpgradeLevel level = levels.get(i);
|
Level level = levels.get(i);
|
||||||
experienceToLevel.put(level.experienceNeeded, i + 1);
|
experienceToLevel.put(level.experienceNeeded, i + 1);
|
||||||
levelToCost.put(i + 1, level.upgradeCost);
|
levelToCost.put(i + 1, level.upgradeCost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LivingUpgrade(ResourceLocation key, Consumer<List<UpgradeLevel>> experienceMapper /* xp needed -> level */) {
|
public LivingUpgrade withBonusSet(String id, Consumer<List<Number>> modifiers) {
|
||||||
this(key, experienceMapper, false);
|
List<Number> values = NonNullList.create();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public Number getBonusValue(String id, int level) {
|
||||||
|
return bonuses.getOrDefault(id, Bonus.DEFAULT).modifiers.get(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LivingUpgrade withAttributeProvider(IAttributeProvider attributeProvider) {
|
public LivingUpgrade withAttributeProvider(IAttributeProvider attributeProvider) {
|
||||||
|
@ -100,6 +122,11 @@ public class LivingUpgrade {
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LivingUpgrade asDowngrade() {
|
||||||
|
this.isNegative = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return key.toString();
|
return key.toString();
|
||||||
|
@ -113,13 +140,45 @@ public class LivingUpgrade {
|
||||||
double getProtection(EntityPlayer player, DamageSource source, int level);
|
double getProtection(EntityPlayer player, DamageSource source, int level);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class UpgradeLevel {
|
public static class Level {
|
||||||
|
@SerializedName("xp")
|
||||||
private final int experienceNeeded;
|
private final int experienceNeeded;
|
||||||
|
@SerializedName("cost")
|
||||||
private final int upgradeCost;
|
private final int upgradeCost;
|
||||||
|
|
||||||
public UpgradeLevel(int experienceNeeded, int upgradeCost) {
|
public Level(int experienceNeeded, int upgradeCost) {
|
||||||
this.experienceNeeded = experienceNeeded;
|
this.experienceNeeded = experienceNeeded;
|
||||||
this.upgradeCost = upgradeCost;
|
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 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<LivingUpgrade.Level> levels = context.deserialize(json.getAsJsonArray("levels"), new TypeToken<List<Level>>(){}.getType());
|
||||||
|
|
||||||
|
LivingUpgrade upgrade = new LivingUpgrade(id, upgradeLevels -> upgradeLevels.addAll(levels));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
package com.wayoftime.bloodmagic.core.util;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.resources.FolderResourcePack;
|
||||||
|
import net.minecraft.client.resources.IResourcePack;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraftforge.fml.relauncher.ReflectionHelper;
|
||||||
|
import net.minecraftforge.fml.relauncher.Side;
|
||||||
|
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.*;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ResourceUtil {
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
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")) {
|
||||||
|
BMLog.DEFAULT.error("Unsupported URI scheme {}", uri.getScheme());
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap());
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static Set<Path> gatherResources(String home, String following) {
|
||||||
|
return gatherResources(home, following, p -> true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static ResourceLocation addContext(ResourceLocation rl, String context) {
|
||||||
|
return new ResourceLocation(rl.getNamespace(), context + rl.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public static void injectDirectoryAsResource(File resourceDir) {
|
||||||
|
if (!resourceDir.exists() || !resourceDir.isDirectory())
|
||||||
|
return;
|
||||||
|
|
||||||
|
FolderResourcePack resourcePack = new FolderResourcePack(resourceDir);
|
||||||
|
Field _defaultResourcePacks = ReflectionHelper.findField(Minecraft.class, "field_110449_ao", "defaultResourcePacks");
|
||||||
|
try {
|
||||||
|
_defaultResourcePacks.setAccessible(true);
|
||||||
|
// noinspection unchecked
|
||||||
|
List<IResourcePack> defaultResourcePacks = (List<IResourcePack>) _defaultResourcePacks.get(Minecraft.getMinecraft());
|
||||||
|
defaultResourcePacks.add(resourcePack);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
src/main/resources/data/living_armor/arrow_protect.json
Normal file
29
src/main/resources/data/living_armor/arrow_protect.json
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"id": "bloodmagic:arrow_protect",
|
||||||
|
"levels": [
|
||||||
|
{ "xp": 30, "cost": 4 },
|
||||||
|
{ "xp": 200, "cost": 9 },
|
||||||
|
{ "xp": 400, "cost": 16 },
|
||||||
|
{ "xp": 800, "cost": 30 },
|
||||||
|
{ "xp": 1500, "cost": 60 },
|
||||||
|
{ "xp": 2500, "cost": 90 },
|
||||||
|
{ "xp": 3500, "cost": 125 },
|
||||||
|
{ "xp": 5000, "cost": 165 },
|
||||||
|
{ "xp": 7000, "cost": 210 },
|
||||||
|
{ "xp": 15000, "cost": 250 }
|
||||||
|
],
|
||||||
|
"bonuses": {
|
||||||
|
"protection": [
|
||||||
|
0.1,
|
||||||
|
0.3,
|
||||||
|
0.4,
|
||||||
|
0.6,
|
||||||
|
0.7,
|
||||||
|
0.75,
|
||||||
|
0.77,
|
||||||
|
0.8,
|
||||||
|
0.83,
|
||||||
|
0.85
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
10
src/main/resources/data/living_armor/arrow_shot.json
Normal file
10
src/main/resources/data/living_armor/arrow_shot.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"id": "bloodmagic:arrow_shot",
|
||||||
|
"levels": [
|
||||||
|
{ "xp": 50, "cost": 20 },
|
||||||
|
{ "xp": 200, "cost": 50 },
|
||||||
|
{ "xp": 700, "cost": 90 },
|
||||||
|
{ "xp": 1500, "cost": 160 },
|
||||||
|
{ "xp": 3000, "cost": 290 }
|
||||||
|
]
|
||||||
|
}
|
19
src/main/resources/data/living_armor/critical_strike.json
Normal file
19
src/main/resources/data/living_armor/critical_strike.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"id": "bloodmagic:critical_strike",
|
||||||
|
"levels": [
|
||||||
|
{ "xp": 200, "cost": 5 },
|
||||||
|
{ "xp": 800, "cost": 12 },
|
||||||
|
{ "xp": 1300, "cost": 22 },
|
||||||
|
{ "xp": 2500, "cost": 35 },
|
||||||
|
{ "xp": 3000, "cost": 49 }
|
||||||
|
],
|
||||||
|
"bonuses": {
|
||||||
|
"damage_boost": [
|
||||||
|
0.1,
|
||||||
|
0.2,
|
||||||
|
0.3,
|
||||||
|
0.4,
|
||||||
|
0.5
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
9
src/main/resources/data/living_armor/jump.json
Normal file
9
src/main/resources/data/living_armor/jump.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"id": "bloodmagic:jump",
|
||||||
|
"levels": [
|
||||||
|
{ "xp": 10, "cost": 1 },
|
||||||
|
{ "xp": 20, "cost": 5 },
|
||||||
|
{ "xp": 30, "cost": 25 },
|
||||||
|
{ "xp": 40, "cost": 125 }
|
||||||
|
]
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue