mirror of
https://github.com/architectury/architectury-api.git
synced 2026-04-02 13:37:43 -05:00
Feature/villager trades (#122)
* Add modify and removing for villager trades - Add mixin for villager trades - Add methods to register modify and removing - Implement base for VillagerMixin to provide additional villager data - Basic Access & Mixin change - Add AT and AW - Add overriding for max offers a villager or the wanderer can have * Add rare check for wandering trader * Remove todo comment * rename some methods * Solve reviews for #122 Move non api stuff into TradeRegistryData Rename fields in MerchantOfferAccess Move trade stuff into internal package Mark internal trade classes as ApiStatus.Internal * Minor refactors (discussed on Discord) * Add doc for AbstractVillagerMixin * Reformat code * Update gradle.properties Co-authored-by: Max <maxh2709@gmail.com>
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* This file is part of architectury.
|
||||
* Copyright (C) 2020, 2021 architectury
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package me.shedaniel.architectury.mixin;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import me.shedaniel.architectury.registry.trade.TradeRegistry;
|
||||
import me.shedaniel.architectury.registry.trade.impl.OfferMixingContext;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
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 net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* {@link AbstractVillager#addOffersFromItemListings(MerchantOffers, VillagerTrades.ItemListing[], int)} creates
|
||||
* a {@link Set} with x random integer from {@link VillagerTrades.ItemListing} array indexes to iterate through.
|
||||
* <p>
|
||||
* If we use {@link TradeRegistry} to remove one offer from a villager
|
||||
* we will end up with just x-1 offers but we still want to have x offers (as long there are enough) for a villager if
|
||||
* there are still {@link VillagerTrades.ItemListing} left.
|
||||
* <p>
|
||||
* To solve this we override the iterator with our own iterator which iterate through all indexes.
|
||||
* As soon {@link OfferMixingContext#maxOffers} offers are created we skip the remaining elements in the iterator {@link OfferMixingContext#skipIteratorIfMaxOffersReached()}.
|
||||
*/
|
||||
@Mixin(AbstractVillager.class)
|
||||
public abstract class AbstractVillagerMixin extends Entity {
|
||||
public AbstractVillagerMixin(EntityType<?> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private final ThreadLocal<OfferMixingContext> offerContext = new ThreadLocal<>();
|
||||
|
||||
|
||||
@Redirect(
|
||||
method = "addOffersFromItemListings(Lnet/minecraft/world/item/trading/MerchantOffers;[Lnet/minecraft/world/entity/npc/VillagerTrades$ItemListing;I)V",
|
||||
at = @At(value = "INVOKE", target = "Ljava/util/Set;iterator()Ljava/util/Iterator;")
|
||||
)
|
||||
public Iterator<Integer> overrideIterator(Set<Integer> set, MerchantOffers offers, VillagerTrades.ItemListing[] itemListings, int maxOffers) {
|
||||
OfferMixingContext context = new OfferMixingContext(MoreObjects.firstNonNull(architectury$getMaxOfferOverride(), maxOffers), itemListings, random);
|
||||
offerContext.set(context);
|
||||
return context.getIterator();
|
||||
}
|
||||
|
||||
@ModifyVariable(
|
||||
method = "addOffersFromItemListings(Lnet/minecraft/world/item/trading/MerchantOffers;[Lnet/minecraft/world/entity/npc/VillagerTrades$ItemListing;I)V",
|
||||
at = @At(value = "STORE"),
|
||||
ordinal = 0
|
||||
)
|
||||
public MerchantOffer handleOffer(MerchantOffer offer) {
|
||||
OfferMixingContext context = offerContext.get();
|
||||
|
||||
if (offer == null || context.getMaxOffers() == 0) {
|
||||
context.skipIteratorIfMaxOffersReached();
|
||||
return null;
|
||||
}
|
||||
|
||||
MerchantOffer handledOffer = architectury$handleOffer(offer);
|
||||
if (handledOffer != null) {
|
||||
context.skipIteratorIfMaxOffersReached();
|
||||
}
|
||||
|
||||
return handledOffer;
|
||||
}
|
||||
|
||||
public MerchantOffer architectury$handleOffer(MerchantOffer offer) {
|
||||
return offer;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer architectury$getMaxOfferOverride() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This file is part of architectury.
|
||||
* Copyright (C) 2020, 2021 architectury
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package me.shedaniel.architectury.mixin;
|
||||
|
||||
import me.shedaniel.architectury.registry.trade.VillagerTradeOfferContext;
|
||||
import me.shedaniel.architectury.registry.trade.impl.TradeRegistryData;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.npc.Villager;
|
||||
import net.minecraft.world.entity.npc.VillagerData;
|
||||
import net.minecraft.world.item.trading.MerchantOffer;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(Villager.class)
|
||||
public abstract class VillagerMixin extends AbstractVillagerMixin {
|
||||
|
||||
public VillagerMixin(EntityType<?> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
}
|
||||
|
||||
@Shadow
|
||||
public abstract VillagerData getVillagerData();
|
||||
|
||||
@Override
|
||||
public MerchantOffer architectury$handleOffer(MerchantOffer offer) {
|
||||
VillagerData vd = getVillagerData();
|
||||
|
||||
VillagerTradeOfferContext context = new VillagerTradeOfferContext(vd, offer, this, random);
|
||||
|
||||
boolean removeResult = TradeRegistryData.invokeVillagerOfferRemoving(context);
|
||||
if (removeResult) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TradeRegistryData.invokeVillagerOfferModify(context);
|
||||
return offer;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Integer architectury$getMaxOfferOverride() {
|
||||
return TradeRegistryData.getVillagerMaxOffers(getVillagerData().getProfession(), getVillagerData().getLevel());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* This file is part of architectury.
|
||||
* Copyright (C) 2020, 2021 architectury
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package me.shedaniel.architectury.mixin;
|
||||
|
||||
import me.shedaniel.architectury.registry.trade.WanderingTraderOfferContext;
|
||||
import me.shedaniel.architectury.registry.trade.impl.TradeRegistryData;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.npc.VillagerTrades;
|
||||
import net.minecraft.world.entity.npc.WanderingTrader;
|
||||
import net.minecraft.world.item.trading.MerchantOffer;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
|
||||
@Mixin(WanderingTrader.class)
|
||||
public abstract class WanderingTraderMixin extends AbstractVillagerMixin {
|
||||
public WanderingTraderMixin(EntityType<?> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private final ThreadLocal<VillagerTrades.ItemListing> vanillaSelectedItemListing = new ThreadLocal<>();
|
||||
|
||||
@ModifyVariable(
|
||||
method = "updateTrades()V",
|
||||
at = @At(value = "INVOKE_ASSIGN"),
|
||||
ordinal = 0
|
||||
)
|
||||
public VillagerTrades.ItemListing storeItemListing(VillagerTrades.ItemListing itemListing) {
|
||||
vanillaSelectedItemListing.set(itemListing);
|
||||
return itemListing;
|
||||
}
|
||||
|
||||
@ModifyVariable(
|
||||
method = "updateTrades()V",
|
||||
at = @At(value = "INVOKE_ASSIGN"),
|
||||
ordinal = 0
|
||||
)
|
||||
public MerchantOffer handleSecondListingOffer(MerchantOffer offer) {
|
||||
if (offer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return invokeWanderingTraderEvents(offer, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MerchantOffer architectury$handleOffer(MerchantOffer offer) {
|
||||
return invokeWanderingTraderEvents(offer, false);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MerchantOffer invokeWanderingTraderEvents(MerchantOffer offer, boolean rare) {
|
||||
WanderingTraderOfferContext context = new WanderingTraderOfferContext(offer, rare, this, random);
|
||||
boolean removeResult = TradeRegistryData.invokeWanderingTraderOfferRemoving(context);
|
||||
if (removeResult) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TradeRegistryData.invokeWanderingTraderOfferModify(context);
|
||||
return offer;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Integer architectury$getMaxOfferOverride() {
|
||||
return TradeRegistryData.getWanderingTraderMaxOffers();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* This file is part of architectury.
|
||||
* Copyright (C) 2020, 2021 architectury
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package me.shedaniel.architectury.registry.trade;
|
||||
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.trading.MerchantOffer;
|
||||
|
||||
public class MerchantOfferAccess {
|
||||
private final MerchantOffer offer;
|
||||
|
||||
MerchantOfferAccess(MerchantOffer offer) {
|
||||
this.offer = offer;
|
||||
}
|
||||
|
||||
public ItemStack getCostA() {
|
||||
return offer.getBaseCostA();
|
||||
}
|
||||
|
||||
public void setCostA(ItemStack itemStack) {
|
||||
offer.baseCostA = itemStack.copy();
|
||||
}
|
||||
|
||||
public ItemStack getCostB() {
|
||||
return offer.getCostB();
|
||||
}
|
||||
|
||||
public void setCostB(ItemStack itemStack) {
|
||||
offer.costB = itemStack.copy();
|
||||
}
|
||||
|
||||
public ItemStack getResult() {
|
||||
return offer.getResult();
|
||||
}
|
||||
|
||||
public void setResult(ItemStack itemStack) {
|
||||
offer.result = itemStack.copy();
|
||||
}
|
||||
|
||||
public int getMaxUses() {
|
||||
return offer.getMaxUses();
|
||||
}
|
||||
|
||||
public void setMaxUses(int maxUses) {
|
||||
offer.maxUses = maxUses;
|
||||
}
|
||||
|
||||
public float getPriceMultiplier() {
|
||||
return offer.getPriceMultiplier();
|
||||
}
|
||||
|
||||
public void setPriceMultiplier(float priceMultiplier) {
|
||||
offer.priceMultiplier = priceMultiplier;
|
||||
}
|
||||
|
||||
public int getXp() {
|
||||
return offer.getXp();
|
||||
}
|
||||
|
||||
public void setXp(int xp) {
|
||||
offer.xp = xp;
|
||||
}
|
||||
|
||||
public MerchantOffer getOffer() {
|
||||
return offer;
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ public class SimpleTrade implements VillagerTrades.ItemListing {
|
||||
* You can take a look at all the values the vanilla game uses right here {@link VillagerTrades#TRADES}.
|
||||
*
|
||||
* @param primaryPrice The first price a player has to pay to get the 'sale' stack.
|
||||
* @param secondaryPrice A optional, secondary price to pay as well as the primary one. If not needed just use {@link ItemStack#EMPTY}.
|
||||
* @param secondaryPrice An optional, secondary price to pay as well as the primary one. If not needed just use {@link ItemStack#EMPTY}.
|
||||
* @param sale The ItemStack which a player can purchase in exchange for the two prices.
|
||||
* @param maxTrades The amount of trades one villager or wanderer can do. When the amount is surpassed, the trade can't be purchased anymore.
|
||||
* @param experiencePoints How much experience points does the player get, when trading. Vanilla uses between 2 and 30 for this.
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file is part of architectury.
|
||||
* Copyright (C) 2020, 2021 architectury
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package me.shedaniel.architectury.registry.trade;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.item.trading.MerchantOffer;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public abstract class TradeOfferContext {
|
||||
private final MerchantOfferAccess offer;
|
||||
private final Entity entity;
|
||||
private final Random random;
|
||||
|
||||
public TradeOfferContext(MerchantOffer offer, Entity entity, Random random) {
|
||||
this.offer = new MerchantOfferAccess(offer);
|
||||
this.entity = entity;
|
||||
this.random = random;
|
||||
}
|
||||
|
||||
public MerchantOfferAccess getOffer() {
|
||||
return offer;
|
||||
}
|
||||
|
||||
public Entity getEntity() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
public Random getRandom() {
|
||||
return random;
|
||||
}
|
||||
}
|
||||
@@ -20,9 +20,16 @@
|
||||
package me.shedaniel.architectury.registry.trade;
|
||||
|
||||
import dev.architectury.injectables.annotations.ExpectPlatform;
|
||||
import me.shedaniel.architectury.registry.trade.impl.TradeRegistryData;
|
||||
import net.minecraft.world.entity.npc.VillagerProfession;
|
||||
import net.minecraft.world.entity.npc.VillagerTrades;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class TradeRegistry {
|
||||
private TradeRegistry() {
|
||||
}
|
||||
@@ -47,6 +54,71 @@ public class TradeRegistry {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the max possible offers a villager can have by its profession and level.
|
||||
*
|
||||
* @param profession The Profession of the villager.
|
||||
* @param level The level of the villager. Vanilla range is 1 to 5, however mods may extend that upper limit further.
|
||||
* @param maxOffers Max possible offers a villager can have.
|
||||
*/
|
||||
public static void setVillagerMaxOffers(VillagerProfession profession, int level, int maxOffers) {
|
||||
if (level < 1) {
|
||||
throw new IllegalArgumentException("Villager Trade level has to be at least 1!");
|
||||
}
|
||||
|
||||
if (maxOffers < 0) {
|
||||
throw new IllegalArgumentException("Villager's max offers has to be at least 0!");
|
||||
}
|
||||
|
||||
Map<Integer, Integer> map = TradeRegistryData.VILLAGER_MAX_OFFER_OVERRIDES.computeIfAbsent(profession, k -> new HashMap<>());
|
||||
map.put(level, maxOffers);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register a callback which provide {@link VillagerTradeOfferContext} to modify the given offer from a villager.
|
||||
* The callback gets called when {@link net.minecraft.world.entity.npc.Villager} generates their offer list.
|
||||
*
|
||||
* @param callback The callback to handle modification for the given offer context.
|
||||
*/
|
||||
public static void modifyVillagerOffers(Consumer<VillagerTradeOfferContext> callback) {
|
||||
Objects.requireNonNull(callback);
|
||||
TradeRegistryData.VILLAGER_MODIFY_HANDLERS.add(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a filter which provide {@link VillagerTradeOfferContext} to test the given offer from a villager.
|
||||
* The filter gets called when {@link net.minecraft.world.entity.npc.Villager} generates their offer list.
|
||||
*
|
||||
* @param filter The filter to test if an offer should be removed. Returning true means the offer will be removed.
|
||||
*/
|
||||
public static void removeVillagerOffers(Predicate<VillagerTradeOfferContext> filter) {
|
||||
Objects.requireNonNull(filter);
|
||||
TradeRegistryData.VILLAGER_REMOVE_HANDLERS.add(filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback which provide {@link WanderingTraderOfferContext} to modify the given offer from the wandering trader.
|
||||
* The callback gets called when {@link net.minecraft.world.entity.npc.WanderingTrader} generates their offer list.
|
||||
*
|
||||
* @param callback The callback to handle modification for the given offer context.
|
||||
*/
|
||||
public static void modifyWanderingTraderOffers(Consumer<WanderingTraderOfferContext> callback) {
|
||||
Objects.requireNonNull(callback);
|
||||
TradeRegistryData.WANDERING_TRADER_MODIFY_HANDLERS.add(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a filter which provide {@link WanderingTraderOfferContext} to test the given offer from the wandering trader.
|
||||
* The filter gets called when {@link net.minecraft.world.entity.npc.WanderingTrader} generates their offer list.
|
||||
*
|
||||
* @param filter The filter to test if an offer should be removed. Returning true means the offer will be removed.
|
||||
*/
|
||||
public static void removeWanderingTraderOffers(Predicate<WanderingTraderOfferContext> filter) {
|
||||
Objects.requireNonNull(filter);
|
||||
TradeRegistryData.WANDERING_TRADER_REMOVE_HANDLERS.add(filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a trade ({@link VillagerTrades.ItemListing}) to a wandering trader by its rarity.
|
||||
* When the mod loader is Forge, the {@code WandererTradesEvent} event is used.
|
||||
@@ -59,4 +131,16 @@ public class TradeRegistry {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the max possible offers the wandering trader can have. This does not affect the rare trade.
|
||||
*
|
||||
* @param maxOffers Max possible offers a villager can have.
|
||||
*/
|
||||
public static void setWanderingTraderMaxOffers(int maxOffers) {
|
||||
if (maxOffers < 0) {
|
||||
throw new IllegalArgumentException("Wandering trader's max offers has to be at least 0!");
|
||||
}
|
||||
|
||||
TradeRegistryData.wanderingTraderMaxOfferOverride = maxOffers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* This file is part of architectury.
|
||||
* Copyright (C) 2020, 2021 architectury
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package me.shedaniel.architectury.registry.trade;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.npc.VillagerData;
|
||||
import net.minecraft.world.entity.npc.VillagerProfession;
|
||||
import net.minecraft.world.entity.npc.VillagerType;
|
||||
import net.minecraft.world.item.trading.MerchantOffer;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class VillagerTradeOfferContext extends TradeOfferContext {
|
||||
|
||||
private final VillagerProfession profession;
|
||||
private final int level;
|
||||
private final VillagerType type;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public VillagerTradeOfferContext(VillagerData vd, MerchantOffer offer, Entity entity, Random random) {
|
||||
super(offer, entity, random);
|
||||
this.profession = vd.getProfession();
|
||||
this.level = vd.getLevel();
|
||||
this.type = vd.getType();
|
||||
}
|
||||
|
||||
public VillagerProfession getProfession() {
|
||||
return profession;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public VillagerType getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package me.shedaniel.architectury.registry.trade;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.item.trading.MerchantOffer;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class WanderingTraderOfferContext extends TradeOfferContext {
|
||||
private final boolean rare;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public WanderingTraderOfferContext(MerchantOffer offer, boolean rare, Entity entity, Random random) {
|
||||
super(offer, entity, random);
|
||||
this.rare = rare;
|
||||
}
|
||||
|
||||
public boolean isRare() {
|
||||
return rare;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* This file is part of architectury.
|
||||
* Copyright (C) 2020, 2021 architectury
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package me.shedaniel.architectury.registry.trade.impl;
|
||||
|
||||
import net.minecraft.world.entity.npc.VillagerTrades;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public class OfferMixingContext {
|
||||
private int currentIndex;
|
||||
private final int maxOffers;
|
||||
private final Iterator<Integer> iterator;
|
||||
private final VillagerTrades.ItemListing[] itemListings;
|
||||
private final Random random;
|
||||
|
||||
public OfferMixingContext(int maxOffers, VillagerTrades.ItemListing[] itemListings, Random random) {
|
||||
this.currentIndex = 0;
|
||||
this.maxOffers = Math.min(maxOffers, itemListings.length);
|
||||
this.itemListings = itemListings;
|
||||
this.random = random;
|
||||
|
||||
List<Integer> shuffled = createShuffledIndexList();
|
||||
this.iterator = shuffled.iterator();
|
||||
}
|
||||
|
||||
public void skipIteratorIfMaxOffersReached() {
|
||||
currentIndex++;
|
||||
if (currentIndex >= getMaxOffers()) {
|
||||
skip();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Iterator<Integer> getIterator() {
|
||||
return iterator;
|
||||
}
|
||||
|
||||
private void skip() {
|
||||
iterator.forEachRemaining(($) -> {
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<Integer> createShuffledIndexList() {
|
||||
List<Integer> shuffledListings = new ArrayList<>();
|
||||
for (int i = 0; i < itemListings.length; i++) {
|
||||
shuffledListings.add(i);
|
||||
}
|
||||
Collections.shuffle(shuffledListings, random);
|
||||
return shuffledListings;
|
||||
}
|
||||
|
||||
public int getMaxOffers() {
|
||||
return maxOffers;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package me.shedaniel.architectury.registry.trade.impl;
|
||||
|
||||
import me.shedaniel.architectury.registry.trade.VillagerTradeOfferContext;
|
||||
import me.shedaniel.architectury.registry.trade.WanderingTraderOfferContext;
|
||||
import net.minecraft.world.entity.npc.VillagerProfession;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public class TradeRegistryData {
|
||||
public static final List<Consumer<VillagerTradeOfferContext>> VILLAGER_MODIFY_HANDLERS = new ArrayList<>();
|
||||
public static final List<Predicate<VillagerTradeOfferContext>> VILLAGER_REMOVE_HANDLERS = new ArrayList<>();
|
||||
public static final List<Consumer<WanderingTraderOfferContext>> WANDERING_TRADER_MODIFY_HANDLERS = new ArrayList<>();
|
||||
public static final List<Predicate<WanderingTraderOfferContext>> WANDERING_TRADER_REMOVE_HANDLERS = new ArrayList<>();
|
||||
|
||||
public static final Map<VillagerProfession, Map<Integer, Integer>> VILLAGER_MAX_OFFER_OVERRIDES = new HashMap<>();
|
||||
public static Integer wanderingTraderMaxOfferOverride = null;
|
||||
|
||||
/**
|
||||
* @param profession The Profession of the villager.
|
||||
* @param level The level the villager needs. Vanilla range is 1 to 5, however mods may extend that upper limit further.
|
||||
* @return Max offers for the villager. Returning null means no override exists
|
||||
*/
|
||||
@Nullable
|
||||
public static Integer getVillagerMaxOffers(VillagerProfession profession, int level) {
|
||||
if (!VILLAGER_MAX_OFFER_OVERRIDES.containsKey(profession)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return VILLAGER_MAX_OFFER_OVERRIDES.get(profession).get(level);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Max offers for the wandering trader. Returning null means no override exists
|
||||
*/
|
||||
@Nullable
|
||||
public static Integer getWanderingTraderMaxOffers() {
|
||||
return wanderingTraderMaxOfferOverride;
|
||||
}
|
||||
|
||||
public static boolean invokeVillagerOfferRemoving(VillagerTradeOfferContext ctx) {
|
||||
return VILLAGER_REMOVE_HANDLERS.stream().anyMatch(predicate -> predicate.test(ctx));
|
||||
}
|
||||
|
||||
public static void invokeVillagerOfferModify(VillagerTradeOfferContext ctx) {
|
||||
VILLAGER_MODIFY_HANDLERS.forEach(consumer -> consumer.accept(ctx));
|
||||
}
|
||||
|
||||
public static boolean invokeWanderingTraderOfferRemoving(WanderingTraderOfferContext ctx) {
|
||||
return WANDERING_TRADER_REMOVE_HANDLERS.stream().anyMatch(predicate -> predicate.test(ctx));
|
||||
}
|
||||
|
||||
public static void invokeWanderingTraderOfferModify(WanderingTraderOfferContext ctx) {
|
||||
WANDERING_TRADER_MODIFY_HANDLERS.forEach(consumer -> consumer.accept(ctx));
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,10 @@
|
||||
"mixins": [
|
||||
"BlockLandingInvoker",
|
||||
"FluidTagsAccessor",
|
||||
"MixinLightningBolt"
|
||||
"MixinLightningBolt",
|
||||
"AbstractVillagerMixin",
|
||||
"VillagerMixin",
|
||||
"WanderingTraderMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"maxShiftBy": 5,
|
||||
|
||||
@@ -46,5 +46,18 @@ accessible field net/minecraft/world/item/ShovelItem FLATTENABLES Ljava/util/Map
|
||||
mutable field net/minecraft/world/item/ShovelItem FLATTENABLES Ljava/util/Map;
|
||||
accessible field net/minecraft/world/item/HoeItem TILLABLES Ljava/util/Map;
|
||||
mutable field net/minecraft/world/item/HoeItem TILLABLES Ljava/util/Map;
|
||||
|
||||
accessible field net/minecraft/world/item/trading/MerchantOffer baseCostA Lnet/minecraft/world/item/ItemStack;
|
||||
mutable field net/minecraft/world/item/trading/MerchantOffer baseCostA Lnet/minecraft/world/item/ItemStack;
|
||||
accessible field net/minecraft/world/item/trading/MerchantOffer costB Lnet/minecraft/world/item/ItemStack;
|
||||
mutable field net/minecraft/world/item/trading/MerchantOffer costB Lnet/minecraft/world/item/ItemStack;
|
||||
accessible field net/minecraft/world/item/trading/MerchantOffer result Lnet/minecraft/world/item/ItemStack;
|
||||
mutable field net/minecraft/world/item/trading/MerchantOffer result Lnet/minecraft/world/item/ItemStack;
|
||||
accessible field net/minecraft/world/item/trading/MerchantOffer maxUses I
|
||||
mutable field net/minecraft/world/item/trading/MerchantOffer maxUses I
|
||||
accessible field net/minecraft/world/item/trading/MerchantOffer priceMultiplier F
|
||||
accessible field net/minecraft/world/item/trading/MerchantOffer xp I
|
||||
|
||||
accessible method net/minecraft/client/renderer/item/ItemProperties registerGeneric (Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/client/renderer/item/ItemPropertyFunction;)Lnet/minecraft/client/renderer/item/ItemPropertyFunction;
|
||||
accessible method net/minecraft/client/renderer/item/ItemProperties register (Lnet/minecraft/world/item/Item;Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/client/renderer/item/ItemPropertyFunction;)V
|
||||
|
||||
|
||||
Reference in New Issue
Block a user