import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.*;
import java.util.ArrayList;
import java.util.Random;
import java.io.IOException;
import javax.imageio.ImageIO;

public abstract class Body extends Obj implements java.io.Serializable {
	static final long serialVersionUID = 7L;

	public abstract int getType ();
	public int getHybridType (int partnerType) { return getType(); }
	public boolean isHybrid () { return false; }

	public abstract String getNameJ ();
	public abstract String getNameE ();
	public abstract Image getImage (int type, int direction);
	
	public abstract void tuneParameters();

	// public variables	
	public static final int EATAMOUNT[] = {100*6, 100*12, 100*24};
	public enum AgeState { BABY, CHILD, ADULT };
	public enum Direction { LEFT, RIGHT };
	public enum Parent { PAPA, MAMA };
	// Facing information
	public static final int LEFT = 0;
	public static final int RIGHT = 1;
	// Expression and condition information
	public static final int BODY = 0;
	public static final int SHIT = 1;
	public static final int ROLL_SHIT = 2;
	public static final int ACCESSORY = 3;
	public static final int BRAID = 4;
	public static final int LICK = 5;
	public static final int CRUSHED = 6;
	public static final int CRUSHED2 = 7;
	public static final int NORMAL = 8;
	public static final int SMILE = 9;
	public static final int CHEER = 10;
	public static final int CRYING = 11;
	public static final int EXCITING= 12;
	public static final int PUFF = 13;
	public static final int TIRED = 14;
	public static final int SLEEPING = 15;
	public static final int REFRESHED = 16;
	public static final int DEAD = 17;
	public static final int RUDE = 18;
	public static final int DAMAGED = 19;
	public static final int SICK = 20;
	public static final int PANTS = 21;
	public static final int STAIN = 22;
	public static final int PANTS2 = 23;
	public static final int PANTS2_ROLL = 24;
	public static final int ROLL_ACCESSORY = 25;
	public static final int NUM_OF_CONDITIONS = 26;
	// Max size of icons
	public static final int MAXSIZE = 128;

	// private variables
	protected enum Hunger { NONE, VERY };
	protected enum Damage { NONE, VERY }
	protected enum Happiness { VERY_HAPPY, HAPPY, AVERAGE, SAD, VERY_SAD };
	protected enum Attitude { VERY_NICE, NICE, AVERAGE, SHITHEAD, SUPER_SHITHEAD };

	private static final int NEEDLE = 100;
	private static final int HAMMER = 100*24*2;
	private static final int HOLDMESSAGE = 20;		// 2sec
	private static final int STAYLIMIT = 20;		// 2sec
	private static final int HEADAGELIMIT = 100;
	private static final int SHITSTAY = 100;

	// Used in image loading.
	protected static final int babySize = MAXSIZE/4;
	protected static final int childSize = MAXSIZE/2;
	protected static final int adultSize = MAXSIZE;
	protected static final int babyIndex = AgeState.BABY.ordinal();
	protected static final int childIndex = AgeState.CHILD.ordinal();
	protected static final int adultIndex = AgeState.ADULT.ordinal();
	protected static final int diameter[] = {babySize, childSize, adultSize};
	private static Image[] images = new Image[3];

	// Language control.
	protected static enum Language { JAPANESE, ENGLISH };
	protected static Language language = Language.JAPANESE;

	// tunable parameters for each Yukkuri
	protected int HUNGRYLIMIT[] = {100*24, 100*24*2, 100*24*4};
	protected int SHITLIMIT[] = {100*12, 100*24, 100*24};
	protected int DAMAGELIMIT[] = {100*24, 100*24*3, 100*24*7};
	protected int BABYLIMIT = 100*24*7;
	protected int CHILDLIMIT = 100*24*21;
	protected int LIFELIMIT = 100*24*365;
	protected int STEP[] = {1, 2, 4};
	protected int RELAXPERIOD = 100*1;
	protected int EXCITEPERIOD = 100*3;
	protected int PREGPERIOD = 100*24;
	protected int SLEEPPERIOD = 100*3;
	protected int ACTIVEPERIOD = 100*6;
	protected int ANGRYPERIOD = 100*1;
	protected int SCAREPERIOD = 100*1;
	protected int sameDest = 30;
	protected int DECLINEPERIOD = 100*6*10; // 10 min.
	protected int DISCIPLINELIMIT = 10;
	protected int BLOCKEDLIMIT = 100;
	protected int DIRTYPERIOD = 300;
	protected int ROBUSTNESS = 1;
	protected int EYESIGHT = Terrarium.MAX_X * Terrarium.MAX_Y;
	protected int STRENGTH[] = {0, 100*12, 100*24*2};
	protected int INCUBATIONPERIOD = 100*12;

	// individual state variables for each Yukkuri.
	protected int destX = -1, destY = -1;			//destination
	protected int countX = 0, countY = 0;			// how many steps to same direction
	protected int dirX = 0, dirY = 0;				// direction to move on
	protected Direction direction = Direction.RIGHT;// direction of face
	protected AgeState ageState;					//BABY, CHILD, ADULT
	protected int damage = 0;						//counter indicating damage
	protected Damage damageState = Damage.NONE;
	protected int hungry = 0;						//counter indicating how hungry
	protected Hunger hungryState = Hunger.NONE;
	protected Attitude attitude = Attitude.AVERAGE;	// counter indicating shithead/nicehead etc.
	protected Happiness happiness = Happiness.AVERAGE;
	protected int shit = 0;
	protected boolean hasAccessory = true;			//true if having accessory
	protected boolean hasPants = false;				//true if having pants
	protected boolean hasBaby = false;				//having baby or not
	protected ArrayList<Integer> babyTypes = new ArrayList<Integer>();
	protected boolean dead = false;					//dead of alive
	protected boolean crashed = false;
	protected boolean exciting = false;				//want to sukkiri or not 
	protected boolean relax = false;
	protected boolean sleeping = false;
	protected long wakeUpTime;
	protected boolean dirty = false;
	protected boolean sick = false;
	protected boolean motherhood = false;
	protected boolean rapist = false;
	protected Body partner = null;
	protected Body parents[] = {null, null};
	protected int shittingDiscipline = 0;
	protected int excitingDiscipline = 0;
	protected int furifuriDiscipline = 0;
	protected int messageDiscipline = 0;
	
	//counter controlling for period
	protected int noDamagePeriod = 0;
	protected int noHungryPeriod = 0;
	protected int pregnantPeriod = 0;
	protected int excitingPeriod = 0;
	protected int sleepingPeriod = 0;
	protected int dirtyPeriod = 0;
	protected int sickPeriod = 0;
	protected int angryPeriod = 0;
	protected int scarePeriod = 0;

	// actions
	protected boolean staying = false;
	protected boolean toFood = false;
	protected boolean toSukkiri = false;
	protected boolean toShit = false;
	protected boolean shitting = false;
	protected boolean birth = false;
	protected boolean angry = false;
	protected boolean furifuri = false;
	protected boolean strike = false;
	protected boolean eating = false;
	protected boolean peropero = false;
	protected boolean sukkiri = false;
	protected boolean scare = false;
	protected boolean eatingShit = false;
	protected boolean silent = false;
	
	// variables related to actions.
	protected String messageBuf;	
	protected int messageCount = 0;
	protected int staycount = 0;
	protected int stayTime = STAYLIMIT;
	protected int falldownDamage = 0;
	protected int bodyAmount = 0;
	protected int blockedCount = 0;

	protected Random rnd = new Random();

	// private methods
	private AgeState checkAgeState()
	{
		if (age < BABYLIMIT) {
			bodyAmount = DAMAGELIMIT[AgeState.BABY.ordinal()];
			return AgeState.BABY;
		}
		else if (age < CHILDLIMIT) {
			bodyAmount = DAMAGELIMIT[AgeState.CHILD.ordinal()];
			return AgeState.CHILD;
		}
		bodyAmount = DAMAGELIMIT[AgeState.ADULT.ordinal()];
		return AgeState.ADULT;
	}

	private Hunger checkHungryState()
	{
		if (hungry < HUNGRYLIMIT[ageState.ordinal()]/4) {
			return Hunger.NONE;
		}
		return Hunger.VERY;
	}

	private Damage checkDamageState()
	{
		if (damage > DAMAGELIMIT[ageState.ordinal()]) {
			dead = true;
			return Damage.VERY;
		}
		if (damage >= DAMAGELIMIT[ageState.ordinal()]/2) {
			return Damage.VERY;
		}
		return Damage.NONE;
	}

	private void checkHungry() {
		if (exciting || hasBaby) {
			hungry += TICK*(babyTypes.size() + (exciting ? 1 : 0));
		} else {
			hungry += TICK;
		}
		if (hungry > HUNGRYLIMIT[ageState.ordinal()]) {
			damage += (hungry - HUNGRYLIMIT[ageState.ordinal()]);
			hungry = HUNGRYLIMIT[ageState.ordinal()];
		}
		if (hungryState == Hunger.NONE && checkHungryState() == Hunger.NONE) {
			noHungryPeriod += TICK;
		} else {
			noHungryPeriod = 0;
		}
		hungryState = checkHungryState();
	}

	private void checkDamage() {
		if (isSick()) {
			damage += TICK;
		}
		else if (checkHungryState() == Hunger.NONE) {
			damage -= TICK;
		}
		else if (hungry >= HUNGRYLIMIT[ageState.ordinal()]) {
			damage += TICK;
		}
		if (damage < 0) {
			damage = 0;
		}
		Damage newDamageState = checkDamageState();
		if (damageState == Damage.NONE && newDamageState == Damage.NONE) {
			noDamagePeriod += TICK;
		}
		else {
			noDamagePeriod = 0;
		}
		damageState = newDamageState;
	}

	private boolean checkShit() {
		boolean cantMove = false;
		if (checkHungryState() == Hunger.NONE) {
			shit += TICK*2;
		}
		else {
			shit += TICK;
		}
		if (shit > SHITLIMIT[ageState.ordinal()] - TICK*SHITSTAY) {
			if (hasPants) {
				setHappiness(Happiness.SAD);
			}
			if (!shitting) {
				showShit();
				wakeup();
			}
			shitting = true;
			cantMove = true;
		}
		else {
			// While shitting is true, the yukkuri might grow up. So, these flags should be clear.
			shitting = false;
			cantMove = false;
		}
		if (shit > SHITLIMIT[ageState.ordinal()]) {
			shitting = false;
			clearActions();
			shit = 0;
			if (ageState == AgeState.BABY) {
				setDirty(true);
				setHappiness(Happiness.SAD);
			}
			if (hasPants) {
				setDirty(true);
				setHappiness(Happiness.VERY_SAD);
			}
			showShit2();
		}
		return cantMove;
	}

	private boolean checkChildbirth() {
		boolean cantMove = false;
		if (hasBaby) {
			pregnantPeriod += TICK;
			if (pregnantPeriod > PREGPERIOD - TICK*100) {
				if (!birth) {
					showBreed();
					wakeup();
				}
				cantMove = true;
				birth = true;
			}
			if (pregnantPeriod > PREGPERIOD) {
				// Keep babyType for generating baby.
				pregnantPeriod = 0;
				birth = false;
				hasBaby = false;
				if (hasPants) {
					setDirty(true);
					setHappiness(Happiness.VERY_SAD);
					babyTypes.clear();
				}
				showBreed2();
			}
		}
		return cantMove;
	}

	private boolean checkSleep() {
		if (sleeping || (wakeUpTime + ACTIVEPERIOD < age && !exciting && relax && !scare && !isVerySad())) {
			clearActions();
			sleeping = true;
			angry = false;
			scare = false;
			setHappiness(Happiness.AVERAGE);
			sleepingPeriod += TICK;
			damage -= TICK;
			if (sleepingPeriod > SLEEPPERIOD) {
				showWakeup();
				wakeup();
			}
		}
		else {
			sleepingPeriod = 0;
			sleeping = false;
		}
		return sleeping;
	}
	
	private void checkDiscipline() {
		int period = (isRude() ? 1 : 2) * DECLINEPERIOD;
		if (age % period == 0) {
			if (--shittingDiscipline < 0) {
				shittingDiscipline = 0;
			}
			if (--excitingDiscipline < 0) {
				excitingDiscipline = 0;
			}
			if (--furifuriDiscipline < 0) {
				furifuriDiscipline = 0;
			}
			if (--messageDiscipline < 0) {
				messageDiscipline = 0;
			}
		}
	}
	
	private boolean canFurifuri() {
		 if (isRude() && (rnd.nextInt(furifuriDiscipline+1) == 0)) {
			 return true;
		 }
		 return false;
	}

	private void wakeup() {
		sleepingPeriod = 0;
		sleeping = false;
		wakeUpTime = age;
	}

	private void checkEmotion() {
		if (angry) {
			angryPeriod += TICK;
			if (angryPeriod > ANGRYPERIOD) {
				angryPeriod = 0;
				angry = false;
			}
		}
		if (scare) {
			scarePeriod += TICK;
			if (scarePeriod > SCAREPERIOD) {
				scarePeriod = 0;
				scare = false;
			}
		}
		if (noHungryPeriod > RELAXPERIOD && noDamagePeriod > RELAXPERIOD
				&& !sleeping && !shitting && !eating
				&& !isSad() && !isVerySad() && !isSick()) {
			if (!exciting && !relax) {
				int r = 1;
				int adjust = excitingDiscipline*(isRude() ? 1 : 2);
				if (rapist && isRude()) {
					r = rnd.nextInt(6 + adjust);
				}
				else if (rapist || isRude()) {
					r = rnd.nextInt(12 + adjust);
				}
				else if (!isHungry() && !wantToShit()) {
					r = rnd.nextInt(24 + adjust);
				}
				if (((ageState == AgeState.ADULT && !hasBaby) || rapist) && r == 0) {
					exciting = true;
					excitingPeriod = 0;
					showExcite();
				}
				else {
					relax = true;
					excitingPeriod = 0;
					showRelax();
				}
				angry = false;
				scare = false;
			}
			else {
				excitingPeriod += TICK;
				if (excitingPeriod > EXCITEPERIOD) {
					excitingPeriod = 0;
					exciting = false;
					relax = false;
				}
			}
		}
	}

	private void checkSick() {
		if (dirty && (damage != 0)) {
			dirtyPeriod += TICK;
			if (dirtyPeriod > DIRTYPERIOD) {
				setSick();
				dirtyPeriod = 0;
			}
		}
		else {
			dirtyPeriod = 0;
		}
		if (sick) {
			sickPeriod++;
		}
	}

	private void checkMessage() {
		--messageCount;
		if (messageCount <= 5) {
			// stop to show the message 0.5 sec. before.
			messageBuf = null;
		}
		if (messageCount <= 0) {
			messageCount = 0;
			furifuri = false;
			strike = false;
			eating = false;
			eatingShit = false;
			peropero = false;
			sukkiri = false;
		}
		if (dead) {
			if (!silent) {
				showDead();
			}
			return;
		}
		else if (messageBuf == null) {
			if (sleeping) {
				if (!isTalking() && rnd.nextInt(10) == 0) {
					showSleep();
				}
			}
			else if (getZ() != 0) {
				showFlying();
			}
		}
	}

	private void stay() {
		staying = true;
		stayTime = STAYLIMIT;
	}

	private void stay(int time) {
		staying = true;
		stayTime = time;
	}
	
	private void setHappiness(Happiness happy) {
		if (dead || isIdiot()) {
			happiness = Happiness.AVERAGE;
			return;
		}
		if (happy == Happiness.SAD) {
			if (happiness != Happiness.VERY_SAD) {
				happiness = happy;
			}
		}
		else if (happy == Happiness.HAPPY) {
			if (happiness != Happiness.VERY_HAPPY) {
				happiness = happy;
			}
		}
		else {
			happiness = happy;
		}
		if (happiness == Happiness.HAPPY || happiness == Happiness.VERY_HAPPY) {
			scare = false;
			angry = false;
		}
		if (happiness == Happiness.SAD || happiness == Happiness.VERY_SAD) {
			angry = false;
		}
	}

	private int randomDirection(int curDir) {
		switch (curDir) {
		case 0:
			curDir = (rnd.nextBoolean() ? 1 : -1);
			break;
		case 1:
			curDir = (rnd.nextBoolean() ? 0 : curDir);
			break;
		case -1:
			curDir = (rnd.nextBoolean() ? 0 : curDir);
			break;
		}
		return curDir;
	}
	
	private int decideDirection(int curPos, int destPos, int range) {
		if (destPos - curPos > range) {
			return 1;
		}
		else if (curPos - destPos > range) {
			return -1;
		}
		return 0;
	}
	
	private void moveBody(boolean dontMove) {
		if (grabbed) {
			// if grabbed, it cannot move.
			falldownDamage = 0;
			return;
		}
		if (z != 0) {
			// if falling down, it cannot move to x-y axis
			vz += 1;
			z -= vz;
			falldownDamage += vz;
			if (z <= 0) {
				z = 0;
				vz = 0;
				int jumpLevel[] = {2, 2, 1};
				if (falldownDamage >= 8/jumpLevel[ageState.ordinal()]) {
					strike(falldownDamage*100*24*4/Terrarium.MAX_Z);
					if (dead) {
						showDieing();
						crashed = true;
					}
				}
			}
			return;
		}
		if (dontMove) {
			return;
		}

		// moving
		int step = STEP[ageState.ordinal()];
		if (hasBaby || hungryState == Hunger.VERY || damageState == Damage.VERY || isSick()) {
			step /= 2;
			if (step == 0) {
				step = 1;
			}
		}

		int freq = STEP[AgeState.ADULT.ordinal()]/step;
		if (age % freq != 0) {
			return;
		}

		step = 1;
		// calculate x direction
		if (destX >= 0) {
			dirX = decideDirection(x, destX, step);
			if (dirX == 0) {
				destX = -1;
			}
		}
		else {
			if (countX++ == 0) {
				dirX = randomDirection(dirX);			
			}
			else if (countX++ >= sameDest * STEP[ageState.ordinal()]) {
				countX = 0;
				if (!hasAccessory && (isSad() || isVerySad())) {
					if (!isTalking()) {
						showNoAccessory();
					}
				}
			}
		}
		// calculate y direction
		if (destY >= 0) {
			dirY = decideDirection(y, destY, step);
			if (dirY == 0) {
				destY = -1;
			}
		}
		else {
			if (countY++ == 0) {
				dirY = randomDirection(dirY);
			}
			else if (countY++ >= sameDest * STEP[ageState.ordinal()]) {
				countY = 0;
				if (!hasAccessory && (isSad() || isVerySad())) {
					if (!isTalking()) {
						showNoAccessory();
					}
				}
			}
		}
		// move to the direction
		x += (dirX * step);
		if (x < 0) {
			x = 0;
			dirX = 1;
		}
		else if (x > Terrarium.MAX_X) {
			x = Terrarium.MAX_X;
			dirX = -1;
		}
		y += (dirY * step);
		if (y < 0) {
			y = 0;
			dirY = 1;
		}
		else if (y > Terrarium.MAX_Y) {
			y = Terrarium.MAX_Y;
			dirY = -1;
		}
		if (Terrarium.onBarrier(x, y, getSize()/4, getSize()/8)) {
			if ((destX >= 0) && (destY >= 0)) {
				blockedCount++;
				if (blockedCount > BLOCKEDLIMIT) {
					dirX = 0;
					dirY = 0;
					destX = -1;
					destY = -1;
					countX = 0;
					countY = 0;
					clearActions();
					setHappiness(Happiness.VERY_SAD);
				}
				else if (blockedCount > BLOCKEDLIMIT/2) {
					if (isRude()) {
						setAngry();
					}
					else {
						exciting = false;
						setHappiness(Happiness.SAD);
					}
				}
				if (!isTalking()) {
					showBlockedByWall();
				}
			}
			x -= (dirX * step);
			y -= (dirY * step);
		}
		else {
			//blockedCount = Math.max(0, blockedCount - 1);
			blockedCount = 0;
		}
		// update direction of the face
		if (dirX == -1) {
			direction = Direction.LEFT;
		}
		else if (dirX == 1) {
			direction = Direction.RIGHT;
		}
	}
	
	protected boolean isRudeMessage() {
		if (isRude()) {
			if (rnd.nextInt(messageDiscipline+1) == 0) {
				return true;
			}
			return false;
		}
		return false;
	}

	protected void setMessage(String message) {
		setMessage(message, HOLDMESSAGE);
	}

	protected void setMessage(String message, int count) {
		messageCount = count;
		messageBuf = message;
		// reset actions.
		furifuri = false;
		strike = false;
		eating = false;
		eatingShit = false;
		peropero = false;
		sukkiri = false;
	}

	protected abstract String msgWantFoodJ();
	protected abstract String msgWantFoodE();

	protected void showWantFood() {
		switch (language) {
		case JAPANESE:
			setMessage(msgWantFoodJ());
			break;
		case ENGLISH:
			setMessage(msgWantFoodE());
			break;
		default:
			throw new LanguageException();
		}
	}

	protected abstract String msgNoFoodJ();
	protected abstract String msgNoFoodE();

	public void showNoFood() {
		toFood = false;
		switch (language) {
		case JAPANESE:
			setMessage(msgNoFoodJ());
			break;
		case ENGLISH:
			setMessage(msgNoFoodE());
			break;
		default:
			throw new LanguageException();
		}
		stay();
		setHappiness(Happiness.SAD);
	}

	protected abstract String msgExciteJ();
	protected abstract String msgExciteE();

	protected void showExcite() {
		switch (language) {
		case JAPANESE:
			setMessage(msgExciteJ());
			break;
		case ENGLISH:
			setMessage(msgExciteE());
		}
	}

	protected abstract String msgRelaxJ();
	protected abstract String msgFuriFuriJ();
	protected abstract String msgRelaxE();
	protected abstract String msgFuriFuriE();

	protected void showRelax() {
		if (isRude() && canFurifuri()) {
			//if yukkuri is rude, she will not do furifuri by discipline.
			switch (language) {
			case JAPANESE:
				setMessage(msgFuriFuriJ(), 30);
				break;
			case ENGLISH:
				setMessage(msgFuriFuriE(), 30);
				break;
			default:
				throw new LanguageException();
			}
			furifuri = true;
			stay(30);
		}
		else if (isRude() || (rnd.nextInt(messageDiscipline+1) == 0)){
			// if yukkuri is not rude, she goes into her shell by discipline.
			switch (language) {
			case JAPANESE:
				setMessage(msgRelaxJ(), 30);
				break;
			case ENGLISH:
				setMessage(msgRelaxE(), 30);
				break;
			default:
				throw new LanguageException();
			}
			stay(30);
		}
	}

	protected abstract String msgWakeupJ();
	protected abstract String msgWakeupE();

	protected void showWakeup() {
		switch (language) {
		case JAPANESE:
			setMessage(msgWakeupJ());
			break;
		case ENGLISH:
			setMessage(msgWakeupE());
			break;
		default:
			throw new LanguageException();
		}
		stay();
	}

	protected abstract String msgSleepJ();
	protected abstract String msgSleepE();

	protected void showSleep() {
		if (sleepingPeriod != 0) {
			switch (language) {
			case JAPANESE:
				setMessage(msgSleepJ());
				break;
			case ENGLISH:
				setMessage(msgSleepE());
				break;
			}
		}	
	}

	protected abstract String msgScreamJ ();
	protected abstract String msgScreamE ();

	protected void showScream() {
		staying = false;
		switch (language) {
		case JAPANESE:
			setMessage(msgScreamJ());
			break;
		case ENGLISH:
			setMessage(msgScreamE());
			break;
		default:
			throw new LanguageException();
		}
		strike = true;
		stay();
	}

	protected abstract String msgScareJ ();
	protected abstract String msgScareE ();

	protected void showScare() {
		switch (language) {
		case JAPANESE:
			setMessage(msgScareJ());
			break;
		case ENGLISH:
			setMessage(msgScareE());
			break;
		default:
			throw new LanguageException();
		}
		setHappiness(Happiness.SAD);
	}

	protected abstract String msgAlarmJ ();
	protected abstract String msgAlarmE ();

	protected void showAlarm() {
		if (sleeping) {
			return;
		}
		switch (language) {
		case JAPANESE:
			setMessage(msgAlarmJ()); 
			break;
		case ENGLISH:
			setMessage(msgAlarmE());
			break;
		default:
			throw new LanguageException();
		}
	}

	protected abstract String msgDyingJ ();
	protected abstract String msgDyingE ();

	protected void showDieing() {
		if (silent) {
			return;
		}
		switch (language) {
		case JAPANESE:
			setMessage(msgDyingJ());
			break;
		case ENGLISH:
			setMessage(msgDyingE());
			break;
		default:
			throw new LanguageException();
		}
		stay();
	}

	protected abstract String msgDeadJ ();
	protected abstract String msgDeadE ();

	protected void showDead() {
		String messages;
		switch (language) {
		case JAPANESE:
			messages = msgDeadJ();
			break;
		case ENGLISH:
			messages = msgDeadE();
			break;
		default:
			throw new LanguageException();
		}
		setMessage(messages);
		if (messageBuf == messages)  {
			// if the message is set successfully, be silent.
			silent = true;
		}
	}

	protected abstract String msgEatingJ ();
	protected abstract String msgEatingE ();

	protected void showEating() {
		switch (language) {
		case JAPANESE:
			setMessage(msgEatingJ());
			break;
		case ENGLISH:
			//Yoga Note* Check はふはふ
			setMessage(msgEatingE());
			break;
		default:
			throw new LanguageException();
		}
		eating = true;
		stay();
	}

	protected abstract String msgEatingShitJ ();
	protected abstract String msgEatingShitE ();

	protected void showEatingShit() {
		switch (language) {
		case JAPANESE:
			setMessage(msgEatingShitJ());
			break;
		case ENGLISH:
			setMessage(msgEatingShitE());
			break;
		default:
			throw new LanguageException();
		}
		eating = true;
		eatingShit = true;
		stay();
	}
	
	protected abstract String msgEatingBitterJ ();
	protected abstract String msgEatingBitterE ();

	protected void showEatingBitter() {
		switch (language) {
		case JAPANESE:
			setMessage(msgEatingBitterJ());
			break;
		case ENGLISH:
			setMessage(msgEatingBitterE());
			break;
		default:
			throw new LanguageException();
		}
		eating = true;
		strike = true;
		stay();
	}

	protected abstract String msgFullJ ();
	protected abstract String msgFullE ();

	protected void showFull() {
		switch (language) {
		case JAPANESE:
			setMessage(msgFullJ());
			break;
		case ENGLISH:
			setMessage(msgFullE());
			break;
		default:
			throw new LanguageException();
		}
		stay();
	}

	protected abstract String msgHealingJ ();
	protected abstract String msgHealingE ();

	protected void showHealing() {
		switch (language) {
		case JAPANESE:
			setMessage(msgHealingJ());
			break;
		case ENGLISH:
			setMessage(msgHealingE());
			break;
		default:
			throw new LanguageException();
		}
		stay();
	}

	protected abstract String msgSukkiriJ ();
	protected abstract String msgSukkiriE ();

	protected void showSukkiri() {
		switch (language) {
		case JAPANESE:
			setMessage(msgSukkiriJ(), 30);
			break;
		case ENGLISH:
			setMessage(msgSukkiriE(), 30);
			break;
		default:
			throw new LanguageException();
		}
		stay(30);
	}

	protected abstract String msgBirthJ ();
	protected abstract String msgBirthE ();

	protected void showBirth() {
		if (dead) {
			return;
		}
		switch (language) {
		case JAPANESE:
			setMessage(msgBirthJ(), 30);
			break;
		case ENGLISH:
			setMessage(msgBirthE(), 30);
			break;
		}
		stay(30);
	}

	protected abstract String msgShitJ ();
	protected abstract String msgShitE ();

	protected void showShit() {
		switch (language) {
		case JAPANESE:
			setMessage(msgShitJ(), TICK*SHITSTAY);
			break;
		case ENGLISH:
			setMessage(msgShitE(), TICK*SHITSTAY);
			break;
		default:
			throw new LanguageException();
		}
	}

	protected abstract String msgShit2J ();
	protected abstract String msgShit2E ();

	protected void showShit2() {
		switch (language) {
		case JAPANESE:
			setMessage(msgShit2J());
			break;
		case ENGLISH:
			setMessage(msgShit2E());
			break;
		default:
			throw new LanguageException();
		}
		if (!hasPants) {
			if (canFurifuri()) {
				furifuri = true;
			}
			stay();
		}
	}

	protected abstract String msgSuriSuriJ ();
	protected abstract String msgSuriSuriE ();

	protected void showSurisuri() {
		switch (language) {
		case JAPANESE:
			setMessage(msgSuriSuriJ());
			break;
		case ENGLISH:
			setMessage(msgSuriSuriE());
			break;
		default:
			throw new LanguageException();
		}
		stay();
	}

	protected abstract String msgPeroPeroJ ();
	protected abstract String msgPeroPeroE ();

	protected void showPeroPero() {
		switch (language) {
		case JAPANESE:
			setMessage(msgPeroPeroJ());
			break;
		case ENGLISH:
			setMessage(msgPeroPeroE());
			break;
		default:
			throw new LanguageException();
		}
		peropero = true;
		stay();
	}

	protected abstract String msgBreedJ ();
	protected abstract String msgBreedE ();

	protected void showBreed() {
		switch (language) {
		case JAPANESE:
			setMessage(msgBreedJ());
			break;
		case ENGLISH:
			setMessage(msgBreedE());
			break;
		}
	}

	protected abstract String msgBreed2J ();
	protected abstract String msgBreed2E ();

	protected void showBreed2() {
		switch (language) {
		case JAPANESE:
			setMessage(msgBreed2J());
			break;
		case ENGLISH:
			setMessage(msgBreed2E());
			break;
		}
		if (!hasPants) {
			if (canFurifuri()) {
				furifuri = true;
			}
			stay();
		}
	}

	protected abstract String msgHateShitJ ();
	protected abstract String msgHateShitE ();

	public void showHateShit() {
		if (toShit) {
			return;
		}
		switch (language) {
		case JAPANESE:
			setMessage(msgHateShitJ());
			break;
		case ENGLISH:
			setMessage(msgHateShitE());
			break;
		default:
			throw new LanguageException();
		}
	}	

	protected abstract String msgHungryJ ();
	protected abstract String msgHungryE ();

	protected void showHungry() {
		switch (language) {
		case JAPANESE:
			setMessage(msgHungryJ());
			break;
		case ENGLISH:
			setMessage(msgHungryE());
			break;
		default:
			throw new LanguageException();
		}
		stay();
	}

	protected abstract String msgNoAccessoryJ ();
	protected abstract String msgNoAccessoryE ();

	protected void showNoAccessory() {
		switch (language) {
		case JAPANESE:
			setMessage(msgNoAccessoryJ());
			break;
		case ENGLISH: 
			setMessage(msgNoAccessoryE());
			break;
		default:
			throw new LanguageException();
		}
	}

	protected abstract String msgHateYukkuriJ ();
	protected abstract String msgHateYukkuriE ();

	public void showHateYukkuri() {
		switch (language) {
		case JAPANESE:
			setMessage(msgHateYukkuriJ());
			break;
		case ENGLISH:
			setMessage(msgHateYukkuriE());
			break;
		default:
			throw new LanguageException();
		}
	}

	protected abstract String msgFlyingJ ();
	protected abstract String msgFlyingE ();

	protected void showFlying() {
		switch (language) {
		case JAPANESE:
			setMessage(msgFlyingJ());
			break;
		case ENGLISH:
			setMessage(msgFlyingE());
			break;
		default:
			throw new LanguageException();
		}
	}

	protected abstract String msgSadnessForChildJ ();
	protected abstract String msgSadnessForChildE ();

	protected void showSadnessForChild() {
		switch (language) {
		case JAPANESE:
			setMessage(msgSadnessForChildJ());
			break;
		case ENGLISH:
			setMessage(msgSadnessForChildE());
			break;
		default:
			throw new LanguageException();
		}
		setHappiness(Happiness.VERY_SAD);
	}

	protected abstract String msgSadnessForPartnerJ (Body partner);
	protected abstract String msgSadnessForPartnerE (Body partner);

	protected void showSadnessForPartner(Body partner) {
		switch (language) {
		case JAPANESE:
			setMessage(msgSadnessForPartnerJ(partner));
			break;
		case ENGLISH:
			setMessage(msgSadnessForPartnerE(partner));
			break;
		default:
			throw new LanguageException();
		}
		setHappiness(Happiness.VERY_SAD);
	}

	protected abstract String msgBlockedByWallJ ();
	protected abstract String msgBlockedByWallE ();

	protected void showBlockedByWall() {
		switch (language) {
		case JAPANESE:
			setMessage(msgBlockedByWallJ());
			break;
		case ENGLISH:
			setMessage(msgBlockedByWallE());
			break;
		default:
			throw new LanguageException();
		}
	}

	protected static Image flipImage(Image img) {
		int w = img.getWidth(null);
		int h = img.getHeight(null);
		int pix[] = new int[w*h];
		int new_pix[] = new int[w*h];
		PixelGrabber pg = new PixelGrabber(img, 0, 0, w, h, pix, 0, w);
		try {
			pg.grabPixels();
		} catch (InterruptedException e) { }
		for (int i=0; i<h; i++){
			for (int j=0; j<w; j++){
				new_pix[(i+1)*w-(j+1)]=pix[i*w+j];
			}
		}
		MemoryImageSource mimg = new MemoryImageSource(w, h, new_pix, 0, w) ;
		return Toolkit.getDefaultToolkit().createImage(mimg);
	}

	// public methods
	public ArrayList<Integer> getBabyTypes() {
		return babyTypes;
	}

	public AgeState getAgeState() {
		return ageState;
	}

	public long getAge() {
		return age;
	}
	
	public long getAgeLimit() {
		return LIFELIMIT;
	}
	
	public static int getHeadageLimit() {
		return HEADAGELIMIT;
	}
	
	public int getDamage() {
		return damage;
	}
	
	public int getDamageLimit() {
		return DAMAGELIMIT[ageState.ordinal()];
	}
	
	public int getHungry() {
		return hungry;
	}
	
	public int getHungryLimit() {
		return HUNGRYLIMIT[ageState.ordinal()];
	}
	
	public int getShit() {
		return shit;
	}
	
	public int getShitLimit() {
		return SHITLIMIT[ageState.ordinal()];
	}

	public int getEatAmount() {
		return EATAMOUNT[ageState.ordinal()];
	}

	public int getStep() {
		return STEP[ageState.ordinal()];
	}

	public String getMessage() {
		return messageBuf;
	}

	public Direction getDirection() {
		return direction;
	}

	public int getAmount() {
		return bodyAmount;
	}
	
	public int getSize() {
		return diameter[ageState.ordinal()];
	}
	
	public int getStrength() {
		return STRENGTH[ageState.ordinal()];
	}
	public int getEyesight() {
		return EYESIGHT;
	}
	
	public Image getShadowImage() {
		return images[ageState.ordinal()];		
	}
	
	public static void loadShadowImages (ClassLoader loader) throws IOException {
		final String path = "images/";

		images[adultIndex] = ImageIO.read(loader.getResourceAsStream(path+"shadow.png"));
		
		images[adultIndex] = images[adultIndex].getScaledInstance(adultSize, adultSize, Image.SCALE_AREA_AVERAGING);
		images[childIndex] = images[adultIndex].getScaledInstance(childSize, childSize, Image.SCALE_AREA_AVERAGING);
		images[babyIndex] = images[adultIndex].getScaledInstance(babySize, babySize, Image.SCALE_AREA_AVERAGING);
	}
	
	public void setAngry() {
		if (dead || sleeping) {
			return;
		}
		if (damageState == Damage.NONE && !isVerySad()) {
			angry = true;
			scare = false;
		}
		furifuri = false;
		exciting = false;
		relax = false;
		excitingPeriod = 0;
		noDamagePeriod = 0;
		noHungryPeriod = 0;
	}

	public void setDirty(boolean flag) {
		dirty = flag;
		if (dirty) {
			setHappiness(Happiness.SAD);
		}
		else {
			setHappiness(Happiness.HAPPY);
			showSukkiri();
		}
	}
	
	public void setSick() {
		if (rnd.nextInt(ROBUSTNESS+1) == 0) {
			sick = true;
		}
	}

	public boolean isHungry() {
		return (!dead && (hungry >= HUNGRYLIMIT[ageState.ordinal()] / 2));
	}
	
	public boolean isFull() {
		return (!dead && (hungry <= HUNGRYLIMIT[ageState.ordinal()] / 20));
	}

	public boolean isTooHungry() {
		return (!dead && hungry >= HUNGRYLIMIT[ageState.ordinal()] && checkDamageState() == Damage.VERY);
	}

	public boolean isDead() {
		return dead;
	}

	public boolean isAdult() {
		return (ageState == AgeState.ADULT);
	}
	
	public boolean isChild() {
		return (ageState == AgeState.CHILD);
	}
	
	public boolean isBaby() {
		return (ageState == AgeState.BABY);
	}

	public boolean isSleeping() {
		return (!dead && sleeping);
	}

	public boolean isShitting() {
		return (!dead && shitting);
	}

	public boolean isExciting() {
		return (!dead && exciting);
	}

	public boolean isAngry() {
		return (!dead && angry);
	}

	public boolean isScare() {
		return (!dead && scare);
	}

	public boolean isSad() {
		return (!dead && happiness == Happiness.SAD);
	}
	
	public boolean isVerySad() {
		return (!dead && happiness == Happiness.VERY_SAD);
	}
	
	public boolean isHappy() {
		return (happiness == Happiness.HAPPY || happiness == Happiness.VERY_HAPPY);
	}

	public boolean isFurifuri() {
		return (!dead && furifuri);
	}

	public boolean isStrike() {
		return (!dead && strike);
	}

	public boolean isDamaged() {
		return (checkDamageState() == Damage.VERY);
	}

	public boolean isBirth() {
		return (!dead && birth);
	}

	public boolean isEating() {
		return (!dead && eating);
	}

	public boolean isEatingShit() {
		return (!dead && eatingShit);
	}

	public boolean isPeroPero() {
		return (!dead && peropero);
	}

	public boolean isSukkiri() {
		return (!dead && sukkiri);
	}

	public boolean isDirty() {
		return (dead || dirty);
	}

	public boolean isCrashed() {
		return crashed;
	}

	public boolean isRude() {
		return (attitude == Attitude.SHITHEAD || attitude == Attitude.SUPER_SHITHEAD);
	}

	public boolean isParent(Body other) {
		return (other.parents[Parent.PAPA.ordinal()] == this || other.parents[Parent.MAMA.ordinal()] == this);
	}

	public boolean isChild(Body other) {
		return other.isParent(this);
	}

	public boolean isPartner(Body other) {
		return partner == other;
	}
	
	public boolean isRaper() {
		return rapist;
	}
	
	public boolean isMotherhood(Body partner) {
		if (isRude() && (getType() != partner.getType()))
			return false;
		else
			return motherhood;
	}
	
	public boolean isOld() {
		return age > (LIFELIMIT*9/10);
	}
	
	public boolean isTalking() {
		return (messageCount > 0);
	}
	
	public boolean isSick() {
		if (sickPeriod > INCUBATIONPERIOD) {
			return true;
		}
		return false;
	}
	
	public boolean isIdiot() {
		return (getType() == Tarinai.type);
	}

	public boolean hasAccessory() {
		return hasAccessory;
	}

	public boolean hasPants() {
		return hasPants;
	}
	
	public boolean wantToShit() {
		int step = (checkHungryState() == Hunger.NONE ? TICK*2 : TICK);
		int adjust = 100*(isRude() ? 1 : 2)*shittingDiscipline;
		if ((SHITLIMIT[ageState.ordinal()] - shit) < (Terrarium.MAX_X*step + adjust)) {
			return true;
		}
		return false;
	}

	public void doSukkiri(Body p) {
		if (dead) {
			return;
		}
		// change own state
		showSukkiri();
		clearActions();
		sukkiri = true;
		exciting = false;
		setHappiness(Happiness.HAPPY);
		hungry += HUNGRYLIMIT[AgeState.BABY.ordinal()];
		hungryState = checkHungryState();
		// if it has pants, cannot get pregnant
		if (hasPants() || p.hasPants()) {
			if (hasPants()) {
				setDirty(true);
			}
			else {
				p.setDirty(true);
			}
			return;
		}
		if ((isSick() || p.isSick()) && rnd.nextBoolean()) {
			p.setSick();
			setSick();
		}
		if (p.isDead()) {
			return;
		}
		//change partner state
		p.showSukkiri();
		p.clearActions();
		p.sukkiri = true;
		p.exciting = false;
		p.setHappiness(Happiness.HAPPY);
		p.hungry += (HUNGRYLIMIT[AgeState.BABY.ordinal()]*2);
		p.hungryState = p.checkHungryState();
		if (isAdult() && p.isAdult())
		{
			partner = p;
			p.partner = this;
		}
		int babyType;
		if (!isHybrid() && !p.isHybrid() && (rnd.nextInt(50) == 0)) {
			babyType = p.getHybridType(getType());
		}
		else if (rnd.nextBoolean()) {
			babyType = getType();
		}
		else {
			babyType = p.getType();
		}
		if ((babyType == Reimu.type) && rnd.nextBoolean()) {
			babyType = WasaReimu.type;
		}
		else if ((babyType == WasaReimu.type) && rnd.nextBoolean()) {
			babyType = Reimu.type;
		}
		if ((isSick() || p.isSick()) || isDamaged() || p.isDamaged()) {
			babyType = Tarinai.type;
		}
		p.babyTypes.add(babyType);
		p.hasBaby = true;
	}

	public void forceToSukkiri() {
		if (dead || exciting || isSick()) {
			return;
		}
		wakeup();
		showExcite();
		exciting = true;
		partner = null;
		stay();
	}
	
	public void forceToSleep() {
		if (dead) {
			return;
		}
		sleepingPeriod = 0;
		sleeping = true;
	}

	public void doSurisuri(Body p) {
		if (dead || p.dead) {
			return;
		}
		showSurisuri();
		setHappiness(Happiness.VERY_HAPPY);
		p.setHappiness(Happiness.VERY_HAPPY);
		if ((isSick() || p.isSick()) && rnd.nextBoolean()) {
			p.setSick();
			setSick();
		}
	}

	public void doPeropero(Body p) {
		if (dead || p.dead) {
			return;
		}
		showPeroPero();
		p.dirty = false;
		setHappiness(Happiness.VERY_HAPPY);
		p.setHappiness(Happiness.VERY_HAPPY);
		if ((isSick() || p.isSick()) && rnd.nextBoolean()) {
			p.setSick();
			setSick();
		}
	}

	public void moveTo(int toX, int toY)
	{
		if (dead) {
			return;
		}
		if (blockedCount != 0) {
			return;
		}
		destX = Math.max(0, Math.min(toX, Terrarium.MAX_X));
		destY = Math.max(0, Math.min(toY, Terrarium.MAX_Y));
	}

	public void moveToFood(int toX, int toY) {
		if (!toFood) {
			showWantFood();
		}
		clearActions();
		toFood = true;
		moveTo(toX, toY);
	}

	public void moveToSukkiri(int toX, int toY) {
		clearActions();
		toSukkiri = true;
		moveTo(toX, toY);
	}

	public void moveToShit(int toX, int toY) {
		clearActions();
		toShit = true;
		moveTo(toX, toY);
	}

	public void moveToToilet(int toX, int toY) {
		clearActions();
		toShit = true;
		moveTo(toX, toY);
	}

	public void lookTo(int toX, int toY) {
		if (dead || sleeping) {
			return;
		}
		if (toX > x) {
			direction = Direction.RIGHT;
		}
		else if (toX < x) {
			direction = Direction.LEFT;
		}
		stay();
	}

	public void eatFood(Food.type foodType, int amount) {
		if (dead) {
			return;
		}
		clearActions();
		if (isFull()) {
			showFull();
			return;
		}
		hungry -= amount;
		if (hungry < 0) {
			hungry = 0;
		}
		if (foodType == Food.type.SHIT) {
			angry = false;
			scare = false;
			setHappiness(Happiness.SAD);
			showEatingShit();
		}
		else if (foodType == Food.type.BITTER) {
			strike(NEEDLE*6);
			angry = false;
			scare = false;
			setHappiness(Happiness.SAD);
			showEatingBitter();
		}
		else if (foodType == Food.type.LEMONPOP){
			angry = false;
			scare = false;
			setHappiness(Happiness.AVERAGE);
			showEating();
			forceToSleep();
		}
		else if (foodType == Food.type.HOT){
			strike(HAMMER);
			angry = false;
			scare = false;
			setHappiness(Happiness.VERY_SAD);
			showEatingBitter();
		}
		else if (foodType == Food.type.VIYUGRA){
			angry = false;
			scare = false;
			setHappiness(Happiness.HAPPY);
			showEating();
			if (!isHungry())
				forceToSukkiri();
		}
		else {
			angry = false;
			scare = false;
			setHappiness(Happiness.HAPPY);
			showEating();
		}
		hungryState = checkHungryState();
	}

	public void eatBody(int amount) {
		bodyAmount -= amount;
		if (bodyAmount <= DAMAGELIMIT[ageState.ordinal()]/2) {
			crashed = true;
		}
		if (bodyAmount <= 0) {
			bodyAmount = 0;
			removed = true;
		}
	}

	public void strike(int amount) {
		if (dead) {
			return;
		}
		damage += amount;
		showScream();
		damageState = checkDamageState();
		setAngry();
		wakeup();
	}

	public void strikeByNeedle() {
		strike(NEEDLE);
	}

	public void strikeByPunish() {
		if (dead) {
			return;
		}
		if (isIdiot()) {
			strike(NEEDLE);
			return;
		}
		if (exciting) {
			excitingDiscipline++;
			exciting = false;
		}
		else if (shitting) {
			shittingDiscipline++;
			shitting = false;
			shit -= ANGRYPERIOD*2;
		}
		else if (furifuri) {
			furifuriDiscipline++;
			furifuri = false;
		}
		else if (messageBuf != null) {
			messageDiscipline++;
			messageBuf = null;
		}
		strike(NEEDLE);
		if (isRude() && (messageDiscipline > DISCIPLINELIMIT) && (furifuriDiscipline != 0)) {
			if (rnd.nextBoolean()) {
				attitude = Attitude.AVERAGE;
				messageDiscipline = 0;
				furifuriDiscipline = 0;
			}
		}
	}

	public void strikeByHammer() {
		if (dead) {
			return;
		}
		strike(HAMMER);
		if (dead) {
			showDieing();
			if (ageState != AgeState.ADULT) {
				crashed = true;
			}
		}
	}

	public void putStress(int numOfBody) {
		if (dead || (age % 10 != 0) || (HEADAGELIMIT >= numOfBody)) {
			return;
		}
		damage += rnd.nextInt(numOfBody - HEADAGELIMIT);
		damageState = checkDamageState();
	}

	public void takeAccessory() {
		if (isIdiot()) {
			return;
		}
		hasAccessory = false;
		if (!dead) {
			showNoAccessory();
			setHappiness(Happiness.VERY_SAD);
		}
	}

	public void giveAccessory() {
		if (isIdiot()) {
			return;
		}
		hasAccessory = true;
		if (!dead) {
			setHappiness(Happiness.VERY_HAPPY);
		}
	}
	
	public void takePants() {
		hasPants = false;
	}
	
	public void givePants() {
		hasPants = true;
		if (!dead) {
			if (!dirty && hasAccessory) {
				setHappiness(Happiness.HAPPY);
			}
		}
	}

	public void giveJuice(){
		if (dead) {
			return;
		}
		if (!sleeping) {
			showHealing();
		}
		damage = 0;
		damageState = checkDamageState();
		hungry = 0;
		hungryState = checkHungryState();
		angry = false;
		scare = false;
		exciting = false;
		setHappiness(Happiness.AVERAGE);
		clearActions();
	}

	public void runAway(int fromX, int fromY) {
		if (dead || sleeping || exciting || angry) {
			return;
		}
		int toX, toY;
		if (x > fromX) {
			toX = Terrarium.MAX_X;
		}
		else {
			toX = 0;
		}
		if (y > fromY) {
			toY = Terrarium.MAX_Y;
		}
		else {
			toY = 0;
		}
		moveTo(toX, toY);
		clearActions();
		scare = true;
	}

	public static void setLanguage(Language lang) { language = lang; }

	public static Language getLanguage() { return language; }

	@Override
	public void remove() {
		removed = true;
		parents[Parent.PAPA.ordinal()] = null;
		parents[Parent.MAMA.ordinal()] = null;
		partner = null;
	}
	
	private void clearRelation() {
		if (parents[Parent.PAPA.ordinal()] != null)
			if (parents[Parent.PAPA.ordinal()].removed)
				parents[Parent.PAPA.ordinal()] = null;
		if (parents[Parent.MAMA.ordinal()] != null)
			if (parents[Parent.MAMA.ordinal()].removed)
				parents[Parent.MAMA.ordinal()] = null;
		if (partner != null)
			if (partner.removed)
				partner = null;
	}
	
	private void clearActions() {
		toSukkiri = false;
		toFood = false;
		toShit = false;
	}

	// calling every tick
	@Override
	public Obj.Event clockTick() {
		// if removed, remove body
		if (removed) {
			return Obj.Event.REMOVED;
		}

		// if partner and parents are removed, clean relationship.
		clearRelation();

		// if dead, do nothing.
		if (dead) {
			moveBody(true); // for falling the body
			checkMessage();
			return Obj.Event.DEAD;
		}

		Event retval = Event.DONOTHING;

		// check age
		age += TICK;
		if (age > LIFELIMIT) {
			dead = true;
			moveBody(true); // for falling the body
			checkMessage();
			return Obj.Event.DEAD;
		}
		ageState = checkAgeState();

		// check status
		checkHungry();
		checkDamage();
		checkSick();

		// check events
		boolean dontMove = false;

		// check shit
		int oldShit = shit;
		if (checkShit()) {
			dontMove = true;
		}
		if (oldShit != 0 && shit == 0) {
			if (!hasPants()) {
				retval = Obj.Event.DOSHIT;
			}
		}

		// check pregnant
		boolean oldHasBaby = hasBaby;
		if (checkChildbirth()) {
			dontMove = true;
		}
		if (oldHasBaby == true && hasBaby == false) {
			if (!hasPants()) {
				retval = Obj.Event.BIRTHBABY;
			}
		}

		// check sleep
		if (checkSleep()) {
			dontMove = true;
		}

		// check relax
		if (isFurifuri()) {
			dontMove = true;
		}
		
		// if eating, cannot move.
		if (eating) {
			dontMove = true;
		}

		// check relax and excitement
		checkEmotion();

		// check discipline level
		checkDiscipline();

		if (staying) {
			staycount += TICK;
			if (staycount > stayTime) {
				staycount = 0;
				staying = false;
			}
			else {
				dontMove = true;
			}
		}

		// move to destination
		// if there is no destination, walking randomly.
		moveBody(dontMove);

		checkMessage();

		return retval;
	}

	public Body(int initX, int initY, int initZ, AgeState initAgeState, Body mama, Body papa) {
		objType = Type.YUKKURI;
		x = initX;
		y = initY;
		z = initZ;
		parents[Parent.PAPA.ordinal()] = papa;
		parents[Parent.MAMA.ordinal()] = mama;
		removed = false;
		if (rnd.nextBoolean()) {
			attitude = Attitude.SHITHEAD;
		}
		else {
			attitude = Attitude.NICE;
		}
		tuneParameters(); // Update individual parameters.
		switch (initAgeState) {
		case BABY:
			age = 0;
			break;
		case CHILD:
			age = BABYLIMIT;
			break;
		case ADULT:
		default:
			age = CHILDLIMIT;
			break;
		}
		age += rnd.nextInt(100);
		ageState = checkAgeState();
		wakeUpTime = age;
		shit = rnd.nextInt(SHITLIMIT[ageState.ordinal()]);
		if (ageState == AgeState.BABY) {
			if (mama != null) {
				if (mama.isDamaged()) {
					damage = rnd.nextInt(mama.getDamage());
					checkAgeState();
					checkDamageState();
				}
				if (mama.isSick()) {
					setSick();	
				}
				showBirth();
			}
		}
	}
}


