Metadata-Version: 2.4
Name: talespire-encoding
Version: 1.3.0
Summary: Encoding and decoding tools for TaleSpire data
Author: Baldrax
License: MIT
Project-URL: Homepage, https://github.com/Baldrax/TaleSpire-Encoding-Python
Project-URL: Repository, https://github.com/Baldrax/TaleSpire-Encoding-Python
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Provides-Extra: dev
Requires-Dist: pytest==8.3.4; extra == "dev"
Requires-Dist: ruff; extra == "dev"

# TaleSpire Encoding

This is a Python package for encoding and decoding TaleSpire data.

It currently supports the following encoding types:

- Slabs (v1, v2)
- Creature Blueprints (v1, v2, v3)
- Stats Links (v1)

It also contains tools for reading in TaleSpire Assets. This requires a path to the
installation location of TaleSpire on your machine.

The package published on PyPI is `talespire-encoding`. The Python import package
is `ts_encoding`.

## Installation

Install the latest release from PyPI:

```bash
pip install talespire-encoding
```

To upgrade to the latest version:

```bash
pip install --upgrade talespire-encoding
```

### To install a specific version

```bash
pip install talespire-encoding==1.2.5
```

## Usage

The Python import package is `ts_encoding`.

```python
# Core API
from ts_encoding import TSSlab, TSCreature, TSStatsLink, TSAssetLib
```

Most classes expose a mutable dictionary named `data`. A common workflow is:

1. Decode a TaleSpire string or URL into `data`.
2. Inspect or edit values in `data`.
3. Encode the object back into a string or URL that can be pasted into TaleSpire.

## Slabs Example Usage

Slabs are the building data copied from TaleSpire. A slab code is not a normal
URL; it is the compressed text string that TaleSpire places on the clipboard.

```python
from ts_encoding import TSSlab

# This is an example slab string copied from TaleSpire.
example_slab_code = ("H4sIAAAAAAAACjv369xFJgZGBgYGgUWHGX9Pme/S4z7T7pZ"
                     "doRonUGwCSIKhgRFMS0L4DTwMDCcYwOIsDBDADOZLQsRB8g"
                     "xQPgOcDwD7jJ0vaAAAAA==")

# Create a slab object, then decode the copied slab string into slab.data.
slab = TSSlab()
slab.decode_slab(example_slab_code)

# The slab contents are now in a dictionary named `slab.data`.
# pprint is a Python helper that prints dictionaries in a more readable format.
from pprint import pprint
pprint(slab.data)

"""
{'layout_count': 1,
 'layouts': [{'instance_count': 9,
              'instances': [{'degrees': 90.0,
                             'pos_x': 0.0,
                             'pos_y': 0.0,
                             'pos_z': 4.0},
                            {'degrees': 0.0,
                             'pos_x': 4.0,
                             'pos_y': 0.0,
                             'pos_z': 4.0},
                            {'degrees': 0.0,
                             'pos_x': 2.0,
                             'pos_y': 0.0,
                             'pos_z': 4.0},
                            {'degrees': 270.0,
                             'pos_x': 0.0,
                             'pos_y': 0.0,
                             'pos_z': 2.0},
                            {'degrees': 180.0,
                             'pos_x': 0.0,
                             'pos_y': 0.0,
                             'pos_z': 0.0},
                            {'degrees': 0.0,
                             'pos_x': 4.0,
                             'pos_y': 0.0,
                             'pos_z': 2.0},
                            {'degrees': 0.0,
                             'pos_x': 2.0,
                             'pos_y': 0.0,
                             'pos_z': 2.0},
                            {'degrees': 0.0,
                             'pos_x': 4.0,
                             'pos_y': 0.0,
                             'pos_z': 0.0},
                            {'degrees': 0.0,
                             'pos_x': 2.0,
                             'pos_y': 0.0,
                             'pos_z': 0.0}],
              'reserved': 0,
              'uuid': '01c3a210-94fb-449f-8c47-993eda3e7126'}],
 'magic_num': 3520002766,
 'num_creatures': 0,
 'version': 2}
"""

# To encode the data again, call encode_slab().
new_slab_code = slab.encode_slab()

# The new_slab_code can be pasted into TaleSpire.
```

### Creating a Slab From Scratch

To create slab data manually, start a new `TSSlab` and edit the `data`
dictionary. Each UUID is a new layout in the dictionary. It is currently up to
you to format the dictionary properly so that it encodes correctly. In future
versions there may be tools to help build the layouts.

Here is an example of creating a single grass tile:

```python
from ts_encoding import TSSlab

new_slab = TSSlab()

# This slab contains one layout, meaning one type of asset.
new_slab.data["layout_count"] = 1

grass_layout = {
    "instance_count": 1,  # The number of grass tiles in this layout.
    "instances": [{
        "degrees": 0.0,  # Yaw rotation in degrees, 0-360.
        "pos_x": 0.0,    # X position within the slab.
        "pos_y": 0.0,    # Y position within the slab.
        "pos_z": 0.0     # Z position within the slab.
    }],
    "reserved": 0,  # Always set to 0.
    "uuid": "01c3a210-94fb-449f-8c47-993eda3e7126"  # Asset UUID for Grass - Lush.
}

new_slab.data["layouts"].append(grass_layout)
new_slab_code = new_slab.encode_slab()

# You can paste new_slab_code into TaleSpire to see your grass tile.
```

`encode_slab()` checks TaleSpire's 30 kB slab-size limit by default. Pass
`ignore_limit=True` to bypass that check.

## Creature Blueprint Example Usage

Creature blueprints are copied as `talespire://creature-blueprint/...` URLs.

```python
from ts_encoding import TSCreature
from pprint import pprint

url_from_TS = ("talespire://creature-blueprint/"
              "AgAMV2hpdGUgTWVlcGxlAQAAACMAYnI6MDAwMDAwMDAwMDAwMDAwMD"
              "AwMDAwMDAwMDQ3MDQxMDABAAAAAI49u_vNJQRDrZctETEJKR8ABAAA"
              "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQQAAIEEAACBBAAAgQQ"
              "AAIEEAACBBAAAgQQAAIEEAACBBAAAgQQAAIEEAACBBAAAgQQAAIEEA"
              "ACBBAAAgQQAAIEEAACBBAAAA")

# Decode the TaleSpire URL into bp.data.
bp = TSCreature()
bp.decode_url(url_from_TS)
pprint(bp.data)

# You can change any of the data in `bp.data`.
bp.data["name"] = "New Name"

# The first stat is HP, so this changes the creature's HP to 100/100.
bp.data["stats"][0] = {"max": 100.0, "value": 100.0}

# To re-encode the data, call encode_url().
new_url = bp.encode_url()

print(new_url)  # You can copy/paste this new URL into TaleSpire.
```

By default, `encode_url()` preserves the decoded version. You can pass
`force_encode_version=1` or `force_encode_version=2` when you need a specific
schema.

## Stats Link Example Usage

Stat links are copied as `talespire://stat-names/...` URLs. They define the
custom stat names used by a TaleSpire board.

```python
from ts_encoding import TSStatsLink
from pprint import pprint

# Example from the TaleSpire 5e Creature Blueprint Database.
stats_link_url = "talespire://stat-names/AQAIAkFDBVNwZWVkB1NUUiBNb2QHREVYIE1vZAdDT04gTW9kB0lOVCBNb2QHV0lTIE1vZAdDSEEgTW9k"

# Decode the copied URL into stats.data.
stats = TSStatsLink()
stats.decode_url(stats_link_url)
pprint(stats.data)

# To create a stats link from scratch, start with a new TSStatsLink.
new_stats = TSStatsLink()

# Tell TaleSpire how many stat names are in this link.
new_stats.data["num_stats"] = 8

# These are the names that will appear in TaleSpire.
new_stats.data["names"] = [
    "AC", "CMD", "STR", "DEX", "CON", "INT", "WIS", "CHA"
]

new_url = new_stats.encode_url()

print(new_url)  # You can copy/paste this URL into TaleSpire to import your stats.
```

Use `None` in `names` for a null stat name.

## Asset Library Example Usage

`TSAssetLib` reads TaleSpire asset metadata from a local TaleSpire installation.
The supplied path should be the base TaleSpire install directory that contains
the `Taleweaver` folder.

```python
from ts_encoding import TSAssetLib

# Change this path to match where TaleSpire is installed on your computer.
talespire_path = r"C:\Program Files (x86)\Steam\steamapps\common\TaleSpire"

# Load all supported asset types: Tiles, Props, Creatures, and Music.
library = TSAssetLib(talespire_path)

# Look up an asset by UUID.
grass = library.asset("01c3a210-94fb-449f-8c47-993eda3e7126")
print(grass.name)
print(grass.asset_type)

# Get a list of every asset that was loaded.
all_assets = library.assets()
```

You can limit the loaded asset types:

```python
library = TSAssetLib(talespire_path, asset_filter=["Tiles", "Props"])
```

Valid asset types are `Tiles`, `Props`, `Creatures`, and `Music`.

## Exceptions

The public exception classes are available from `ts_encoding`:

```python
from ts_encoding import (
    BadSlabCode,
    InvalidAssetType,
    InvalidTaleSpireDirectory,
    SlabExceedsSizeLimit,
    UnsupportedSlabVersion,
)
```

All package-specific exceptions inherit from `TSEncodingException`.

## Development

Install the project locally with test dependencies:

```bash
python -m pip install -e .[dev]
```

Run the test suite:

```bash
python -m pytest
```

Asset-library tests need a local TaleSpire installation. They look for common
Steam install paths and also respect the `TALESPIRE_PATH` environment variable.
When TaleSpire is not found, those tests are skipped.

More development notes are in [CONTRIBUTING.md](CONTRIBUTING.md).
