Jump to content

Recommended Posts

Posted

Hello there!

 

I want to be able to spawn particles if a machine or block isActive. I made a custom furnace and told it to spawn particles if IsActive(), and set this method in the tileentity class and returned this.cookTime > 0. When I right click and access the gui everything is fine and dandy, but if I put ores into the furnace and go out of the gui, the isActive stays true forever, until I right click and access the gui again, then it updates and particles turn off.

 

So cookTime only changes its numbers when I am inside the gui. If I leave the gui, cookTime stops at the last number it was at.

 

I'm assuming this is a server/client thing and needs to be resolved in the container class, but I am confused as to how to do it.

 

I tried using the IsActive thing the tutorial used in the block class, but for other machines I want to use things like cookTime to determine whether or not a machine is active.

 

Any tips on how I should move forward is greatly appriciated!

 

Block class:

package com.IvanTune.futuregate.block;

import java.util.Random;

import javax.swing.Icon;

import com.IvanTune.futuregate.FutureGate;
import com.IvanTune.futuregate.init.ModBlocks;
import com.IvanTune.futuregate.reference.Reference;
import com.IvanTune.futuregate.tileentity.TileEntityCustomFurnace;
import com.IvanTune.futuregate.tileentity.TileEntityCustomMacerator;

import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.block.Block;
import net.minecraft.block.BlockContainer;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.IIcon;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;

public class BlockCustomFurnace extends BlockContainer {

private Random rand = new Random();

private final boolean isActive;

private static boolean keepInventory;

public BlockCustomFurnace(boolean isActive) {
	super(Material.rock);

	this.isActive = isActive;
}

public int getRenderType() {
	return -1;
}

public boolean isOpaqueCube() {
	return false;
}

public boolean renderAsNormalBlock() {
	return false;
}


public Item getItemDropped(int par1, Random random, int par3) {
	return Item.getItemFromBlock(ModBlocks.blockCustomFurnaceIdle);
}

public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float hitX,
		float hitY, float hitZ) {
	if (world.isRemote) {
		return true;
	} else {
		TileEntityCustomFurnace tileentityCustomFurnace = (TileEntityCustomFurnace) world.getTileEntity(x, y, z);

		if (tileentityCustomFurnace != null) {
			player.openGui(FutureGate.instance, 0, world, x, y, z);

		}

		return true;
	}
}

public TileEntity createNewTileEntity(World world, int i) {
	return new TileEntityCustomFurnace();
}

@SideOnly(Side.CLIENT)
public void registerBlockIcons(IIconRegister iconRegister){
	this.blockIcon = iconRegister.registerIcon(Reference.MOD_ID + ":" + this.getUnlocalizedName().substring(5));
}

@SideOnly(Side.CLIENT)
public void randomDisplayTick(World world, int x, int y, int z, Random random) {

	TileEntityCustomFurnace te = (TileEntityCustomFurnace) world.getTileEntity(x, y, z);

	if (te.isActive()) {

		int dir = world.getBlockMetadata((int)x, (int)y, (int)z);

		float x1 = (float) x + 0.5F;
		float y1 = (float) ((float) y + random.nextInt(4) * 0.1);
		float z1 = (float) z + 0.5F;

		float f = 0.52F;
		float f1 = random.nextFloat() * 0.6F - 0.3F;
		if (dir == 1) {
			world.spawnParticle("smoke", (double) (x1 + f), (double) (y1), (double) (z1 + f1), 0.0D, 0.0D, 0.0D);
			world.spawnParticle("flame", (double) (x1 + f), (double) (y1), (double) (z1 + f1), 0.0D, 0.0D, 0.0D);
		} 
		else if (dir == 0) {
			world.spawnParticle("smoke", (double) (x1 + f1), (double) (y1), (double) (z1 + f-1), 0.0D, 0.0D, 0.0D);
			world.spawnParticle("flame", (double) (x1 + f1), (double) (y1), (double) (z1 + f-1), 0.0D, 0.0D, 0.0D);
		} 
		else if (dir == 2) {
			world.spawnParticle("smoke", (double) (x1 + f1), (double) (y1), (double) (z1 + f), 0.0D, 0.0D, 0.0D);
			world.spawnParticle("flame", (double) (x1 + f1), (double) (y1), (double) (z1 + f), 0.0D, 0.0D, 0.0D);
		} 
		else if (dir == 3) {
			world.spawnParticle("smoke", (double) (x1 + f - 1), (double) (y1), (double) (z1 + f1), 0.0D, 0.0D, 0.0D);
			world.spawnParticle("flame", (double) (x1 + f - 1), (double) (y1), (double) (z1 + f1), 0.0D, 0.0D, 0.0D);
	}
	}
}

    @Override
    public void onBlockPlacedBy(World world, int x, int y, int z, EntityLivingBase player, ItemStack par6ItemStack) {
        int dir = MathHelper.floor_double((double) ((player.rotationYaw * 4F) / 360F) + 0.5D) & 3;
        world.setBlockMetadataWithNotify(x, y, z, dir, 0);

    }

public static void updateCustomFurnaceBlock(boolean active, World worldObj, int xCoord, int yCoord, int zCoord) {
	int i = worldObj.getBlockMetadata(xCoord, yCoord, zCoord);
	TileEntity tileentity = worldObj.getTileEntity(xCoord, yCoord, zCoord);
	keepInventory = true;

	if (active) {
	    worldObj.setBlock(xCoord, yCoord, zCoord, ModBlocks.blockCustomFurnaceActive);
	} else {
		worldObj.setBlock(xCoord, yCoord, zCoord, ModBlocks.blockCustomFurnaceIdle);
	}

	keepInventory = false;

	worldObj.setBlockMetadataWithNotify(xCoord, yCoord, zCoord, i, 2);

	if (tileentity != null) {
		tileentity.validate();
		worldObj.setTileEntity(xCoord, yCoord, zCoord, tileentity);
	}
}

public void breakBlock(World world, int x, int y, int z, Block oldBlock, int oldMetadata) {
	if (!keepInventory) {
		TileEntityCustomFurnace tileentity = (TileEntityCustomFurnace) world.getTileEntity(x, y, z);

		if (tileentity != null) {
			for (int i = 0; i < tileentity.getSizeInventory(); i++) {
				ItemStack itemstack = tileentity.getStackInSlot(i);

				if (itemstack != null) {
					float f = this.rand.nextFloat() * 0.8F + 0.1F;
					float f1 = this.rand.nextFloat() * 0.8F + 0.1F;
					float f2 = this.rand.nextFloat() * 0.8F + 0.1F;

					while (itemstack.stackSize > 0) {
						int j = this.rand.nextInt(21) + 10;

						if (j > itemstack.stackSize) {
							j = itemstack.stackSize;
						}

						itemstack.stackSize -= j;

						EntityItem item = new EntityItem(world, (double) ((float) x + f), (double) ((float) y + f1),
								(double) ((float) z + f2),
								new ItemStack(itemstack.getItem(), j, itemstack.getItemDamage()));

						if (itemstack.hasTagCompound()) {
							item.getEntityItem().setTagCompound((NBTTagCompound) itemstack.getTagCompound().copy());
						}

						float f3 = 0.05F;
						item.motionX = (double) ((float) this.rand.nextGaussian() * f3);
						item.motionY = (double) ((float) this.rand.nextGaussian() * f3 + 0.2F);
						item.motionZ = (double) ((float) this.rand.nextGaussian() * f3);

						world.spawnEntityInWorld(item);
					}

				}
			}

			world.func_147453_f(x, y, z, oldBlock);
		}
	}

	super.breakBlock(world, x, y, z, oldBlock, oldMetadata);
}

public boolean hasComparatorInputOverride() {
	return true;
}

public int getComparatorInputOverride(World world, int x, int y, int z, int i) {
	return Container.calcRedstoneFromInventory((IInventory) world.getTileEntity(x, y, z));
}

public Item getItem(World world, int x, int y, int z) {
	return Item.getItemFromBlock(ModBlocks.blockCustomFurnaceIdle);
}

}

 

TileEntityFurnace:

package com.IvanTune.futuregate.tileentity;

import com.IvanTune.futuregate.block.BlockCustomFurnace;

import cpw.mods.fml.common.registry.GameRegistry;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemHoe;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemSword;
import net.minecraft.item.ItemTool;
import net.minecraft.item.crafting.FurnaceRecipes;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.Packet;
import net.minecraft.network.play.server.S35PacketUpdateTileEntity;
import net.minecraft.tileentity.TileEntity;

public class TileEntityCustomFurnace extends TileEntity implements ISidedInventory {

private String localizedName;

private static final int[] slots_top = new int[] { 0 };
private static final int[] slots_bottom = new int[] { 2 };
private static final int[] slots_sides = new int[] { 1 };

private ItemStack[] slots = new ItemStack[3];

public int furnaceSpeed = 100;

public int burnTime;

public int currentItemBurnTime;

public int cookTime;

public int getSizeInventory() {
	return this.slots.length;
}

public String getInventoryName() {
	return this.hasCustomInventoryName() ? this.localizedName : "container.customFurnace";
}

public boolean hasCustomInventoryName() {
	return this.localizedName != null && this.localizedName.length() > 0;
}

public void setGuiDisplayName(String displayName) {
	this.localizedName = displayName;

}

public ItemStack getStackInSlot(int i) {

	return this.slots[i];
}

public ItemStack decrStackSize(int i, int j) {
	if (this.slots[i] != null) {
		ItemStack itemstack;

		if (this.slots[i].stackSize <= j) {

			itemstack = this.slots[i];
			this.slots[i] = null;
			return itemstack;
		} else {
			itemstack = this.slots[i].splitStack(j);

			if (this.slots[i].stackSize == 0) {
				this.slots[i] = null;
			}

			return itemstack;
		}
	}

	return null;
}

public ItemStack getStackInSlotOnClosing(int i) {
	if (this.slots[i] != null) {
		ItemStack itemstack = this.slots[i];
		this.slots[i] = null;
		return itemstack;
	}

	return null;
}

public void setInventorySlotContents(int i, ItemStack itemstack) {
	this.slots[i] = itemstack;

	if (itemstack != null && itemstack.stackSize > this.getInventoryStackLimit()) {
		itemstack.stackSize = this.getInventoryStackLimit();
	}
}

public int getInventoryStackLimit() {

	return 64;
}



public void readFromNBT(NBTTagCompound nbt) {
	super.readFromNBT(nbt);

	NBTTagList list = nbt.getTagList("Items", 10);
	this.slots = new ItemStack[this.getSizeInventory()];

	for (int i = 0; i < list.tagCount(); i++) {
		NBTTagCompound compound = (NBTTagCompound) list.getCompoundTagAt(i);
		byte b = compound.getByte("Slot");

		if (b >= 0 && b < this.slots.length) {
			this.slots[b] = ItemStack.loadItemStackFromNBT(compound);

		}
	}

	this.burnTime = (int) nbt.getShort("BurnTime");
	this.cookTime = (int) nbt.getShort("CookTime");
	this.currentItemBurnTime = (int) nbt.getShort("CurrentBurnTime");

	if (nbt.hasKey("CustomName")) {
		this.localizedName = nbt.getString("CustomName");
	}
}

public void writeToNBT(NBTTagCompound nbt) {
	super.writeToNBT(nbt);

	nbt.setShort("BurnTime", (short) this.burnTime);
	nbt.setShort("CookTime", (short) this.cookTime);
	nbt.setShort("CurrentBurnTime", (short) this.currentItemBurnTime);

	NBTTagList list = new NBTTagList();

	for (int i = 0; i < this.slots.length; i++) {
		if (this.slots[i] != null) {
			NBTTagCompound compound = new NBTTagCompound();
			compound.setByte("Slot", (byte) i);
			this.slots[i].writeToNBT(compound);
			list.appendTag(compound);

		}

	}

	nbt.setTag("Items", list);

	if (this.hasCustomInventoryName()) {
		nbt.setString("CustomName", this.localizedName);
	}
}

public boolean isUseableByPlayer(EntityPlayer entityplayer) {

	return this.worldObj.getTileEntity(this.xCoord, this.yCoord, this.zCoord) != this ? false
			: entityplayer.getDistanceSq((double) this.xCoord + 0.5D, (double) this.yCoord + 0.5D,
					(double) this.zCoord + 0.5D) <= 64.0D;
}



public boolean isBurning() {
	return this.burnTime > 0;
}
public void updateEntity() {
	boolean flag = this.burnTime > 0;
	boolean flag1 = false;

	if (this.burnTime > 0) {
		this.burnTime--;
	}

	if (!this.worldObj.isRemote) {
		if (this.burnTime == 0 && this.canSmelt()) {
			this.currentItemBurnTime = this.burnTime = getItemBurnTime(this.slots[1]);

			if (this.burnTime > 0) {
				flag1 = true;

				if (this.slots[1] != null) {
					this.slots[1].stackSize--;

					if (this.slots[1].stackSize == 0) {
						this.slots[1] = this.slots[1].getItem().getContainerItem(this.slots[1]);

					}

				}
			}

		}

		if (this.isBurning() && this.canSmelt()) {
			this.cookTime++;

			if (this.cookTime == this.furnaceSpeed) {
				this.cookTime = 0;
				this.smeltItem();
				flag1 = true;
			}
		} else {
			this.cookTime = 0;
		}

		if (flag != this.burnTime > 0) {
			flag1 = true;
			BlockCustomFurnace.updateCustomFurnaceBlock(this.burnTime > 0, this.worldObj, this.xCoord, this.yCoord,
					this.zCoord);

		}
	}

	if (flag1) {
		this.markDirty();
	}

}

private boolean canSmelt() {
	if (this.slots[0] == null) {
		return false;
	} else {
		ItemStack itemstack = FurnaceRecipes.smelting().getSmeltingResult(this.slots[0]);

		if (itemstack == null) return false;
		if (this.slots[2] == null) return true;
		if (!this.slots[2].isItemEqual(itemstack)) return false;

		int result = this.slots[2].stackSize + itemstack.stackSize;

		return (result <= getInventoryStackLimit() && result <= itemstack.getMaxStackSize());
	}
}

public void smeltItem() {
	if (this.canSmelt()) {
		ItemStack itemstack = FurnaceRecipes.smelting().getSmeltingResult(this.slots[0]);

		if (this.slots[2] == null) {
			this.slots[2] = itemstack.copy();
		} else if (this.slots[2].isItemEqual(itemstack)) {
			this.slots[2].stackSize += itemstack.stackSize;
		}

		this.slots[0].stackSize--;

		if (this.slots[0].stackSize <= 0) {
			this.slots[0] = null;
		}
	}
}

public static int getItemBurnTime(ItemStack itemstack) {
	if (itemstack == null) {
		return 0;
	} else {
		Item item = itemstack.getItem();

		if (item instanceof ItemBlock && Block.getBlockFromItem(item) != Blocks.air) {
			Block block = Block.getBlockFromItem(item);

			if (block == Blocks.wooden_slab) {
				return 150;
			}

			if (block.getMaterial() == Material.wood) {
				return 300;
			}

			if (block == Blocks.coal_block) {
				return 16000;
			}
		}

		if (item instanceof ItemTool && ((ItemTool) item).getToolMaterialName().equals("WOOD"))
			return 200;
		if (item instanceof ItemSword && ((ItemSword) item).getToolMaterialName().equals("WOOD"))
			return 200;
		if (item instanceof ItemHoe && ((ItemHoe) item).getToolMaterialName().equals("WOOD"))
			return 200;
		if (item == Items.stick)
			return 100;
		if (item == Items.coal)
			return 1600;
		if (item == Items.lava_bucket)
			return 100;
		if (item == Item.getItemFromBlock(Blocks.sapling))
			return 100;
		if (item == Items.blaze_rod)
			return 100;

		return GameRegistry.getFuelValue(itemstack);
	}
}

public static boolean isItemFuel(ItemStack itemstack) {
	return getItemBurnTime(itemstack) > 0;
}

public boolean isItemValidForSlot(int i, ItemStack itemstack) {

	return i == 2 ? false : (i == 1 ? isItemFuel(itemstack) : true);
}

public int[] getAccessibleSlotsFromSide(int var1) {

	return var1 == 0 ? slots_bottom : (var1 == 1 ? slots_top : slots_sides);
}

public boolean canInsertItem(int i, ItemStack itemstack, int j) {

	return this.isItemValidForSlot(i, itemstack);
}

public boolean canExtractItem(int i, ItemStack itemstack, int j) {

	return j != 0 || i != 1 || itemstack.getItem() == Items.bucket;
}

public int getBurnTimeRemainingScaled(int i) {
	if (this.currentItemBurnTime == 0) {
		this.currentItemBurnTime = this.furnaceSpeed;

	}
	return this.burnTime * i / this.currentItemBurnTime;
}

public int getCookProgressScaled(int i) {
	return this.cookTime * i / this.furnaceSpeed;
}

public void openInventory() {
}

public void closeInventory() {
}

public boolean isActive() {
return this.cookTime > 0;
}

}

 

Container:

package com.IvanTune.futuregate.gui;

import com.IvanTune.futuregate.tileentity.TileEntityCustomFurnace;
import com.IvanTune.futuregate.tileentity.TileEntityCustomMacerator;

import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.ICrafting;
import net.minecraft.inventory.Slot;
import net.minecraft.inventory.SlotFurnace;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.FurnaceRecipes;


public class ContainerCustomMacerator extends Container {

private TileEntityCustomMacerator customMacerator;

public int lastBurnTime;

public int lastItemBurnTime;

public int lastCookTime;

public ContainerCustomMacerator(InventoryPlayer inventory, TileEntityCustomMacerator tileentity){
	this.customMacerator = tileentity;

	this.addSlotToContainer(new Slot(tileentity, 0, 56, 35));
	this.addSlotToContainer(new Slot(tileentity, 1, 8, 56));
	this.addSlotToContainer(new SlotFurnace(inventory.player, tileentity, 2, 116, 35));

	for(int i = 0; i < 3; i++){
		for(int j = 0; j < 9; j++){
			this.addSlotToContainer(new Slot(inventory, j + i*9 + 9, 8 + j*18, 84 + i*18));
		}
	}

	for(int i = 0; i < 9; i++){
		this.addSlotToContainer(new Slot(inventory, i, 8 + i*18, 142));
	}
}


public void addCraftingToCrafters(ICrafting icrafting){
	super.addCraftingToCrafters(icrafting);
	icrafting.sendProgressBarUpdate(this, 0, this.customMacerator.cookTime);
	icrafting.sendProgressBarUpdate(this, 1, this.customMacerator.power);
}

public void detectAndSendChanges(){
	super.detectAndSendChanges();

	for(int i = 0; i < this.crafters.size(); i++){
		ICrafting icrafting = (ICrafting) this.crafters.get(i);

		if(this.lastCookTime != this.customMacerator.cookTime){
			icrafting.sendProgressBarUpdate(this, 0, this.customMacerator.cookTime);
		}

		if(this.lastBurnTime != this.customMacerator.power){
			icrafting.sendProgressBarUpdate(this, 1, this.customMacerator.power);
		}

	}

	this.lastCookTime = this.customMacerator.cookTime;
	this.lastBurnTime = this.customMacerator.power;

}

@SideOnly(Side.CLIENT)
public void updateProgressBar(int slot, int newValue){
	if(slot == 0) this.customMacerator.cookTime = newValue;
	if(slot == 1) this.customMacerator.power = newValue;

}
public ItemStack transferStackInSlot(EntityPlayer player, int clickedSlotNumber){
	ItemStack itemstack = null;
	Slot slot = (Slot) this.inventorySlots.get(clickedSlotNumber);

	if(slot != null && slot.getHasStack()){
		ItemStack itemstack1 = slot.getStack();
		itemstack = itemstack1.copy();

		if(clickedSlotNumber == 2){
			if(!this.mergeItemStack(itemstack1, 3, 39, true)){
				return null;
			}

			slot.onSlotChange(itemstack1, itemstack);

		}
		else if(clickedSlotNumber != 1 && clickedSlotNumber != 0){
			if(FurnaceRecipes.smelting().getSmeltingResult(itemstack1) != null){
				if(!this.mergeItemStack(itemstack1, 0, 1, false)){
					return null;

				}
			}
			else if(TileEntityCustomMacerator.hasItemPower(itemstack1)){
				if(!this.mergeItemStack(itemstack1, 1, 2, false)){
					return null;
				}
			}
			else if(clickedSlotNumber >= 3 && clickedSlotNumber < 30){
				if(!this.mergeItemStack(itemstack1, 30, 39, false)){
					return null;
				}
			}
			else if(clickedSlotNumber >= 30 && clickedSlotNumber < 39){
				if(!this.mergeItemStack(itemstack1, 3, 29, false)){
					return null;
				}
			}
		}
		else if(!this.mergeItemStack(itemstack1, 3, 38, false)){
			return null;
		}

		if(itemstack1.stackSize == 0){
			slot.putStack((ItemStack)null);
		}
		else{
			slot.onSlotChanged();
		}

		if(itemstack1.stackSize == itemstack.stackSize){
			return null;
		}

		slot.onPickupFromSlot(player, itemstack1);
	}

	return itemstack;

}

public boolean canInteractWith(EntityPlayer entityplayer) {

	return this.customMacerator.isUseableByPlayer(entityplayer);
}

}

 

Posted

Do what diesieben07 said. But just also wanted to explain that you wouldn't want to use an isActive field in the Block class because that would affect all blocks of that type! You need to understand that there is only one Block class instance in the game, and that actual multitude of blocks in the world are actually just a map of that instance to a block location. Minecraft does allow for a little bit of difference using the block metadata, so potentially you use metadata to do what you want. But really a tile entity is intended to process any more complex information required per block location. Since you already have a tile entity, another approach would be to use the isBurning() method there as that would accurately reflect the state of each furnace separately.

 

But anyway, since you are actually changing the block like vanilla furnace then you don't have to check any field in the block or tile entity -- the block type itself is enough to show that it is a burning furnace.

 

Just wanted to explain some of the concepts related to blocks that have complex functionality.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted

@diesieben07

 

Thanks for the reply! :)

 

I had it set like that and used this.isActive on the randomDisplayTick method to spawn the particles on the furnace and all was fine and dandy.

 

Then I started making other machines with different slots and all that, and then the block went active when I didn't want to (When it got power it went active, not when doing an operation).

 

I'm sorry if this question sounds stupid, but what determines whether or not the block turns active? Any way to control it?

 

 

@jabelar

 

Thanks for taking the time to explain that!

I tried using the isBurning() method, which returns the same as the isActive method I made in tileentity (this.cookTime > 0), but cookTime only works when I am inside the gui, so I was thinking it has something to do with client and server not beeing synced or something, but I have not been able to figure it out.

 

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.