/*********************************************************************************
* PlayerCharacter.java
* Purpose: Defines additional info used by the game for the player character
*
* Copyright 2010 by Christopher Reid
*********************************************************************************/
package creid.mythos;
import creid.mythos.graphics.GameColors;
public class PlayerCharacter extends Creature
{
//-----------------------------------------------------
// Constants
//-----------------------------------------------------
//player move mask
static final int PLAYER_MOVES = Actor.WALK + Actor.DROWN;
//player abilities
static final int PLAYER_OPS = Operations.OPEN_DOOR + Operations.CLOSE_DOOR;
//Size of player's items
public final static int PLAYER_INVENTORY_SIZE = 20;
//-----------------------------------------------------
// Attributes
//-----------------------------------------------------
//Our next action
public Action nextAction;
//-----------------------------------------------------
// Constructor
//-----------------------------------------------------
public PlayerCharacter(String name)
{
super(
"PLAYER", //key
name, //Name
Creature.CS_PERSON, //Symbol
GameColors.WHITE, //color
10, //Base Health
0, //BodyParts stat
0, //Coordination stat
0, //Mind stat
Actor.NORMAL_SPEED, //Base Speed
PLAYER_MOVES, //Mobile mask
PLAYER_OPS, //Terrain op mask
10, //Perception
Mythos.FACTION_PLAYER, //faction
0l, //turn of creation
0, //level, class brings up to 1
Entity.NEVER_SPAWN, //rarity
true, //has body/equipment slots
false //has items, will be manually set next
);
mobuls = 0;
itemActions = new ItemAction(this, ItemAction.PICKUP + ItemAction.DROP);
inventory = new Inventory(this, PLAYER_INVENTORY_SIZE);
nextAction = null;
calcStats();
}
//-------------------------------------------------
// calcStats:
// calculate all modifiers and stats
// Should be called whenever something happens that
// alters the creature's abilities in any way
// i.e. stat boost, equipping an item, spell effect, etc
//-------------------------------------------------
public void calcStats()
{
calcPlayerMeleeHit();
calcPlayerMeleeDamage();
calcPlayerEvasion();
calcPlayerArmor();
calcPlayerRangedDamage();
}
void calcPlayerArmor()
{
//protection from worn items
for (int i = 0; i < bodyParts.length; i++)
if (bodyParts[i].hasEquippedItem())
{
Item item = bodyParts[i].equippedItem;
if (item.hasProperty("armor"))
armor += item.getNProperty("armor").value;
}
}
void calcPlayerEvasion()
{
defense = coordination;
}
void calcPlayerMeleeHit()
{
meleeAttack.hit = Math.round(((float)(coordination + body)) / 2f);
}
protected void calcPlayerMeleeDamage()
{
int damage = body;
//damage increased by using a weapon (or rings of damage, spiked armor, etc)
for (int i = 0; i < bodyParts.length; i++)
if (bodyParts[i].hasEquippedItem())
{
Item item = bodyParts[i].equippedItem;
if (item.type == Item.I_WEAPON && item.hasProperty("damage"))
damage += item.getNProperty("damage").value;
}
meleeAttack.damage = damage;
}
protected void calcPlayerRangedDamage()
{
int damage = 0;
if (bodyParts[BodyPart.HUM_QUIVER].hasEquippedItem())
{
Item missile = bodyParts[BodyPart.HUM_QUIVER].equippedItem;
//impact = ranged damage
if (missile.hasProperty(Property.RANGED_DAMAGE))
damage = missile.getNProperty(Property.RANGED_DAMAGE).value;
//unbalanced weapons can still cause some damage
if (damage == 0 && missile.type == Item.I_WEAPON)
damage += Math.max(1, missile.getNProperty(Property.MELEE_DAMAGE).value / 5);
if (bodyParts[BodyPart.HUM_MAIN_HAND].hasEquippedItem())
{
Item weapon = bodyParts[BodyPart.HUM_MAIN_HAND].equippedItem;
if (weapon.type == Item.I_LAUNCHER && missile.hasProperty("ammo") &&
weapon.getSProperty("ammo").value.equals(
missile.getSProperty("ammo").value))
{
//TODO:setEffectiveRangedDamage(damage + weapon.getProperty("impact").getIVal());
}
}
}
//TODO:setEffectiveRangedDamage(damage);
}
@Override
public void takeDamage(int damage, int type, String cause)
{
super.takeDamage(damage, type, cause);
}
//-----------------------------------------------------
//checkLOS
// trace a path from the player to the destination to
// see if there is a valid LOS to it
//-----------------------------------------------------
public boolean checkLOS(Location destination)
{
return checkLOS(location, destination);
}
boolean checkLOS(Location source, Location destination)
{
boolean inLOS = false;
boolean loop = true;
while (loop)
{
//this grid is in LOS
source.inLOS = true;
//stop if this grid block further LOS, or if this is the destination
if (source.terrain.opaque)
loop = false;
//calculate distance from target
int diffX = destination.x - source.x;
int diffY = destination.y - source.y;
//we have arrived;
if (diffX == 0 && diffY == 0)
{
inLOS = true;
loop = false;
break;
}
//Get the sign of the diffs
int dx = 0;
if (diffX > 0)
dx++;
else if (diffX < 0)
dx--;
int dy = 0;
if (diffY > 0)
dy++;
else if (diffY < 0)
dy--;
//Is the line x-dominant
if (Math.abs(diffX) - 2 * Math.abs(diffY) >= 0)
dy = 0;
//y-dominant
else if (Math.abs(diffY) - 2 * Math.abs(diffX) >= 0)
dx = 0;
//move to the next square
int x = source.x + dx;
int y = source.y + dy;
source = destination.map.map[x][y];
}
return inLOS;
}
//-----------------------------------------------------
// updateLOS
// fix square's inLOS flags after a player move
//-----------------------------------------------------
public void updateLOS()
{
Level map = location.map;
for (int x = 0; x < map.xSize; x++)
{
for (int y = 0; y < map.ySize; y++)
{
Location cell = map.map[x][y];
if (Mythos.DEBUG_SHOW_LEVEL)
cell.updateDisplay();
//basic check: is it even possible this square could be in LOS
if (x < location.x - perception -1 ||
x > location.x + perception + 1 ||
y < location.y - perception - 1 ||
y > location.y + perception + 1)
{
cell.inLOS = false;
}
//otherwise see if we can trace a line from the player to the square
else
{
if(checkLOS(cell))
{
cell.inLOS = true;
cell.updateDisplay();
}
else
cell.inLOS = false;
}
}
}
}
//-----------------------------------------------------
// shoot
// Throw or shoot an item at a target
//-----------------------------------------------------
public boolean shoot()
{
//Turn the quivered item into a projectile
Item item = bodyParts[BodyPart.HUM_QUIVER].takeOffOneItem();
Projectile missile = new Projectile(item, 0 /*getEffectiveRangedHit()*/, //TODO
0/*getEffectiveRangedDamage()*/, location, //TODO
Mythos.display.infoPanel.targetPanel.target.location, Actor.NORMAL_SPEED,
Projection.TRANSIENT, Mythos.game.turn - 1);
Mythos.logger.debugLog("Created projectile " + missile.name);
//start the projectile on our square
location.projection = missile;
//Add projectile to action queue
Mythos.game.actionQueue.add(missile);
//Warn the player if they are out of ammo
if (!bodyParts[BodyPart.HUM_QUIVER].hasEquippedItem())
Mythos.logger.messageLog("Your quiver is emptied!");
return true;
}
//-----------------------------------------------------
// die
// override normal die method; if player dies the
// game is over
//-----------------------------------------------------
@Override
public void die(String cause)
{
Mythos.logger.messageLog("You were killed by " + cause);
Mythos.game.state = Mythos.STATE_GAME_OVER;
}
//-----------------------------------------------------
// freeAction
// an action took no time, decrement next turn to ensure
// player immediately gets to go again
//-----------------------------------------------------
public void freeAction()
{
nextAction = new FreeAction(this);
}
//-----------------------------------------------------
// chooseAction
//Instead of running AI, just use the player-selected
//action
//
// Note that this method is blocking; it will wait until
// nextAction is set to something (via player command)
//-----------------------------------------------------
public Action chooseAction()
{
Mythos.logger.debugLog("Now accepting player commands");
Mythos.game.input.inputAllowed = true;
while(nextAction == null)
try
{
Thread.sleep(50);
}
catch (Exception ex)
{
System.err.println(ex);
}
Mythos.game.input.inputAllowed = false;
Action act = nextAction;
nextAction = null;
Mythos.logger.debugLog("Got player command");
return act;
}
//----------------------------------------------------------------
// push
// Try to move/operate/melee on an adjacent square
//----------------------------------------------------------------
public void push(int x, int y)
{
Location target = location;
target = target.map.map[target.x + x][target.y + y];
Action action = new Movement(move);
action.target = location;
//If we are staying put, just do the move and be done with it
if (x != Actor.STAY || y != Actor.STAY)
//Is the target square occupied?
if (target.isOccupied())
{
//Is the creature friendly?
if (target.getOccupant().faction == faction)
{
//TODO: Place swapping
//Do a swap instead
//SwapAction swap
//result =
Mythos.logger.messageLog("You stop to avoid hitting the " + target.getOccupant().name);
}
else //Clobberin' Time
{
//Do a melee action instead
//mover.meleeAttack(to.getOccupant(), Mythos.game.getTurn());
action = new MeleeAttack(meleeAttack);
action.target = target.getOccupant();
}
}
//Is the target square passable?
else if(target.terrain.isPassable(move))
action.target = target;
else if(target.terrain.isOperable(ops))
{
action = new Operations(ops);
action.target = target;
}
nextAction = action;
}
}
|