add single item crafting
This commit is contained in:
parent
5c68feedbb
commit
f6b036b517
9 changed files with 106 additions and 158 deletions
|
@ -30,6 +30,7 @@ import common.inventory.InventoryPlayer;
|
|||
import common.inventory.Slot;
|
||||
import common.item.CheatTab;
|
||||
import common.item.ItemStack;
|
||||
import common.packet.CPacketAction;
|
||||
import common.packet.CPacketCheat;
|
||||
import common.util.ExtMath;
|
||||
|
||||
|
@ -299,11 +300,13 @@ public abstract class GuiContainer extends Gui
|
|||
ItemRenderer.disableStandardItemLighting();
|
||||
ItemRenderer.enableGUIStandardItemLighting();
|
||||
InventoryPlayer inventoryplayer = this.gm.player.inventory;
|
||||
ItemStack itemstack = inventoryplayer.getItemStack();
|
||||
ItemStack stack = inventoryplayer.getItemStack();
|
||||
if(this.gm.itemCheat)
|
||||
itemstack = itemstack == null ? this.cheatStack : itemstack;
|
||||
stack = stack == null ? this.cheatStack : stack;
|
||||
if(Bind.CRAFT.isDown())
|
||||
stack = stack == null && this.theSlot != null && this.theSlot.canCheatItem() && this.theSlot.getHasStack() ? this.gm.player.inventoryContainer.getSingleRecipe(this.theSlot.getStack()) : stack;
|
||||
|
||||
if (itemstack != null)
|
||||
if (stack != null)
|
||||
{
|
||||
int j2 = 8;
|
||||
int k2 = 8;
|
||||
|
@ -311,22 +314,26 @@ public abstract class GuiContainer extends Gui
|
|||
|
||||
if (this.dragSplitting && this.dragSplittingSlots.size() > 1)
|
||||
{
|
||||
itemstack = itemstack.copy();
|
||||
itemstack.size = this.dragSplittingRemnant;
|
||||
stack = stack.copy();
|
||||
stack.size = this.dragSplittingRemnant;
|
||||
|
||||
if (itemstack.size == 0)
|
||||
if (stack.size == 0)
|
||||
{
|
||||
s = "" + TextColor.YELLOW + "0";
|
||||
}
|
||||
}
|
||||
else if(itemstack == this.cheatStack) {
|
||||
s = TextColor.DGREEN + "+" + TextColor.GREEN + ItemStack.formatAmount(itemstack.size);
|
||||
else if(stack == this.cheatStack) {
|
||||
s = TextColor.DGREEN + "+" + TextColor.GREEN + ItemStack.formatAmount(stack.size);
|
||||
}
|
||||
|
||||
this.drawItemStack(itemstack, mouseX - j2, mouseY - k2, s);
|
||||
this.drawItemStack(stack, mouseX - j2, mouseY - k2, s);
|
||||
}
|
||||
|
||||
if (inventoryplayer.getItemStack() == null && this.cheatStack == null && this.theSlot != null && this.theSlot.getHasStack())
|
||||
if (inventoryplayer.getItemStack() == null && this.cheatStack == null && stack != null)
|
||||
{
|
||||
this.renderToolTip(stack, mouseX, mouseY);
|
||||
}
|
||||
else if (inventoryplayer.getItemStack() == null && this.cheatStack == null && this.theSlot != null && this.theSlot.getHasStack())
|
||||
{
|
||||
ItemStack itemstack1 = this.theSlot.getStack();
|
||||
this.renderToolTip(itemstack1, mouseX, mouseY);
|
||||
|
@ -520,6 +527,11 @@ public abstract class GuiContainer extends Gui
|
|||
}
|
||||
}
|
||||
Slot slot = this.getSlotAtPosition(mouseX, mouseY);
|
||||
if(slot != null && Bind.CRAFT.isDown() && slot.canCheatItem() && slot.getHasStack() && this.gm.player.inventoryContainer.getSingleRecipe(slot.getStack()) != null) {
|
||||
this.gm.player.client.addToSendQueue(new CPacketAction(CPacketAction.Action.CRAFT_ITEM, slot.slotNumber));
|
||||
this.ignoreMouseUp = true;
|
||||
return;
|
||||
}
|
||||
long i = System.currentTimeMillis();
|
||||
this.doubleClick = this.lastClickSlot == slot && i - this.lastClickTime < 250L && this.lastClickButton == mouseButton;
|
||||
this.ignoreMouseUp = false;
|
||||
|
|
|
@ -31,6 +31,7 @@ public enum Bind implements Identifyable, CVar {
|
|||
SELECT7("select7", "Auswahl #7", Keysym.N7),
|
||||
SELECT8("select8", "Auswahl #8", Keysym.N8),
|
||||
SELECT9("select9", "Auswahl #9", Keysym.N9),
|
||||
CRAFT("craft", "Herstellen", Keysym.M),
|
||||
RENAME("rename", "Umbenennen", Keysym.N),
|
||||
CONSOLE("console", "Konsole", Keysym.F1),
|
||||
COMMAND("command", "Befehl / Chat", Keysym.C),
|
||||
|
|
|
@ -336,7 +336,7 @@ public class EntitySheep extends EntityAnimal
|
|||
DyeColor j = ((EntitySheep)mother).getFleeceColor();
|
||||
this.inventoryCrafting.setInventorySlotContents(0, new ItemStack(ItemDye.getByColor(i)));
|
||||
this.inventoryCrafting.setInventorySlotContents(1, new ItemStack(ItemDye.getByColor(j)));
|
||||
ItemStack itemstack = CraftingRegistry.getMatching(this.inventoryCrafting, ((EntitySheep)father).worldObj);
|
||||
ItemStack itemstack = CraftingRegistry.getMatching(this.inventoryCrafting);
|
||||
DyeColor k;
|
||||
|
||||
if (itemstack != null && itemstack.getItem() instanceof ItemDye dye)
|
||||
|
|
|
@ -24,7 +24,6 @@ import common.item.material.ItemDye;
|
|||
import common.item.tool.ItemArmor;
|
||||
import common.tags.TagObject;
|
||||
import common.tileentity.TileEntityBanner;
|
||||
import common.world.World;
|
||||
|
||||
public abstract class CraftingRegistry
|
||||
{
|
||||
|
@ -45,7 +44,6 @@ public abstract class CraftingRegistry
|
|||
};
|
||||
|
||||
private static final List<IRecipe> recipes = Lists.<IRecipe>newArrayList();
|
||||
private static final List<IRecipe> base = Lists.<IRecipe>newArrayList();
|
||||
|
||||
static void register()
|
||||
{
|
||||
|
@ -231,14 +229,14 @@ public abstract class CraftingRegistry
|
|||
add(new ItemStack(ItemRegistry.byName(wood.getName() + "_fence"), 3), "W#W", "W#W", '#', Items.stick, 'W', planks);
|
||||
add(new ItemStack(ItemRegistry.byName(wood.getName() + "_fence_gate"), 1), "#W#", "#W#", '#', Items.stick, 'W', planks);
|
||||
add(new ItemStack(ItemRegistry.byName(wood.getName() + "_door"), 3), "##", "##", "##", '#', planks);
|
||||
addBasic(new ItemStack(planks, 4), "#", '#', ItemRegistry.byName(wood.getName() + "_log"));
|
||||
add(new ItemStack(planks, 4), "#", '#', ItemRegistry.byName(wood.getName() + "_log"));
|
||||
Item slab = ItemRegistry.byName(wood.getName() + "_slab");
|
||||
add(new ItemStack(slab, 6), "###", '#', planks);
|
||||
add(new ItemStack(ItemRegistry.byName(wood.getName() + "_stairs"), 4), "# ", "## ", "###", '#', planks);
|
||||
add(new ItemStack(Items.daylight_detector), "GGG", "QQQ", "WWW", 'G', Items.glass, 'Q', Items.quartz, 'W', slab);
|
||||
|
||||
add(new ItemStack(Items.chest), "###", "# #", "###", '#', planks);
|
||||
addBasic(new ItemStack(Items.workbench), "##", "##", '#', planks);
|
||||
add(new ItemStack(Items.workbench), "##", "##", '#', planks);
|
||||
add(new ItemStack(Items.assembly_unit), "----", "XXXX", "X##X", '#', Items.construction_table, '-', Items.titanium_ingot, 'X', planks);
|
||||
|
||||
|
||||
|
@ -372,12 +370,6 @@ public abstract class CraftingRegistry
|
|||
});
|
||||
}
|
||||
|
||||
private static ShapedRecipes addBasic(ItemStack stack, Object ... recipeComponents) {
|
||||
ShapedRecipes recipe = add(stack, recipeComponents);
|
||||
base.add(recipe);
|
||||
return recipe;
|
||||
}
|
||||
|
||||
private static ShapedRecipes add(ItemStack stack, Object ... recipeComponents)
|
||||
{
|
||||
String s = "";
|
||||
|
@ -475,11 +467,11 @@ public abstract class CraftingRegistry
|
|||
recipes.add(new ShapelessRecipes(stack, list));
|
||||
}
|
||||
|
||||
public static ItemStack getMatching(InventoryCrafting inv, World world)
|
||||
public static ItemStack getMatching(InventoryCrafting inv)
|
||||
{
|
||||
for (IRecipe irecipe : recipes)
|
||||
{
|
||||
if (irecipe.matches(inv, world))
|
||||
if (irecipe.matches(inv))
|
||||
{
|
||||
return irecipe.getCraftingResult(inv);
|
||||
}
|
||||
|
@ -488,24 +480,11 @@ public abstract class CraftingRegistry
|
|||
return null;
|
||||
}
|
||||
|
||||
public static ItemStack getBasic(InventoryCrafting inv, World world)
|
||||
{
|
||||
for (IRecipe irecipe : base)
|
||||
{
|
||||
if (irecipe.matches(inv, world))
|
||||
{
|
||||
return irecipe.getCraftingResult(inv);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ItemStack[] getRequired(InventoryCrafting inv, World world)
|
||||
public static ItemStack[] getRemaining(InventoryCrafting inv)
|
||||
{
|
||||
for (IRecipe irecipe : recipes)
|
||||
{
|
||||
if (irecipe.matches(inv, world))
|
||||
if (irecipe.matches(inv))
|
||||
{
|
||||
return irecipe.getRemainingItems(inv);
|
||||
}
|
||||
|
@ -533,7 +512,7 @@ public abstract class CraftingRegistry
|
|||
}
|
||||
|
||||
private static interface IRecipe {
|
||||
boolean matches(InventoryCrafting inv, World world);
|
||||
boolean matches(InventoryCrafting inv);
|
||||
ItemStack getCraftingResult(InventoryCrafting inv);
|
||||
int getRecipeSize();
|
||||
ItemStack getRecipeOutput();
|
||||
|
@ -542,7 +521,7 @@ public abstract class CraftingRegistry
|
|||
|
||||
private static class RecipeAddPattern implements IRecipe
|
||||
{
|
||||
public boolean matches(InventoryCrafting inv, World worldIn)
|
||||
public boolean matches(InventoryCrafting inv)
|
||||
{
|
||||
boolean flag = false;
|
||||
|
||||
|
@ -762,7 +741,7 @@ public abstract class CraftingRegistry
|
|||
|
||||
private static class RecipeDuplicatePattern implements IRecipe
|
||||
{
|
||||
public boolean matches(InventoryCrafting inv, World worldIn)
|
||||
public boolean matches(InventoryCrafting inv)
|
||||
{
|
||||
ItemStack itemstack = null;
|
||||
ItemStack itemstack1 = null;
|
||||
|
@ -888,7 +867,7 @@ public abstract class CraftingRegistry
|
|||
/**
|
||||
* Used to check if a recipe matches current crafting inventory
|
||||
*/
|
||||
public boolean matches(InventoryCrafting inv, World worldIn)
|
||||
public boolean matches(InventoryCrafting inv)
|
||||
{
|
||||
this.field_92102_a = null;
|
||||
int i = 0;
|
||||
|
@ -1145,7 +1124,7 @@ public abstract class CraftingRegistry
|
|||
/**
|
||||
* Used to check if a recipe matches current crafting inventory
|
||||
*/
|
||||
public boolean matches(InventoryCrafting inv, World worldIn)
|
||||
public boolean matches(InventoryCrafting inv)
|
||||
{
|
||||
List<ItemStack> list = Lists.<ItemStack>newArrayList();
|
||||
|
||||
|
@ -1262,7 +1241,7 @@ public abstract class CraftingRegistry
|
|||
/**
|
||||
* Used to check if a recipe matches current crafting inventory
|
||||
*/
|
||||
public boolean matches(InventoryCrafting inv, World worldIn)
|
||||
public boolean matches(InventoryCrafting inv)
|
||||
{
|
||||
ItemStack itemstack = null;
|
||||
List<ItemStack> list = Lists.<ItemStack>newArrayList();
|
||||
|
@ -1461,7 +1440,7 @@ public abstract class CraftingRegistry
|
|||
/**
|
||||
* Used to check if a recipe matches current crafting inventory
|
||||
*/
|
||||
public boolean matches(InventoryCrafting inv, World worldIn)
|
||||
public boolean matches(InventoryCrafting inv)
|
||||
{
|
||||
for (int i = 0; i <= inv.getWidth() - this.recipeWidth; ++i)
|
||||
{
|
||||
|
@ -1596,7 +1575,7 @@ public abstract class CraftingRegistry
|
|||
/**
|
||||
* Used to check if a recipe matches current crafting inventory
|
||||
*/
|
||||
public boolean matches(InventoryCrafting inv, World worldIn)
|
||||
public boolean matches(InventoryCrafting inv)
|
||||
{
|
||||
List<ItemStack> list = Lists.newArrayList(this.recipeItems);
|
||||
|
||||
|
|
|
@ -10,8 +10,12 @@ import common.init.CraftingRegistry;
|
|||
import common.item.ItemStack;
|
||||
import common.item.tool.ItemArmor;
|
||||
|
||||
public class ContainerPlayer extends Container
|
||||
{
|
||||
public class ContainerPlayer extends Container {
|
||||
private final InventoryCrafting baseCrafting = new InventoryCrafting(new Container() {
|
||||
public boolean canInteractWith(EntityNPC playerIn) {
|
||||
return false;
|
||||
}
|
||||
}, 1, 1);
|
||||
private final InventoryCrafting craftMatrix = new InventoryCrafting(this, 2, 2);
|
||||
private final IInventory craftResult = new InventoryCraftResult();
|
||||
private final boolean isLocalWorld;
|
||||
|
@ -115,7 +119,22 @@ public class ContainerPlayer extends Container
|
|||
*/
|
||||
public void onCraftMatrixChanged(IInventory inventoryIn)
|
||||
{
|
||||
this.craftResult.setInventorySlotContents(0, CraftingRegistry.getBasic(this.craftMatrix, this.thePlayer.worldObj));
|
||||
this.craftResult.setInventorySlotContents(0, CraftingRegistry.getMatching(this.craftMatrix));
|
||||
}
|
||||
|
||||
public ItemStack getSingleRecipe(ItemStack stack) {
|
||||
this.baseCrafting.setInventorySlotContents(0, stack);
|
||||
stack = CraftingRegistry.getMatching(this.baseCrafting);
|
||||
this.baseCrafting.setInventorySlotContents(0, null);
|
||||
return stack;
|
||||
}
|
||||
|
||||
public ItemStack craftSingleRecipe(ItemStack stack) {
|
||||
this.baseCrafting.setInventorySlotContents(0, stack);
|
||||
SlotCrafting.handleRemaining(this.baseCrafting, this.thePlayer);
|
||||
stack = this.baseCrafting.getStackInSlot(0);
|
||||
this.baseCrafting.setInventorySlotContents(0, null);
|
||||
return stack;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -59,7 +59,7 @@ public class ContainerWorkbench extends Container
|
|||
*/
|
||||
public void onCraftMatrixChanged(IInventory inventoryIn)
|
||||
{
|
||||
this.craftResult.setInventorySlotContents(0, CraftingRegistry.getMatching(this.craftMatrix, this.worldObj));
|
||||
this.craftResult.setInventorySlotContents(0, CraftingRegistry.getMatching(this.craftMatrix));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,16 +6,35 @@ import common.item.ItemStack;
|
|||
|
||||
public class SlotCrafting extends Slot
|
||||
{
|
||||
/** The craft matrix inventory linked to this result slot. */
|
||||
private final InventoryCrafting craftMatrix;
|
||||
|
||||
/** The player that is using the GUI where this slot resides. */
|
||||
private final EntityNPC thePlayer;
|
||||
|
||||
/**
|
||||
* The number of items that have been crafted so far. Gets passed to ItemStack.onCrafting before being reset.
|
||||
*/
|
||||
private int amountCrafted;
|
||||
public static void handleRemaining(InventoryCrafting craftMatrix, EntityNPC thePlayer) {
|
||||
ItemStack[] aitemstack = CraftingRegistry.getRemaining(craftMatrix);
|
||||
|
||||
for (int i = 0; i < aitemstack.length; ++i)
|
||||
{
|
||||
ItemStack itemstack = craftMatrix.getStackInSlot(i);
|
||||
ItemStack itemstack1 = aitemstack[i];
|
||||
|
||||
if (itemstack != null)
|
||||
{
|
||||
craftMatrix.decrStackSize(i, 1);
|
||||
}
|
||||
|
||||
if (itemstack1 != null)
|
||||
{
|
||||
if (craftMatrix.getStackInSlot(i) == null)
|
||||
{
|
||||
craftMatrix.setInventorySlotContents(i, itemstack1);
|
||||
}
|
||||
else if (!thePlayer.inventory.addItemStackToInventory(itemstack1))
|
||||
{
|
||||
thePlayer.dropPlayerItemWithRandomChoice(itemstack1, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SlotCrafting(EntityNPC player, InventoryCrafting craftingInventory, IInventory p_i45790_3_, int slotIndex, int xPosition, int yPosition)
|
||||
{
|
||||
|
@ -32,27 +51,12 @@ public class SlotCrafting extends Slot
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrease the size of the stack in slot (first int arg) by the amount of the second int arg. Returns the new
|
||||
* stack.
|
||||
*/
|
||||
public ItemStack decrStackSize(int amount)
|
||||
{
|
||||
if (this.getHasStack())
|
||||
{
|
||||
this.amountCrafted += Math.min(amount, this.getStack().size);
|
||||
}
|
||||
|
||||
return super.decrStackSize(amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* the itemStack passed in is the output - ie, iron ingots, and pickaxes, not ore and wood. Typically increases an
|
||||
* internal count then calls onCrafting(item).
|
||||
*/
|
||||
protected void onCrafting(ItemStack stack, int amount)
|
||||
{
|
||||
this.amountCrafted += amount;
|
||||
this.onCrafting(stack);
|
||||
}
|
||||
|
||||
|
@ -61,96 +65,12 @@ public class SlotCrafting extends Slot
|
|||
*/
|
||||
protected void onCrafting(ItemStack stack)
|
||||
{
|
||||
// if (this.amountCrafted > 0)
|
||||
// {
|
||||
// stack.onCrafting(this.thePlayer.worldObj, this.thePlayer, this.amountCrafted);
|
||||
// }
|
||||
|
||||
this.amountCrafted = 0;
|
||||
|
||||
// if (stack.getItem() == Items.crafting_table)
|
||||
// {
|
||||
// this.thePlayer.triggerAchievement(AchievementList.buildWorkBench);
|
||||
// }
|
||||
//
|
||||
// if (stack.getItem() instanceof ItemPickaxe)
|
||||
// {
|
||||
// this.thePlayer.triggerAchievement(AchievementList.buildPickaxe);
|
||||
// }
|
||||
//
|
||||
// if (stack.getItem() == Items.furnace)
|
||||
// {
|
||||
// this.thePlayer.triggerAchievement(AchievementList.buildFurnace);
|
||||
// }
|
||||
//
|
||||
// if (stack.getItem() instanceof ItemHoe)
|
||||
// {
|
||||
// this.thePlayer.triggerAchievement(AchievementList.buildHoe);
|
||||
// }
|
||||
//
|
||||
// if (stack.getItem() == Items.bread)
|
||||
// {
|
||||
// this.thePlayer.triggerAchievement(AchievementList.makeBread);
|
||||
// }
|
||||
//
|
||||
// if (stack.getItem() == Items.cake)
|
||||
// {
|
||||
// this.thePlayer.triggerAchievement(AchievementList.bakeCake);
|
||||
// }
|
||||
//
|
||||
// if (stack.getItem() instanceof ItemPickaxe && ((ItemPickaxe)stack.getItem()).getToolMaterial() != ToolMaterial.WOOD)
|
||||
// {
|
||||
// this.thePlayer.triggerAchievement(AchievementList.buildBetterPickaxe);
|
||||
// }
|
||||
//
|
||||
// if (stack.getItem() instanceof ItemSword)
|
||||
// {
|
||||
// this.thePlayer.triggerAchievement(AchievementList.buildSword);
|
||||
// }
|
||||
//
|
||||
// if (stack.getItem() == Items.enchanting_table)
|
||||
// {
|
||||
// this.thePlayer.triggerAchievement(AchievementList.enchantments);
|
||||
// }
|
||||
//
|
||||
// if (stack.getItem() == Items.bookshelf)
|
||||
// {
|
||||
// this.thePlayer.triggerAchievement(AchievementList.bookcase);
|
||||
// }
|
||||
//
|
||||
// if (stack.getItem() == Items.golden_apple && stack.getMetadata() == 1)
|
||||
// {
|
||||
// this.thePlayer.triggerAchievement(AchievementList.overpowered);
|
||||
// }
|
||||
}
|
||||
|
||||
public void onPickupFromSlot(EntityNPC playerIn, ItemStack stack)
|
||||
{
|
||||
this.onCrafting(stack);
|
||||
ItemStack[] aitemstack = CraftingRegistry.getRequired(this.craftMatrix, playerIn.worldObj);
|
||||
|
||||
for (int i = 0; i < aitemstack.length; ++i)
|
||||
{
|
||||
ItemStack itemstack = this.craftMatrix.getStackInSlot(i);
|
||||
ItemStack itemstack1 = aitemstack[i];
|
||||
|
||||
if (itemstack != null)
|
||||
{
|
||||
this.craftMatrix.decrStackSize(i, 1);
|
||||
}
|
||||
|
||||
if (itemstack1 != null)
|
||||
{
|
||||
if (this.craftMatrix.getStackInSlot(i) == null)
|
||||
{
|
||||
this.craftMatrix.setInventorySlotContents(i, itemstack1);
|
||||
}
|
||||
else if (!this.thePlayer.inventory.addItemStackToInventory(itemstack1))
|
||||
{
|
||||
this.thePlayer.dropPlayerItemWithRandomChoice(itemstack1, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
handleRemaining(this.craftMatrix, this.thePlayer);
|
||||
}
|
||||
|
||||
public boolean canCheatItem() {
|
||||
|
|
|
@ -98,7 +98,8 @@ public class CPacketAction implements Packet<IPlayer>
|
|||
// SET_VIEWDIST,
|
||||
WARP_MODE,
|
||||
START_PROFILING,
|
||||
STOP_PROFILING;
|
||||
STOP_PROFILING,
|
||||
CRAFT_ITEM;
|
||||
// SHUTDOWN;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2810,6 +2810,22 @@ public class Player extends User implements ICrafting, Executor, IPlayer
|
|||
this.profiling = false;
|
||||
break;
|
||||
|
||||
case CRAFT_ITEM: {
|
||||
if(this.entity.inventory.getItemStack() == null) {
|
||||
Slot slot = this.entity.openContainer.getSlot(packetIn.getAuxData());
|
||||
if(slot != null && slot.canCheatItem() && slot.getHasStack()) {
|
||||
ItemStack stack = this.entity.inventoryContainer.getSingleRecipe(slot.getStack());
|
||||
if(stack != null) {
|
||||
slot.putStack(this.entity.inventoryContainer.craftSingleRecipe(slot.getStack()));
|
||||
this.entity.inventory.setItemStack(stack);
|
||||
this.entity.openContainer.detectAndSendChanges();
|
||||
this.updateHeldItem();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Ungültige Aktion!");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue