Modpacks & Shared Controllers
In addition to single-character mods, PyToNingans supports Modpacks—multi-entity bundles capable of executing coordinated behaviors, shared team mechanics, and synchronized events.
Modpack Directory Layout
A Modpack bundles multiple entities under a shared controller:
mods/
└── vocaloid_crew_pack/
├── pack.json # Modpack manifest and entity roster
├── pack.py # Shared controller inheriting BaseModPack
└── entities/ # Contained entity subdirectories
├── hatsune_miku/
│ ├── config.json
│ └── sprite_sheet.png
├── kagamine_rin/
│ ├── config.json
│ └── sprite_sheet.png
└── kagamine_len/
├── config.json
└── sprite_sheet.png
The pack.json Manifest
{
"id": "vocaloid_crew_pack",
"name": "Vocaloid Desktop Companions",
"version": "1.2.0",
"author": "MikuFan99",
"description": "A delightful bundle containing Hatsune Miku, Rin, and Len!",
"is_pack": true,
"config_version": 10,
"contained_entities": [
"Hatsune Miku",
"Kagamine Rin",
"Kagamine Len"
]
}
The Shared Controller (pack.py)
Every Modpack can define a pack.py script that extends BaseModPack:
"""Shared controller for Vocaloid Desktop Companions."""
from pytoningans.core.api import BaseModPack, PackMenuAction
class PackController(BaseModPack):
"""Coordinates behavior and shared abilities across all pack members."""
def on_pack_loaded(self) -> None:
print(f"Modpack {self.name} loaded with {len(self.entities)} entities!")
def on_entity_spawned(self, entity_id: str) -> None:
# Example: Play a fanfare when a pack member joins the desktop
pass
def get_custom_actions(self) -> list[PackMenuAction]:
"""Provides custom context menu actions shared across pack members."""
return [
PackMenuAction(
label="Sing Duet",
callback=self._trigger_duet_routine,
tooltip="Synchronize dancing and singing animations"
)
]
def _trigger_duet_routine(self) -> None:
# Custom coordination logic
pass
Key Controller Lifecycle Hooks
on_pack_loaded(): Called when the pack is registered into the game session.on_entity_spawned(entity_id): Fired whenever a member of the pack is spawned.on_pack_unloaded(): Cleanup hook when the pack is uninstalled or deactivated.get_custom_actions(): Returns action buttons added to the context menu of all member characters.