Skip to content

Commit

Permalink
feat: Add PreventTrinketEquipPower and PreventTrinketUnequipPower powers
Browse files Browse the repository at this point in the history
  • Loading branch information
shap-po committed Aug 28, 2024
1 parent 7fa0bf2 commit 014e98d
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 1 deletion.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,29 @@ All fields are optional.
}
```
### Prevent trinket equip/unequip
This power prevents the player from equipping or unequipping a trinket.
```jsonc
{
"type": "shappoli:prevent_trinket_equip", // or shappoli:prevent_trinket_unequip
"slot": {
// Trinket slot, optional
},
"slots": [
// List of trinket slots, optional
],
"item_condition": {
// Item condition, optional
},
"entity_condition": {
// Entity condition, optional
},
"allow_in_creative": true // Allow in creative mode, default true
}
```
### Modify villager reputation
This power modifies the player's reputation with a villager, which is used to determine the prices of trades.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.github.shap_po.shappoli.integration.trinkets.power;

import com.github.shap_po.shappoli.Shappoli;
import com.github.shap_po.shappoli.integration.trinkets.data.ShappoliTrinketsDataTypes;
import com.github.shap_po.shappoli.integration.trinkets.data.TrinketSlotData;
import com.github.shap_po.shappoli.integration.trinkets.util.TrinketsUtil;
import dev.emi.trinkets.api.SlotReference;
import io.github.apace100.apoli.data.ApoliDataTypes;
import io.github.apace100.apoli.power.Power;
import io.github.apace100.apoli.power.PowerType;
import io.github.apace100.apoli.power.factory.PowerFactory;
import io.github.apace100.calio.data.SerializableData;
import io.github.apace100.calio.data.SerializableDataTypes;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Pair;
import net.minecraft.world.World;

import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;

public abstract class BasePreventTrinketPower extends Power {
protected final Predicate<Entity> entityCondition;
protected final Predicate<Pair<World, ItemStack>> itemCondition;
protected final List<TrinketSlotData> slots;
protected final boolean allowCreative;

public BasePreventTrinketPower(
PowerType<?> type,
LivingEntity entity,
Predicate<Entity> entityCondition,
Predicate<Pair<World, ItemStack>> itemCondition,
List<TrinketSlotData> slots,
boolean allowInCreative
) {
super(type, entity);
this.entityCondition = entityCondition;
this.itemCondition = itemCondition;
this.slots = slots;
this.allowCreative = allowInCreative;
}

public boolean doesApply(LivingEntity actor, SlotReference slotReference, ItemStack item) {
if ((actor instanceof PlayerEntity player) && player.isCreative() && allowCreative) {
return false;
}
return (
(slots.isEmpty() || slots.stream().anyMatch(slot -> slot.test(slotReference))) &&
(entityCondition == null || entityCondition.test(actor)) &&
(itemCondition == null || itemCondition.test(TrinketsUtil.getItemConditionPair(actor, item)))
);
}

@FunctionalInterface
public interface Factory {
BasePreventTrinketPower create(
PowerType<?> type,
LivingEntity entity,
Predicate<Entity> entityCondition,
Predicate<Pair<World, ItemStack>> itemCondition,
List<TrinketSlotData> slots,
boolean allowInCreative
);
}


public static PowerFactory createFactory(String identifier, Factory serializerFactory) {
Objects.requireNonNull(serializerFactory);
return new PowerFactory<>(
Shappoli.identifier(identifier),
new SerializableData()
.add("entity_condition", ApoliDataTypes.ENTITY_CONDITION, null)
.add("item_condition", ApoliDataTypes.ITEM_CONDITION, null)
.add("slot", ShappoliTrinketsDataTypes.TRINKET_SLOT, null)
.add("slots", ShappoliTrinketsDataTypes.TRINKET_SLOTS, null)
.add("allow_in_creative", SerializableDataTypes.BOOLEAN, true),
data -> (type, player) ->
serializerFactory.create(
type,
player,
data.get("entity_condition"),
data.get("item_condition"),
TrinketSlotData.getSlots(data),
data.getBoolean("allow_in_creative")
)
).allowCondition();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.github.shap_po.shappoli.integration.trinkets.power;

import com.github.shap_po.shappoli.integration.trinkets.data.TrinketSlotData;
import io.github.apace100.apoli.power.PowerType;
import io.github.apace100.apoli.power.factory.PowerFactory;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Pair;
import net.minecraft.world.World;

import java.util.List;
import java.util.function.Predicate;

public class PreventTrinketEquipPower extends BasePreventTrinketPower {
public PreventTrinketEquipPower(
PowerType<?> type,
LivingEntity entity,
Predicate<Entity> entityCondition,
Predicate<Pair<World, ItemStack>> itemCondition,
List<TrinketSlotData> slots,
boolean allowCreative
) {
super(type, entity, entityCondition, itemCondition, slots, allowCreative);
}

public static PowerFactory createFactory() {
return createFactory("prevent_trinket_equip", PreventTrinketEquipPower::new);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.github.shap_po.shappoli.integration.trinkets.power;

import com.github.shap_po.shappoli.integration.trinkets.data.TrinketSlotData;
import io.github.apace100.apoli.power.PowerType;
import io.github.apace100.apoli.power.factory.PowerFactory;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Pair;
import net.minecraft.world.World;

import java.util.List;
import java.util.function.Predicate;

public class PreventTrinketUnequipPower extends BasePreventTrinketPower {
public PreventTrinketUnequipPower(
PowerType<?> type,
LivingEntity entity,
Predicate<Entity> entityCondition,
Predicate<Pair<World, ItemStack>> itemCondition,
List<TrinketSlotData> slots,
boolean allowCreative
) {
super(type, entity, entityCondition, itemCondition, slots, allowCreative);
}

public static PowerFactory createFactory() {
return createFactory("prevent_trinket_unequip", PreventTrinketUnequipPower::new);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.github.shap_po.shappoli.mixin.integration.trinkets;

import com.github.shap_po.shappoli.integration.trinkets.power.PreventTrinketUnequipPower;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import dev.emi.trinkets.SurvivalTrinketSlot;
import dev.emi.trinkets.api.SlotReference;
import dev.emi.trinkets.api.TrinketInventory;
import io.github.apace100.apoli.component.PowerHolderComponent;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack;
import net.minecraft.screen.slot.Slot;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;

@Mixin(SurvivalTrinketSlot.class)
public class SurvivalTrinketSlotMixin extends Slot {
@Shadow
@Final
private TrinketInventory trinketInventory;
@Shadow
@Final
private int slotOffset;

public SurvivalTrinketSlotMixin(Inventory inventory, int index, int x, int y) {
super(inventory, index, x, y);
}

@ModifyReturnValue(method = "canTakeItems", at = @At("RETURN"), remap = false)
public boolean shappoli$preventTrinketUnequip(boolean original, PlayerEntity player) {
ItemStack stack = this.getStack();
SlotReference slotRef = new SlotReference(trinketInventory, slotOffset);

return !PowerHolderComponent.hasPower(player, PreventTrinketUnequipPower.class,
p -> p.doesApply(player, slotRef, stack)
) && original;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.github.shap_po.shappoli.mixin.integration.trinkets;

import com.github.shap_po.shappoli.integration.trinkets.power.PreventTrinketEquipPower;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import dev.emi.trinkets.TrinketSlot;
import dev.emi.trinkets.api.SlotReference;
import io.github.apace100.apoli.component.PowerHolderComponent;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;

@Mixin(TrinketSlot.class)
public interface TrinketSlotMixin {
@ModifyReturnValue(method = "canInsert", at = @At("RETURN"), remap = false)
private static boolean shappoli$preventTrinketEquip(boolean original, ItemStack stack, SlotReference slotRef, LivingEntity entity) {
return !PowerHolderComponent.hasPower(entity, PreventTrinketEquipPower.class,
p -> p.doesApply(entity, slotRef, stack)
) && original;
}
}
4 changes: 3 additions & 1 deletion src/main/resources/shappoli.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
"compatibilityLevel": "JAVA_17",
"mixins": [
"VillagerEntityMixin",
"integration.trinkets.LivingEntityMixin"
"integration.trinkets.LivingEntityMixin",
"integration.trinkets.SurvivalTrinketSlotMixin",
"integration.trinkets.TrinketSlotMixin"
],
"injectors": {
"defaultRequire": 1
Expand Down
35 changes: 35 additions & 0 deletions src/test/resources/data/shappoli/powers/trinket/prevent_equip.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"type": "apoli:multiple",

"prevent_equip": {
"type": "shappoli:prevent_trinket_equip",
"slot": {
"group": "feet"
},
"slots": [
{
"name": "necklace"
}
],
"item_condition": {
"type": "apoli:empty",
"inverted": true
}
},

"prevent_unequip": {
"type": "shappoli:prevent_trinket_unequip",
"slot": {
"group": "feet"
},
"slots": [
{
"name": "necklace"
}
],
"item_condition": {
"type": "apoli:empty",
"inverted": true
}
}
}

0 comments on commit 014e98d

Please sign in to comment.