diff --git a/build.gradle b/build.gradle index 1f8dca92..903523a9 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ apply plugin: 'net.minecraftforge.gradle' apply plugin: 'eclipse' apply plugin: 'maven-publish' -version = '1.16.3-3.0.3-8' +version = '1.16.3-3.0.4-9' group = 'com.yourname.modid' // http://maven.apache.org/guides/mini/guide-naming-conventions.html archivesBaseName = 'BloodMagic' diff --git a/changelog.txt b/changelog.txt index a49f7dad..fc6f8a31 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,18 +2,25 @@ Version 3.0.4 ------------------------------------------------------ +- Added two new explosives: + - The Shaped Charge, which will break a 5x5x5 volume when placed + - The Deforester Charge, which can chop down a tree up to a maximum of 128 logs. + - (Both explosives drop all blocks affected, and do not cause further environmental nor entity damage.) + +- Added new alchemy arrays: + - Two arrays, which changes the current daylight cycle to day and night. Recipes are temp. + - Fixed the following Living Armour upgrades so that they are now obtainable: - Experienced - Body Builder +- Fixed Living Armour not upgrading under certain conditions. + - Fixed the two rituals involving Living Armour so that they are properly activatable using a Weak Activation Crystal. - Fixed NPE in Blood Altar when trying to interact with the contained Fluids - Also fixed bug that prevented the Altar from accepting fluids piped in as inputs. - Fixed a client-side crash when right clicking the ground with a Lava Crystal when on a server - now properly makes a fire without consuming the client. - -- Added new alchemy arrays: - - Two arrays, which changes the current daylight cycle to day and night. Recipes are temp. - Fixed the JEI so that it no longer ouputs an error when loading Alchemy Array recipes without a crafting output. - Fixed crash with Actually Additions diff --git a/src/generated/resources/data/bloodmagic/recipes/array/day.json b/src/generated/resources/data/bloodmagic/recipes/array/day.json index b2583c2b..32c6212b 100644 --- a/src/generated/resources/data/bloodmagic/recipes/array/day.json +++ b/src/generated/resources/data/bloodmagic/recipes/array/day.json @@ -5,7 +5,7 @@ "item": "minecraft:coal" }, "addedinput": { - "item": "minecraft:clock" + "item": "minecraft:coal" }, "output": { "item": "minecraft:air" diff --git a/src/main/java/wayoftime/bloodmagic/BloodMagic.java b/src/main/java/wayoftime/bloodmagic/BloodMagic.java index 5adf8a38..2039a0bc 100644 --- a/src/main/java/wayoftime/bloodmagic/BloodMagic.java +++ b/src/main/java/wayoftime/bloodmagic/BloodMagic.java @@ -49,6 +49,7 @@ import wayoftime.bloodmagic.common.data.recipe.BloodMagicRecipeProvider; import wayoftime.bloodmagic.common.item.BloodMagicItems; import wayoftime.bloodmagic.common.registries.BloodMagicEntityTypes; import wayoftime.bloodmagic.common.registries.BloodMagicRecipeSerializers; +import wayoftime.bloodmagic.core.AnointmentRegistrar; import wayoftime.bloodmagic.core.LivingArmorRegistrar; import wayoftime.bloodmagic.core.recipe.IngredientBloodOrb; import wayoftime.bloodmagic.core.registry.AlchemyArrayRegistry; @@ -101,6 +102,7 @@ public class BloodMagic // RegistrarBloodMagic.BLOOD_ORBS.createAndRegister(modBus, "bloodorbs"); BloodMagicItems.BLOOD_ORBS.createAndRegister(modBus, "bloodorbs"); LivingArmorRegistrar.UPGRADES.createAndRegister(modBus, "upgrades"); + AnointmentRegistrar.ANOINTMENTS.createAndRegister(modBus, "anointments"); BloodMagicItems.BASICITEMS.register(modBus); BloodMagicBlocks.BASICBLOCKS.register(modBus); BloodMagicBlocks.DUNGEONBLOCKS.register(modBus); @@ -171,6 +173,7 @@ public class BloodMagic RITUAL_MANAGER.discover(); ModRituals.initHarvestHandlers(); LivingArmorRegistrar.register(); + AnointmentRegistrar.register(); AlchemyArrayRegistry.registerBaseArrays(); } diff --git a/src/main/java/wayoftime/bloodmagic/anointment/Anointment.java b/src/main/java/wayoftime/bloodmagic/anointment/Anointment.java new file mode 100644 index 00000000..8e546f4f --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/anointment/Anointment.java @@ -0,0 +1,165 @@ +package wayoftime.bloodmagic.anointment; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Consumer; + +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +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 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.ResourceLocation; +import net.minecraftforge.registries.ForgeRegistryEntry; +import wayoftime.bloodmagic.core.living.LivingStats; +import wayoftime.bloodmagic.core.living.LivingUpgrade; +import wayoftime.bloodmagic.core.living.LivingUpgrade.Level; + +@JsonAdapter(Anointment.Deserializer.class) +public class Anointment extends ForgeRegistryEntry +{ + public static final Anointment DUMMY = new Anointment(new ResourceLocation("dummy")); + + private final ResourceLocation key; +// private final Set incompatible; + private final Map bonuses; + private IAttributeProvider attributeProvider; + private IDamageProvider damageProvider; + + public Anointment(ResourceLocation key) + { + this.key = key; + this.bonuses = Maps.newHashMap(); + } + + public Anointment withBonusSet(String id, Consumer> modifiers) + { +// List values = DefaultedList.of(); + List values = new ArrayList(); + modifiers.accept(values); + + bonuses.put(id, new Bonus(id, values)); + return this; + } + + public Number getBonusValue(String id, int level) + { + List modifiers = bonuses.getOrDefault(id, Bonus.DEFAULT).modifiers; + if (modifiers.isEmpty() || level == 0) + return 0; + + return modifiers.get(level - 1); + } + + public ResourceLocation getKey() + { + return key; + } + + @Override + public String toString() + { + return key.toString(); + } + + public Anointment withAttributeProvider(IAttributeProvider attributeProvider) + { + this.attributeProvider = attributeProvider; + return this; + } + + public IAttributeProvider getAttributeProvider() + { + return attributeProvider; + } + + public Anointment withDamageProvider(IDamageProvider damageProvider) + { + this.damageProvider = damageProvider; + return this; + } + + public IDamageProvider getDamageProvider() + { + return damageProvider; + } + + public interface IAttributeProvider + { + void handleAttributes(AnointmentHolder holder, Multimap modifiers, UUID uuid, Anointment anoint, int level); + } + + public interface IDamageProvider + { + double getAdditionalDamage(PlayerEntity player, ItemStack weapon, double damage, LivingStats stats, LivingEntity attacked, LivingUpgrade upgrade, int level); + } + + public static class Bonus + { + private static final Bonus DEFAULT = new Bonus("null", Collections.emptyList()); + + private final String id; + private final List modifiers; + + public Bonus(String id, List modifiers) + { + this.id = id; + this.modifiers = modifiers; + } + + public String getId() + { + return id; + } + } + + public static class Deserializer implements JsonDeserializer + { + @Override + public Anointment deserialize(JsonElement element, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException + { + JsonObject json = element.getAsJsonObject(); + ResourceLocation id = new ResourceLocation(json.getAsJsonPrimitive("id").getAsString()); + List levels = context.deserialize(json.getAsJsonArray("levels"), new TypeToken>() + { + }.getType()); + boolean negative = json.has("negative") && json.getAsJsonPrimitive("negative").getAsBoolean(); + + Anointment upgrade = new Anointment(id); +// 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 bonuses = context.deserialize(json.getAsJsonObject("bonuses"), new TypeToken>() + { + }.getType()); + bonuses.forEach((k, v) -> upgrade.withBonusSet(k, numbers -> Collections.addAll(numbers, v))); + } + + return upgrade; + } + } +} diff --git a/src/main/java/wayoftime/bloodmagic/anointment/AnointmentHolder.java b/src/main/java/wayoftime/bloodmagic/anointment/AnointmentHolder.java new file mode 100644 index 00000000..15626ab4 --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/anointment/AnointmentHolder.java @@ -0,0 +1,147 @@ +package wayoftime.bloodmagic.anointment; + +import java.util.Map; + +import com.google.common.collect.Maps; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraft.util.Hand; +import net.minecraft.util.ResourceLocation; +import wayoftime.bloodmagic.core.AnointmentRegistrar; +import wayoftime.bloodmagic.util.Constants; + +public class AnointmentHolder +{ + private final Map anointments; + + public AnointmentHolder(Map anointments) + { + this.anointments = anointments; + } + + public AnointmentHolder() + { + this(Maps.newHashMap()); + } + + public CompoundNBT serialize() + { + CompoundNBT compound = new CompoundNBT(); + ListNBT statList = new ListNBT(); + anointments.forEach((k, v) -> { + CompoundNBT anoint = new CompoundNBT(); + anoint.putString("key", k.getKey().toString()); + anoint.putInt("level", v.getLevel()); + anoint.putInt("damage", v.getDamage()); + anoint.putInt("max_damage", v.getMaxDamage()); + statList.add(anoint); + }); + compound.put("upgrades", statList); +// +// compound.putInt("maxPoints", maxPoints); + + return compound; + } + + public void deserialize(CompoundNBT nbt) + { + ListNBT statList = nbt.getList("anointments", 10); + statList.forEach(tag -> { + if (!(tag instanceof CompoundNBT)) + return; + + Anointment anoint = AnointmentRegistrar.ANOINTMENT_MAP.getOrDefault(new ResourceLocation(((CompoundNBT) tag).getString("key")), Anointment.DUMMY); +// LivingUpgrade upgrade = LivingArmorRegistrar.UPGRADE_MAP.getOrDefault(new ResourceLocation(((CompoundNBT) tag).getString("key")), LivingUpgrade.DUMMY); + if (anoint == Anointment.DUMMY) + return; +// double experience = ((CompoundNBT) tag).getDouble("exp"); + AnointmentData data = new AnointmentData(((CompoundNBT) tag).getInt("level"), ((CompoundNBT) tag).getInt("damage"), ((CompoundNBT) tag).getInt("max_damage")); + anointments.put(anoint, data); + }); +// +// maxPoints = nbt.getInt("maxPoints"); + } + + public static AnointmentHolder fromNBT(CompoundNBT holderTag) + { + AnointmentHolder holder = new AnointmentHolder(); + holder.deserialize(holderTag); + return holder; + } + + public static AnointmentHolder fromItemStack(ItemStack stack) + { + CompoundNBT nbtTag = stack.getTag(); + if (nbtTag == null) + { + return null; + } + + CompoundNBT holderTag = nbtTag.getCompound(Constants.NBT.ANOINTMENTS); + if (holderTag != null) + { + return fromNBT(holderTag); + } + + return null; + } + + public void toItemStack(ItemStack stack) + { + CompoundNBT nbtTag = stack.getOrCreateTag(); + CompoundNBT childTag = this.serialize(); + + nbtTag.put(Constants.NBT.ANOINTMENTS, childTag); + } + + public static AnointmentHolder fromPlayer(PlayerEntity player, Hand hand) + { + return fromPlayer(player, hand, false); + } + + public static AnointmentHolder fromPlayer(PlayerEntity player, Hand hand, boolean createNew) + { + ItemStack heldItem = player.getHeldItem(hand); + + AnointmentHolder holder = fromItemStack(heldItem); + return holder == null && createNew ? new AnointmentHolder() : holder; + } + + public static void toPlayer(PlayerEntity player, Hand hand, AnointmentHolder holder) + { + ItemStack heldItem = player.getHeldItem(hand); + holder.toItemStack(heldItem); + } + + public static class AnointmentData + { + private int level; + private int damage; + private int maxDamage; + + public AnointmentData(int level, int damage, int maxDamage) + { + this.level = level; + this.damage = damage; + this.maxDamage = maxDamage; + } + + public int getLevel() + { + return this.level; + } + + public int getDamage() + { + return this.damage; + } + + public int getMaxDamage() + { + return this.maxDamage; + } + } +} diff --git a/src/main/java/wayoftime/bloodmagic/anointment/AnointmentUtil.java b/src/main/java/wayoftime/bloodmagic/anointment/AnointmentUtil.java new file mode 100644 index 00000000..71ff1ed1 --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/anointment/AnointmentUtil.java @@ -0,0 +1,6 @@ +package wayoftime.bloodmagic.anointment; + +public class AnointmentUtil +{ + +} diff --git a/src/main/java/wayoftime/bloodmagic/common/registration/impl/AnointmentDeferredRegister.java b/src/main/java/wayoftime/bloodmagic/common/registration/impl/AnointmentDeferredRegister.java new file mode 100644 index 00000000..e689d0d9 --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/common/registration/impl/AnointmentDeferredRegister.java @@ -0,0 +1,19 @@ +package wayoftime.bloodmagic.common.registration.impl; + +import java.util.function.Supplier; + +import wayoftime.bloodmagic.anointment.Anointment; +import wayoftime.bloodmagic.common.registration.WrappedDeferredRegister; + +public class AnointmentDeferredRegister extends WrappedDeferredRegister +{ + public AnointmentDeferredRegister(String modid) + { + super(modid, Anointment.class); + } + + public AnointmentRegistryObject register(String name, Supplier sup) + { + return register(name, sup, AnointmentRegistryObject::new); + } +} \ No newline at end of file diff --git a/src/main/java/wayoftime/bloodmagic/common/registration/impl/AnointmentRegistryObject.java b/src/main/java/wayoftime/bloodmagic/common/registration/impl/AnointmentRegistryObject.java new file mode 100644 index 00000000..f017a514 --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/common/registration/impl/AnointmentRegistryObject.java @@ -0,0 +1,13 @@ +package wayoftime.bloodmagic.common.registration.impl; + +import net.minecraftforge.fml.RegistryObject; +import wayoftime.bloodmagic.anointment.Anointment; +import wayoftime.bloodmagic.common.registration.WrappedRegistryObject; + +public class AnointmentRegistryObject extends WrappedRegistryObject +{ + public AnointmentRegistryObject(RegistryObject registryObject) + { + super(registryObject); + } +} diff --git a/src/main/java/wayoftime/bloodmagic/core/AnointmentRegistrar.java b/src/main/java/wayoftime/bloodmagic/core/AnointmentRegistrar.java new file mode 100644 index 00000000..38db1b2e --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/core/AnointmentRegistrar.java @@ -0,0 +1,98 @@ +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 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.anointment.Anointment; +import wayoftime.bloodmagic.common.registration.impl.AnointmentDeferredRegister; +import wayoftime.bloodmagic.common.registration.impl.AnointmentRegistryObject; +import wayoftime.bloodmagic.gson.Serializers; + +public class AnointmentRegistrar +{ + public static final AnointmentDeferredRegister ANOINTMENTS = new AnointmentDeferredRegister(BloodMagic.MODID); + + public static final Map ANOINTMENT_MAP = new HashMap<>(); + + private static final Map DEFINITIONS = ((Supplier>) () -> { + Map def = new HashMap<>(); + def.put("melee_damage", BloodMagic.rl("melee_damage")); +// def.put("arrow_shot", BloodMagic.rl("arrow_shot")); +// def.put("critical_strike", BloodMagic.rl("critical_strike")); +// def.put("digging", BloodMagic.rl("digging")); +// def.put("experienced", BloodMagic.rl("experienced")); +// def.put("fall_protect", BloodMagic.rl("fall_protect")); +// def.put("fire_resist", BloodMagic.rl("fire_resist")); +// def.put("grave_digger", BloodMagic.rl("grave_digger")); +// def.put("health", BloodMagic.rl("health")); +// def.put("jump", BloodMagic.rl("jump")); +// def.put("knockback_resist", BloodMagic.rl("knockback_resist")); +// def.put("melee_damage", BloodMagic.rl("melee_damage")); +// def.put("physical_protect", BloodMagic.rl("physical_protect")); +// def.put("poison_resist", BloodMagic.rl("poison_resist")); +// def.put("sprint_attack", BloodMagic.rl("sprint_attack")); +// def.put("speed", BloodMagic.rl("speed")); +// def.put("self_sacrifice", BloodMagic.rl("self_sacrifice")); + return def; + }).get(); + + public static final AnointmentRegistryObject ANOINTMENT_MELEE_DAMAGE = ANOINTMENTS.register("melee_damage", () -> parseDefinition("melee_damage").withAttributeProvider((stats, attributeMap, uuid, upgrade, level) -> { +// attributeMap.put(Attributes.KNOCKBACK_RESISTANCE, new AttributeModifier(uuid, "KB Modifier", upgrade.getBonusValue("kb", level).doubleValue(), AttributeModifier.Operation.ADDITION)); + attributeMap.put(Attributes.ATTACK_DAMAGE, new AttributeModifier(uuid, "Damage", upgrade.getBonusValue("damage", level).intValue(), AttributeModifier.Operation.ADDITION)); + })); + + public static void register() + { + registerAnointment(ANOINTMENT_MELEE_DAMAGE.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 registerAnointment(Anointment anoint) + { + ANOINTMENT_MAP.put(anoint.getKey(), anoint); + } + + public static Anointment parseDefinition(String fileName) + { +// System.out.println("Attempting to parse Anointment: " + fileName); + ResourceLocation path = DEFINITIONS.get(fileName); + if (path == null) + return Anointment.DUMMY; + + try + { + URL schematicURL = Anointment.class.getResource(resLocToResourcePath(path)); + System.out.println("Attempting to load Anointment: " + schematicURL + ", path: " + resLocToResourcePath(path)); + return Serializers.GSON.fromJson(Resources.toString(schematicURL, Charsets.UTF_8), Anointment.class); +// return GSON.fromJson(IOUtils.toString(path.toUri(), StandardCharsets.UTF_8), LivingUpgrade.class); + } catch (Exception e) + { + e.printStackTrace(); + return Anointment.DUMMY; + } + } + + public static String resLocToResourcePath(ResourceLocation resourceLocation) + { + return "/data/" + resourceLocation.getNamespace() + "/anointment/" + resourceLocation.getPath() + ".json"; + } +} diff --git a/src/main/java/wayoftime/bloodmagic/util/Constants.java b/src/main/java/wayoftime/bloodmagic/util/Constants.java index b09d608b..a264b5dc 100644 --- a/src/main/java/wayoftime/bloodmagic/util/Constants.java +++ b/src/main/java/wayoftime/bloodmagic/util/Constants.java @@ -130,6 +130,8 @@ public class Constants public static final String TANK = "tank"; public static final String BREATH = "breath"; + + public static final String ANOINTMENTS = "anointment_holder"; } public static class JSON diff --git a/src/main/resources/data/bloodmagic/anointment/melee_damage.json b/src/main/resources/data/bloodmagic/anointment/melee_damage.json new file mode 100644 index 00000000..e6805b67 --- /dev/null +++ b/src/main/resources/data/bloodmagic/anointment/melee_damage.json @@ -0,0 +1,10 @@ +{ + "id": "bloodmagic:melee_damage", + "bonuses": { + "damage": [ + 2, + 3, + 4 + ] + } +} \ No newline at end of file diff --git a/src/main/resources/data/bloodmagic/patchouli_books/guide/en_us/entries/demon_will/explosive_consumables.json b/src/main/resources/data/bloodmagic/patchouli_books/guide/en_us/entries/demon_will/explosive_consumables.json new file mode 100644 index 00000000..6dbf8dd7 --- /dev/null +++ b/src/main/resources/data/bloodmagic/patchouli_books/guide/en_us/entries/demon_will/explosive_consumables.json @@ -0,0 +1,31 @@ +{ + "name": "Explosive Charges", + "icon": "bloodmagic:shaped_charge", + "category": "demon_will", + "extra_recipe_mappings":[ + ["bloodmagic:shaped_charge", 1], + ["bloodmagic:deforester_charge", 3] + ], + "pages": [ + { + "type": "text", + "text": "While conventional $(item)TNT$() is nice and flashy, it unfortunately is rather unrefined. However, by mixing some fuel with a casing infused with Demon Will, you can create an explosive that, when placed, blows up a 5x5x5 volume! It even gives you all the drops!" + }, + { + "type": "crafting_soulforge", + "heading": "Shaped Charge", + "recipe": "bloodmagic:soulforge/shaped_charge", + "text": "A simple explosive with perfect efficiency!" + }, + { + "type": "text", + "text": "Although $(item)Shaped Charges$() are useful for mining, they seem a bit unsophisticated for chopping down trees. Thankfully, with some minor modifications you can create an explosive that will make you call \"Timber!\". Placing these on a log or some leaves will cause it to break down an entire tree! Assuming that there's not more than 128 logs involved." + }, + { + "type": "crafting_soulforge", + "heading": "Deforester Charge", + "recipe": "bloodmagic:soulforge/deforester_charge", + "text": "Ready to tackle those redwoods." + } + ] +} \ No newline at end of file