package simulation.entity;
import java.beans.PropertyChangeSupport;
import sim.engine.SimState;
import sim.engine.Stoppable;
import sim.field.grid.SparseGrid2D;
import simulation.SimulationModel;
public abstract class Animal extends Entity implements Eatable {
private static final long serialVersionUID = 1L;
private static final double FLEE_MOVE_COEF = 1.5;
@SuppressWarnings("unused")
private static final Double MAX_ATTACK_AND_DEFEND = 100.0;
protected String type;
protected Double stepByDay;
protected Double meterByCase;
protected Double smellPoint;
protected Double visionPoint;
protected Double movePoint;
protected Double maxLifetime;
protected Double minimumWeightToDeath;
protected Double weightConsumeByDay;
protected Double maxNbDaySafe;
protected Double attackPoint;
protected Double defendPoint;
protected Boolean isUseHiddenDefense;
protected Double age;
protected Double weight;
protected Boolean isHidden;
protected Stoppable stoppable;
protected PropertyChangeSupport support = new PropertyChangeSupport(this);
public PropertyChangeSupport getSupport() {
return support;
}
public Animal(String type, SimulationModel simModel) {
super(simModel);
this.type = type;
}
//Getter and Setter
public void setStepByDay(Double stepByDay) {
this.stepByDay = stepByDay;
}
public void setMeterByCase(Double meterByCase) {
this.meterByCase = meterByCase;
}
public void setSmellPoint(Double smellPoint) {
this.smellPoint = smellPoint;
}
public void setVisionPoint(Double visionPoint) {
this.visionPoint = visionPoint;
}
public void setMovePoint(Double movePoint) {
this.movePoint = movePoint;
}
public void setMaxLifetime(Double maxLifetime) {
this.maxLifetime = maxLifetime;
}
public void setMinimumWeightToDeath(Double minimumWeightToDeath) {
this.minimumWeightToDeath = minimumWeightToDeath;
}
public void setWeightConsumeByDay(Double weightConsumeByDay) {
this.weightConsumeByDay = weightConsumeByDay;
}
public void setMaxNbDaySafe(Double maxNbDaySafe) {
this.maxNbDaySafe = maxNbDaySafe;
}
public void setAttackPoint(Double attackPoint) {
this.attackPoint = attackPoint;
}
public void setDefendPoint(Double defendPoint) {
this.defendPoint = defendPoint;
}
public void setIsUseHiddenDefense(Boolean isUseHiddenDefense) {
this.isUseHiddenDefense = isUseHiddenDefense;
}
public void setAge(Double age) {
this.age = age;
}
public void setWeight(Double weight) {
this.weight = weight;
}
public void setIsHidden(Boolean isHidden) {
this.isHidden = isHidden;
}
public void setStoppable(Stoppable stoppable) {
this.stoppable = stoppable;
}
//Conversion methods
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Double DayToStep(Double days){
//1 Day = 4 steps
//2 days => 2*4 steps
return days*stepByDay;
}
public Double StepToDay(Double steps){
//1 Day = 4 steps
//8 steps => 8/4 2 days
return steps/stepByDay;
}
public Double ValueByStepToValueByDay(Double valueByStep){
//1 Day = 4 steps
//100gr eaten by step => 100*4 gr eaten by day
return valueByStep*stepByDay;
}
public Double ValueByDayToValueByStep(Double valueByDay){
//1 Day = 4 steps
//400gr eaten by Days => 400/4gr eaten by step
return valueByDay/stepByDay;
}
public Double CaseToMeters(Double cases){
//1 case = 20 meters
//2 cases => 2*20 meters
return cases*meterByCase;
}
public int MeterToCase(Double meters){
//1 case = 20 meters
//40 meters => 2 cases
return (int) (meters/meterByCase);
}
public Double ValueByMetersToValueByCase(Double valueByMeters){
//1 case = 20 meters
//2 s/meters = 40 s/case
return valueByMeters*stepByDay;
}
public Double ValueByCaseToValueByMeters(Double valueByCase){
//1 case = 20 meters
//40 s/case = 2 s/meters
return valueByCase/stepByDay;
}
// Main action
public void step(SimState arg0) {
x = simModel.getYard().getObjectLocation(this).x;
y = simModel.getYard().getObjectLocation(this).y;
if (weight < (minimumWeightToDeath + weightConsumeByDay * maxNbDaySafe/2)) {
isHidden = false;
}
//System.out.println("myY :" +getY());
if (!isHidden) {
action();
}
weight -= ValueByDayToValueByStep(weightConsumeByDay);
age += StepToDay(1.0);
//System.out.println(getType()+" #"+hashCode()+" my Age : "+age+" my Weight :"+weight);
support.firePropertyChange("age", this.getType(), StepToDay(age)/365f);
support.firePropertyChange("weight", this.getType(), weight);
checkAlive();
}
public abstract void action();
public void checkAlive() {
if (weight < minimumWeightToDeath || age > maxLifetime) {
die();
}
}
protected void die() {
this.getSupport().firePropertyChange("died",this.getType(), null);
System.out.println(this.getType()+" #"+this.hashCode()+" dies !");
simModel.getYard().remove(this);
stoppable.stop();
}
// Move
protected Double moveTo(SparseGrid2D yard, Integer[] coordinates) {
Double move = (double) MeterToCase(movePoint);
// System.out.println(this.getType()+" #"+this.hashCode()+" trying to move to ("+coordinates[0]+","+coordinates[1]+") and movePoint ("+movePoint+")");
if((coordinates[0]!=-1) && (coordinates[1]!=-1)) {//if valid coordinates
while (move > 0 && !isSameLocation(coordinates[0],coordinates[1])) {
int dX = getXShortestDirection(coordinates[0]);
if (dX > 0){
setX(simModel.getYard().stx((int) (this.getX() + 1)));
//System.out.println("x+1 -> "+this.getX());
} else if (dX < 0){
setX(simModel.getYard().stx((int) (this.getX() - 1)));
//System.out.println("x-1 -> "+this.getX());
}
int dY = getYShortestDirection(coordinates[1]);
if (dY > 0) {
setY(simModel.getYard().sty((int)getY() + 1));
//System.out.println("y+1 -> "+this.getY());
} else if (dY < 0) {
setY(simModel.getYard().sty((int)getY() - 1));
//System.out.println("y-1 -> "+this.getY());
}
move--;
}
return (movePoint - move);
} else {
return movePoint;
}
}
// Eat and Attacked
public void attacked() {
System.out.println(this.getType()+" #"+this.hashCode()+" is attacked !");
if(isUseHiddenDefense) {
isHidden = true;
} else {
flee();
}
}
private void flee() {
System.out.println(this.getType()+" #"+this.hashCode()+" flees aways !");
double random = Math.random() * 1000;
//movePoint in m/day -> m/step -> c/step
double destinationX = this.x * (Math.cos(random) * MeterToCase(ValueByDayToValueByStep(movePoint)) * FLEE_MOVE_COEF);
double destinationY = this.y * (Math.sin(random) * MeterToCase(ValueByDayToValueByStep(movePoint)) * FLEE_MOVE_COEF);
setX((int) Math.round(destinationX));
setY((int) Math.round(destinationY));
simModel.getYard().setObjectLocation(this, (int)this.x, (int)this.y);
weight -= ValueByDayToValueByStep(weightConsumeByDay);
}
public Double getEatingEnergy() {
return weight;
}
public boolean canBeEaten() {
return !isHidden;
}
@Override
public void eaten() {
//System.out.println(this.getType()+" #"+this.hashCode()+" is eaten !");
die();
}
// Other
public String toString() {
return "(x: " + getX() + ", y: " + getY() + ", age: " + age + ")";
}
}