package Roguelike.Entity;
import Roguelike.Ability.AbilityTree;
import Roguelike.Ability.ActiveAbility.ActiveAbility;
import Roguelike.Ability.IAbility;
import Roguelike.Ability.PassiveAbility.PassiveAbility;
import Roguelike.AssetManager;
import Roguelike.Dialogue.DialogueManager;
import Roguelike.Entity.AI.BehaviourTree.BehaviourTree;
import Roguelike.Entity.Tasks.AbstractTask;
import Roguelike.Fields.Field;
import Roguelike.GameEvent.GameEventHandler;
import Roguelike.Global;
import Roguelike.Global.Direction;
import Roguelike.Global.Passability;
import Roguelike.Global.Statistic;
import Roguelike.Items.Item;
import Roguelike.Items.Item.EquipmentSlot;
import Roguelike.Lights.Light;
import Roguelike.Sprite.Sprite;
import Roguelike.StatusEffect.StatusEffect;
import Roguelike.Tiles.GameTile;
import Roguelike.Tiles.Point;
import Roguelike.Util.EnumBitflag;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.XmlReader;
import com.badlogic.gdx.utils.XmlReader.Element;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
public class GameEntity extends Entity
{
// ####################################################################//
// region Constructor
// ----------------------------------------------------------------------
public GameEntity()
{
}
// endregion Constructor
// ####################################################################//
// region Public Methods
// ----------------------------------------------------------------------
public void applyDepthScaling()
{
for ( Global.Statistic stat : Global.Statistic.values() )
{
int current = statistics.get( stat );
float scaleVal = (float) Math.sqrt( Global.LevelManager.totalDepth );
scaleVal /= 10.0f;
scaleVal += 1;
int scaledVal = (int) ( (float) current * scaleVal );
statistics.put( stat, scaledVal );
}
}
// ----------------------------------------------------------------------
public void attack( Entity other, Direction dir )
{
Global.calculateDamage( this, other, getVariable( Statistic.ATTACK ), other.getVariable( Statistic.DEFENSE ), getVariable( Statistic.PENETRATION ), true );
}
// ----------------------------------------------------------------------
@Override
public void update( float cost )
{
if ( inventory.isVariableMapDirty )
{
isVariableMapDirty = true;
inventory.isVariableMapDirty = false;
}
actionDelayAccumulator += cost;
for ( AbilityTree a : slottedAbilities )
{
if (a != null)
{
a.current.current.onTurn();
}
}
for ( GameEventHandler h : getAllHandlers() )
{
h.onTurn( this, 1 );
}
processStatuses();
if ( popupDuration > 0 )
{
popupDuration -= 1;
}
if (dialogue != null)
{
if (dialogue.exclamationManager != null)
{
dialogue.exclamationManager.update( 1 );
}
}
}
// ----------------------------------------------------------------------
public boolean inCombat()
{
for ( Point p : visibilityCache.getCurrentShadowCast() )
{
GameTile otile = tile[0][0].level.getGameTile(p);
if (otile.entity != null && !otile.entity.isAllies( this ))
{
return true;
}
}
return false;
}
// ----------------------------------------------------------------------
@Override
public int getStatistic( Statistic stat )
{
int val = statistics.get( stat ) + inventory.getStatistic( Statistic.emptyMap, stat );
HashMap<String, Integer> variableMap = getBaseVariableMap();
variableMap.put( stat.toString().toLowerCase(), val );
for ( AbilityTree a : slottedAbilities )
{
if ( a != null && a.current.current instanceof PassiveAbility )
{
PassiveAbility passive = (PassiveAbility) a.current.current;
val += passive.getStatistic( variableMap, stat );
}
}
for ( int i = 0; i < statusEffects.size; i++ )
{
StatusEffect se = statusEffects.get( i );
val += se.getStatistic( variableMap, stat );
}
return val;
}
// ----------------------------------------------------------------------
@Override
protected void internalLoad( String entity )
{
XmlReader xml = new XmlReader();
Element xmlElement = null;
try
{
xmlElement = xml.parse( Gdx.files.internal( "Entities/" + entity + ".xml" ) );
}
catch ( IOException e )
{
e.printStackTrace();
}
internalLoad( xmlElement );
}
// ----------------------------------------------------------------------
protected void internalLoad( Element xmlElement )
{
String extendsElement = xmlElement.getAttribute( "Extends", null );
if ( extendsElement != null )
{
internalLoad( extendsElement );
}
super.baseInternalLoad( xmlElement );
defaultHitEffect = xmlElement.getChildByName( "HitEffect" ) != null ? AssetManager.loadSprite( xmlElement.getChildByName( "HitEffect" ) ) : defaultHitEffect;
AI = xmlElement.getChildByName( "AI" ) != null ? BehaviourTree.load( Gdx.files.internal( "AI/" + xmlElement.get( "AI" ) + ".xml" ) ) : AI;
canSwap = xmlElement.getBoolean( "CanSwap", canSwap );
canMove = xmlElement.getBoolean( "CanMove", canMove );
blood = xmlElement.get( "Blood", "Blood" );
Element factionElement = xmlElement.getChildByName( "Factions" );
if ( factionElement != null )
{
if (factionElement.getBooleanAttribute( "Override", false ))
{
factions.clear();
}
for ( Element faction : factionElement.getChildrenByName( "Faction" ) )
{
factions.add( faction.getText().toLowerCase() );
}
}
Element abilitiesElement = xmlElement.getChildByName( "Abilities" );
if ( abilitiesElement != null )
{
for ( int i = 0; i < abilitiesElement.getChildCount(); i++ )
{
Element abilityElement = abilitiesElement.getChild( i );
AbilityTree tree = null;
if (abilityElement.getChildCount() == 0)
{
tree = new AbilityTree( abilityElement.getText() );
}
else
{
IAbility ability = null;
if ( abilityElement.getName().equalsIgnoreCase( "Active" ) )
{
ability = ActiveAbility.load( abilityElement );
}
else if ( abilityElement.getName().equalsIgnoreCase( "Passive" ) )
{
ability = PassiveAbility.load( abilityElement );
}
else
{
throw new RuntimeException( "Invalid ability type '" + abilityElement.getName() + "'" );
}
tree = new AbilityTree( ability );
}
tree.current.current.setCaster( this );
slottedAbilities.add( tree );
}
}
Element dialogueElement = xmlElement.getChildByName( "Dialogue" );
if ( dialogueElement != null )
{
if (dialogueElement.getChildCount() > 0)
{
dialogue = DialogueManager.load( dialogueElement );
}
else
{
dialogue = DialogueManager.load( dialogueElement.getText() );
}
}
essence = xmlElement.getInt( "Essence", MathUtils.random( HP ) );
Element statusesElement = xmlElement.getChildByName( "Statuses" );
if ( statusesElement != null )
{
for ( int i = 0; i < statusesElement.getChildCount(); i++ )
{
Element statusElement = statusesElement.getChild( i );
StatusEffect effect = StatusEffect.load( statusElement.getText() );
pendingStatusEffects.add( effect );
}
processStatuses();
}
}
// ----------------------------------------------------------------------
@Override
public Array<GameEventHandler> getAllHandlers()
{
Array<GameEventHandler> handlers = new Array<GameEventHandler>();
for ( AbilityTree a : slottedAbilities )
{
if ( a != null && a.current.current instanceof PassiveAbility )
{
handlers.add( (PassiveAbility) a.current.current );
}
}
for ( int i = 0; i < statusEffects.size; i++ )
{
StatusEffect se = statusEffects.get( i );
handlers.add( se );
}
for ( EquipmentSlot slot : EquipmentSlot.values() )
{
Item i = inventory.getEquip( slot );
if ( i != null )
{
handlers.add( i );
}
}
return handlers;
}
// ----------------------------------------------------------------------
@Override
public void applyDamage( int dam, Entity damager )
{
super.applyDamage( dam, damager );
AI.setData( "EnemyPos", Global.PointPool.obtain().set( damager.tile[ 0 ][ 0 ].x, damager.tile[ 0 ][ 0 ].y ) );
if (blood != null)
{
if (dam >= getMaxHP() / 3)
{
Direction dir = Direction.values()[ MathUtils.random( Direction.values().length - 1 ) ];
GameTile bloodTile = tile[ 0 ][ 0 ].level.getGameTile( tile[ 0 ][ 0 ].x + dir.getX(), tile[ 0 ][ 0 ].y + dir.getY() );
if (bloodTile.getPassable( travelType, this ))
{
Field field = Field.load( blood );
field.spawnSpeed = 0.05f;
field.trySpawnInTile( bloodTile, 1 );
}
}
if (HP > 0 && dam >= HP)
{
Direction dir = Direction.getDirection( damager.tile[0][0], tile[0][0] );
Point start = new Point(tile[0][0]);
Array<Point> cone = Direction.buildCone( dir, start, 1 );
cone.add( start );
int count = MathUtils.random( 2 );
for (int i = 0; i < count; i++)
{
Point p = cone.removeIndex( MathUtils.random( cone.size - 1 ) );
GameTile bloodTile = tile[ 0 ][ 0 ].level.getGameTile( p );
if (bloodTile.getPassable( travelType, this ))
{
Field field = Field.load( blood );
field.spawnSpeed = 0.05f;
field.trySpawnInTile( bloodTile, 1 );
}
}
}
}
if (!updatedAbilityDam)
{
for (AbilityTree ab : slottedAbilities)
{
if (ab != null)
{
ab.current.current.onDamaged();
}
}
updatedAbilityDam = true;
}
}
// ----------------------------------------------------------------------
@Override
public void applyHealing( int heal )
{
super.applyHealing( heal );
if (!updatedAbilityHeal)
{
for (AbilityTree ab : slottedAbilities)
{
if (ab != null)
{
ab.current.current.onHealed();
}
}
updatedAbilityHeal = true;
}
}
// ----------------------------------------------------------------------
@Override
public void removeFromTile()
{
for ( int x = 0; x < size; x++ )
{
for ( int y = 0; y < size; y++ )
{
if ( tile[ x ][ y ] != null && tile[ x ][ y ].entity == this )
{
tile[ x ][ y ].entity = null;
}
tile[ x ][ y ] = null;
}
}
}
// ----------------------------------------------------------------------
public boolean isAllies( GameEntity other )
{
for ( String faction : factions )
{
if ( other.factions.contains( faction ) ) { return true; }
}
return false;
}
// ----------------------------------------------------------------------
public boolean isAllies( HashSet<String> other )
{
if ( other == null ) { return false; }
for ( String faction : factions )
{
if ( other.contains( faction ) ) { return true; }
}
return false;
}
// ----------------------------------------------------------------------
public float getActionDelay()
{
float speed = getStatistic( Statistic.SPEED ) / 10.0f - 1;
return (float)Math.pow( 0.75, speed );
}
// ----------------------------------------------------------------------
public void getLight( Array<Light> output )
{
if ( light != null )
{
output.add( light );
}
// for ( EquipmentSlot slot : EquipmentSlot.values() )
// {
// Item i = inventory.getEquip( slot );
//
// if ( i != null && i.light != null )
// {
// output.add( i.light );
// }
// }
}
// ----------------------------------------------------------------------
public static GameEntity load( String name )
{
GameEntity e = new GameEntity();
e.fileName = name;
e.internalLoad( name );
e.statistics.put( Statistic.WALK, 1 );
e.isVariableMapDirty = true;
e.recalculateMaps();
e.HP = e.getMaxHP();
e.isVariableMapDirty = true;
e.recalculateMaps();
return e;
}
// ----------------------------------------------------------------------
public static GameEntity load( Array<Element> xml )
{
GameEntity e = new GameEntity();
e.xmlData = xml;
for ( Element el : xml )
{
e.internalLoad( el );
}
e.statistics.put( Statistic.WALK, 1 );
e.isVariableMapDirty = true;
e.recalculateMaps();
e.HP = e.getMaxHP();
e.isVariableMapDirty = true;
e.recalculateMaps();
return e;
}
// ----------------------------------------------------------------------
public EnumBitflag<Passability> getTravelType()
{
recalculateMaps();
return travelType;
}
// endregion Public Methods
// ####################################################################//
// region Data
// ----------------------------------------------------------------------
public boolean seen = false;
public String fileName;
public Array<Element> xmlData;
// ----------------------------------------------------------------------
public Array<AbilityTree> slottedAbilities = new Array<AbilityTree>();
// ----------------------------------------------------------------------
public Sprite defaultHitEffect = AssetManager.loadSprite( "EffectSprites/Strike/Strike", 0.1f, "Hit" );
// ----------------------------------------------------------------------
public Array<AbstractTask> tasks = new Array<AbstractTask>();
public float actionDelayAccumulator;
public boolean canSwap;
public boolean canMove = true;
// ----------------------------------------------------------------------
public DialogueManager dialogue;
// ----------------------------------------------------------------------
public HashSet<String> factions = new HashSet<String>();
public BehaviourTree AI;
// ----------------------------------------------------------------------
public boolean updatedAbilityDam = false;
public boolean updatedAbilityHeal = false;
// ----------------------------------------------------------------------
public String blood;
// ----------------------------------------------------------------------
public Point spawnPos;
// endregion Data
// ####################################################################//
}