Skip to content

Save Format

Joan Andrés edited this page Jun 7, 2019 · 1 revision

Hale uses an entirely text-based save format which consists of a short header followed by JSON data. The entire file is gzipped to cut down on file size and to improve read and write speed. The save folder location depends on your platform.

On Windows - C:\Users\<USERNAME>\Documents\My Games\hale\saves
On Linux - /home/<USERNAME>/.local/share/hale/saves

Format Basics

All hale save files are stored on disk with the .json.gz file extension. This indicates that the file consists of JSON data that has been compressed via gzip. In order to work with a save file, you must first unzip the contents. Then, you can open up the file in a good text editor (one that can handle many thousands of lines without trouble). Before you can open the file again in the game, you will need to compress it again and create a new .json.gz file with your modified contents.

The Header

Each save file has a short header at the beginning of the file. This is used by the game to quickly preview the contents of a save file. Rather than JSON, this data is formatted in a very simple line delimited way. Each line is parsed as a separate key. An example is as follows:

HEADER BUILDID 0 Large Cavern Level 7 Anech Griman Arana Ducky END HEADER

The "Build ID" is an internal string used to check for save compatibility. The next line is the currently loaded area. Then follows the current level of the player character party and the names of each of the party members.

Concepts

After the header, the remainder of the file consists of JSON data. JSON is a very simple data-interchange format designed to be parseable as JavaScript. You can read more about it here.

Essentially, JSON consists of a set of keys, each one associated with a value. For, example, the first few lines of JSON will look something like this:

"id" : "Pale Pass",
"currentDifficulty" : "Normal",
"date" : 2,
"partyCurrency" : 500,
"currentArea" : "net.sf.hale.Area@739abd2b",

This is indicating the that the ID of the current campaign is "Pale Pass". The difficulty is set to "Normal", the current in game date is 2 rounds after the starting time, and the party has 2000 copper pieces (20 gold pieces of currency).

Using JSON, data can easily be nested to many layers of depth. This is done by surrounding a value with brackets {}. In the save file, data is also indented by a number of spaces to help indicate the nesting level. This is shown in many of the examples below.

Resource IDs

The first entry in many of the objects defined in the save file will be "id". This is indicating the resource on disk that is used as the base for the object. Typically, you look for a file called "id" with a .txt extension, in a directory dependent on the resource type. Note that all resources can potentially be present in both the "core" package ("core/" directory or "core.zip") or the "campaign" package ("campaigns/Pale Pass 2/" or "campaigns/Pale Pass 2.zip" in the above example). Campaign resources override core resources and take precedence. See [Modding] for details.

References

The save file uses a simple system of reference tracking for objects that need to be mentioned at different places throughout the file. This is shown with the "ref" key. Each asset is defined only once in the save file, and then additional uses of it simply reference the corresponding ref. So, in the above example, one can search for the ref "net.sf.hale.Area@739abd2b" and find the other places in the save file where this area is used. Conversely, if you see a string of characters starting with "net.sf.hale" ending with an 8 digit code, you know that it is a reference. You can then search for that string and find the location in the file where the reference is defined.


The JSON Data

After the header, each save file consists of a single JSON object, enclosed in { and }. This object represents the complete save game state, and has many subsections. Depending on what the player has done within a game, certain sections will not be shown in every save file.

Basic Game Data

The first section includes basic information like the campaign ID, the current difficulty setting, and the date. It also includes a reference to the current area, which is the area that will be shown when the player loads this save game file. The area's data can be found by searching for the reference in the file.

Party

The Party section is a short section defining the player character party. Here is an example:

"party" : {
  "name" : "The War Hounds",
  "selectedCharacter" : 0,
  "characters" : [
    "net.sf.hale.entity.Creature@3fdb484d", "net.sf.hale.entity.Creature@5567d7fb", "net.sf.hale.entity.Creature@1494cb8b", "net.sf.hale.entity.Creature@34bf1d3b"
  ]
}

Here, we can see the party is named "The War Hounds". The selected character is the one at index 0, or the first entry in the list of characters. The characters themselves are not defined at this point. Instead, the characters array lists only references. In order to see the character definitions, you can easily search for the reference within the save game file.

Loaded Areas

The next section is the largest, and consists of the detailed information about all areas that have been visited by the player's party. Each area is its own object, surrounded by brackets. The area definition is broken down into a few simple subsections. First, there is a reference and a name. The name tells you where you will find the area definition on disk. In this case, the area can be found in "campaigns/Pale Pass/areas/The Willow Inn.txt". Areas are always located in the "areas" directory of the campaign data.

"loadedAreas" : [
  {
    "ref" : "net.sf.hale.Area@695cd9c0",
    "name" : "The Willow Inn",

Explored

The explored section often consists of many entries, but it is simply x,y coordinates of all the tiles within the area that have been sighted by the player. For example:

  "explored" : [
    "1,2", "1,3", "1,4", "1,5", "2,2", "2,3", "2,4", "2,5", "2,6", "3,2", 
    "3,3", "3,4", "3,5", "3,6", "4,2", "4,3", "4,4", "4,5", "4,6", "4,7", 
    "5,1", "5,2", "5,3", "5,4", "5,5", "5,6", "5,7", "6,1", "6,2", "6,3", 
  ]

Encounters

Encounters are groups of creatures within an area that are not part of the player's party. Encounters can be friendly or hostile. Each encounter has a name, which tells you the base resource filename in the "encounters/" directory, x and y coordinates, the last time it spawned creatures, the faction (whether it is hostile or friendly) and a list of creatures. Like in the player's party, the list of creatures is done by reference. All creatures within a given area will be defined later, in the "entities" section. Here is an example encounter:

    {
      "name" : "willowInn",
      "x" : 9,
      "y" : 4,
      "lastSpawnTime" : 3,
      "faction" : "Neutral",
      "creatures" : [
        "net.sf.hale.entity.Creature@239c7c21", "net.sf.hale.entity.Creature@76996cca"
      ]
    }

Effects

The effects section shows detailed information about any spells that are currently affecting the area, rather than a specific creature. This section will also include very detailed information about the animation state of the effect. If there are no effects currently in the area (which is usually the case unless your party was just in combat) this section will not be present. Due to the complexity, it is usually easiest to ignore this section.

Entities

The entities section defines the most interesting part of the world: the creatures (friendly and hostile) and the items within a given area. Many references from different sections, such as the player party and encounters will point here.

Each entity has a list of basic information. First, the id, type, and reference are shown. The base definitions for entities of type "Creature" are found in the "creatures/" directory, while the definitions for entities of type "Item", "Container", and "Door" are found in the "items/" directory.

Then, a list of basic information is added. For player characters, this will include all of the entity information such as race, gender, and attributes. For most other creatures, it will instead pull that information from the base resource in the "creatures/" or "items/" directory, and only include differences from that base asset.

Each creature entity will also have additional subsections, including inventory, skills, roles, abilities, and quickbar for player characters.

Here is an example of the data definition for a hostile creature:

    {
        "id" : "goblin_spearman",
        "type" : "CREATURE",
        "ref" : "net.sf.hale.entity.Creature@5c28305d",
        "xPosition" : 13,
        "yPosition" : 24,
        "xLastPosition" : 0,
        "yLastPosition" : 1,
        "effects" : {
        },
        "xp" : 1500,
        "currentHP" : 34,
        "unspentSkillPoints" : 0,
        "inventory" : {
          "equippedSlot0" : {
            "id" : "shortspear",
            "type" : "ITEM",
            "ref" : "net.sf.hale.entity.Item@1acaf0ed",
            "itemQuality" : "Good"
          },
          "equippedSlot2" : {
            "id" : "armor_leather_base",
            "type" : "ITEM",
            "ref" : "net.sf.hale.entity.Item@18f6559",
            "itemQuality" : "Good"
          },
          "equippedSlot3" : {
            "id" : "gloves_leather_base",
            "type" : "ITEM",
            "ref" : "net.sf.hale.entity.Item@6d46b6db",
            "itemQuality" : "Good"
          },
          "equippedSlot6" : {
            "id" : "boots_leather_base",
            "type" : "ITEM",
            "ref" : "net.sf.hale.entity.Item@3d8f1be9",
            "itemQuality" : "Good"
          }
        },
        "skillSet" : {
          "Barter" : 5
        },
        "roleSet" : {
          "baseRoleID" : "Warrior",
          "Warrior" : 5
        },
        "abilitySet" : {
         "abilities" : [
           {
             "abilityID" : "ShieldDefense",
             "levelObtained" : 1
           }
         ]
       }
      }

This shows that the creature is based off of the resource located in "creatures/goblin_spearman.txt". It shows the creatures position and a few other key statistics. The creature does not currently have any spell effects applied to it.

Inventory

In the inventory section, starting here:

      "inventory" : {
          "equippedSlot0" : {
            "id" : "shortspear",
            "type" : "ITEM",
            "ref" : "net.sf.hale.entity.Item@1acaf0ed",
            "itemQuality" : "Good"
          }

We can see that in the creature's first equipment slow (the main hand slot), they have a shortspear equipped. This item is defined in detail in the "items/shortspear.txt" file.

Skills

The skills section shows a list of the skills and associated skill level for the creature. Here, we have:

        "skillSet" : {
          "Barter" : 5
        }

This indicates that the creature has five ranks in the barter skill.

Roles

The creature's roles are listed:

        "roleSet" : {
          "baseRoleID" : "Warrior",
          "Warrior" : 5
        }

This shows that the creature's base Role is "Warrior", and they have 5 levels in that role.

Abilities

Many creatures will have one or more abilities defined:

        "abilitySet" : {
          "abilities" : [
            {
              "abilityID" : "ShieldDefense",
              "levelObtained" : 1
            }
          ]
        }

In this example, the creature has one ability, "ShieldDefense". This ability will be defined as the file "ShieldDefense.json", which is located in the core/abilities/ folder. In this specific case, at "core/abilities/Shield/ShieldDefense.json".

Quickbar

Player characters will have additional sections. The quickbar section contains the set of icons at the bottom of the screen when playing. For example, here is a simple quickbar:

        "quickbar" : {
          "lastViewSet" : 0,
          "slots" : {
            "slot0" : {
              "type" : "use",
              "itemID" : "potionHealing",
              "itemQuality" : "Mediocre"
            },
            "slot1" : {
              "type" : "use",
              "itemID" : "potionStrength",
              "itemQuality" : "Mediocre"
            }
          }

The quickbar can potentially consist of many slots. Each slot has a "type", which is either "use", "equip", or "ability". This indicates that the slot is to use an item, equip an item, or use an ability, respectively. Then, the quickbar shows specific information for each type. In this case, the two slots above show that "slot0" is to use an item with ID "potionHealing" (found in "items/potionHealing.json) of quality "Mediocre", and "slot1" is to use an item of ID "potionStrength" and quality "Mediocre".

Triggers

After the areas section, the data file contains information on all triggers that have been loaded. Triggers are used to run scripts in response to certain player actions. This can include entered an area or a specific location. A trigger looks like this:

{
  "id" : "willowInnInside",
  "areaLoaded" : true
}

This tells us that the trigger definition is found in the "triggers/willowInnInside.json" file, and that the area associated with this trigger has been loaded.

Transitions

The transitions sections is short and similar to the triggers. Transitions are the "travel" icons you see throughout the world which link areas together and to the world map.

Here is an example transition:

{
  "name" : "BlackRiverCrossingToWorldMap2",
  "activated" : true
}

This transition can be found in "transitions/BlackRiverCrossingToWorldMap2.json".

Merchants

The merchants section defines the data associated with all of the merchants the player has interacted with. This includes the id (merchants are found in the "merchants/" directory), the last time that the merchant respawned its inventory, and the current list of items held by the merchant.

For example:

{
  "id" : "fareach_Blacksmith",
  "lastRespawnRound" : 2412,
  "currentItems" : {
    {
      "itemID" : "armor_leather_base",
      "quality" : "Decent",
      "quantity" : 1
    }, {
      "itemID" : "armor_scale_base",
      "quality" : "Decent",
      "quantity" : 1
    }
...

This tells us that this merchant is based on the file "merchants/fareach_Blacksmith.txt". It last spawned goods in round 2412, and currently contains items including leather armor and scale armor. The itemID fields of each of the item entries indicates where the item data can be found, for example "items/armor_leather_base.json" for the first entry.

Quest Entries

The quest entries section shows all of the data about the quests the player has currently been assigned or completed. It is broken into two sections, the "activeEntries" and the "completedEntries". Each entry is further divided into one or more subentries. For example:

  {
    "title" : "A Strange Dream",
    "completed" : false,
    "showLogNotifications" : true,
    "subEntries" : [
      {
        "title" : "The dream",
        "showTitle" : true,
        "completed" : false,
        "description" : "You had a strange dream while sleeping at the Willow Inn.  You saw a man shrouded in black, and a strange glowing sword.  You don't know what it all means.  In any event, you should continue on the road to Fareach.  "
      }
    ]
  }

This shows us that the player currently has an entry titled "A Strange Dream", that is not yet completed. It has one sub entry, which has also not yet been completed.

World Map Locations

The world map locations sections contains one entry for each location that the player can currently see on their world map. This data is defined in the "campaign.txt" file within the campaign folder. An example:

{
  "name" : "Fareach",
  "revealed" : true
}

Script State

Finally, at the bottom of the file is the "script state". This consists of variables stored by scripts within the game to indicate that the player has taken certain actions, or that certain actions need to be taken. For example:

"scriptState" : {
  "sleptWillowInn" : true
}

This is an entry used by some of the quest scripts to indicate that the player has already taken the first step in the campaign, of sleeping in the Willow Inn.

Example Modifications

Due to the fact that the save file is in a simple text format, it is quite easy to make simple modifications. Here are some examples:

Add Money to the Party

Search for the key "partyCurrency" in the file. Change the value it is pointing to to whatever you like. For example

"partyCurrency" : 1000000,

The value is in copper pieces times 100, so 1000000 corresponds to 10 PP in the game.

Improve a Party Member

Find the "party" key near the start of the file. In it, there is a list of "characters", each one a reference. Search for any one of those references within the file, and you will find where the corresponding party member is defined. For example, you might see the following:

"party" : {
  "name" : "The War Hounds",
  "selectedCharacter" : 0,
  "characters" : [
    "net.sf.hale.entity.Creature@73aecc3a", "net.sf.hale.entity.Creature@58e22f2b", "net.sf.hale.entity.Creature@6986dda3", "net.sf.hale.entity.Creature@15412e75"
  ]
},

Then, if you search for "net.sf.hale.entity.Creature@73aecc3a", you will find the definition of your first party member. You can easily modify many aspects of this character:

  • Attributes : search for "strength", "dexterity", "constitution", "intelligence", "wisdom", or "charisma" and increase your party members base attributes.

  • Experience Points : search for "xp" and give your party member additional XP, which will allow you to level up when you load the save file

  • Inventory: search for "inventory" and then "equippedSlot0" to find your character's main weapon. You can easily change the "itemQuality" to a better value, such as "Exceptional", "Phenomenal", or "Masterwork". The list of valid item qualities is found in "core/itemQualities.json".

Reset an Area

If you have problems or wish to replay an area, you can generally reset it by removing its entry entirely from the save file. Note, however, that some things dealing with the area are stored elsewhere, particularly in the "scriptState" section of the file, but also in "merchants", "triggers", or "transitions".

First, you must make certain that your party is not within the area you wish to remove. Otherwise, you would delete your party!

Then, find the area you wish to remove by searching for its name. The brackets surrounding an area will be located 2 indentation levels (4 spaces) deep:

{
  "ref" : "net.sf.hale.Area@39518cc",
  "name" : "Large Cavern",
...

You need to delete everything starting from the opening brace above, all the way down to the corresponding closing brace. Be sure not to delete the starting brace for another area when doing this.

Reset a Merchant

Perhaps there is a merchant you are trying to buy from, but you don't like their current inventory? You can easily reset a merchant by removing its entry in the save file, in the same way as you can remove an Area entry. You simply need to find the appropriate merchant in the "merchants" section, towards the end of the file.

If you are not too worried about losing any items stored by any merchants, you can simply remove the entire "merchants" section.