aoe2-genie-tooling
High-level Python toolkit for editing Age of Empires II Definitive Edition DAT files.
An object-oriented API with proper wrappers that handle multi-civ propagation automatically.
Installation
pip install aoe2-genie-tooling
Documentation
Full documentation is available in the docs/ directory or at https://gokumodder.github.io/aoe2-genie-tooling/.
Why This Library?
- Proper Objects – Units, Graphics, Sounds return handle objects with typed properties
- Multi-Civ Propagation – Changes automatically apply across all civilizations
- Attribute Flattening – Access deeply nested properties directly (
unit.move_soundinstead ofunit.bird.move_sound) - Type-Safe Datasets – IntEnum constants for resources, tasks, attack classes
- Fluent Builders - Easy-to-use builders for tasks (
unit.add_task.combat()) and effects
Quick Start
from aoe2_genie_tooling import GenieWorkspace
# Load workspace
workspace = GenieWorkspace.load("empires2_x2_p1.dat")
# Get managers
unit_manager = workspace.unit_manager
graphic_manager = workspace.graphic_manager
sound_manager = workspace.sound_manager
tech_manager = workspace.tech_manager
civ_manager = workspace.civ_manager
# Create a custom unit
unit = unit_manager.create("Elite Guard", base_unit_id=4) # Clone from Archer
unit.hit_points = 120
unit.max_range = 6.0
# Save changes
workspace.save("output.dat")
Units: Create, Clone, Move, Delete
Creating Units
unit_manager = workspace.unit_manager
# Create new unit (clones from base_unit_id)
unit = unit_manager.create(
name="My Unit",
base_unit_id=4, # Clone from Archer
unit_id=1500, # Optional: specific ID (auto-assigns if None)
enable_for_civs=[0, 1, 2], # Optional: specific civs (all if None)
)
Cloning Units
unit_manager = workspace.unit_manager
# Clone existing unit to a new ID
clone = unit_manager.clone_into(
src_unit_id=4, # Source: Archer
dst_unit_id=2000, # Destination ID
name="Archer Clone",
on_conflict="overwrite", # "error" | "overwrite"
)
Moving Units
unit_manager = workspace.unit_manager
# Move unit to different ID (leaves placeholder at source)
unit_manager.move(
src_unit_id=2000,
dst_unit_id=2500,
on_conflict="swap", # "error" | "overwrite" | "swap"
)
Getting Existing Units
unit_manager = workspace.unit_manager
# Get handle for existing unit
archer = unit_manager.get(4)
print(archer.name) # "Archer"
print(archer.hit_points) # 30
Attacks & Armors
unit_manager = workspace.unit_manager
unit = unit_manager.get(4) # Archer
# Read current attacks
for attack in unit.attacks:
print(f"Class {attack.class_}: {attack.amount} damage")
# Add new attack (pierce damage)
unit.add_attack(class_=3, amount=6)
# Add armor (melee armor)
unit.add_armour(class_=4, amount=2)
Tasks (Unit Commands)
from Datasets import Task, Resource
unit_manager = workspace.unit_manager
unit = unit_manager.get(83) # Villager
# List all tasks
for task in unit.tasks.list_tasks():
print(f"Task {task.id}: type={task.task_type}")
# Add a gather task using builder
unit.add_task.gather(
resource_in=Resource.FOOD,
resource_out=Resource.FOOD,
work_range=0.5,
)
# Remove a task by ID
unit.remove_task(0)
# Clear all tasks
unit.clear_tasks()
Graphics: Create and Assign
graphic_manager = workspace.graphic_manager
unit_manager = workspace.unit_manager
# Create a new graphic
graphic = graphic_manager.add_graphic(
file_name="my_unit_attack.slp",
frame_count=15,
angle_count=8,
)
print(f"Created graphic ID: {graphic.id}")
# Assign to unit
unit = unit_manager.create("Slinger", base_unit_id=4)
unit.standing_graphic_1 = graphic.id
unit.attack_graphic = graphic.id
Sounds: Create and Assign
sound_manager = workspace.sound_manager
unit_manager = workspace.unit_manager
# Create a new sound
sound = sound_manager.add_new("Custom Attack")
sound.new_sound(filename="custom_attack.wav", probability=100)
print(f"Created sound ID: {sound.id}")
# Assign to unit
unit = unit_manager.get(4)
unit.attack_sound = sound.id
unit.move_sound = sound.id
Complete Example: Custom Unit with Assets
from aoe2_genie_tooling import GenieWorkspace
from Datasets import Resource, UnitClass
workspace = GenieWorkspace.load("empires2_x2_p1.dat")
# Get managers
unit_manager = workspace.unit_manager
graphic_manager = workspace.graphic_manager
sound_manager = workspace.sound_manager
# 1. Create custom graphics
idle_gfx = graphic_manager.add_graphic("hero_idle.slp", frame_count=10, angle_count=8)
attack_gfx = graphic_manager.add_graphic("hero_attack.slp", frame_count=15, angle_count=8)
# 2. Create custom sound
attack_sound = sound_manager.add_new("hero_attack")
attack_sound.new_sound("hero_attack.wav")
# 3. Create the unit (clone from Knight)
hero = unit_manager.create("Hero Knight", base_unit_id=38)
# 4. Set basic stats
hero.hit_points = 200
hero.speed = 1.5
hero.class_ = UnitClass.CAVALRY
# 5. Assign graphics
hero.standing_graphic_1 = idle_gfx.id
hero.attack_graphic = attack_gfx.id
# 6. Assign sound
hero.attack_sound = attack_sound.id
# 7. Set attacks and armor
hero.set_attack(class_=4, amount=15) # Melee
hero.set_attack(class_=11, amount=10) # Bonus vs Buildings
hero.add_armour(class_=3, amount=5) # Pierce armor
# 8. Set cost
hero.cost.food = 100
hero.cost.gold = 80
# 9. Set resource storage (drops gold on death)
hero.resource_1(type=Resource.GOLD, amount=50.0, flag=2)
# 10. Save
workspace.save("output.dat")
print(f"Created Hero Knight at ID {hero.id}")
Datasets (Constants)
from Datasets import (
Resource, # FOOD, WOOD, GOLD, STONE, etc.
UnitClass, # INFANTRY, ARCHER, CAVALRY, etc.
Attribute, # Object attributes for effects
Task, # GATHER, BUILD, ATTACK, etc.
StoreMode, # Resource storage flags
)
unit.class_ = UnitClass.ARCHER
Exceptions
| Exception | When Raised |
|---|---|
UnitIdConflictError |
Target ID exists with on_conflict="error" |
GapNotAllowedError |
Would create gaps with fill_gaps="error" |
InvalidIdError |
Negative or non-existent ID |
TemplateNotFoundError |
No template unit for cloning |
ValidationError |
Workspace validation failed |
Requirements
- Python 3.11+
- GenieDatParser (local dependency - Rust-backed DAT parser)
License
LGPL-3.0
Author
GoKuModder
Metadata
Release files for aoe2-genie-tooling 1.2.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| aoe2_genie_tooling-1.2.4.tar.gz | 161.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aoe2_genie_tooling-1.2.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 412.7 kB
Release files / aoe2_genie_tooling-1.2.4.tar.gz
| Download URL | aoe2_genie_tooling-1.2.4.tar.gz |
|---|---|
| Size | 161.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3071fe40bfbf1ee8b765b62b77bb3614f0f318a9499cf3b807a067630157f89a
|
|
BLAKE2b-256 checksum How to use checksums |
0796a9a577bf7ba6cdcc99491cec5deced912f42b0105dafca7be293dfd3e4a7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 3, 2026.
Transparency logRelease files / aoe2_genie_tooling-1.2.4-py3-none-any.whl
| Download URL | aoe2_genie_tooling-1.2.4-py3-none-any.whl |
|---|---|
| Size | 251.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
caf0f6e9dfe265f2e0af06815322360f1208d9f1aaf79fd93d5cd21083c006cd
|
|
BLAKE2b-256 checksum How to use checksums |
b0e0e981eed081de880b2a5d5426caeb136a63eb5db399d777c0217d40fa00a3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 3, 2026.
Transparency log