package game.npc;
import game.strat.Intelligence;
import game.strat.Strategy;
import java.util.Vector;
import gameEngine.drawable.*;
import gameEngine.renderer.IRenderer;
import gameEngine.drawable.particle.*;
import gameEngine.scene.*;
import graphicslib3D.*;
/**
* Acts as an abstract class for creating characters in Midnight Animus. This
* class was origionally called Character but was changed due to the fact that
* the Character class already exists in standard Java. Characters
* must have a position in the world, a model which represents them, animation
* keyframe indices, and hit points among other things. To create a specific
* type of person, a subclass will need to be created which initializes
* the model, textures, and stats for that particular person.
*
* While this class contains an intelligence that is required for NPCs to
* evaluate different strategies, a player character can be created by
* overriding methods having to do with intelligence.
*
* It is important to note that the x and y coordinates used in this class
* are relative to the game plane - not the x and y positions of the characters
* in 3D space.
*
* @author Steven Kodani and Kuen-Lung Yeh
*/
public abstract class Person extends Group implements IAnimationListener
{
//Height is in feet
private final float TEXT_HEIGHT = 6.0f;
//x, y, and heading should never be accessed directly
private float x, y;
private float heading;
private Intelligence myInt;
private int maxHP, currHP, baseAttack, armor;
private float movementSpeed;
//The variable myLists contains the lists that the character belongs to.
//When the character "dies", they will be removed from those lists.
private Vector> myLists;
private MD5Model model;
/**
* Constructs a new Person by initializing their statistics, loading their
* model and texture information, and setting their initial keyframe indices.
* @param pX The x position of the person.
* @param pY The y position of the person.
* @param pHeading The direction that the person is facing in degrees.
* @param pHP The maximum (and current) amount of HP that the character has.
* @param pBaseAttack The default attack value for the character.
* @param pArmor The armor rating of the character.
* @param pMovementSpeed The movement speed of the character in ft/s.
* @param texts The game's list of billboard texts.
* @param cam The game camera used for calculating billboard text rotations.
* @param pModel The model representing this person.
*/
public Person(float pX, float pY, float pHeading, int pHP, int pBaseAttack,
int pArmor, int pMovementSpeed, MD5Model pModel)
{
super("Person");
x = 0;
y = 0;
heading = 0;
addX(pX);
addY(pY);
setHeading(pHeading);
maxHP = pHP;
currHP = pHP;
movementSpeed = pMovementSpeed;
baseAttack = pBaseAttack;
armor = pArmor;
myInt = new Intelligence();
myLists = new Vector>();
//Set up the model for this character
model = pModel;
model.addListener(this);
model.addController(new AnimationController());
addChildren(model);
}
/**
* Removes all particle effects and damage texts from this person.
*/
public void clearTemporaryChildren()
{
removeAll();
addChildren(model);
}
/**
* Returns the character's model.
* @return The character's model.
*/
public MD5Model getModel()
{ return model; }
/**
* Sets the keyframe bounds for this person's model. Will not do anything
* if the model has not yet been intialized.
* @param start The keyframe that this person should start at.
* @param end The keyframe that this person should end at.
*/
public void setKeyframeBounds(int start, int end)
{
if (model != null)
model.setKeyframeBounds(start, end);
}
/**
* Adds a strategy to this character's intelligence
* @param s The character's new strategy.
*/
public void addStrategy(Strategy s)
{ myInt.addStrategy(s); }
/**
* Adds a list which the character belongs to. When the character "dies",
* it will be removed from the all of the lists that it belongs to.
* @param list A list that the character belongs to.
*/
public void addList(Vector list)
{ myLists.add(list); }
/**
* Returns the x position of the character.
* @return The x position of the character.
*/
public float getX()
{ return x; }
/**
* Adds a given amount to the character's x coordinate.
* @param dX The amount to add to the character's x coordinate.
*/
public void addX(float dX)
{
x += dX;
super.translate(dX, 0, 0);
}
/**
* Returns the y position of the character.
* @return The y position of the character.
*/
public float getY()
{ return y; }
/**
* Adds a given amount to the character's y coordinate.
* @param dY The amount to add to the character's y coordinate.
*/
public void addY(float dY)
{
y += dY;
super.translate(0, 0, dY);
}
/**
* Sets the character's heading based on an input parameter.
* @param h The character's new heading in degrees.
*/
public void setHeading(float h)
{
heading = h;
clearRotate();
super.rotate(0, (float) Math.toRadians(heading), 0);
}
/**
* Rotates the character by a given amount.
* @param dH The amount to rotate the character.
*/
public void alterHeading(float dH)
{
heading += dH;
super.rotate(0, (float) Math.toRadians(dH), 0);
}
/**
* Returns this person's heading.
* @return This person's heading.
*/
public float getHeading()
{ return heading; }
/**
* Inflicts damage on a character. The amount of damage will be mitigated
* by the amount of armor the character has. If the result of the damage
* destroys the person, then the person will be removed from all of the lists
* that they belong to.
* @param dHP The amount of damage done to the character prior to armor mitigation.
*/
public void damageHP(int dHP)
{
int damage = Math.max(0, dHP - armor);
currHP -= damage;
if (currHP <= 0)
{
for (int i = 0; i < myLists.size(); i++)
myLists.get(i).remove(this);
LifeTimeController life = new LifeTimeController(4.5f);
addController(life);
myInt.forceStrategy(null);
doAnimationDeath();
}
//Create a particle effect meant to look like sparks
float[] color =
{
0.5f, 0.5f, 0.2f
};
Emitter emit = new BurstEmitter(2, 10.0f, true);
ParticleSystem particles = new ParticleSystem(color, 2.0f, 0.02f,
emit, 0.1f, Math.min(20, armor * 2));
particles.addController(new AnimationController());
particles.addForce(new Vector3D(0.0, -32.174, 0.0));
particles.setFloorOffset(-4.0f);
particles.setBounceMode(ParticleSystem.COLLIDE_BOUNCE);
particles.translate(getX(), 4.0f, getY());
getParent().addChildren(particles);
//Add a new damage text string which will float above the character for a short time
StrokeText damageText = new StrokeText(Integer.toString(damage), true, true);
damageText.setRenderQueueMode(IRenderer.RENDERMODE_OPAQUE);
damageText.translate(0, 6, 0);
damageText.setColor(0.8f, 0.1f, 0.1f);
damageText.addController(new FloatController());
damageText.addController(new LifeTimeController(5));
addChildren(damageText);
}
/**
* Returns the character's current HP count.
* @return The character's current HP count.
*/
public int getHP()
{ return currHP; }
/**
* Returns the character's maximum HP value.
* @return The character's maximum HP value.
*/
public int getMaxHP()
{ return maxHP; }
/**
* Returns the person's current HP to their maximum.
*/
public void restoreHP()
{ currHP = maxHP; }
/**
* Heals the character by a certain amount by adding the heal amount
* to the character's current HP. If too much HP is healed, then the
* character's current HP will be set to their maximum HP.
* @param dHP The amount of HP to heal.
*/
public void healHP(int dHP)
{
int tempHP = currHP;
currHP = Math.min(maxHP, currHP + dHP);
//Add a new heal text string which will float above the character for a short time
StrokeText healText =
new StrokeText(Integer.toString(currHP - tempHP), true, true);
healText.setRenderQueueMode(IRenderer.RENDERMODE_OPAQUE);
healText.translate(0, 6, 0);
healText.setColor(0.0f, 1.0f, 0.2f);
healText.addController(new FloatController());
healText.addController(new LifeTimeController(5));
addChildren(healText);
}
/**
* Returns the character's movement speed.
* @return The character's movement speed.
*/
public float getMovementSpeed()
{ return movementSpeed; }
/**
* Sets the character's movement speed.
* @param pMovementSpeed The character's new movement speed.
*/
public void setMovementSpeed(float pMovementSpeed)
{ movementSpeed = pMovementSpeed; }
/**
* Returns the character's base attack.
* @return The character's base attack.
*/
public int getBaseAttack()
{ return baseAttack; }
/**
* Mutates this person's base attack value.
* @param pBaseAttack This person's new base attack value.
*/
public void setBaseAttack(int pBaseAttack)
{ baseAttack = pBaseAttack; }
/**
* Returns the character's armor rating.
* @return The character's armor rating.
*/
public int getArmor()
{ return armor; }
/**
* Sets the character's armor rating.
* @param pArmor The character's new armor rating.
*/
public void setArmor(int pArmor)
{ armor = pArmor; }
/**
* Returns the person currently targeted by this character's current
* strategy. Will return null if there is no current strategy selected
* or if the selected strategy doesn't have a target.
* @return The person that this person is currently targeting.
*/
public Person getTarget()
{
if (myInt.getCurrentStrategy() == null)
return null;
else
return myInt.getCurrentStrategy().getTarget();
}
/**
* Sets the target of this person by setting the current strategy's target
* if there is a current strategy. If they don't have a current strategy,
* then their target will not be set.
* @param p The person who will be this person's new target.
*/
public void setTarget(Person p)
{
if (myInt.getCurrentStrategy() != null)
myInt.getCurrentStrategy().setTarget(p);
}
/**
* Returns the intelligence for this person.
* @return The intelligence for this person.
*/
public Intelligence getIntelligence()
{ return myInt; }
/**
* Elapses time in the person's intelligence. This method "drives" the
* actions of the character.
* @param elapsedTime The amount of time that has elapsed in milliseconds
* since the last time that this method was called.
*/
public void tick(int elapsedTime)
{ myInt.tick(elapsedTime); }
/**
* Moves the character toward or away from a specified point. This method
* has the potential to change the character's heading. The character
* moves at a speed specified by their movement speed property.
* @param pX The x coordinate of the point which the character is moving to.
* @param pY The y coordinate of the point which the character is moving to.
* @param forward A boolean variable specifiying whether the character is
* moving toward the specified point or if the character is moving away
* from the specified point.
* @param elapsedTime The number of milliseconds that have elapsed since
* the last movement. This is used to calculate how far the character moves.
*/
public void move(float pX, float pY, boolean forward, int elapsedTime)
{
//Change the heading to the specified point
lookAt(pX, pY, forward);
//Add a certain amount to the current position based on the new heading of the character
double dX = (movementSpeed * Math.sin(Math.toRadians(heading))) * elapsedTime / 1000;
double dY = (movementSpeed * Math.cos(Math.toRadians(heading))) * elapsedTime / 1000;
addX((float) dX);
addY((float) dY);
doAnimationRun();
}
/**
* Moves the character forward or backward based on their current heading.
* The character moves at a speed specified by their movement speed property.
* @param forward A boolean variable specifiying whether or not the character
* should move forward or backward.
* @param elapsedTime The number of milliseconds that have elapsed since
* the last movement. This is used to calculate how far the character moves.
*/
public void move(boolean forward, int elapsedTime)
{
//Add a certain amount to the current position based on the new heading of the character
double dX = (movementSpeed * Math.sin(Math.toRadians(heading))) * elapsedTime / 1000;
double dY = (movementSpeed * Math.cos(Math.toRadians(heading))) * elapsedTime / 1000;
if (!forward)
{
dX *= -1;
dY *= -1;
}
addX((float) dX);
addY((float) dY);
doAnimationRun();
}
/**
* Adjust this character's heading so that they will be facing a specified
* point.
* @param pX The x-component of the point the character should look at.
* @param pY The y-component of the point the character should look at.
* @param forward Whether the character should look at the specified point
* or look away from it.
*/
public void lookAt(float pX, float pY, boolean forward)
{
//Calculate the distance between the character and the target point
double dist = Math.sqrt((x - pX) * (x - pX) + (y - pY) * (y - pY));
//Protect from divide by zero
if (dist < .001 && dist > -.001)
dist = 1;
//If the point to move toward isn't in a strait line on the Y axis,
//calculate a new heading for the character
if ((pX - x) > 0.001 || (pX - x) < -0.001)
setHeading((float) (Math.toDegrees(Math.acos((pY - y) / dist)) * Math.signum(pX - x)));
else
if ((pY - y) > 0)
setHeading(0);
else
setHeading(180);
//If looking away, subtract 180 degrees from the heading
if (!forward)
setHeading(getHeading() - 180);
}
/**
* Is called when this person's model has finished a cycle of its current
* animation. When the animation cycle has finished set the character to
* use their idle animation.
*/
public void cycleCompleted(MD5Model model)
{ doAnimationIdle(); }
/**
* Triggers the character's idle animation. The keyframe range used for the
* idle animation varies depending on the model so this method must be
* implemented in a character's concrete subclass.
*/
public abstract void doAnimationIdle();
/**
* Triggers the person's run animation. If not overriden in a subclass
* then the character's idle animation will be used instead.
*/
public void doAnimationRun()
{ doAnimationIdle(); }
/**
* Triggers the person's attack animation. If not overriden in a subclass
* then the character's idle animation will be used instead.
*/
public void doAnimationDefaultAttack()
{ doAnimationIdle(); }
/**
* Triggers the person's special attack animation. If not overriden in a
* subclass then the character's idle animation will be used instead.
*/
public void doAnimationEnhancedAttack()
{ doAnimationIdle(); }
/**
* Triggers the person's spell casting animation. If not overriden in a
* subclass then the character's idle animation will be used instead.
*/
public void doAnimationCastSpell()
{ doAnimationIdle(); }
/**
* Triggers the person's ultimate ability animation. If not overriden in a
* subclass then the character's idle animation will be used instead.
*/
public void doAnimationUltimateAbility()
{ doAnimationIdle(); }
/**
* Triggers the person's miscellaneous animation. If not overriden in a
* subclass then the character's idle animation will be used instead.
*/
public void doAnimationMiscellaneous()
{ doAnimationIdle(); }
/**
* Triggers the person's death animation. If not overriden in a subclass
* then the character's idle animation will be used instead.
*/
public void doAnimationDeath()
{ doAnimationIdle(); }
}