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

Basic villager trades #1819

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 80 additions & 1 deletion src/main/java/cn/nukkit/entity/passive/EntityVillager.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
package cn.nukkit.entity.passive;

import java.util.ArrayList;
import java.util.List;

import cn.nukkit.Player;
import cn.nukkit.entity.Entity;
import cn.nukkit.entity.EntityAgeable;
import cn.nukkit.entity.EntityCreature;
import cn.nukkit.inventory.Inventory;
import cn.nukkit.inventory.InventoryHolder;
import cn.nukkit.inventory.TradeInventory;
import cn.nukkit.inventory.TradeInventoryRecipe;
import cn.nukkit.item.Item;
import cn.nukkit.level.format.FullChunk;
import cn.nukkit.nbt.tag.CompoundTag;
import cn.nukkit.nbt.tag.ListTag;

public class EntityVillager extends EntityCreature implements EntityNPC, EntityAgeable {
public class EntityVillager extends EntityCreature implements InventoryHolder, EntityNPC, EntityAgeable {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about implementing InventoryHolder here, since it has no physical inventory

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or does it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Villager trading is inventory and since villager contains trade recipes, i find them as inventory holders.


public static final int NETWORK_ID = 115;
private TradeInventory inventory;
private List<TradeInventoryRecipe> recipes = new ArrayList<TradeInventoryRecipe>();

public EntityVillager(FullChunk chunk, CompoundTag nbt) {
super(chunk, nbt);
Expand Down Expand Up @@ -43,6 +56,67 @@ public String getName() {
public void initEntity() {
super.initEntity();
this.setMaxHealth(20);
this.inventory = new TradeInventory(this);
if(this.namedTag.getCompound("Offers") != null) {
ListTag<CompoundTag> nbtRecipes = this.namedTag.getCompound("Offers").getList("Recipes", CompoundTag.class);
for(CompoundTag nbt : nbtRecipes.getAll()) {
recipes.add(TradeInventoryRecipe.toNBT(nbt));
}
} else {
CompoundTag nbt = new CompoundTag("Offers");
nbt.putList(new ListTag<CompoundTag>("Recipes"));
nbt.putList(getDefaultTierExpRequirements());
this.namedTag.putCompound("Offers", nbt);
}
}

public void setTradeTier(int tier) {
this.namedTag.putInt("TradeTier", tier);
}

public int getTradeTier() {
return this.namedTag.getInt("TradeTier");
}

public void setWilling(boolean value) {
this.namedTag.putBoolean("Willing", value);
}

public boolean isWilling() {
return this.namedTag.getBoolean("Willing");
}

@Override
public boolean onInteract(Player player, Item item) {
Copy link
Contributor

@lt-name lt-name May 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Google Translate)
I think we also need to rewrite the onInteract(Player player, Item item, Vector3 clickedPos) method
like this:

    @Override
    public boolean onInteract(Player player, Item item, Vector3 clickedPos) {
        if (super.onInteract(player, item, clickedPos)) {
            return true;
        }
        return this.onInteract(player, item);
    }

if(this.namedTag.getCompound("Offers") != null) {
player.addWindow(this.getInventory());
return true;
}
return false;
}

public void addTradeRcipe(TradeInventoryRecipe recipe) {
PetteriM1 marked this conversation as resolved.
Show resolved Hide resolved
this.recipes.add(recipe);
ListTag<CompoundTag> nbtRecipes = this.getOffers().getList("Recipes", CompoundTag.class);
nbtRecipes.add(recipe.toNBT());
}

public List<TradeInventoryRecipe> getRecipes(){
return this.recipes;
}

public CompoundTag getOffers() {
return this.namedTag.getCompound("Offers");
}

private ListTag<CompoundTag> getDefaultTierExpRequirements() {
ListTag<CompoundTag> tag = new ListTag<CompoundTag>("TierExpRequirements");
tag.add(new CompoundTag().putInt("0", 0));
tag.add(new CompoundTag().putInt("1", 10));
tag.add(new CompoundTag().putInt("2", 70));
tag.add(new CompoundTag().putInt("3", 150));
tag.add(new CompoundTag().putInt("4", 250));
return tag;
}

public boolean isBaby() {
Expand All @@ -53,4 +127,9 @@ public void setBaby(boolean baby) {
this.setDataFlag(DATA_FLAGS, DATA_FLAG_BABY, baby);
this.setScale(baby ? 0.5f : 1);
}

@Override
public Inventory getInventory() {
return this.inventory;
}
}
3 changes: 2 additions & 1 deletion src/main/java/cn/nukkit/inventory/InventoryType.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public enum InventoryType {
BEACON(1, "Beacon", 13),
OFFHAND(1, "Offhand", -1),
MINECART_CHEST(27, "Minecart with Chest", 0),
MINECART_HOPPER(5, "Minecart with Hopper", 8);
MINECART_HOPPER(5, "Minecart with Hopper", 8),
TRADING(3, "Villager Trade", 15);

private final int size;
private final String title;
Expand Down
54 changes: 54 additions & 0 deletions src/main/java/cn/nukkit/inventory/TradeInventory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package cn.nukkit.inventory;

import java.io.IOException;
import java.nio.ByteOrder;

import cn.nukkit.Player;
import cn.nukkit.entity.passive.EntityVillager;
import cn.nukkit.item.Item;
import cn.nukkit.nbt.NBTIO;
import cn.nukkit.network.protocol.UpdateTradePacket;

public class TradeInventory extends BaseInventory {

public TradeInventory(InventoryHolder holder) {
super(holder, InventoryType.TRADING);
}

public void onOpen(Player who) {
super.onOpen(who);

UpdateTradePacket pk = new UpdateTradePacket();
pk.windowId = (byte) who.getWindowId(this);
pk.windowType = (byte) InventoryType.TRADING.getNetworkType();
pk.isWilling = this.getHolder().isWilling();
pk.screen2 = true;
pk.trader = this.getHolder().getId();
pk.tradeTier = this.getHolder().getTradeTier();
pk.player = who.getId();
try {
pk.offers = NBTIO.write(this.getHolder().getOffers(),ByteOrder.LITTLE_ENDIAN);
} catch(IOException ex) {}

who.dataPacket(pk);
}

public void onClose(Player who) {
for(int i = 0; i <= 1; i++) {
Item item = getItem(i);
if(who.getInventory().canAddItem(item)) {
who.getInventory().addItem(item);
} else {
who.dropItem(item);
}
this.clear(i);
}

super.onClose(who);
}

public EntityVillager getHolder() {
return (EntityVillager) this.holder;
}

}
117 changes: 117 additions & 0 deletions src/main/java/cn/nukkit/inventory/TradeInventoryRecipe.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package cn.nukkit.inventory;

import cn.nukkit.item.Item;
import cn.nukkit.nbt.NBTIO;
import cn.nukkit.nbt.tag.CompoundTag;
import lombok.Getter;

public class TradeInventoryRecipe {

public static int A_ITEM = 0;
public static int B_ITEM = 1;
@Getter
private Item sellItem;
@Getter
private Item buyItem;
@Getter
private Item secondBuyItem;

private int tier = -1;
private int maxUses = 999;
private int buyCountA = 0;
private int buyCountB = 0;
private int uses = 0;
private int demand = 0;
private int rewardsExp = 0;
private int traderExp = 0;
private float priceMultiplierA = 0F;
private float priceMultiplierB = 0F;


public TradeInventoryRecipe(Item sellItem, Item buyItem) {
this(sellItem, buyItem, Item.get(0));
}

public TradeInventoryRecipe(Item sellItem, Item buyItem, Item secondBuyItem) {
this.sellItem = sellItem;
this.buyItem = buyItem;
this.secondBuyItem = secondBuyItem;
}

public TradeInventoryRecipe setTier(int tier) {
this.tier = tier;
return this;
}

public TradeInventoryRecipe setMaxUses(int maxUses) {
this.maxUses = maxUses;
return this;
}

public TradeInventoryRecipe setBuyCount(int count, int type) {
switch(type) {
case 0:
this.buyCountA = count;
break;
case 1:
this.buyCountB = count;
break;
}
this.buyCountA = count;
return this;
}

public TradeInventoryRecipe setDemand(int demand) {
this.demand = demand;
return this;
}

public TradeInventoryRecipe setMultiplier(float multiplier, int type) {
switch(type) {
case 0:
this.priceMultiplierA = multiplier;
break;
case 1:
this.priceMultiplierB = multiplier;
break;
}
return this;
}

public TradeInventoryRecipe setRewardExp(int reward) {
this.rewardsExp = reward;
return this;
}

public CompoundTag toNBT() {
CompoundTag nbt = new CompoundTag();
nbt.putCompound("buyA", NBTIO.putItemHelper(buyItem, -1));
nbt.putCompound("buyB", NBTIO.putItemHelper(secondBuyItem,-1));
nbt.putCompound("sell", NBTIO.putItemHelper(sellItem, -1));
nbt.putInt("tier", tier);
nbt.putInt("buyCountA", buyCountA);
nbt.putInt("buyCountB", buyCountB);
nbt.putInt("uses", uses);
nbt.putInt("maxUses", maxUses);
nbt.putInt("rewardExp", rewardsExp);
nbt.putInt("demand", demand);
nbt.putInt("traderExp", traderExp);
nbt.putFloat("priceMultiplierA", priceMultiplierA);
nbt.putFloat("priceMultiplierB", priceMultiplierB);
return nbt;
}

public static TradeInventoryRecipe toNBT(CompoundTag nbt) {
TradeInventoryRecipe recipe = new TradeInventoryRecipe(NBTIO.getItemHelper(nbt.getCompound("sell")), NBTIO.getItemHelper(nbt.getCompound("buyA")), NBTIO.getItemHelper(nbt.getCompound("buyB")))
.setTier(nbt.getInt("tier"))
.setBuyCount(nbt.getInt("buyCountA"), A_ITEM)
.setBuyCount(nbt.getInt("buyCountB"), B_ITEM)
.setMaxUses(nbt.getInt("maxUses"))
.setMultiplier(nbt.getInt("priceMultiplierA"), A_ITEM)
.setMultiplier(nbt.getInt("priceMultiplierB"), B_ITEM)
.setDemand(nbt.getInt("demand"))
.setRewardExp(nbt.getInt("rewardExp"));
return recipe;
}

}