274 lines
11 KiB
Python
274 lines
11 KiB
Python
|
|
"""Seed builtin world presets (fantasy + sci-fi).
|
||
|
|
|
||
|
|
Idempotent: skips presets whose name already exists.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import uuid
|
||
|
|
|
||
|
|
from sqlalchemy import select
|
||
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
|
|
||
|
|
from app.models import WorldPreset
|
||
|
|
|
||
|
|
# --------------------------------------------------------------------------- #
|
||
|
|
# Fantasy preset
|
||
|
|
# --------------------------------------------------------------------------- #
|
||
|
|
FANTASY_PRESET = {
|
||
|
|
"name": "Classic Fantasy",
|
||
|
|
"description": (
|
||
|
|
"A high-fantasy world with taverns, dungeons, magic, and monsters. "
|
||
|
|
"Standard d20-style stats. Default setting for new players."
|
||
|
|
),
|
||
|
|
"language": "en",
|
||
|
|
"rules": [
|
||
|
|
"Magic requires mana; mana regenerates with sleep.",
|
||
|
|
"Combat is turn-based; stats.health is the HP pool.",
|
||
|
|
"NPCs remember their relationship to the player across sessions.",
|
||
|
|
"Death is permanent unless a resurrection scroll is used.",
|
||
|
|
],
|
||
|
|
"time_schema": {"hours_in_day": 24, "initial_date": "day_1_hour_8"},
|
||
|
|
"schemas": [
|
||
|
|
{
|
||
|
|
"type": "character",
|
||
|
|
"verbose": "Character",
|
||
|
|
"plural": "characters",
|
||
|
|
"properties": [
|
||
|
|
{"name": "name", "type": "string", "required": True},
|
||
|
|
{"name": "description", "type": "string", "required": False},
|
||
|
|
{"name": "stats", "type": "object", "required": True, "properties": [
|
||
|
|
{"name": "health", "type": "integer", "required": True, "min": 0, "max": 100},
|
||
|
|
{"name": "mana", "type": "integer", "required": False, "min": 0, "max": 100},
|
||
|
|
{"name": "strength", "type": "integer", "required": True, "min": 1, "max": 20},
|
||
|
|
{"name": "dexterity", "type": "integer", "required": False, "min": 1, "max": 20},
|
||
|
|
{"name": "intelligence", "type": "integer", "required": False, "min": 1, "max": 20},
|
||
|
|
]},
|
||
|
|
{"name": "inventory", "type": "array", "required": False, "items": {"type": "object"}},
|
||
|
|
{"name": "relationship", "type": "string", "required": False},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"type": "item",
|
||
|
|
"verbose": "Item",
|
||
|
|
"plural": "items",
|
||
|
|
"properties": [
|
||
|
|
{"name": "name", "type": "string", "required": True},
|
||
|
|
{"name": "description", "type": "string", "required": False},
|
||
|
|
{"name": "qty", "type": "integer", "required": False, "min": 1, "max": 9999},
|
||
|
|
{"name": "value", "type": "integer", "required": False, "min": 0},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"type": "location",
|
||
|
|
"verbose": "Location",
|
||
|
|
"plural": "locations",
|
||
|
|
"properties": [
|
||
|
|
{"name": "name", "type": "string", "required": True},
|
||
|
|
{"name": "description", "type": "string", "required": True},
|
||
|
|
{"name": "exits", "type": "array", "required": False, "items": {"type": "string"}},
|
||
|
|
{"name": "is_safe", "type": "boolean", "required": False},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"type": "faction",
|
||
|
|
"verbose": "Faction",
|
||
|
|
"plural": "factions",
|
||
|
|
"properties": [
|
||
|
|
{"name": "name", "type": "string", "required": True},
|
||
|
|
{"name": "description", "type": "string", "required": False},
|
||
|
|
{"name": "alignment", "type": "string", "required": False},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
],
|
||
|
|
"environment_schema": [
|
||
|
|
{
|
||
|
|
"name": "player", "type": "object", "required": True,
|
||
|
|
"properties": [
|
||
|
|
{"name": "name", "type": "string", "required": True},
|
||
|
|
{"name": "stats", "type": "object", "required": True, "properties": [
|
||
|
|
{"name": "health", "type": "integer", "required": True, "min": 0, "max": 100},
|
||
|
|
{"name": "mana", "type": "integer", "required": False, "min": 0, "max": 100},
|
||
|
|
{"name": "strength", "type": "integer", "required": True, "min": 1, "max": 20},
|
||
|
|
]},
|
||
|
|
{"name": "inventory", "type": "array", "required": False},
|
||
|
|
{"name": "backstory", "type": "string", "required": False},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{"name": "current_location", "type": "string", "required": True},
|
||
|
|
{
|
||
|
|
"name": "plot_rails", "type": "object", "required": True,
|
||
|
|
"properties": [
|
||
|
|
{"name": "hooks", "type": "array", "required": True},
|
||
|
|
{"name": "current_goals", "type": "array", "required": True},
|
||
|
|
{"name": "completed_goals", "type": "array", "required": False},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
],
|
||
|
|
"environment_initial": {
|
||
|
|
"player": {
|
||
|
|
"name": "Hero",
|
||
|
|
"stats": {"health": 100, "mana": 10, "strength": 10},
|
||
|
|
"inventory": [],
|
||
|
|
"backstory": "A wanderer with a mysterious past.",
|
||
|
|
},
|
||
|
|
"current_location": "The Rusty Tankard Tavern",
|
||
|
|
"plot_rails": {
|
||
|
|
"hooks": [
|
||
|
|
"Strange travelers have been seen near the old ruins.",
|
||
|
|
"The tavern keeper is looking for someone to deliver a package.",
|
||
|
|
],
|
||
|
|
"current_goals": ["Find lodging for the night and learn local rumors."],
|
||
|
|
"completed_goals": [],
|
||
|
|
},
|
||
|
|
},
|
||
|
|
"is_public": True,
|
||
|
|
"status": "ready",
|
||
|
|
}
|
||
|
|
|
||
|
|
# --------------------------------------------------------------------------- #
|
||
|
|
# Sci-fi preset
|
||
|
|
# --------------------------------------------------------------------------- #
|
||
|
|
SCI_FI_PRESET = {
|
||
|
|
"name": "Deep Space Outpost",
|
||
|
|
"description": (
|
||
|
|
"A sci-fi setting on a remote space station. The player is a junior officer "
|
||
|
|
"investigating strange signals from the outer rim. Resource management and "
|
||
|
|
"social deduction blend with exploration."
|
||
|
|
),
|
||
|
|
"language": "en",
|
||
|
|
"rules": [
|
||
|
|
"Oxygen and power are limited resources; track them via env_update.",
|
||
|
|
"The station's AI is an NPC with its own agenda.",
|
||
|
|
"Combat is lethal — avoid open conflict when possible.",
|
||
|
|
"Distress signals from other ships may be traps.",
|
||
|
|
],
|
||
|
|
"time_schema": {"hours_in_day": 24, "initial_date": "day_1_hour_8"},
|
||
|
|
"schemas": [
|
||
|
|
{
|
||
|
|
"type": "character",
|
||
|
|
"verbose": "Character",
|
||
|
|
"plural": "characters",
|
||
|
|
"properties": [
|
||
|
|
{"name": "name", "type": "string", "required": True},
|
||
|
|
{"name": "role", "type": "string", "required": False},
|
||
|
|
{"name": "stats", "type": "object", "required": True, "properties": [
|
||
|
|
{"name": "health", "type": "integer", "required": True, "min": 0, "max": 100},
|
||
|
|
{"name": "oxygen", "type": "integer", "required": True, "min": 0, "max": 100},
|
||
|
|
{"name": "tech_skill", "type": "integer", "required": False, "min": 1, "max": 20},
|
||
|
|
]},
|
||
|
|
{"name": "inventory", "type": "array", "required": False},
|
||
|
|
{"name": "loyalty", "type": "string", "required": False},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"type": "item",
|
||
|
|
"verbose": "Item",
|
||
|
|
"plural": "items",
|
||
|
|
"properties": [
|
||
|
|
{"name": "name", "type": "string", "required": True},
|
||
|
|
{"name": "description", "type": "string", "required": False},
|
||
|
|
{"name": "qty", "type": "integer", "required": False, "min": 1, "max": 9999},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"type": "location",
|
||
|
|
"verbose": "Location",
|
||
|
|
"plural": "locations",
|
||
|
|
"properties": [
|
||
|
|
{"name": "name", "type": "string", "required": True},
|
||
|
|
{"name": "description", "type": "string", "required": True},
|
||
|
|
{"name": "exits", "type": "array", "required": False},
|
||
|
|
{"name": "is_sealed", "type": "boolean", "required": False},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"type": "faction",
|
||
|
|
"verbose": "Faction",
|
||
|
|
"plural": "factions",
|
||
|
|
"properties": [
|
||
|
|
{"name": "name", "type": "string", "required": True},
|
||
|
|
{"name": "description", "type": "string", "required": False},
|
||
|
|
{"name": "allegiance", "type": "string", "required": False},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
],
|
||
|
|
"environment_schema": [
|
||
|
|
{
|
||
|
|
"name": "player", "type": "object", "required": True,
|
||
|
|
"properties": [
|
||
|
|
{"name": "name", "type": "string", "required": True},
|
||
|
|
{"name": "role", "type": "string", "required": False},
|
||
|
|
{"name": "stats", "type": "object", "required": True, "properties": [
|
||
|
|
{"name": "health", "type": "integer", "required": True, "min": 0, "max": 100},
|
||
|
|
{"name": "oxygen", "type": "integer", "required": True, "min": 0, "max": 100},
|
||
|
|
{"name": "tech_skill", "type": "integer", "required": False, "min": 1, "max": 20},
|
||
|
|
]},
|
||
|
|
{"name": "inventory", "type": "array", "required": False},
|
||
|
|
{"name": "backstory", "type": "string", "required": False},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{"name": "current_location", "type": "string", "required": True},
|
||
|
|
{
|
||
|
|
"name": "plot_rails", "type": "object", "required": True,
|
||
|
|
"properties": [
|
||
|
|
{"name": "hooks", "type": "array", "required": True},
|
||
|
|
{"name": "current_goals", "type": "array", "required": True},
|
||
|
|
{"name": "completed_goals", "type": "array", "required": False},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
],
|
||
|
|
"environment_initial": {
|
||
|
|
"player": {
|
||
|
|
"name": "Operative",
|
||
|
|
"role": "Junior Officer",
|
||
|
|
"stats": {"health": 100, "oxygen": 100, "tech_skill": 8},
|
||
|
|
"inventory": [],
|
||
|
|
"backstory": "Fresh out of the academy, assigned to the Outer Rim Station.",
|
||
|
|
},
|
||
|
|
"current_location": "Station Command Module",
|
||
|
|
"plot_rails": {
|
||
|
|
"hooks": [
|
||
|
|
"Anomalous signal detected from sector 7G.",
|
||
|
|
"The station AI has been unusually quiet lately.",
|
||
|
|
],
|
||
|
|
"current_goals": ["Report to the commanding officer and check the signal log."],
|
||
|
|
"completed_goals": [],
|
||
|
|
},
|
||
|
|
},
|
||
|
|
"is_public": True,
|
||
|
|
"status": "ready",
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
BUILTIN_PRESETS = [FANTASY_PRESET, SCI_FI_PRESET]
|
||
|
|
|
||
|
|
|
||
|
|
async def seed_builtin_presets(session: AsyncSession) -> None:
|
||
|
|
"""Insert builtin presets if they don't yet exist. Owned by the first admin (or a system sentinel)."""
|
||
|
|
for preset_data in BUILTIN_PRESETS:
|
||
|
|
existing = (
|
||
|
|
await session.execute(
|
||
|
|
select(WorldPreset).where(WorldPreset.name == preset_data["name"])
|
||
|
|
)
|
||
|
|
).scalar_one_or_none()
|
||
|
|
if existing is not None:
|
||
|
|
continue
|
||
|
|
# Find any admin to own the preset, or use a sentinel UUID
|
||
|
|
from app.models import User
|
||
|
|
from sqlalchemy import func
|
||
|
|
|
||
|
|
admin = (
|
||
|
|
await session.execute(
|
||
|
|
select(User).where(User.is_admin.is_(True)).limit(1)
|
||
|
|
)
|
||
|
|
).scalar_one_or_none()
|
||
|
|
owner_id = admin.id if admin else uuid.UUID("00000000-0000-0000-0000-000000000001")
|
||
|
|
preset = WorldPreset(
|
||
|
|
owner_id=owner_id,
|
||
|
|
**preset_data,
|
||
|
|
version=1,
|
||
|
|
)
|
||
|
|
session.add(preset)
|
||
|
|
await session.commit()
|