Skip to content

Commit

Permalink
New Mechanics
Browse files Browse the repository at this point in the history
+Added Chinese translation.
+Armor type Relics can now be repaired with Relic Shards.
+Armor type Relics can now be affixed to chestplates and helmets: equip a chestplate/helmet, then shift-right click whilst holding a Relic. Note that this cannot be undone and this can only be done once per armor piece.
*Item sounds for the Orbs of Regret and Tomes are now volume controlled by PlayerEx client config.
*Updated Fabric Loader/API versions.
  • Loading branch information
CleverNucleus committed Nov 19, 2022
1 parent 4a4a397 commit 6584561
Show file tree
Hide file tree
Showing 12 changed files with 241 additions and 60 deletions.
43 changes: 4 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,51 +32,16 @@ Relics come with a rarity system: Common, Uncommon, Rare, Epic, Mythical, Legend

All randomness and chance to drop is weighted and fully configurable.

#### Configuring Relic Attributes with Datapacks

The following can be configured using Datapacks:

- Which attributes are allowed to be attached to Relic items.
- The chance that a specific attribute will be attached to a Relic item.
- The attribute modifier type (`ADDITION` or `MULTIPLY_TOTAL`).
- The chance that the attribute modifier will be of a given modifier type.
- The minimum and maximum values allowed for a given attribute and modifier type.
- The increment value for attribute values.

Your datapack should be such that it contains `data/relicex/attributes/properties.json`. The `properties.json` file should be a copy/paste of the one contained in the relicex mod jar (see [here](https://github.com/CleverNucleus/RelicEx/blob/main/src/main/resources/data/relicex/attributes/properties.json)), and in this file you may add or subtract entries to add or remove attributes. Any attribute with the `weight` property will have a chance to be attached to a Relic item in-game.

The contents of the `weight` property defines all the aforementioned configuring options:

```json
"weight": "r65:a70:x1:y10:z1:u0.05:v0.5:w0.01"
```

What do these numbers mean?

| Letter | Min/Max | Description |
| :----: | :---: | :---------- |
| `r` | 1/100 | How rarely this attribute should be attached to a Relic item. |
| `a` | 0/100 | The chance that this attribute modifier will be `ADDITION`. Set to 100 to disable `MULTIPLY_TOTAL` modifier types, or 0 to disable `ADDITION` types. The modifier can only be either `ADDITION` or `MULTIPLY_TOTAL`. |
| `x` | 0/* | The minimum value this attribute modifier can have, for `ADDITION` type modifiers. |
| `y` | 0/* | The maximum value this attribute modifier can have, for `ADDITION` type modifiers. |
| `z` | 0/* | The increment** value for this attribute modifier, for `ADDITION` type modifiers. |
| `u` | 0/* | The minimum value this attribute modifier can have, for `MULTIPLY_TOTAL` type modifiers. |
| `v` | 0/* | The maximum value this attribute modifier can have, for `MULTIPLY_TOTAL` type modifiers. |
| `w` | 0/* | The increment** value for this attribute modifier, for `MULTIPLY_TOTAL` type modifiers. |

___*The minimum value should be less than both the increment and max values. The maximum value should be greater than the minimum and increment values, such that `min < increment < max`.___

___**The increment value should be a multiple of the max value, such that if the max value is `10`, the increment value cannot be `1.5`. Or, if the max value is `0.5`, the increment value could be `0.01`, but not `0.03`.___

Note that the colon, `:`, acts as a separator, and the letters are there to help the user know which numbers mean what. The order of the numbers ___must not change___.

#### Misc

- Unwanted Relics can be smelted down into Relic Shards, which can be smashed to drop Experience Points.
- Chest and Head Relics can be affixed to chestplates and helmets by equipping an armor item and right-clicking the Relic whilst crouching.
- Chest and Head Relics can be repaired with Relic Shards.
- Health Potions cannot be stored, they heal the instant that they are picked up.
- The Dragon Stone drops from the Ender Dragon upon death (configurable). Care should be taken when handling it as using it will reset the player's attributes, Levels and Skill Points. For this sake a safety is implemented, such that it must be right-clicked twice.

#### Contributors/Credits

- [Bonsaiheldin](https://opengameart.org/content/shiny-rpg-potions-16x16)
- [Joe Williamson](https://twitter.com/joecreates)
- [Joe Williamson](https://twitter.com/joecreates)
- [MoneySoup008](https://github.com/MoneySoup008)
12 changes: 6 additions & 6 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
org.gradle.jvmargs=-Xmx1G

minecraft_version=1.19.2
yarn_mappings=1.19.2+build.1
loader_version=0.14.9
yarn_mappings=1.19.2+build.28
loader_version=0.14.10

mod_version = 3.2.5
mod_version = 3.2.6
maven_group = com.github.clevernucleus
archives_base_name = relicex

fabric_version=0.59.0+1.19.2
fabric_version=0.66.0+1.19.2
dataattributes_version=1.2.7
opc_version=0.6.4

Expand All @@ -18,5 +18,5 @@ placeholder_api=2.0.0-beta.7+1.19
playerex_version=3.3.7
arl_version=0.2.6
trinkets_version=3.4.0
cloth_config_version=8.0.75
modmenu_version=4.0.6
cloth_config_version=8.2.88
modmenu_version=4.1.0
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.github.clevernucleus.relicex.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
Expand Down Expand Up @@ -66,7 +68,7 @@ private static float randomAttribute(EntityAttributeCollection collection) {
}) + (0.3F * weight.rarity());
}

public static void readFromNbt(NbtCompound tag, String slot, Multimap<EntityAttribute, EntityAttributeModifier> modifiers) {
public static void readFromNbt(NbtCompound tag, String slot, Multimap<EntityAttribute, EntityAttributeModifier> modifiersNBT, Multimap<EntityAttribute, EntityAttributeModifier> modifiersITM) {
if(!tag.contains(KEY_ATTRIBUTES, NbtType.LIST)) return;
NbtList list = tag.getList(KEY_ATTRIBUTES, NbtType.COMPOUND);

Expand All @@ -78,12 +80,42 @@ public static void readFromNbt(NbtCompound tag, String slot, Multimap<EntityAttr
if(attribute.get() == null) continue;
Operation operation = Operation.fromId((int)entry.getByte(KEY_OPERATION));
EntityAttributeModifier modifier = new EntityAttributeModifier(SlotKey.from(slot).uuid(), "RelicEx Modifier", entry.getDouble(KEY_VALUE), operation);
modifiers.put(attribute.get(), modifier);
modifiersNBT.put(attribute.get(), modifier);
}

for(EntityAttribute attributeITM : modifiersITM.keySet()) {
var collectionITM = modifiersITM.get(attributeITM);

if(modifiersNBT.containsKey(attributeITM)) {
List<EntityAttributeModifier> temp = new ArrayList<>();
var collectionNBT = modifiersNBT.get(attributeITM);

for(EntityAttributeModifier modifierITM : collectionITM) {
Operation operationITM = modifierITM.getOperation();

for(EntityAttributeModifier modifierNBT : collectionNBT) {
Operation operationNBT = modifierNBT.getOperation();

if(operationITM == operationNBT) {
EntityAttributeModifier modifier3 = new EntityAttributeModifier(modifierITM.getId(), "RelicEx Modifier", Math.max(modifierITM.getValue(), modifierNBT.getValue()), operationITM);
temp.add(modifier3);
} else {
temp.add(modifierITM);
temp.add(modifierNBT);
}
}
}

modifiersNBT.removeAll(attributeITM);
modifiersNBT.putAll(attributeITM, temp);
} else {
modifiersNBT.putAll(attributeITM, collectionITM);
}
}
}

public static float getValueIfArmor(NbtCompound tag, EntityAttribute attributeIn) {
if(!tag.contains(KEY_ATTRIBUTES, NbtType.LIST)) return 0.0F;
public static float getValueIfArmor(NbtCompound tag, EntityAttribute attributeIn, float fallback) {
if(!tag.contains(KEY_ATTRIBUTES, NbtType.LIST)) return fallback;
NbtList list = tag.getList(KEY_ATTRIBUTES, NbtType.COMPOUND);

for(int i = 0; i < list.size(); i++) {
Expand All @@ -95,7 +127,7 @@ public static float getValueIfArmor(NbtCompound tag, EntityAttribute attributeIn
return (float)entry.getDouble(KEY_VALUE);
}

return 0.0F;
return fallback;
}

public void writeToNbt(NbtCompound tag) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.List;

import com.github.clevernucleus.dataattributes.api.item.ItemHelper;
import com.github.clevernucleus.relicex.RelicEx;
import com.github.clevernucleus.relicex.impl.EntityAttributeCollection;
import com.github.clevernucleus.relicex.impl.Rareness;
import com.github.clevernucleus.relicex.impl.RelicType;
Expand All @@ -15,14 +16,20 @@
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ArmorItem;
import net.minecraft.item.ArmorMaterials;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtList;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvent;
import net.minecraft.text.Text;
import net.minecraft.util.Hand;
import net.minecraft.util.TypedActionResult;
import net.minecraft.world.World;

public class ArmorRelicItem extends ArmorItem implements ItemHelper {
Expand All @@ -46,11 +53,42 @@ public void onStackCreated(ItemStack itemStack, int count) {
collection.writeToNbt(tag);
}

@Override
public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {
if(!user.isSneaking()) return super.use(world, user, hand);

ItemStack itemStack = user.getStackInHand(hand);
EquipmentSlot equipmentSlot = MobEntity.getPreferredEquipmentSlot(itemStack);
ItemStack itemStack2 = user.getEquippedStack(equipmentSlot);
Item item = itemStack2.getItem();

if(item instanceof ArmorItem && !(item instanceof ArmorRelicItem)) {
NbtCompound tag2 = itemStack2.getOrCreateNbt();

if(!tag2.contains(EntityAttributeCollection.KEY_ATTRIBUTES)) {
if(!world.isClient) {
NbtCompound tag = itemStack.getOrCreateNbt();
NbtList list = tag.getList(EntityAttributeCollection.KEY_ATTRIBUTES, NbtType.COMPOUND);
String rareness = tag.getString(EntityAttributeCollection.KEY_RARENESS);
tag2.put(EntityAttributeCollection.KEY_ATTRIBUTES, list);
tag2.putString(EntityAttributeCollection.KEY_RARENESS, rareness);
} else {
user.playSound(this.getEquipSound(itemStack), SoundCategory.NEUTRAL, 0.75F, 1.0F);
}

itemStack.setCount(0);
return TypedActionResult.success(itemStack, world.isClient);
}
}

return super.use(world, user, hand);
}

@Override
public Multimap<EntityAttribute, EntityAttributeModifier> getAttributeModifiers(ItemStack stack, EquipmentSlot slot) {
NbtCompound tag = stack.getOrCreateNbt();
Multimap<EntityAttribute, EntityAttributeModifier> modifiers = ArrayListMultimap.create();
EntityAttributeCollection.readFromNbt(tag, this.slot.getName(), modifiers);
EntityAttributeCollection.readFromNbt(tag, this.slot.getName(), modifiers, ArrayListMultimap.create());

return slot == this.slot ? modifiers : super.getAttributeModifiers(stack, slot);
}
Expand All @@ -62,17 +100,17 @@ public int getEnchantability() {

@Override
public boolean canRepair(ItemStack stack, ItemStack ingredient) {
return false;
return ingredient.isOf(RelicEx.RELIC_SHARD);
}

@Override
public int getProtection(ItemStack itemStack) {
return (int)EntityAttributeCollection.getValueIfArmor(itemStack.getOrCreateNbt(), EntityAttributes.GENERIC_ARMOR);
return (int)EntityAttributeCollection.getValueIfArmor(itemStack.getOrCreateNbt(), EntityAttributes.GENERIC_ARMOR, 0.0F);
}

@Override
public float getToughness(ItemStack itemStack) {
return EntityAttributeCollection.getValueIfArmor(itemStack.getOrCreateNbt(), EntityAttributes.GENERIC_ARMOR_TOUGHNESS);
return EntityAttributeCollection.getValueIfArmor(itemStack.getOrCreateNbt(), EntityAttributes.GENERIC_ARMOR_TOUGHNESS, 0.0F);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand han

if(!(value > 0.0D) || !(refundPoints > 0)) return super.use(world, user, hand);
if(world.isClient) {
user.playSound(RelicEx.LEVEL_REFUND_SOUND, SoundCategory.NEUTRAL, 0.75F, 1.0F);
user.playSound(RelicEx.LEVEL_REFUND_SOUND, SoundCategory.NEUTRAL, ExAPI.getConfig().skillUpVolume(), 1.0F);
} else {
playerData.addRefundPoints(this.greater ? refundPoints : 1);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.github.clevernucleus.relicex.impl.EntityAttributeCollection;
import com.github.clevernucleus.relicex.impl.Rareness;
import com.github.clevernucleus.relicex.impl.RelicType;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;

import dev.emi.trinkets.api.SlotReference;
Expand Down Expand Up @@ -59,7 +60,7 @@ public Multimap<EntityAttribute, EntityAttributeModifier> getModifiers(ItemStack
String key = slotType.getGroup() + "/" + slotType.getName();
NbtCompound tag = stack.getOrCreateNbt();
var modifiers = super.getModifiers(stack, slot, entity, uuid);
EntityAttributeCollection.readFromNbt(tag, key, modifiers);
EntityAttributeCollection.readFromNbt(tag, key, modifiers, ArrayListMultimap.create());

return modifiers;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand han
ExConfig config = ExAPI.getConfig();

if(world.isClient) {
user.playSound(RelicEx.LEVEL_REFUND_SOUND, SoundCategory.NEUTRAL, 0.75F, 1.0F);
user.playSound(RelicEx.LEVEL_REFUND_SOUND, SoundCategory.NEUTRAL, ExAPI.getConfig().levelUpVolume(), 1.0F);
} else {
playerData.add(ExAPI.LEVEL, 1);
playerData.addSkillPoints(config.skillPointsPerLevelUp());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.github.clevernucleus.relicex.mixin;

import java.util.List;

import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;

import com.github.clevernucleus.dataattributes.api.item.ItemHelper;
import com.github.clevernucleus.relicex.impl.EntityAttributeCollection;
import com.github.clevernucleus.relicex.impl.Rareness;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;

import net.fabricmc.fabric.api.util.NbtType;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.item.ArmorItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.text.Text;
import net.minecraft.world.World;

@Mixin(ArmorItem.class)
abstract class ArmorItemMixin extends Item implements ItemHelper {

@Shadow
@Final
protected EquipmentSlot slot;

@Shadow
@Final
private Multimap<EntityAttribute, EntityAttributeModifier> attributeModifiers;

private ArmorItemMixin(Settings settings) { super(settings); }

@Override
public void appendTooltip(ItemStack stack, World world, List<Text> tooltip, TooltipContext context) {
NbtCompound tag = stack.getNbt();

if(tag == null || !tag.contains(EntityAttributeCollection.KEY_RARENESS, NbtType.STRING)) return;
Rareness rareness = Rareness.fromKey(tag.getString(EntityAttributeCollection.KEY_RARENESS));
tooltip.add(rareness.formatted());
}

@Override
public Multimap<EntityAttribute, EntityAttributeModifier> getAttributeModifiers(ItemStack stack, EquipmentSlot slot) {
NbtCompound tag = stack.getOrCreateNbt();
Multimap<EntityAttribute, EntityAttributeModifier> modifiers = ArrayListMultimap.create();
EntityAttributeCollection.readFromNbt(tag, this.slot.getName(), modifiers, this.attributeModifiers);

return slot == this.slot ? (modifiers.isEmpty() ? this.attributeModifiers : modifiers) : super.getAttributeModifiers(stack, slot);
}

@Override
public int getProtection(ItemStack itemStack) {
return (int)EntityAttributeCollection.getValueIfArmor(itemStack.getOrCreateNbt(), EntityAttributes.GENERIC_ARMOR, ((ArmorItem)(Object)this).getProtection());
}

@Override
public float getToughness(ItemStack itemStack) {
return (int)EntityAttributeCollection.getValueIfArmor(itemStack.getOrCreateNbt(), EntityAttributes.GENERIC_ARMOR_TOUGHNESS, ((ArmorItem)(Object)this).getToughness());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.github.clevernucleus.relicex.mixin;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

import com.github.clevernucleus.relicex.item.ArmorRelicItem;

import net.minecraft.item.Item;
import net.minecraft.recipe.RepairItemRecipe;

@Mixin(RepairItemRecipe.class)
abstract class RepairItemRecipeMixin {

@Redirect(method = "matches", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isDamageable()Z"))
private boolean relicex_matches(Item item) {
return item instanceof ArmorRelicItem ? false : item.isDamageable();
}
}
Loading

0 comments on commit 6584561

Please sign in to comment.