package dangerzone;
/*
 * This code is copyright Richard H. Clark, TheyCallMeDanger, OreSpawn, 2015-2020.
 * You may use this code for reference for modding the DangerZone game program,
 * and are perfectly welcome to cut'n'paste portions for your mod as well.
 * DO NOT USE THIS CODE FOR ANY PURPOSE OTHER THAN MODDING FOR THE DANGERZONE GAME.
 * DO NOT REDISTRIBUTE THIS CODE. 
 * 
 * This copyright remains in effect until January 1st, 2021. 
 * At that time, this code becomes public domain.
 * 
 * WARNING: There are bugs. Big bugs. Little bugs. Every size in-between bugs.
 * This code is NOT suitable for use in anything other than this particular game. 
 * NO GUARANTEES of any sort are given, either express or implied, and Richard H. Clark, 
 * TheyCallMeDanger, OreSpawn are not responsible for any damages, direct, indirect, or otherwise. 
 * You should have made backups. It's your own fault for not making them.
 * 
 * NO ATTEMPT AT SECURITY IS MADE. This code is USE AT YOUR OWN RISK.
 * Regardless of what you may think, the reality is, that the moment you 
 * connected your computer to the Internet, Uncle Sam, among many others, hacked it.
 * DO NOT KEEP VALUABLE INFORMATION ON INTERNET-CONNECTED COMPUTERS.
 * Or your phone...
 * 
 */


import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.util.List;
import java.util.ListIterator;

import javax.imageio.ImageIO;

import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;

import dangerzone.blocks.Block;
import dangerzone.blocks.Blocks;
import dangerzone.entities.Cockroach;
import dangerzone.entities.Entity;
import dangerzone.entities.EntityBlockItem;
import dangerzone.entities.EntityLiving;
import dangerzone.items.Item;
import dangerzone.items.ItemAxe;
import dangerzone.items.ItemPickAxe;
import dangerzone.items.ItemShovel;
import dangerzone.items.ItemSword;
import dangerzone.items.Items;
import dangerzone.threads.LightingThread;
import dangerzone.threads.ServerConnection;
import dangerzone.threads.ServerThread;





public class Player extends EntityLiving {
	public Socket toServer;
	public Socket toClient;
	public ServerThread server_thread;			//Handy reference for packets that only go to specific player (on server)
	public ServerConnection server_connection;  //Same as DangerZone.server_connection (on client)
	public float armangle = 0f;
	public int armdir = 0;
	public byte[] tdata = null;
	public boolean donewtexture = false;
	public String myname = null;
	public int starthealth = 50;
	public int swimdelay = 0;
	public int lightupdatecounter = 0;
	public int inventoryticker = 0;
	public int player_privs = 0;
	public Entity morph = null;	
	public Entity morphto = null;	
	public boolean do_morph;
	public float morphspeed = -0.01f;
	public float morphscale = 1.0f;

	
	public Player(World w){
		super(w);
		width = 0.65f;
		height = 1.75f;	
		eyeheight = 1.65f;
		uniquename = "DangerZone:Player";		
		has_inventory = true;
		setMaxHealth(starthealth);
		setHealth(starthealth);
		setDefense(1);
		setMaxHunger(50);
		setHunger(50);
		setAttackDamage(1);
		setMaxAir(50);
		setAir(50);
		setCanDespawn(false);
	};
	
	public void init(){
		super.init();
		eyeheight = 0.943f*getHeight();
		setSitting(false);
		morphto = morph = null;
	}
		
	
	public void doAttackFrom(Entity e, /*DamageTypes*/int dt, float pain){	
			if(getGameMode() != GameModes.SURVIVAL)return; //not in survival mode! No damage!			
			if(!world.isServer){
				if(!takesDamageFrom(dt))return;
				setHealth(getHealth()-pain);
				float sf = getHeight()*getWidth()/3;
				if(sf < 0.15f)sf = 0.15f;
				if(sf > 5)sf = 5;
				double dir = world.rand.nextDouble()*Math.PI*2;
				this.motionx += Math.cos(dir)*sf;
				this.motionz += Math.sin(dir)*sf;
				this.motiony += 0.25f*sf;
				if(getHealth() > 0){			
					this.world.playSound(getHurtSound(), dimension, posx, posy+getHeight()/2, posz, 1.0f, 1.0f+(world.rand.nextFloat()-world.rand.nextFloat())*0.2f);
				}else{					
					this.world.playSound(getDeathSound(), dimension, posx, posy+getHeight()/2, posz, 1.0f, 1.0f+(world.rand.nextFloat()-world.rand.nextFloat())*0.1f);			
					this.doDeathDrops();
					this.deadflag = true;
					this.onDeath();
					this.removeAllEffects();									
				}				
			}else{
				super.doAttackFrom(e, dt, pain);
			}
	}
	
	public void doEntityAction(float deltaT){	
		//none!
	}
	
	public String getHurtSound(){
		if(morph != null)return morph.getHurtSound();
		int i = world.rand.nextInt(3);
		if(i == 0)return "DangerZone:ouch1";
		if(i == 1)return "DangerZone:ouch2";
		return "DangerZone:ouch3";		
	}
	
	public String getLivingSound(){
		if(morph != null)return morph.getLivingSound();
		return null;
	}
	
	public float getScale(){
		if(do_morph){
			return morphscale;
		}
		return super.getScale();
	}
	
	
	public float getRightArmAngle(){
		return -armangle;
	}
	
	public void update(float deltaT){
		
		stray_entity_ticker = 0; //Let's make sure we don't get removed just because we don't get updates for ourselves!!!
		
		if(morph != null){
			//we need this stuff for distance attacks and such... might as well do both client and server...
			morph.posx = posx;
			morph.posy = posy;
			morph.posz = posz;
			morph.rotation_yaw = rotation_yaw;
			morph.rotation_pitch = rotation_pitch;
			morph.rotation_roll = rotation_roll;
			morph.rotation_yaw_head = rotation_yaw_head;
			morph.rotation_pitch_head = rotation_pitch_head;
			morph.rotation_roll_head = rotation_roll_head;
		}
		
		//update what we are mounted on first!
		if(!world.isServer){
			//and some particles while we are at it!
			if(getGameMode() != GameModes.GHOST && isSolidAtLevel(dimension, posx, posy-0.02f, posz)){
				if(isFlying()){
					setFlying(false);
					Utils.spawnParticles(this.world, "DangerZone:ParticleBreak", 50, this.dimension, posx, posy, posz, world.getblock(dimension, (int)posx, (int)(posy-0.2f), (int)posz), true);
					this.world.playSound(getHurtSound(), dimension, posx, posy+getHeight()/2, posz, 0.5f, 1);
				}
				//forward to server!
				if(DangerZone.start_server && Math.sqrt(motionx*motionx + motionz*motionz) > 0.25f && world.rand.nextInt(3) == 1){
					Utils.spawnParticles(this.world, "DangerZone:ParticleBreak", 1, this.dimension, posx, posy, posz, world.getblock(dimension, (int)posx, (int)(posy-0.2f), (int)posz), true);
				}
			}

			Entity e = getRiddenEntity();
			if(e != null){
				e.smoothMotion(); //adjust the difference between where we think it is and where the server thinks it is.
				e.update(deltaT);
			}
			
		}else{
			if(morph != null){
				if(this.world.rand.nextInt(600) == 1){			
					this.world.playSound(getLivingSound(), dimension, posx, posy+getHeight()/2, posz, getLivingSoundVolume(), getLivingSoundPitch());	
				}
			}		
		}
		
		//morph check!
		//this gets done on BOTH client and server
		String morphname = getMorphName();
		if(!do_morph){
			if(morph == null){	
				if(morphname != null){
					Entity ent = world.createEntityByName(morphname, dimension, posx, posy, posz);
					if(ent != null){
						ent.init();
						//looks good so far!
						morphto = ent;
						morphspeed = -0.01f;
						do_morph = true;
						if(!world.isServer)world.playSound("DangerZone:morph1", dimension, posx, posy, posz, 0.25f, 1.0f+((world.rand.nextFloat()-world.rand.nextFloat())*0.3f));
					}
				}
			}else{
				if(!morph.uniquename.equals(morphname)){
					if(morphname == null){
						morphto = this;
						morphspeed = -0.01f;
						do_morph = true;
						if(!world.isServer)world.playSound("DangerZone:morph1", dimension, posx, posy, posz, 0.25f, 1.0f+((world.rand.nextFloat()-world.rand.nextFloat())*0.3f));
						setMorphName(null);
					}else{
						if(morphname.equals(this.uniquename)){
							morphto = this;
							morphspeed = -0.01f;
							do_morph = true;
							if(!world.isServer)world.playSound("DangerZone:morph1", dimension, posx, posy, posz, 0.25f, 1.0f+((world.rand.nextFloat()-world.rand.nextFloat())*0.3f));
							setMorphName(null);
						}else{
							Entity ent = world.createEntityByName(morphname, dimension, posx, posy, posz);
							if(ent != null){
								ent.init();
								//looks good so far!
								morphto = ent;
								morphspeed = -0.01f;
								do_morph = true;
								if(!world.isServer)world.playSound("DangerZone:morph1", dimension, posx, posy, posz, 0.25f, 1.0f+((world.rand.nextFloat()-world.rand.nextFloat())*0.3f));
							}
						}
					}				
				}
				//show what we are holding if model supports it!
				if(morph.has_inventory){
					morph.sethotbarindex(gethotbarindex());
					morph.setHotbar(gethotbarindex(), getHotbar(gethotbarindex()));
				}
			}
		}
		
		if(do_morph){
			morphscale += morphspeed;
			
			if(morphscale < 0.01f){
				morphscale = 0.01f;
				morphspeed = 0.01f;
				morph = morphto;
				model = morphto.model;
				if(morph == this){
					morph = null;
					model = DangerZoneBase.modelhumanoid;
				}
				if(!world.isServer)world.playSound("DangerZone:morph2", dimension, posx, posy, posz, 0.25f, 1.0f+((world.rand.nextFloat()-world.rand.nextFloat())*0.3f));
			}			

			if(morphscale >= 1){
				morphscale = 1;
				morphspeed = -0.01f;
				do_morph = false;
			}
			
			if(!world.isServer){
				Utils.spawnParticles(world, "DangerZone:ParticleSparkle", 5, dimension, 
						posx + (world.rand.nextFloat()-world.rand.nextFloat())*getWidth()/2, 
						posy+world.rand.nextFloat()*getHeight(), 
						posz + (world.rand.nextFloat()-world.rand.nextFloat())*getWidth()/2, 
						true);
				Utils.spawnParticles(world, "DangerZone:ParticleSmoke", 5, dimension, 
						posx + (world.rand.nextFloat()-world.rand.nextFloat())*getWidth()/2, 
						posy+world.rand.nextFloat()*getHeight(), 
						posz + (world.rand.nextFloat()-world.rand.nextFloat())*getWidth()/2, 
						true);
				Utils.spawnParticles(world, "DangerZone:ParticleFire", 5, dimension, 
						posx + (world.rand.nextFloat()-world.rand.nextFloat())*getWidth()/2, 
						posy+world.rand.nextFloat()*getHeight(), 
						posz + (world.rand.nextFloat()-world.rand.nextFloat())*getWidth()/2, 
						true);
			}
			
		}
		
		//people whining about player motion too squishy.... sigh... fine... unsquish it a bit...
		if(!getForward() && !getBackward() && !getLeft() && !getRight() && getGameMode() != GameModes.GHOST){
			if(world.isServer){
				motionx *= (1.0f-(0.35f*deltaT));
				motionz *= (1.0f-(0.35f*deltaT));
			}else{
				float rate = DangerZone.entityupdaterate;
				rate /= DangerZone.serverentityupdaterate;
				motionx *= (1.0f-(0.35f*deltaT*rate));
				motionz *= (1.0f-(0.35f*deltaT*rate));	
			}
		}
			
		if(getSitting()){
			eyeheight = 0.586f*getHeight();
		}else{
			eyeheight = 0.943f*getHeight();
		}
	
		
		if(!world.isServer){
			
			InventoryContainer ic = null;
			Item icitem = null;
			
			inventoryticker++;
			if(inventoryticker > 5){
				inventoryticker = 0;
				//update inventory items!
				for(int j=0;j<maxinv;j++){
					ic = entity_inventory[j];
					if(ic != null){
						icitem = ic.getItem();
						if(icitem != null){
							icitem.update(this, ic);
						}
					}
				}
			}
			
			if(morph == null){
				setMaxHealth(starthealth+getExperience()/1000);
				setDefense(1.0f + (float)getExperience()/100000f);
				setAttackDamage(1.0f + (float)getExperience()/10000f);
			}
			

			
			//did we die?
			if(getHealth() <= 0)deadflag = true;
			
			//Do a little regeneration!
			if(this == DangerZone.player && getGameMode() == GameModes.SURVIVAL){
				//use up some food
				if(getHunger() > 0){
					setHunger(getHunger()-0.0002f); //slower than health heal!!!
				}
				//regenerate a bit if not hungry
				if(getHealth() >= 0 && getHealth() < getMaxHealth() && getHunger() > getMaxHunger()/2){
					//setHealth(0.002f+getHealth());
					setHealth(getHealth()+(getMaxHealth()*0.00004f));
				}
				//uh oh. starving to death!
				if(getHunger() <= 0){
					//setHealth(getHealth()-0.001f);
					if(world.rand.nextInt(100) == 1)doAttackFrom(null, DamageTypes.HUNGER, 0.1f);
				}
			}			

			
			if(armdir != 0){
				if(armdir > 0){
					armangle += 22f*deltaT;
					if(armangle > 150f){
						armdir = -1;
					}
				}
				if(armdir < 0){
					armangle -= 22f*deltaT;
					if(armangle <= 0){
						armdir = 0;
						armangle = 0;
					}
				}
				setAttacking(true);
			}else{
				setAttacking(false);
			}
			
			/*
			 * If player is carrying something that makes light...
			 * Send request to Lighting thread.
			 */
			lightupdatecounter++;
			if(lightupdatecounter > 5){
				ic = getHotbar(gethotbarindex());
				if(ic != null){
					float lvl = 0;			
					if(ic.bid != 0){
						lvl = Blocks.getLightLevel(ic.bid, world, dimension, (int)posx, (int)(posy+1.25f), (int)posz);
					}
					if(ic.iid != 0){
						lvl = Items.getLightLevel(ic.iid);
					}
					if(lvl != 0){
						//LightingThread.updateLightMaps(world, lvl, dimension, (int)posx, (int)(posy+1.25f), (int)posz);
						LightingThread.addRequest(dimension, (int)posx, (int)(posy+1.25f), (int)posz, lvl);
					}
				}
				lightupdatecounter = 0;
			}		
		}else{
			if(morph != null){
				//System.out.printf("server morph !null\n");
				if(getLeft() || getRight()){
					//System.out.printf("server morph dist att\n");
					double px, py, pz;					
					px = (float)Math.sin(Math.toRadians(rotation_yaw_head))*(float)Math.cos(Math.toRadians(rotation_pitch_head));
					py = (float)Math.sin(Math.toRadians(rotation_pitch_head));
					pz = (float)Math.cos(Math.toRadians(rotation_yaw_head))*(float)Math.cos(Math.toRadians(rotation_pitch_head));
					//make a temporary entity to stuff the coordinates into....
					Entity tempent = world.createEntityByName("DangerZone:Cockroach", dimension, (px*32)+posx, (py*32)+posy, (pz*32)+posz);
					if(tempent != null){
						morph.doDistanceAttack(tempent);
					}
					//don't actually spawn it!
					//this will work for thrown objects pretty well. Not so much for anything that is actually spawned at their feet!
				}
			}
		}

		boolean wasOnGround = getOnGround();
		super.update(deltaT);
		
		if(!world.isServer){
			if(!wasOnGround){
				if(getOnGround()){
					int bid = world.getblock(dimension, (int)posx, (int)(posy-0.2f), (int)posz);
					//if just came down on the ground...
					Utils.spawnParticles(this.world, "DangerZone:ParticleBreak", 50, this.dimension, posx, posy, posz, bid, true);
					Blocks.doblocktick(this.world, dimension, (int)posx, (int)(posy-0.2f), (int)posz, bid);
					//see if we stomped on a cockroach!
					if(steppedOnCockroach()){
						for(int i=0;i<Dimensions.dimensionsMAX;i++){
							int id = (this.dimension+i+1)%Dimensions.dimensionsMAX;
							if(Dimensions.DimensionArray[id] != null){
								Dimensions.DimensionArray[id].teleportToDimension(this, world, id, (int)this.posx, (int)this.posy, (int)this.posz);
								world.playSound("DangerZone:big_splat", dimension, posx, posy, posz, 1.0f, 1.0f+(world.rand.nextFloat()-world.rand.nextFloat())*0.2f);
								Utils.spawnParticles(this.world, "DangerZone:ParticleHurt", 10, this.dimension, posx, posy, posz, true);
								break;
							}
						}
					}					
				}
			}
		}
	}
	
	public void tryfly(){
		if(morph != null && morph.canFly && !isFlying()){
			setFlying(true);
			posy += 0.05f; //go!
			motiony += 0.25f;				
		}
	}
	
	public void jump(){	

		if(getInLiquid()){
			if(Blocks.isLiquid(world.getblock(dimension, (int)posx, (int)(posy+1.5f),(int)posz))){
				swimdelay++;
				if(swimdelay > 40)swimdelay = 0;
				if(swimdelay > 15){
					motiony += 0.035f;
					if(isSolidAtLevel(dimension, posx, posy-0.35f, posz))motiony += 0.55f;
					if(!world.isServer)if(getHunger() > 0 && getGameMode() == GameModes.SURVIVAL)setHunger(getHunger()-0.002f); //swimming makes us hungry!
				}
				
			}
		}
						
		if(!isSolidAtLevel(dimension, posx, posy-0.02f, posz))return;
		
		if(Math.abs(motiony)>0.05f)return;
		
		float jumpfactor = 0.80f + (getHeight())/8.0f;
		float jumpadjust = getTotalEffect(Effects.STRENGTH);
		if(jumpadjust != 0)jumpfactor += jumpfactor*jumpadjust/4;
		jumpadjust = getTotalEffect(Effects.WEAKNESS);
		if(jumpadjust != 0)jumpfactor /= jumpadjust;
		
		if(isBaby()){
			if(world.getblock(dimension, (int)posx, (int)posy+1, (int)posz) != 0){
				motiony += 0.75f;	
			}else{
				motiony += jumpfactor*0.95f;	
			}
		}else{
			if(world.getblock(dimension, (int)posx, (int)posy+2, (int)posz) != 0 || world.getblock(dimension, (int)posx, (int)posy+3, (int)posz) != 0){
				motiony += 0.75f;	
			}else{
				motiony += jumpfactor*0.95f;	
			}
		}
		
		//System.out.printf("Jump! posy, moy == %f, %f\n", posy, motiony);
		if(!world.isServer)if(getHunger() > 0 && getGameMode() == GameModes.SURVIVAL)setHunger(getHunger()-0.08f); //jumping makes us hungry!
	}
	
	public void leftclick(World world, int focus_x, int focus_y, int focus_z, int focus_side, int eid){
		Entity e = null;
		int do_uses = 0;
		if(world.isServer){
			//System.out.printf("server left eid = %d\n", eid);
			InventoryContainer ic = getHotbar(gethotbarindex());
			e = DangerZone.server.entityManager.findEntityByID(eid);
			boolean leftcontinue = true;
			if(ic != null){
				Item it = ic.getItem();
				if(it != null)leftcontinue = it.onLeftClick(this, e, ic);
				Block bl = ic.getBlock();
				if(bl != null)leftcontinue = bl.onLeftClick(this, e, ic);
			}
			if(leftcontinue){
				if(e != null){
					int dt = DamageTypes.GENERIC;
					float damage = getAttackDamage();				
					if(ic != null){
						if(ic.bid != 0){
							dt = DamageTypes.BLOCK;
						}else{
							if(ic.iid > 0 && ic.iid < Items.itemsMAX){
								damage += Items.getAttackStrength(ic.iid);
								Item it = ic.getItem();
								if(it != null){
									if(it instanceof ItemSword)dt = DamageTypes.SWORD;
									if(it instanceof ItemPickAxe)dt = DamageTypes.TOOL;
									if(it instanceof ItemAxe)dt = DamageTypes.TOOL;
									if(it instanceof ItemShovel)dt = DamageTypes.TOOL;
								}
							}
						}
					}
					if(e.leftClickedByPlayer(this, ic)){
						e.doAttackFrom(this, dt, damage);	
					}
				}
			}
		}else{
			armdir = 1;
			//e = findHitEntity(true);
			//System.out.printf("client left eid = %d\n", eid);
			e = DangerZone.entityManager.findEntityByID(eid);
			if(e == null){
				server_connection.playerActionToServer(0, 0, 0); //0 = left mouse click	
			}else{
				server_connection.playerActionToServer(0, 0, e.entityID); //0 = left mouse click	
			}
			InventoryContainer ic = getHotbar(gethotbarindex());
			boolean leftcontinue = true;
			if(ic != null){
				Item it = ic.getItem();
				if(it != null)leftcontinue = it.onLeftClick(this, e, ic);
				Block bl = ic.getBlock();
				if(bl != null)leftcontinue = bl.onLeftClick(this, e, ic);
			}
			if(leftcontinue){
			if(e == null){ //Server will handle entities that get hit. We still had to check and TELL IT!
				if(focus_x > 0 && focus_y > 0 && focus_z > 0){				
					int bid = world.getblock(dimension, focus_x, focus_y, focus_z);
					if(bid > 0){
						do_uses = 1;
						if(Blocks.leftClickOnBlock(bid, this, dimension, focus_x, focus_y, focus_z) || this.getGameMode() != GameModes.SURVIVAL){
							float dmg = getAttackDamage();
							if(ic != null && ic.iid != 0){
								if(Blocks.isWood(bid) || Blocks.isLeaves(bid)){
									dmg += Items.getWoodStrength(ic.iid);
								}
								if(Blocks.isStone(bid)){
									dmg += Items.getStoneStrength(ic.iid);
								}
								if(Blocks.isDirt(bid)){
									dmg += Items.getDirtStrength(ic.iid);
								}
								if(ic.getItem() instanceof ItemSword && !Blocks.isLeaves(bid)){
									dmg = 0;
								}
								if(ic.getItem() instanceof ItemSword && Blocks.isLeaves(bid)){
									dmg += Items.getAttackStrength(ic.iid);
								}
								ic.getItem().leftClickOnBlock(this, dimension, focus_x, focus_y, focus_z, focus_side);
							}
							
							if(dmg != 0){
								float damageadjust = getTotalEffect(Effects.STRENGTH);
								if(damageadjust != 0)dmg *= damageadjust;
								damageadjust = getTotalEffect(Effects.WEAKNESS);
								if(damageadjust != 0)dmg /= damageadjust;
							}
							
							String particlename = Blocks.getParticleName(bid);
							if(particlename == null || particlename.equals(""))particlename = "DangerZone:ParticleBreak";
							Utils.spawnParticles(this.world, particlename, 10, this.dimension, (double)focus_x+0.5f, (double)focus_y+0.5f, (double)focus_z+0.5f, bid, true);
							int md = Blocks.getMinDamage(bid);
							if(dmg >= md)WorldRenderer.focus_damage += dmg;
							//System.out.printf("min,  act, tot: %d, %d, %d\n", (int)md, (int)dmg, (int)WorldRenderer.focus_damage);
							if(dmg != 0 && (WorldRenderer.focus_damage >= WorldRenderer.focus_maxdamage || this.getGameMode() != GameModes.SURVIVAL)){
								world.playSound(Blocks.getBreakSound(bid), dimension, focus_x, focus_y, focus_z, 0.35f, 1.0f);																	
								Blocks.onBlockBroken(bid, this, dimension, focus_x, focus_y, focus_z);
								int dbid;
								int diid;
								int howmany;
								dbid = Blocks.getBlockDrop(bid, this, this.world, dimension, focus_x, focus_y, focus_z);
								diid = Blocks.getItemDrop(bid, this, this.world, dimension, focus_x, focus_y, focus_z);
								howmany = Blocks.getDropCount(bid, this, this.world, dimension, focus_x, focus_y, focus_z);
								world.setblockandmeta(dimension, focus_x, focus_y, focus_z, 0, 0);
								if(howmany > 0){
									for(int i=0;i<howmany;i++){
										if(dbid > 0){
											EntityBlockItem eb = (EntityBlockItem)world.createEntityByName(DangerZone.blockitemname, dimension, (double)focus_x+0.5f, (double)focus_y+0.5f, (double)focus_z+0.5f);
											if(eb != null){
												eb.setBID(dbid);
												eb.setIID(0);
												world.spawnEntityInWorld(eb);
											}
										}
										if(diid > 0){
											EntityBlockItem eb = (EntityBlockItem)world.createEntityByName(DangerZone.blockitemname, dimension, (double)focus_x+0.5f, (double)focus_y+0.5f, (double)focus_z+0.5f);
											if(eb != null){
												eb.setBID(0);
												eb.setIID(diid);
												world.spawnEntityInWorld(eb);
											}
										}
									}
								}
							}else{
								world.playSound(Blocks.getHitSound(bid), dimension, focus_x, focus_y, focus_z, 0.35f, 1.0f);
							}
						}
					}
				}
			}else{
				do_uses = 1;
				//System.out.printf("spawn hurt at %f,  %f, %f\n", hit_x, hit_y, hit_z);
				Utils.spawnParticles(this.world, "DangerZone:ParticleHurt", 10, this.dimension, e.posx, e.posy, e.posz, true);
				if(getHunger() > 0 && getGameMode() == GameModes.SURVIVAL)setHunger(getHunger()-0.25f); //hitting things makes us hungry!
			}			
			if(do_uses != 0 && this.getGameMode() == GameModes.SURVIVAL){
				if(ic != null){
					if(ic.iid > 0 && ic.iid < Items.itemsMAX){
						if(Items.getMaxStack(ic.iid) == 1){
							ic.currentuses++;
							setHotbarChanged(gethotbarindex());
							if(ic.currentuses >= Items.getMaxUses(ic.iid)){
								if(world.rand.nextInt(2)==1){
									world.playSound("DangerZone:toolbreak1", dimension, focus_x, focus_y, focus_z, 0.45f, 1.0f);		
								}else{
									world.playSound("DangerZone:toolbreak2", dimension, focus_x, focus_y, focus_z, 0.45f, 1.0f);
								}
								setHotbar(gethotbarindex(), null);
							}
						}
					}
				}			
			}
			}
		}
	}
	
	public void middleclick(World world, int focus_x, int focus_y, int focus_z, int eid){
		if(focus_x > 0 && focus_y > 0 && focus_z > 0){
			int bid = world.getblock(dimension, focus_x, focus_y, focus_z);			
			if(bid != 0){
				world.playSound(Blocks.getHitSound(bid), dimension, focus_x, focus_y, focus_z, 0.35f, 1.0f);
				DangerZone.messagetimer = 100;
				DangerZone.messagestring = Blocks.BlockArray[bid].uniquename;
				//System.out.printf("meta = %d\n", world.getblockmeta(dimension, focus_x, focus_y, focus_z));
			}
		}
	}
	
	
//Handle right-clicks by player
	public void rightclick(World world, int focusx, int focusy, int focusz, int side, int eid){
		Entity e = null;
		InventoryContainer ic = getHotbar(gethotbarindex());
		boolean rightcontinue = true;
		if(world.isServer){		
			//System.out.printf("rt click server eid = %d\n", eid);
			e = DangerZone.server.entityManager.findEntityByID(eid);
			if(e != null){					
				if(e.rightClickedByPlayer(this, ic) && getGameMode() == GameModes.SURVIVAL){
					//decrement inventory?					
					if(ic != null){
						ic.count--;
						if(ic.count <= 0){
							ic = null;
						}
					}
					this.server_thread.sendInventoryUpdateToPlayer(0, gethotbarindex(), ic);
				}else{						
					if(ic != null){
						Item it = ic.getItem();
						if(it != null)rightcontinue = it.onRightClick(this, e, ic);
						Block bl = ic.getBlock();
						if(bl != null)rightcontinue = bl.onRightClick(this, e, ic);
						if(getGameMode() == GameModes.SURVIVAL && rightcontinue){
							if(ic.iid > 0 && ic.iid < Items.itemsMAX && Items.getMaxStack(ic.iid) == 1){
								ic.currentuses++;
								this.server_thread.sendInventoryUpdateToPlayer(0, gethotbarindex(), ic);
								if(ic.currentuses >= Items.getMaxUses(ic.iid)){
									this.server_thread.sendInventoryUpdateToPlayer(0, gethotbarindex(), null);										
								}
							}else{
								ic.count--;
								if(ic.count <= 0){
									ic = null;
								}
								this.server_thread.sendInventoryUpdateToPlayer(0, gethotbarindex(), ic);
							}
						}
					}
				}
			}else{
				if(ic != null){
					Item it = ic.getItem();
					if(it != null)rightcontinue = it.onRightClick(this, e, ic);
					Block bl = ic.getBlock();
					if(bl != null)rightcontinue = bl.onRightClick(this, e, ic);
					if(getGameMode() == GameModes.SURVIVAL && rightcontinue){
						//System.out.printf("rt click server2 count = %d\n", ic.count);
						if(ic.count == 1){
							ic.currentuses++;
							this.server_thread.sendInventoryUpdateToPlayer(0, gethotbarindex(), ic);
							if(ic.currentuses >= Items.getMaxUses(ic.iid)){
								this.server_thread.sendInventoryUpdateToPlayer(0, gethotbarindex(), null);										
							}
						}else{
							ic.count--;
							if(ic.count <= 0){
								ic = null;
							}
							this.server_thread.sendInventoryUpdateToPlayer(0, gethotbarindex(), ic);
						}
					}
				}
			}
		}else{
			//e = findHitEntity(false);
			e = DangerZone.entityManager.findEntityByID(eid);
			if(e != null){	
				server_connection.playerActionToServer(0, 1, e.entityID); //1 = right mouse click with entity
				e.rightClickedByPlayer(this, ic); //might need a GUI!
			}else{
				int bid = 0;
				int iid = 0;
				if(ic!= null){
					if(ic.count >= 1){
						bid = ic.bid;
						iid = ic.iid;
					}
				}
				//System.out.printf("rt click client = %d, %d\n", bid, iid);
				if(focusx > 0 && focusy >= 0 && focusz > 0){
					int fbid = world.getblock(dimension, focusx, focusy, focusz);
					boolean cont = Blocks.rightClickOnBlock(fbid, this, dimension, focusx, focusy, focusz);
					if(cont){						
						if(bid != 0){
							Blocks.doPlaceBlock(bid, fbid, this, world, dimension, focusx, focusy, focusz, side);						
						}else{
							if(ic != null && iid != 0){						
								boolean delme = Items.rightClickOnBlock(iid, this, dimension, focusx, focusy, focusz, side);	
								world.playSound(Blocks.getHitSound(fbid), dimension, focusx, focusy, focusz, 0.35f, 1.0f);
								if(getGameMode() == GameModes.SURVIVAL && delme){
									if(ic.count == 1){
										ic.currentuses++;
										setHotbarChanged(gethotbarindex());
										if(ic.currentuses >= Items.getMaxUses(ic.iid)){
											setHotbar(gethotbarindex(), null);										
										}
									}else{
										//System.out.printf("rt click client count= %d\n", ic.count);
										ic.count--;
										if(ic.count <= 0){
											setHotbar(gethotbarindex(), null);
											ic = null;
										}
										setHotbarChanged(gethotbarindex());
									}
								}
							}
						}
					}
				}else{
					server_connection.playerActionToServer(0, 1, 0); //1 = right mouse click
				}
			}
		}
	}
	
	
	/*
	 * Add a block or item into a slot
	 */
	public boolean putMeInASlot(int bid, int iid, int uses){
		
		//Can we add to an existing one?
		for(int i=0;i<10;i++){
			InventoryContainer ic = getHotbar(i);
			if(ic != null){
				if(ic.bid == bid && ic.iid == iid){
					if(ic.bid != 0){
						if(ic.count < Blocks.getMaxStack(bid)){
							ic.count++;
							setHotbarChanged(i);
							sendInventoryUpdate(0, i, ic);
							return true;
						}
					}
					if(ic.iid != 0){
						if(ic.count < Items.getMaxStack(iid)){
							ic.count++;
							setHotbarChanged(i);
							sendInventoryUpdate(0, i, ic);
							return true;
						}
					}
				}
			}
		}
		
		for(int i=0;i<50;i++){
			InventoryContainer ic = getInventory(i);
			if(ic != null){
				if(ic.bid == bid && ic.iid == iid){
					if(ic.bid != 0){
						if(ic.count < Blocks.getMaxStack(bid)){
							ic.count++;
							setInventoryChanged(i);
							sendInventoryUpdate(1, i, ic);
							return true;
						}
					}
					if(ic.iid != 0){
						if(ic.count < Items.getMaxStack(iid)){
							ic.count++;
							setInventoryChanged(i);
							sendInventoryUpdate(1, i, ic);
							return true;
						}
					}
				}
			}
		}
				
		//find empty spot
		for(int i=0;i<10;i++){
			if(getHotbar(i) == null){
				InventoryContainer ic = new InventoryContainer();
				setHotbar(i, ic);
				ic.bid = bid;
				ic.iid = iid;
				ic.count = 1;
				ic.currentuses = uses;
				sendInventoryUpdate(0, i, ic);
				return true;
			}
		}
		for(int i=0;i<50;i++){
			if(getInventory(i) == null){
				InventoryContainer ic = new InventoryContainer();
				setInventory(i, ic);
				ic.bid = bid;
				ic.iid = iid;
				ic.count = 1;
				ic.currentuses = uses;
				sendInventoryUpdate(1, i, ic);
				return true;
			}
		}
		return false;
	}
	
	public void sendInventoryUpdate(int which, int slot, InventoryContainer ic){
		if(this.world.isServer){
			this.server_thread.sendInventoryUpdateToPlayer(which, slot, ic);
		}else{
			this.server_connection.inventoryUpdate(which, slot, ic);
		}
	}

	
	private boolean steppedOnCockroach(){
		List<Entity> nearby_list = null;
		ListIterator<Entity> li;
		double dist;
		
		//Get a list of entities within reach of largest mob expected because we may hit their hitbox!
		if(this.world.isServer){
			return false;
		}else{
			nearby_list = DangerZone.entityManager.findEntitiesInRange(4.0f, dimension, posx, posy, posz);
		}
		

		if(nearby_list != null){
			if(!nearby_list.isEmpty()){			
				Entity e = null;
				li = nearby_list.listIterator();
				while(li.hasNext()){
					e = (Entity)li.next();
					if(e instanceof Cockroach){ 
						dist = getDistanceFromEntity(e); 						
						if(dist < 0.75f){
							Utils.spawnParticles(this.world, "DangerZone:ParticleHurt", 10, this.dimension, e.posx, e.posy, e.posz, true);
							return true;
						}
					}
				}

			}
		}
				
		return false;
	}
	
	public Texture getTexture(){
		
		if(morph != null){
			return morph.getTexture();
		}
		
		if(donewtexture){ //Make a texture for another player
			File file = null;	
			
			try {
				file = File.createTempFile("TmpSkin", ".tmp");
				file.deleteOnExit();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}				
			int width = 64;
			int height = 32;
			String format = "PNG"; 
			BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			   
			for(int x = 0; x < width; x++){
			    for(int y = 0; y < height; y++){
			        int i = (x + (width * y)) * 4;
			        int r = tdata[i] & 0xFF;
			        int g = tdata[i+1] & 0xFF;
			        int b = tdata[i+2] & 0xFF;
			        int a = tdata[i+3] & 0xFF;
			        image.setRGB(x, y, (a << 24) | (r << 16) | (g << 8) | b );
			    }
			}
			   
			try {
			    ImageIO.write(image, format, file);
			} catch (IOException e) { 
				e.printStackTrace(); 
			}
			image.flush();
			
			if(texture != null){
				texture.release();
				texture = null;
			}
			
			try {
				texture = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream(file.getPath()));	
				tdata = texture.getTextureData();
			} catch (IOException e) {
				e.printStackTrace();
				texture = null;
			}			
			donewtexture = false;
		}
		
		if(texture == null){ //load my own texture
			try {
				texture = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("Player.png"));	
				tdata = texture.getTextureData();
			} catch (IOException e) {
				e.printStackTrace();
				texture = null;
			}
		}
		return texture;
	}
	
	public float getWidth(){
		if(morph != null){
			float w = morph.getWidth();
			if(isBaby())w /= 4;
			return w;
		}
		return super.getWidth();
	}
	
	
	public float getHeight(){
		if(morph != null){
			float h = morph.getHeight();
			if(isBaby())h /= 4;
			return h;
		}
		return super.getHeight();
	}
	
	public float getNameHeight(){
		if(morph != null)return morph.getNameHeight();
		return height;
	}
	
	public void onKill(Entity victim){
		if(victim == null)return;
		if(!(victim instanceof EntityLiving))return; //filter out at least some of the things we shouldn't morph to!
		//See if we can morph!
		float ef = getTotalEffect(Effects.MORPH);
		if(ef != 0){			
			//System.out.printf("morph to: %s, cs = %s\n", morphname, world.isServer?"true":"false");
			setMorphName(victim.uniquename);
			if(world.isServer){ //should be!
				this.server_thread.sendVarStringUpdate(2, victim.uniquename);
			}		
		}
	}
	
	public float getAttackDamage(){
		if(morph != null)return morph.getAttackDamage();
		return super.getAttackDamage();
	}
	
	public float getDefense(){
		float df = 0;
		if(morph != null)df = morph.getDefense();
		return df + super.getDefense();
	}
	
	 public float getAdjustedFallDamage(float ouch){
		 if(morph != null)return morph.getAdjustedFallDamage(ouch);
		 return ouch;
	 }
	 
	 public void setOnFire(int fire){
		 if(morph != null && morph.isImmuneToFire){
			 setOnFire(0);
			 return;
		 }
		 super.setOnFire(fire);
	 }
	 
	 public void setAttacking(boolean tf){
		 if(morph != null)morph.setAttacking(tf);
		 super.setAttacking(tf);
	 }
	 
	 public boolean takesDamageFrom(/*DamageTypes*/int dt){
		 if(morph != null){
			 if(!morph.takesDamageFrom(dt))return false;
		 }
		 return super.takesDamageFrom(dt);
	 }
	 
	 public void setFlying(boolean tf){
		 if(morph != null)morph.setFlying(tf);
		 super.setFlying(tf);
	 }
	

}
