Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Weapon/Armor Leveling System (#23) #49

Merged
82 changes: 65 additions & 17 deletions src/main/java/com/bibireden/playerex/mixin/ItemStackMixin.java
naomieow marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package com.bibireden.playerex.mixin;

import com.bibireden.data_attributes.api.item.ItemFields;
import com.bibireden.data_attributes.endec.nbt.NbtDeserializer;
import com.bibireden.data_attributes.endec.nbt.NbtSerializer;
import com.bibireden.playerex.PlayerEX;
import com.bibireden.playerex.api.PlayerEXTags;
import com.bibireden.playerex.api.items.ItemWithAttributes;
import com.bibireden.playerex.api.items.WeaponItem;
import com.bibireden.playerex.config.PlayerEXConfigModel;
import com.bibireden.playerex.util.PlayerEXUtil;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.llamalad7.mixinextras.sugar.Local;
import io.wispforest.endec.Endec;
import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
Expand All @@ -19,15 +24,13 @@
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.Attribute;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.attributes.*;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
Expand All @@ -39,22 +42,31 @@
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.*;
import java.util.function.Consumer;

@Mixin(ItemStack.class)
abstract class ItemStackMixin {
abstract class ItemStackMixin implements WeaponItem, ItemWithAttributes {
@Unique
private static final Endec<HashMap<String, Double>> ATTRIBUTES_CODEC = Endec.DOUBLE.mapOf().xmap(HashMap::new, Map::copyOf);

@Unique
private static final String ATTRIBUTES_KEY = "playerex$attributes";

@Unique
private HashMap<String, Double> playerex$attributes;

@Shadow
public abstract boolean hurt(int amount, RandomSource random, @Nullable ServerPlayer serverPlayer);

@Shadow
public abstract CompoundTag getOrCreateTag();

@Inject(method = "use(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/InteractionHand;)Lnet/minecraft/world/InteractionResultHolder;", at = @At(value = "HEAD"), cancellable = true)
public void preventUse(Level level, Player player, InteractionHand usedHand, CallbackInfoReturnable<InteractionResultHolder<ItemStack>> cir) {
if (!PlayerEX.CONFIG.getItemBreakingEnabled()) return;

ItemStack stack = (ItemStack)(Object)this;
ItemStack stack = (ItemStack) (Object) this;
if (PlayerEXUtil.isBroken(stack)) {
cir.setReturnValue(InteractionResultHolder.fail(stack));
}
Expand All @@ -64,7 +76,7 @@ public void preventUse(Level level, Player player, InteractionHand usedHand, Cal
public void preventUseOnBlock(UseOnContext context, CallbackInfoReturnable<InteractionResult> cir) {
if (!PlayerEX.CONFIG.getItemBreakingEnabled()) return;

ItemStack stack = (ItemStack)(Object)this;
ItemStack stack = (ItemStack) (Object) this;
if (PlayerEXUtil.isBroken(stack)) {
cir.setReturnValue(InteractionResult.FAIL);
}
Expand All @@ -74,7 +86,7 @@ public void preventUseOnBlock(UseOnContext context, CallbackInfoReturnable<Inter
public void preventDamage(int amount, RandomSource random, ServerPlayer user, CallbackInfoReturnable<Boolean> cir) {
if (!PlayerEX.CONFIG.getItemBreakingEnabled()) return;

ItemStack stack = (ItemStack)(Object)this;
ItemStack stack = (ItemStack) (Object) this;
if (PlayerEXUtil.isBroken(stack)) {
cir.setReturnValue(true);
}
Expand All @@ -84,8 +96,8 @@ public void preventDamage(int amount, RandomSource random, ServerPlayer user, Ca
public <T extends LivingEntity> void preventBreak(int amount, T entity, Consumer<T> onBroken, CallbackInfo ci) {
if (!PlayerEX.CONFIG.getItemBreakingEnabled()) return;

ItemStack stack = (ItemStack)(Object)this;
if (stack.getItem().builtInRegistryHolder().is(PlayerEXTags.UNBREAKABLE_ITEMS)) {
ItemStack stack = (ItemStack) (Object) this;
if (stack.getItemHolder().is(PlayerEXTags.UNBREAKABLE_ITEMS)) {
if (!PlayerEXUtil.isBroken(stack)) {
CompoundTag tag = stack.getTag();
tag.putBoolean("broken", true);
Expand All @@ -99,7 +111,7 @@ public <T extends LivingEntity> void preventBreak(int amount, T entity, Consumer
public void removeBrokenOnRepair(int damage, CallbackInfo ci) {
if (!PlayerEX.CONFIG.getItemBreakingEnabled()) return;

ItemStack stack = (ItemStack)(Object)this;
ItemStack stack = (ItemStack) (Object) this;
if (PlayerEXUtil.isBroken(stack) && damage < stack.getDamageValue()) {
CompoundTag tag = stack.getTag();
tag.putBoolean("broken", false);
Expand All @@ -111,7 +123,7 @@ public void removeBrokenOnRepair(int damage, CallbackInfo ci) {
public void preventArmour(EquipmentSlot slot, CallbackInfoReturnable<Multimap<Attribute, AttributeModifier>> cir) {
if (!PlayerEX.CONFIG.getItemBreakingEnabled()) return;

ItemStack stack = (ItemStack)(Object)this;
ItemStack stack = (ItemStack) (Object) this;
HashMultimap<Attribute, AttributeModifier> hashmap = HashMultimap.create(cir.getReturnValue());
if (PlayerEXUtil.isBroken(stack)) {
PlayerEXUtil.removeModifier(hashmap, Attributes.ARMOR);
Expand Down Expand Up @@ -167,7 +179,9 @@ public void preventArmour(EquipmentSlot slot, CallbackInfoReturnable<Multimap<At
}

@ModifyVariable(method = "getTooltipLines", at = @At(value = "STORE", ordinal = 1), ordinal = 1)
private double playerex$modifyAdditionAttributeKnockback(double original) { return original / 10.0; }
private double playerex$modifyAdditionAttributeKnockback(double original) {
return original / 10.0;
}

// todo: not sure about the implementation(s) here...
@Inject(method = "getTooltipLines", at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z", ordinal = 7, shift = At.Shift.AFTER))
Expand Down Expand Up @@ -238,4 +252,38 @@ public void preventArmour(EquipmentSlot slot, CallbackInfoReturnable<Multimap<At
);
}
}

@SuppressWarnings("AddedMixinMembersNamePattern")
@Unique
@Override
public boolean isWeapon() {
return ((ItemStack) (Object) this).is(PlayerEXTags.WEAPONS);
}

@Inject(method = "save", at = @At("RETURN"))
private void playerex$insertAttributesMap(CompoundTag compoundTag, CallbackInfoReturnable<CompoundTag> cir) {
compoundTag.put(ATTRIBUTES_KEY, ATTRIBUTES_CODEC.encodeFully(NbtSerializer::of, playerex$attributes));
}

@SuppressWarnings("AddedMixinMembersNamePattern")
@Override
public double getAttributeValue(@NotNull RangedAttribute attr) {
if (playerex$attributes == null) {
CompoundTag tag = getOrCreateTag();
playerex$attributes = tag.contains(ATTRIBUTES_KEY) ? ATTRIBUTES_CODEC.decodeFully(NbtDeserializer::of, tag.get(ATTRIBUTES_KEY)) : new HashMap<>();
}

return playerex$attributes.computeIfAbsent(attr.getDescriptionId(), s -> attr.getDefaultValue());
}

@SuppressWarnings("AddedMixinMembersNamePattern")
@Override
public void setAttributeValue(@NotNull RangedAttribute attr, double value) {
if (playerex$attributes == null) {
CompoundTag tag = getOrCreateTag();
playerex$attributes = tag.contains(ATTRIBUTES_KEY) ? ATTRIBUTES_CODEC.decodeFully(NbtDeserializer::of, tag.get(ATTRIBUTES_KEY)) : new HashMap<>();
}

playerex$attributes.put(attr.getDescriptionId(), value);
}
}
5 changes: 4 additions & 1 deletion src/main/kotlin/com/bibireden/playerex/api/PlayerEXTags.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ import net.minecraft.world.item.Item
object PlayerEXTags {
@JvmField
val UNBREAKABLE_ITEMS: TagKey<Item> = TagKey.create(Registries.ITEM, PlayerEX.id("unbreakable"))
}

@JvmField
val WEAPONS: TagKey<Item> = TagKey.create(Registries.ITEM, PlayerEX.id("weapons"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ object PlayerEXAttributes {
@JvmField
val RANGED_CRITICAL_DAMAGE = register("ranged_crit_damage", 0.0, 0.0, 1_000_000.0)

@JvmField
val EXPERIENCE = register("experience", 0.0, 0.0, 1_000_000_000_000.0)

@ApiStatus.Internal
fun register(path: String, base: Double, min: Double, max: Double): RangedAttribute {
val attribute = RangedAttribute("attribute.name.${PlayerEX.MOD_ID}.$path", base, min, max)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.bibireden.playerex.api.items

import net.minecraft.world.entity.ai.attributes.RangedAttribute

interface ItemWithAttributes {
fun getAttributeValue(attr: RangedAttribute): Double
fun setAttributeValue(attr: RangedAttribute, value: Double)
}
25 changes: 25 additions & 0 deletions src/main/kotlin/com/bibireden/playerex/api/items/WeaponItem.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.bibireden.playerex.api.items

import com.bibireden.playerex.api.attribute.PlayerEXAttributes.EXPERIENCE

interface WeaponItem {
fun isWeapon(): Boolean

var xp: Double
get() {
val self = this as ItemWithAttributes

return self.getAttributeValue(EXPERIENCE)
}
set(value) {
val self = this as ItemWithAttributes

self.setAttributeValue(EXPERIENCE, value)
}

companion object {
fun getExpReq(level: Int): Double {
return (1.0 / 100.0) * (level * level) + 50.0
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,15 @@ class PlayerEXConfigModel {
@JvmField
var expNegationFactor: Int = 95

data class WeaponLevelingSettings(
@JvmField
var maxLevel: Int = 500,

@JvmField
var damagePerLevel: Double = 0.1,
)

@JvmField @Nest var weaponLevelingSettings = WeaponLevelingSettings()

enum class Tooltip { Default, Vanilla, PlayerEX }
}
8 changes: 8 additions & 0 deletions src/main/resources/data/playerex/tags/items/weapons.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"values": [
"#minecraft:swords",
"#minecraft:axes",
"minecraft:bow",
"minecraft:trident"
]
}
Loading