package Roguelike.Fields; import java.io.IOException; import java.util.Comparator; import java.util.HashMap; import Roguelike.AssetManager; import Roguelike.Global; import Roguelike.Global.Passability; import Roguelike.Entity.Entity; import Roguelike.Fields.DurationStyle.AbstractDurationStyle; import Roguelike.Fields.FieldInteractionTypes.AbstractFieldInteractionType; import Roguelike.Fields.OnDeathEffect.AbstractOnDeathEffect; import Roguelike.Fields.OnTurnEffect.AbstractOnTurnEffect; import Roguelike.Fields.SpreadStyle.AbstractSpreadStyle; import Roguelike.GameEvent.IGameObject; import Roguelike.Lights.Light; import Roguelike.Sprite.Sprite; import Roguelike.Sprite.SpriteAnimation.StretchAnimation; import Roguelike.Sprite.TilingSprite; import Roguelike.Tiles.GameTile; import Roguelike.Util.EnumBitflag; import Roguelike.Util.FastEnumMap; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.XmlReader; import com.badlogic.gdx.utils.XmlReader.Element; public class Field implements IGameObject { /** * match based on tags, not name (hot, wet, explosive, poisonous, gas) * interation: propogate, spawn, die, none on urn, damage, heal, status, * spawn duration, permanent, time, single modify passability: allow, * restrict * * water spead fast flow fires turns it to steam single stacks disappear * after time add swim passability * * steam blocks vision dissipates after short time * * smoke same as steam * * fire spreads to neighbouring entities does damage disppears after nbot * doing damage for some turns when dies naturally turn into smoke * * electricity disappears after a few turns discharges into entity, high dam * annihaltes with water, discharging into every connected water tile * * ice eveny movement gets doubled fire melts it, turning it into water * * lava flow slow spawns fire around it does high dam * * others poison bog/cobweb thuorns/razorcrystals explosive gas */ public enum FieldLayer { GROUND, AIR } public String fileName; public String fieldName; public String[] tags = new String[0]; public GameTile tile; public Array<SpriteGroup> groups = new Array<SpriteGroup>( ); public FieldLayer layer = FieldLayer.GROUND; public int stacks = 1; public AbstractDurationStyle durationStyle; public AbstractSpreadStyle spreadStyle; public Array<AbstractOnTurnEffect> onTurnEffects = new Array<AbstractOnTurnEffect>(); public HashMap<String, AbstractFieldInteractionType> fieldInteractions = new HashMap<String, AbstractFieldInteractionType>(); public Array<AbstractOnDeathEffect> onDeathEffects = new Array<AbstractOnDeathEffect>(); public HashMap<String, Object> data = new HashMap<String, Object>(); public EnumBitflag<Passability> allowPassability = new EnumBitflag<Passability>(); public EnumBitflag<Passability> restrictPassability = new EnumBitflag<Passability>(); public float spawnSpeed = 0.25f; public SpriteGroup getSpriteGroup() { for (int i = 0; i < groups.size; i++) { SpriteGroup group = groups.get( i ); int thisStacks = group.stacks; int prevStacks = i > 0 ? groups.get( i - 1 ).stacks : 0; if (stacks >= prevStacks && stacks <= thisStacks) { return groups.get( i ); } } return groups.get( groups.size - 1 ); } public void update( float cost ) { if ( tile == null ) { return; } // we are dead float onTurnAccumulator = (Float) getData( "OnTurnAccumulator", 0.0f ); onTurnAccumulator += cost; while ( onTurnAccumulator > 1 ) { onTurnAccumulator -= 1; if ( tile.environmentEntity != null ) { processOnTurnEffectsForEntity( tile.environmentEntity, 1 ); } } setData( "OnTurnAccumulator", onTurnAccumulator ); durationStyle.update( cost, this ); if ( stacks > 0 && spreadStyle != null ) { spreadStyle.update( cost, this ); } } public void processOnTurnEffectsForEntity( Entity entity, float cost ) { if ( entity.isImmune( fieldName.toLowerCase() ) ) { return; } for ( AbstractOnTurnEffect effect : onTurnEffects ) { effect.process( this, entity, cost ); } } public void onNaturalDeath() { for ( AbstractOnDeathEffect effect : onDeathEffects ) { effect.process( this, tile ); } } public void trySpawnInTile( GameTile newTile, int stacks ) { Field newField = copy(); newField.stacks = stacks; FastEnumMap<FieldLayer, Field> fieldStore = new FastEnumMap<FieldLayer, Field>( FieldLayer.class ); for ( FieldLayer layer : FieldLayer.values() ) { Field tileField = newTile.fields.get( layer ); if ( tileField != null ) { // if same field, increase stacks if ( tileField.fieldName.equals( fieldName ) ) { tileField.stacks++; fieldStore.put( tileField.layer, tileField ); } // if different field, interact else { // First check for interaction on self Field srcField = newField; Field dstField = tileField; AbstractFieldInteractionType interaction = getInteraction( srcField.fieldInteractions, dstField ); if ( interaction == null ) { srcField = tileField; dstField = newField; interaction = getInteraction( srcField.fieldInteractions, dstField ); } if ( interaction != null ) { if ( !fieldStore.containsKey( layer ) ) { fieldStore.put( layer, null ); } newTile.fields.put( layer, null ); Field field = interaction.process( srcField, dstField ); if ( field != null ) { fieldStore.put( field.layer, field ); } } else { fieldStore.put( tileField.layer, tileField ); } } } } if ( !fieldStore.containsKey( newField.layer ) ) { Field tileField = newTile.fields.get( newField.layer ); if ( tileField != null && tileField.stacks > 1 ) { fieldStore.put( newField.layer, tileField ); } else { fieldStore.put( newField.layer, newField ); } } for ( FieldLayer layer : FieldLayer.values() ) { if ( fieldStore.containsKey( layer ) ) { Field field = fieldStore.get( layer ); if ( field == null ) { newTile.clearField( layer ); } else { Field oldField = newTile.fields.get(field.layer); if (oldField == null || !oldField.fieldName.equals( field.fieldName )) { Sprite sprite = field.getSpriteGroup().sprite; if (sprite != null) { sprite.spriteAnimation = new StretchAnimation( spawnSpeed, null, 0, StretchAnimation.StretchEquation.EXPAND ); } } newTile.addField( field ); } } } } public static AbstractFieldInteractionType getInteraction( HashMap<String, AbstractFieldInteractionType> interactions, Field field ) { if ( interactions.containsKey( field.fieldName ) ) { return interactions.get( field.fieldName ); } for ( String tag : field.tags ) { if ( interactions.containsKey( tag ) ) { return interactions.get( tag ); } } return null; } public Object getData( String key, Object fallback ) { if ( data.containsKey( key ) ) { return data.get( key ); } return fallback; } public void setData( String key, Object val ) { data.put( key, val ); } public Field copy() { Field field = new Field(); field.fileName = fileName; field.fieldName = fieldName; field.tags = tags; for (SpriteGroup group : groups) { field.groups.add( group.copy() ); } field.layer = layer; field.stacks = stacks; field.durationStyle = durationStyle; field.spreadStyle = spreadStyle; field.onTurnEffects = onTurnEffects; field.fieldInteractions = fieldInteractions; field.onDeathEffects = onDeathEffects; field.allowPassability = allowPassability; field.restrictPassability = restrictPassability; return field; } // ---------------------------------------------------------------------- protected void internalLoad( String fileName ) { XmlReader xmlReader = new XmlReader(); Element xml = null; try { xml = xmlReader.parse( Gdx.files.internal( "Fields/" + fileName + ".xml" ) ); } catch ( IOException e ) { e.printStackTrace(); } String extendsElement = xml.getAttribute( "Extends", null ); if ( extendsElement != null ) { String[] split = extendsElement.split( "," ); for ( String file : split ) { internalLoad( file ); } } String tagsElement = xml.get( "Tags", null ); if ( tagsElement != null ) { Array<String> tagArr = new Array<String>(); if ( tags != null ) { tagArr.addAll( tags ); } String[] split = tagsElement.toLowerCase().split( "," ); tagArr.addAll( split ); tags = tagArr.toArray( String.class ); } fieldName = xml.get( "FieldName", fieldName ); Element spriteElement = xml.getChildByName( "Sprite" ); Element tilingSpriteElement = xml.getChildByName( "TilingSprite" ); if (spriteElement != null || tilingSpriteElement != null) { SpriteGroup group = new SpriteGroup(); group.parse( xml ); groups.add( group ); } Element groupsElement = xml.getChildByName( "Groups" ); if (groupsElement != null) { for (int i = 0; i < groupsElement.getChildCount(); i++) { SpriteGroup group = new SpriteGroup(); group.parse( groupsElement.getChild( i ) ); groups.add( group ); } } groups.sort( new Comparator<SpriteGroup>() { @Override public int compare( SpriteGroup o1, SpriteGroup o2 ) { return o1.stacks - o2.stacks; } } ); layer = xml.get( "Layer", null ) != null ? FieldLayer.valueOf( xml.get( "Layer" ).toUpperCase() ) : layer; Element durationElement = xml.getChildByName( "Duration" ); if ( durationElement != null ) { durationStyle = AbstractDurationStyle.load( durationElement.getChild( 0 ) ); } Element spreadElement = xml.getChildByName( "Spread" ); if ( spreadElement != null ) { spreadStyle = AbstractSpreadStyle.load( spreadElement.getChild( 0 ) ); } Element onTurnElement = xml.getChildByName( "OnTurn" ); if ( onTurnElement != null ) { for ( int i = 0; i < onTurnElement.getChildCount(); i++ ) { Element effectElement = onTurnElement.getChild( i ); AbstractOnTurnEffect effect = AbstractOnTurnEffect.load( effectElement ); onTurnEffects.add( effect ); } } Element onDeathElement = xml.getChildByName( "OnDeath" ); if ( onDeathElement != null ) { for ( int i = 0; i < onDeathElement.getChildCount(); i++ ) { Element deathElement = onDeathElement.getChild( i ); AbstractOnDeathEffect death = AbstractOnDeathEffect.load( deathElement ); onDeathEffects.add( death ); } } Element interactionsElement = xml.getChildByName( "Interactions" ); if ( interactionsElement != null ) { for ( int i = 0; i < interactionsElement.getChildCount(); i++ ) { Element interactionEl = interactionsElement.getChild( i ); String key = interactionEl.getName(); Element content = interactionEl.getChild( 0 ); fieldInteractions.put( key.toLowerCase(), AbstractFieldInteractionType.load( content ) ); } } String allowString = xml.get( "Allow", null ); if ( allowString != null ) { EnumBitflag<Passability> pass = Passability.parse( allowString ); allowPassability.setAll( pass ); } String restrictString = xml.get( "Restrict", null ); if ( restrictString != null ) { EnumBitflag<Passability> pass = Passability.parse( restrictString ); restrictPassability.setAll( pass ); } } // ---------------------------------------------------------------------- public static Field load( String name ) { Field f = new Field(); f.fileName = name; f.internalLoad( name ); return f; } @Override public String getName() { return fieldName; } @Override public String getDescription() { return ""; } @Override public Sprite getIcon() { if (groups.get( 0 ).sprite != null) { return groups.get( 0 ).sprite; } return groups.get( 0 ).tilingSprite.getSprite( new EnumBitflag<Global.Direction>( ) ); } public static class SpriteGroup { public Sprite sprite; public TilingSprite tilingSprite; public Light light; public int stacks; public SpriteGroup() { } public void parse(Element xml) { stacks = xml.getIntAttribute( "Stacks", 0 ); Element spriteElement = xml.getChildByName( "Sprite" ); if (spriteElement != null) { sprite = AssetManager.loadSprite( spriteElement ); } Element tilingSpriteElement = xml.getChildByName( "TilingSprite" ); if (tilingSpriteElement != null) { tilingSprite = TilingSprite.load( tilingSpriteElement ); } Element lightElement = xml.getChildByName( "Light" ); if (lightElement != null) { light = Light.load( lightElement ); } } public SpriteGroup copy() { SpriteGroup group = new SpriteGroup(); group.sprite = sprite != null ? sprite.copy() : null; group.tilingSprite = tilingSprite != null ? tilingSprite.copy() : null; group.light = light != null ? light.copy() : null ; group.stacks = stacks; return group; } } }