package com.shade.entities.bird;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.Sound;
import org.newdawn.slick.geom.Circle;
import org.newdawn.slick.geom.Shape;
import org.newdawn.slick.state.StateBasedGame;
import com.crash.Body;
import com.shade.base.Entity;
import com.shade.base.Level;
import com.shade.base.util.StateManager;
import com.shade.controls.DayPhaseTimer;
import com.shade.crash.CrashLevel;
import com.shade.crash.Repelable;
import com.shade.entities.Basket;
import com.shade.entities.Player;
import com.shade.entities.Roles;
import com.shade.entities.mushroom.Mushroom;
import com.shade.levels.Model;
import com.shade.lighting.LuminousEntity;
/**
* The real deal; this mole is the sum of different mole states.
*
* No I haven't heard of encapsulation.
*
* TODO: Make the birds edge-of-level smart - CUT. TODO: Make the bird flight
* more realistic by adding some phases TODO: Make the bird get "mad" as the
* player approaches TODO: Make the bird "land" correctly on fences - done.
*
* @author Jonathan Jou <j.j@duke.edu>
*/
public final class Bird extends Body implements LuminousEntity {
protected enum States {
WAITING, RETURNING, ATTACKING, SLEEPING, MIGRATING
}
protected CrashLevel level;
protected StateManager manager;
protected Mushroom target;
protected float heading;
protected float range;
protected float speed;
protected boolean attacking;
private float luminosity;
protected static Sound alert, attack;
static {
try {
alert = new Sound("entities/bird/alert.ogg");
attack = new Sound("entities/bird/attack.ogg");
} catch (SlickException e) {
e.printStackTrace();
}
}
public Bird(int x, int y, int range, float speed) throws SlickException {
heading = (float) (Math.PI);
this.range = range;
this.speed = speed;
initShape(x, y);
initStates();
}
public boolean isAttacking() {
return attacking;
}
private void initShape(float x, float y) {
shape = new Circle(x, y, 21f);
}
private void initStates() throws SlickException {
manager = new StateManager();
manager.add(new ReturningBird(this));
manager.add(new AttackingBird(this));
manager.add(new WaitingBird(this));
manager.add(new SleepingBird(this));
}
protected void kill() {
level.remove(this);
}
public void move(double rate) {
float x = (float) (Math.cos(heading - Math.PI / 2) * speed * rate);
float y = (float) (Math.sin(heading - Math.PI / 2) * speed * rate);
nudge(x, y);
}
public float getLuminosity() {
return luminosity;
}
public void setLuminosity(float l) {
luminosity = l;
}
public void addToLevel(Level<?> l) {
level = (CrashLevel) l;
}
public int getRole() {
return Roles.BIRD.ordinal();
}
public void onCollision(Entity obstacle) {
manager.onCollision(obstacle);
}
public boolean canChase() {
return playerInSight() && playerInRange();
}
public boolean playerInSight() {
Object[] o = level.getEntitiesByRole(Roles.PLAYER.ordinal());
if (o.length > 0) {
Player p = (Player) o[0];
return level.lineOfSight(this, p, this, (Basket) level
.getEntitiesByRole(Roles.BASKET.ordinal())[0])
&& p.getLuminosity() > 0.6;
}
return false;
}
public boolean playerInRange() {
Object[] o = level.getEntitiesByRole(Roles.PLAYER.ordinal());
if (o.length > 0) {
Player p = (Player) o[0];
float distx = p.getXCenter() - getXCenter();
float disty = p.getYCenter() - getYCenter();
return Math.sqrt(distx * distx + disty * disty) < range;
}
return false;
}
public void render(StateBasedGame game, Graphics g) {
g.rotate(getXCenter(), getYCenter(), (float) Math.toDegrees(heading));
manager.render(game, g);
g.resetTransform();
}
public void yawn() {
Model mode = (Model) level;
if (mode.getTimer().getDaylightStatus() == DayPhaseTimer.DayLightStatus.NIGHT) {
manager.enter(States.SLEEPING);
}
}
public void wake() {
Model mode = (Model) level;
if (mode.getTimer().getDaylightStatus() == DayPhaseTimer.DayLightStatus.DAWN) {
manager.enter(States.WAITING);
}
}
public void update(StateBasedGame game, int delta) {
manager.update(game, delta);
testAndWrap();
}
/**
* Checks whether a linkable is over the edge of the screen and wraps it if
* it is.
*/
protected void testAndWrap() {
if (getXCenter() <= 5) {
shape.setCenterX(795);
}
if (getXCenter() > 795) {
shape.setCenterX(5);
}
if (getYCenter() <= 5) {
shape.setCenterY(595);
}
if (getYCenter() > 595) {
shape.setCenterY(5);
}
}
public Shape castShadow(float direction, float depth) {
return null;
}
public int getZIndex() {
return 20;
}
public int compareTo(LuminousEntity l) {
return getZIndex() - l.getZIndex();
}
public void removeFromLevel(Level<?> l) {
level.remove(this);
}
}