package Roguelike.Save; import Roguelike.Ability.AbilityTree; import Roguelike.Ability.ActiveAbility.ActiveAbility; import Roguelike.AssetManager; import Roguelike.DungeonGeneration.DungeonFileParser.DFPRoom; import Roguelike.DungeonGeneration.Room; import Roguelike.DungeonGeneration.Room.RoomDoor; import Roguelike.DungeonGeneration.Symbol; import Roguelike.Entity.ActivationAction.*; import Roguelike.GameEvent.AdditionalSprite; import Roguelike.GameEvent.Constant.ConstantEvent; import Roguelike.GameEvent.Damage.*; import Roguelike.GameEvent.OnDeath.AbstractOnDeathEvent; import Roguelike.GameEvent.OnDeath.FieldOnDeathEvent; import Roguelike.GameEvent.OnDeath.HealOnDeathEvent; import Roguelike.GameEvent.OnExpire.AbilityOnExpireEvent; import Roguelike.GameEvent.OnExpire.KillOnExpireEvent; import Roguelike.GameEvent.OnHit.AbilityOnHitEvent; import Roguelike.GameEvent.OnTask.CancelTaskEvent; import Roguelike.GameEvent.OnTask.CostTaskEvent; import Roguelike.GameEvent.OnTask.DamageTaskEvent; import Roguelike.GameEvent.OnTask.StatusTaskEvent; import Roguelike.GameEvent.OnTurn.DamageOverTimeEvent; import Roguelike.GameEvent.OnTurn.HealOverTimeEvent; import Roguelike.Global; import Roguelike.Global.Direction; import Roguelike.Global.Statistic; import Roguelike.Items.Inventory; import Roguelike.Items.Item; import Roguelike.Items.Item.EquipmentSlot; import Roguelike.Items.Item.ItemCategory; import Roguelike.Levels.LevelManager; import Roguelike.Lights.Light; import Roguelike.Pathfinding.ShadowCastCache; import Roguelike.Quests.Input.QuestInputFlagEquals; import Roguelike.Quests.Input.QuestInputFlagPresent; import Roguelike.Quests.Output.QuestOuputConditionDialogueValue; import Roguelike.Quests.Output.QuestOutput; import Roguelike.Quests.Output.QuestOutputConditionActionEnabled; import Roguelike.Quests.Output.QuestOutputConditionEntityAlive; import Roguelike.Quests.Quest; import Roguelike.Save.SaveLevel.SaveLevelItem; import Roguelike.Save.SaveLevel.SaveOrb; import Roguelike.Sound.SoundInstance; import Roguelike.Sprite.Sprite; import Roguelike.Sprite.Sprite.AnimationMode; import Roguelike.Sprite.Sprite.AnimationState; import Roguelike.Sprite.SpriteAnimation.AbstractSpriteAnimation; import Roguelike.Sprite.SpriteAnimation.BumpAnimation; import Roguelike.Sprite.SpriteAnimation.MoveAnimation; import Roguelike.Sprite.SpriteAnimation.StretchAnimation; import Roguelike.Sprite.TilingSprite; import Roguelike.StatusEffect.StatusEffect; import Roguelike.Tiles.GameTile; import Roguelike.Tiles.Point; import Roguelike.Util.EnumBitflag; import Roguelike.Util.FastEnumMap; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.utils.*; import com.badlogic.gdx.utils.XmlReader.Element; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import kryo.FastEnumMapSerializer; import java.io.IOException; import java.util.HashMap; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; public final class SaveFile { private static Kryo kryo; public LevelManager levelManager; public ObjectSet<String> usedQuests; public ObjectMap<String, String> worldFlags; public ObjectMap<String, String> worldFlagsCopy; public ObjectMap<String, String> runFlags; public ObjectMap<String, String> deferredFlags; public boolean isDead; public int lives; public void save() { setupKryo(); FileHandle attemptFile = Gdx.files.local( "attempt_save.dat" ); Output output = null; try { output = new Output( new GZIPOutputStream( attemptFile.write( false ) ) ); } catch ( IOException e ) { e.printStackTrace(); } kryo.writeObject( output, levelManager ); kryo.writeObject( output, usedQuests ); kryo.writeObject( output, worldFlags ); kryo.writeObject( output, worldFlagsCopy ); kryo.writeObject( output, runFlags ); kryo.writeObject( output, deferredFlags ); output.writeBoolean( isDead ); output.writeInt( lives ); output.close(); byte[] bytes = attemptFile.readBytes(); FileHandle actualFile = Gdx.files.local( "save.dat" ); actualFile.writeBytes( bytes, false ); attemptFile.delete(); System.out.println( "Saved" ); } public void load() { setupKryo(); Input input = null; try { input = new Input( new GZIPInputStream( Gdx.files.local( "save.dat" ).read() ) ); } catch ( IOException e ) { e.printStackTrace(); } levelManager = kryo.readObject( input, LevelManager.class ); usedQuests = kryo.readObject( input, ObjectSet.class ); worldFlags = kryo.readObject( input, ObjectMap.class ); worldFlagsCopy = kryo.readObject( input, ObjectMap.class ); runFlags = kryo.readObject( input, ObjectMap.class ); deferredFlags = kryo.readObject( input, ObjectMap.class ); isDead = input.readBoolean(); lives = input.readInt(); input.close(); } private void setupKryo() { if ( kryo == null ) { kryo = new Kryo(); kryo.setRegistrationRequired( true ); kryo.setAsmEnabled( true ); registerSerializers( kryo ); registerClasses( kryo ); } } private void registerSerializers( Kryo kryo ) { kryo.register( FastEnumMap.class, new FastEnumMapSerializer() ); kryo.register( Array.class, new Serializer<Array>() { private Class genericType; { setAcceptsNull( true ); } @Override public void write( Kryo kryo, Output output, Array array ) { int length = array.size; output.writeInt( length, true ); if ( length == 0 ) { genericType = null; return; } if ( genericType != null ) { Serializer serializer = kryo.getSerializer( genericType ); genericType = null; for ( Object element : array ) { kryo.writeObjectOrNull( output, element, serializer ); } } else { for ( Object element : array ) { kryo.writeClassAndObject( output, element ); } } } @Override public void setGenerics( Kryo kryo, Class[] generics ) { if ( kryo.isFinal( generics[ 0 ] ) ) { genericType = generics[ 0 ]; } } @Override public Array read( Kryo kryo, Input input, Class<Array> type ) { Array array = new Array(); kryo.reference( array ); int length = input.readInt( true ); array.ensureCapacity( length ); if ( genericType != null ) { Class elementClass = genericType; Serializer serializer = kryo.getSerializer( genericType ); genericType = null; for ( int i = 0; i < length; i++ ) { array.add( kryo.readObjectOrNull( input, elementClass, serializer ) ); } } else { for ( int i = 0; i < length; i++ ) { array.add( kryo.readClassAndObject( input ) ); } } return array; } } ); kryo.register( Color.class, new Serializer<Color>() { @Override public Color read( Kryo kryo, Input input, Class<Color> type ) { Color color = new Color(); Color.rgba8888ToColor( color, input.readInt() ); return color; } @Override public void write( Kryo kryo, Output output, Color color ) { output.writeInt( Color.rgba8888( color ) ); } } ); kryo.register( Sprite.class, new Serializer<Sprite>() { @Override public Sprite read( Kryo kryo, Input input, Class<Sprite> type ) { String fileName = input.readString(); float animDelay = input.readFloat(); float repeatDelay = input.readFloat(); Color color = kryo.readObject( input, Color.class ); int modeVal = input.readInt(); AnimationMode mode = AnimationMode.values()[ modeVal ]; float[] scale = input.readFloats( 2 ); boolean drawActualSize = input.readBoolean(); SoundInstance sound = kryo.readObjectOrNull( input, SoundInstance.class ); AbstractSpriteAnimation anim = (AbstractSpriteAnimation)kryo.readClassAndObject( input ); Sprite sprite = AssetManager.loadSprite( fileName, animDelay, color, mode, sound, drawActualSize ); sprite.spriteAnimation = anim; sprite.baseScale = scale; sprite.repeatDelay = repeatDelay; return sprite; } @Override public void write( Kryo kryo, Output output, Sprite sprite ) { output.writeString( sprite.fileName ); output.writeFloat( sprite.animationDelay ); output.writeFloat( sprite.repeatDelay ); kryo.writeObject( output, sprite.colour ); output.writeInt( sprite.animationState.mode.ordinal() ); output.writeFloats( sprite.baseScale ); output.writeBoolean( sprite.drawActualSize ); kryo.writeObjectOrNull( output, sprite.sound, SoundInstance.class ); kryo.writeClassAndObject( output, sprite.spriteAnimation ); } } ); kryo.register( SoundInstance.class, new Serializer<SoundInstance>() { @Override public SoundInstance read( Kryo kryo, Input input, Class<SoundInstance> type ) { SoundInstance sound = new SoundInstance( ); sound.name = input.readString(); sound.sound = AssetManager.loadSound( sound.name ); sound.minPitch = input.readFloat(); sound.maxPitch = input.readFloat(); sound.volume = input.readFloat(); sound.range = input.readInt(); sound.falloffMin = input.readInt(); return sound; } @Override public void write( Kryo kryo, Output output, SoundInstance sound ) { output.writeString( sound.name ); output.writeFloat( sound.minPitch ); output.writeFloat( sound.maxPitch ); output.writeFloat( sound.volume ); output.writeInt( sound.range ); output.writeInt( sound.falloffMin ); } } ); kryo.register( AbilityTree.class, new Serializer<AbilityTree> () { @Override public void write( Kryo kryo, Output output, AbilityTree object ) { SaveAbilityTree saveTree = new SaveAbilityTree(); saveTree.store( object ); kryo.writeObject( output, saveTree ); } @Override public AbilityTree read( Kryo kryo, Input input, Class<AbilityTree> type ) { SaveAbilityTree saveTree = kryo.readObject( input, SaveAbilityTree.class ); return saveTree.create(); } } ); kryo.register( ActiveAbility.class, new Serializer<ActiveAbility>() { @Override public void write( Kryo kryo, Output output, ActiveAbility object ) { output.writeString( object.creationPath ); kryo.writeObjectOrNull( output, object.creationData, Element.class); } @Override public ActiveAbility read( Kryo kryo, Input input, Class<ActiveAbility> type ) { String creationPath = input.readString(); Element creationData = kryo.readObjectOrNull( input, Element.class ); ActiveAbility ab; if (creationPath != null) { ab = ActiveAbility.load( creationPath ); } else { ab = ActiveAbility.load( creationData ); } return ab; } } ); kryo.register( Element.class, new Serializer<Element>() { @Override public Element read( Kryo kryo, Input input, Class<Element> type ) { String xml = input.readString(); XmlReader reader = new XmlReader(); Element element = reader.parse( xml ); return element; } @Override public void write( Kryo kryo, Output output, Element element ) { output.writeString( element.toString() ); } } ); kryo.register( ObjectMap.class, new Serializer<ObjectMap>() { @Override public void write( Kryo kryo, Output output, ObjectMap object ) { Array<Object[]> data = new Array<Object[]>( ); for (Object key : object.keys()) { Object value = object.get( key ); data.add( new Object[]{key, value} ); } kryo.writeObject( output, data ); } @Override public ObjectMap read( Kryo kryo, Input input, Class<ObjectMap> type ) { Array<Object[]> data = kryo.readObject( input, Array.class ); ObjectMap map = new ObjectMap( ); for (Object[] pair : data) { map.put( pair[0], pair[1] ); } return map; } } ); kryo.register( IntMap.class, new Serializer<IntMap>() { @Override public void write( Kryo kryo, Output output, IntMap object ) { Array<Object[]> data = new Array<Object[]>( ); for (Object entryObj : object.entries()) { IntMap.Entry entry = (IntMap.Entry)entryObj; data.add( new Object[]{entry.key, entry.value} ); } kryo.writeObject( output, data ); } @Override public IntMap read( Kryo kryo, Input input, Class<IntMap> type ) { Array<Object[]> data = kryo.readObject( input, Array.class ); IntMap map = new IntMap( ); for (Object[] pair : data) { map.put( (Integer)pair[0], pair[1] ); } return map; } } ); } private void registerClasses( Kryo kryo ) { kryo.register( SaveEnvironmentEntity.class ); kryo.register( SaveField.class ); kryo.register( SaveFile.class ); kryo.register( SaveGameEntity.class ); kryo.register( SaveLevel.class ); kryo.register( SaveAbilityTree.class ); kryo.register( SaveAbilityTree.SaveAbilityStage.class ); kryo.register( SaveOrb.class ); kryo.register( SaveLevelItem.class ); kryo.register( Point.class ); kryo.register( StatusEffect.class ); kryo.register( StatusEffect.DurationType.class ); kryo.register( Inventory.class ); kryo.register( Element.class ); kryo.register( DFPRoom.class ); kryo.register( Item.class ); kryo.register( Item.WeaponDefinition.class ); kryo.register( Item.SpriteGroup.class ); kryo.register( Light.class ); kryo.register( ShadowCastCache.class ); kryo.register( EnumBitflag.class ); kryo.register( Symbol.class ); kryo.register( Symbol[].class ); kryo.register( Symbol[][].class ); kryo.register( Room.class ); kryo.register( RoomDoor.class ); kryo.register( DFPRoom.Placement.class ); kryo.register( AnimationState.class ); kryo.register( AnimationMode.class ); kryo.register( LevelManager.class ); kryo.register( LevelManager.LevelData.class ); kryo.register( LevelManager.BranchData.class ); kryo.register( TilingSprite.class ); kryo.register( HashMap.class ); kryo.register( String[].class ); kryo.register( int[].class ); kryo.register( Object.class ); kryo.register( Object[].class ); kryo.register( char[].class ); kryo.register( char[][].class ); kryo.register( float[].class ); kryo.register( Float[].class ); kryo.register( Float[][].class ); kryo.register( boolean[].class ); kryo.register( boolean[][].class ); kryo.register( ObjectSet.class ); kryo.register( BumpAnimation.class ); kryo.register( MoveAnimation.class ); kryo.register( StretchAnimation.class ); kryo.register( MoveAnimation.MoveEquation.class ); kryo.register( StretchAnimation.StretchEquation.class ); kryo.register( EquipmentSlot.class ); kryo.register( ItemCategory.class ); kryo.register( Statistic.class ); kryo.register( Direction.class ); kryo.register( Global.Rarity.class ); kryo.register( GameTile.OrbType.class ); kryo.register( Item.WeaponDefinition.HitType.class ); kryo.register( ActiveAbility.CooldownType.class ); kryo.register( ConstantEvent.class ); kryo.register( DamageEvent.class ); kryo.register( FieldEvent.class ); kryo.register( HealEvent.class ); kryo.register( StatusEvent.class ); kryo.register( BlockEvent.class ); kryo.register( FieldOnDeathEvent.class ); kryo.register( HealOnDeathEvent.class ); kryo.register( AbstractOnDeathEvent.class ); kryo.register( CancelTaskEvent.class ); kryo.register( CostTaskEvent.class ); kryo.register( DamageTaskEvent.class ); kryo.register( StatusTaskEvent.class ); kryo.register( DamageOverTimeEvent.class ); kryo.register( HealOverTimeEvent.class ); kryo.register( KillOnExpireEvent.class ); kryo.register( AbilityOnExpireEvent.class ); kryo.register( AdditionalSprite.class ); kryo.register( AbilityOnHitEvent.class ); kryo.register( Quest.class ); kryo.register( QuestInputFlagPresent.class ); kryo.register( QuestInputFlagEquals.class ); kryo.register( QuestOutput.class ); kryo.register( QuestOutputConditionEntityAlive.class ); kryo.register( QuestOuputConditionDialogueValue.class ); kryo.register( QuestOutputConditionActionEnabled.class ); kryo.register( ActivationActionGroup.class ); kryo.register( ActivationActionAbility.class ); kryo.register( ActivationActionAddItem.class ); kryo.register( ActivationActionChangeLevel.class ); kryo.register( ActivationActionSetEnabled.class ); kryo.register( ActivationActionSetPassable.class ); kryo.register( ActivationActionSetSprite.class ); kryo.register( ActivationActionSpawnEntity.class ); kryo.register( ActivationActionActivate.class ); kryo.register( ActivationActionSpawnField.class ); kryo.register( ActivationActionKillThis.class ); kryo.register( ActivationActionRemoveItem.class ); kryo.register( ActivationActionAddStatus.class ); kryo.register( ActivationActionDealDamage.class ); kryo.register( ActivationConditionProximity.class ); kryo.register( ActivationConditionHasItem.class ); } }