diff --git a/CHANGELOG.md b/CHANGELOG.md index 23349bb..2e82090 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ # Changelog ## Unreleased -- / + +- Added `MoreJS.postUpdateOffers` event which triggers after a Villager updates their offers ## [0.13.1] - 2024-09-13 diff --git a/example_scripts/trading.js b/example_scripts/trading.js index ad3ea7b..023b319 100644 --- a/example_scripts/trading.js +++ b/example_scripts/trading.js @@ -81,3 +81,7 @@ MoreJS.updateOffer((event) => { const item = Item.of("minecraft:stick").set("minecraft:attribute_modifiers", attributes); event.offer.output = item; }); + +MoreJS.postUpdateOffers(event => { + event.addTrade(VillagerUtils.createSimpleTrade("minecraft:emerald_block", "minecraft:nether_star")); +}) diff --git a/src/main/java/com/almostreliable/morejs/core/Events.java b/src/main/java/com/almostreliable/morejs/core/Events.java index f801e83..c26a7c2 100644 --- a/src/main/java/com/almostreliable/morejs/core/Events.java +++ b/src/main/java/com/almostreliable/morejs/core/Events.java @@ -9,10 +9,7 @@ import com.almostreliable.morejs.features.structure.StructureAfterPlaceEventJS; import com.almostreliable.morejs.features.structure.StructureLoadEventJS; import com.almostreliable.morejs.features.teleport.EntityTeleportsEventJS; -import com.almostreliable.morejs.features.villager.events.StartTradingEventJS; -import com.almostreliable.morejs.features.villager.events.UpdateOfferEventJS; -import com.almostreliable.morejs.features.villager.events.VillagerTradingEventJS; -import com.almostreliable.morejs.features.villager.events.WandererTradingEventJS; +import com.almostreliable.morejs.features.villager.events.*; import dev.latvian.mods.kubejs.event.EventGroup; import dev.latvian.mods.kubejs.event.EventHandler; @@ -22,7 +19,8 @@ public interface Events { EventHandler WANDERING_TRADING = GROUP.server("wandererTrades", () -> WandererTradingEventJS.class); EventHandler PLAYER_START_TRADING = GROUP.server("playerStartTrading", () -> StartTradingEventJS.class); EventHandler UPDATE_OFFER = GROUP.server("updateOffer", - () -> UpdateOfferEventJS.class).hasResult(); + () -> SingleUpdateOfferEventJS.class).hasResult(); + EventHandler POST_UPDATE_OFFERS = GROUP.server("postUpdateOffers", () -> PostUpdateOfferEventJS.class); EventHandler IS_ENCHANTABLE = GROUP.server("isEnchantable", () -> EnchantmentTableServerEventJS.class); EventHandler ENCHANTMENT_TABLE_CHANGED = GROUP.server("enchantmentTableChanged", diff --git a/src/main/java/com/almostreliable/morejs/features/villager/events/PostUpdateOfferEventJS.java b/src/main/java/com/almostreliable/morejs/features/villager/events/PostUpdateOfferEventJS.java new file mode 100644 index 0000000..a512770 --- /dev/null +++ b/src/main/java/com/almostreliable/morejs/features/villager/events/PostUpdateOfferEventJS.java @@ -0,0 +1,45 @@ +package com.almostreliable.morejs.features.villager.events; + +import com.almostreliable.morejs.core.Events; +import net.minecraft.world.entity.npc.AbstractVillager; +import net.minecraft.world.entity.npc.Villager; +import net.minecraft.world.entity.npc.VillagerProfession; +import net.minecraft.world.entity.npc.VillagerTrades; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; + +import javax.annotation.Nullable; + +public class PostUpdateOfferEventJS extends UpdateOfferEventJS { + + @SuppressWarnings("ConstantValue") + public static void invoke(AbstractVillager villager, MerchantOffers allOffers) { + if (villager instanceof Villager v) { + var data = v.getVillagerData(); + if (data == null) { + return; + } + + if (data.getProfession() == VillagerProfession.NONE) { + return; + } + } + + Events.POST_UPDATE_OFFERS.post(new PostUpdateOfferEventJS(villager, allOffers)); + } + + public PostUpdateOfferEventJS(AbstractVillager villager, MerchantOffers allOffers) { + super(villager, allOffers); + } + + public void addOffer(@Nullable MerchantOffer offer) { + if (offer != null) { + getAllOffers().add(offer); + } + } + + public void addTrade(VillagerTrades.ItemListing trade) { + var offer = trade.getOffer(getEntity(), getRandom()); + addOffer(offer); + } +} diff --git a/src/main/java/com/almostreliable/morejs/features/villager/events/SingleUpdateOfferEventJS.java b/src/main/java/com/almostreliable/morejs/features/villager/events/SingleUpdateOfferEventJS.java new file mode 100644 index 0000000..75ab36e --- /dev/null +++ b/src/main/java/com/almostreliable/morejs/features/villager/events/SingleUpdateOfferEventJS.java @@ -0,0 +1,49 @@ +package com.almostreliable.morejs.features.villager.events; + +import com.google.common.base.Preconditions; +import net.minecraft.world.entity.npc.AbstractVillager; +import net.minecraft.world.entity.npc.VillagerTrades; +import net.minecraft.world.item.trading.MerchantOffer; +import net.minecraft.world.item.trading.MerchantOffers; + +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.List; + +public class SingleUpdateOfferEventJS extends UpdateOfferEventJS { + + private final VillagerTrades.ItemListing[] possibleTrades; + private MerchantOffer offer; + @Nullable private List cachedWandererTrades; + + public SingleUpdateOfferEventJS(AbstractVillager villager, MerchantOffers allOffers, VillagerTrades.ItemListing[] possibleTrades, MerchantOffer offer) { + super(villager, allOffers); + this.possibleTrades = possibleTrades; + this.offer = offer; + } + + public List getUsedTrades() { + return Arrays.asList(possibleTrades); + } + + public MerchantOffer getOffer() { + return offer; + } + + public void setOffer(MerchantOffer offer) { + Preconditions.checkNotNull(offer, "Offer must not be null"); + this.offer = offer; + } + + public void setOffer(VillagerTrades.ItemListing trade) { + MerchantOffer newOffer = trade.getOffer(getEntity(), getLevel().getRandom()); + if (newOffer != null) { + this.offer = newOffer; + } + } + + @Nullable + public MerchantOffer createRandomOffer() { + return createRandomOffer(getUsedTrades()); + } +} diff --git a/src/main/java/com/almostreliable/morejs/features/villager/events/UpdateOfferEventJS.java b/src/main/java/com/almostreliable/morejs/features/villager/events/UpdateOfferEventJS.java index ba7c379..33e4538 100644 --- a/src/main/java/com/almostreliable/morejs/features/villager/events/UpdateOfferEventJS.java +++ b/src/main/java/com/almostreliable/morejs/features/villager/events/UpdateOfferEventJS.java @@ -1,7 +1,6 @@ package com.almostreliable.morejs.features.villager.events; import com.almostreliable.morejs.features.villager.VillagerUtils; -import com.google.common.base.Preconditions; import dev.latvian.mods.kubejs.entity.KubeLivingEntityEvent; import net.minecraft.util.RandomSource; import net.minecraft.world.entity.LivingEntity; @@ -14,19 +13,15 @@ import java.util.Arrays; import java.util.List; -public class UpdateOfferEventJS implements KubeLivingEntityEvent { +public abstract class UpdateOfferEventJS implements KubeLivingEntityEvent { private final AbstractVillager villager; private final MerchantOffers allOffers; - private final VillagerTrades.ItemListing[] possibleTrades; - private MerchantOffer offer; @Nullable private List cachedWandererTrades; - public UpdateOfferEventJS(AbstractVillager villager, MerchantOffers allOffers, VillagerTrades.ItemListing[] possibleTrades, MerchantOffer offer) { + public UpdateOfferEventJS(AbstractVillager villager, MerchantOffers allOffers) { this.villager = villager; this.allOffers = allOffers; - this.possibleTrades = possibleTrades; - this.offer = offer; } public RandomSource getRandom() { @@ -88,31 +83,6 @@ public MerchantOffers getAllOffers() { return allOffers; } - public List getUsedTrades() { - return Arrays.asList(possibleTrades); - } - - public MerchantOffer getOffer() { - return offer; - } - - public void setOffer(MerchantOffer offer) { - Preconditions.checkNotNull(offer, "Offer must not be null"); - this.offer = offer; - } - - public void setOffer(VillagerTrades.ItemListing trade) { - MerchantOffer newOffer = trade.getOffer(villager, getLevel().getRandom()); - if (newOffer != null) { - this.offer = newOffer; - } - } - - @Nullable - public MerchantOffer createRandomOffer() { - return createRandomOffer(getUsedTrades()); - } - @Nullable public MerchantOffer createRandomOffer(List possibleTrades) { if (possibleTrades.isEmpty()) { @@ -121,7 +91,7 @@ public MerchantOffer createRandomOffer(List possible int i = getLevel().getRandom().nextInt(possibleTrades.size()); VillagerTrades.ItemListing randomListing = possibleTrades.get(i); - return randomListing.getOffer(villager, getLevel().getRandom()); + return randomListing.getOffer(getEntity(), getLevel().getRandom()); } public List getVillagerTrades(VillagerProfession profession) { diff --git a/src/main/java/com/almostreliable/morejs/mixin/villager/AbstractVillagerMixin.java b/src/main/java/com/almostreliable/morejs/mixin/villager/AbstractVillagerMixin.java index dbf7a51..48b6109 100644 --- a/src/main/java/com/almostreliable/morejs/mixin/villager/AbstractVillagerMixin.java +++ b/src/main/java/com/almostreliable/morejs/mixin/villager/AbstractVillagerMixin.java @@ -1,7 +1,7 @@ package com.almostreliable.morejs.mixin.villager; import com.almostreliable.morejs.core.Events; -import com.almostreliable.morejs.features.villager.events.UpdateOfferEventJS; +import com.almostreliable.morejs.features.villager.events.SingleUpdateOfferEventJS; import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.ref.LocalIntRef; import net.minecraft.world.entity.npc.AbstractVillager; @@ -18,7 +18,7 @@ public class AbstractVillagerMixin { @Redirect(method = "addOffersFromItemListings", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/trading/MerchantOffers;add(Ljava/lang/Object;)Z")) private boolean mid$foo(MerchantOffers offers, Object o, MerchantOffers givenMerchantOffers, VillagerTrades.ItemListing[] possibleTrades, int maxNumbers, @Local(ordinal = 1) LocalIntRef i) { MerchantOffer offer = (MerchantOffer) o; - var e = new UpdateOfferEventJS((AbstractVillager) (Object) this, offers, possibleTrades, offer); + var e = new SingleUpdateOfferEventJS((AbstractVillager) (Object) this, offers, possibleTrades, offer); if (Events.UPDATE_OFFER.post(e).interruptFalse()) { i.set(i.get() - 1); return false; diff --git a/src/main/java/com/almostreliable/morejs/mixin/villager/VillagerMixin.java b/src/main/java/com/almostreliable/morejs/mixin/villager/VillagerMixin.java new file mode 100644 index 0000000..de351cc --- /dev/null +++ b/src/main/java/com/almostreliable/morejs/mixin/villager/VillagerMixin.java @@ -0,0 +1,19 @@ +package com.almostreliable.morejs.mixin.villager; + +import com.almostreliable.morejs.features.villager.events.PostUpdateOfferEventJS; +import net.minecraft.world.entity.npc.AbstractVillager; +import net.minecraft.world.entity.npc.Villager; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Villager.class) +public class VillagerMixin { + + @Inject(method = "updateTrades", at = @At(value = "RETURN")) + private void morejs$invokePostUpdateOffer(CallbackInfo ci) { + var self = (AbstractVillager) (Object) this; + PostUpdateOfferEventJS.invoke(self, self.getOffers()); + } +} diff --git a/src/main/java/com/almostreliable/morejs/mixin/villager/WanderingTraderMixin.java b/src/main/java/com/almostreliable/morejs/mixin/villager/WanderingTraderMixin.java index c02a343..5124bc1 100644 --- a/src/main/java/com/almostreliable/morejs/mixin/villager/WanderingTraderMixin.java +++ b/src/main/java/com/almostreliable/morejs/mixin/villager/WanderingTraderMixin.java @@ -1,7 +1,8 @@ package com.almostreliable.morejs.mixin.villager; import com.almostreliable.morejs.core.Events; -import com.almostreliable.morejs.features.villager.events.UpdateOfferEventJS; +import com.almostreliable.morejs.features.villager.events.PostUpdateOfferEventJS; +import com.almostreliable.morejs.features.villager.events.SingleUpdateOfferEventJS; import net.minecraft.world.entity.npc.AbstractVillager; import net.minecraft.world.entity.npc.VillagerTrades; import net.minecraft.world.entity.npc.WanderingTrader; @@ -9,7 +10,9 @@ import net.minecraft.world.item.trading.MerchantOffers; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(WanderingTrader.class) public abstract class WanderingTraderMixin { @@ -17,7 +20,7 @@ public abstract class WanderingTraderMixin { @Redirect(method = "updateTrades", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/trading/MerchantOffers;add(Ljava/lang/Object;)Z")) private boolean mid$foo(MerchantOffers offers, Object o) { MerchantOffer offer = (MerchantOffer) o; - var e = new UpdateOfferEventJS((AbstractVillager) (Object) this, + var e = new SingleUpdateOfferEventJS((AbstractVillager) (Object) this, offers, VillagerTrades.WANDERING_TRADER_TRADES.get(2), offer); @@ -27,4 +30,10 @@ public abstract class WanderingTraderMixin { return offers.add(e.getOffer()); } + + @Inject(method = "updateTrades", at = @At(value = "RETURN")) + private void morejs$invokePostUpdateOffer(CallbackInfo ci) { + var self = (AbstractVillager) (Object) this; + PostUpdateOfferEventJS.invoke(self, self.getOffers()); + } } diff --git a/src/main/resources/morejs.mixins.json b/src/main/resources/morejs.mixins.json index d9f3ea8..e56d337 100644 --- a/src/main/resources/morejs.mixins.json +++ b/src/main/resources/morejs.mixins.json @@ -16,6 +16,7 @@ "villager.MerchantMenuMixin", "villager.MerchantOfferAccessor", "villager.MerchantOfferMixin", + "villager.VillagerMixin", "villager.VillagerTradesMixin$EmeraldForItemsMixin", "villager.VillagerTradesMixin$EmeraldsForVillagerTypeItemMixin", "villager.VillagerTradesMixin$EnchantBookForEmeraldsMixin",