feat/playscreen #1

Merged
ptarrant merged 3 commits from feat/playscreen into dev 2025-11-25 22:06:06 +00:00
14 changed files with 154 additions and 24 deletions

View File

@@ -281,6 +281,7 @@ def get_npcs_at_location(location_id: str):
"role": npc.role, "role": npc.role,
"appearance": npc.appearance.brief, "appearance": npc.appearance.brief,
"tags": npc.tags, "tags": npc.tags,
"image_url": npc.image_url,
}) })
return success_response({ return success_response({

View File

@@ -3,6 +3,7 @@ npc_id: npc_blacksmith_hilda
name: Hilda Ironforge name: Hilda Ironforge
role: blacksmith role: blacksmith
location_id: crossville_village location_id: crossville_village
image_url: /static/images/npcs/crossville/blacksmith_hilda.png
personality: personality:
traits: traits:

View File

@@ -3,6 +3,7 @@ npc_id: npc_grom_ironbeard
name: Grom Ironbeard name: Grom Ironbeard
role: bartender role: bartender
location_id: crossville_tavern location_id: crossville_tavern
image_url: /static/images/npcs/crossville/grom_ironbeard.png
personality: personality:
traits: traits:

View File

@@ -3,6 +3,7 @@ npc_id: npc_mayor_aldric
name: Mayor Aldric Thornwood name: Mayor Aldric Thornwood
role: mayor role: mayor
location_id: crossville_village location_id: crossville_village
image_url: /static/images/npcs/crossville/mayor_aldric.png
personality: personality:
traits: traits:

View File

@@ -3,6 +3,7 @@ npc_id: npc_mira_swiftfoot
name: Mira Swiftfoot name: Mira Swiftfoot
role: rogue role: rogue
location_id: crossville_tavern location_id: crossville_tavern
image_url: /static/images/npcs/crossville/mira_swiftfoot.png
personality: personality:
traits: traits:

View File

@@ -296,6 +296,7 @@ class NPC:
location_id: str location_id: str
personality: NPCPersonality personality: NPCPersonality
appearance: NPCAppearance appearance: NPCAppearance
image_url: Optional[str] = None
knowledge: Optional[NPCKnowledge] = None knowledge: Optional[NPCKnowledge] = None
relationships: List[NPCRelationship] = field(default_factory=list) relationships: List[NPCRelationship] = field(default_factory=list)
inventory_for_sale: List[NPCInventoryItem] = field(default_factory=list) inventory_for_sale: List[NPCInventoryItem] = field(default_factory=list)
@@ -316,6 +317,7 @@ class NPC:
"name": self.name, "name": self.name,
"role": self.role, "role": self.role,
"location_id": self.location_id, "location_id": self.location_id,
"image_url": self.image_url,
"personality": self.personality.to_dict(), "personality": self.personality.to_dict(),
"appearance": self.appearance.to_dict(), "appearance": self.appearance.to_dict(),
"knowledge": self.knowledge.to_dict() if self.knowledge else None, "knowledge": self.knowledge.to_dict() if self.knowledge else None,
@@ -400,6 +402,7 @@ class NPC:
name=data["name"], name=data["name"],
role=data["role"], role=data["role"],
location_id=data["location_id"], location_id=data["location_id"],
image_url=data.get("image_url"),
personality=personality, personality=personality,
appearance=appearance, appearance=appearance,
knowledge=knowledge, knowledge=knowledge,

View File

@@ -212,6 +212,7 @@ class NPCLoader:
name=data["name"], name=data["name"],
role=data["role"], role=data["role"],
location_id=data["location_id"], location_id=data["location_id"],
image_url=data.get("image_url"),
personality=personality, personality=personality,
appearance=appearance, appearance=appearance,
knowledge=knowledge, knowledge=knowledge,

View File

@@ -740,8 +740,75 @@ Set-Cookie: coc_session=<session_token>; HttpOnly; Secure; SameSite=Lax; Max-Age
--- ---
## Health
### Health Check
| | |
|---|---|
| **Endpoint** | `GET /api/v1/health` |
| **Description** | Check API service status and version |
| **Auth Required** | No |
**Response (200 OK):**
```json
{
"app": "Code of Conquest",
"version": "0.1.0",
"status": 200,
"timestamp": "2025-11-16T10:30:00Z",
"result": {
"status": "ok",
"service": "Code of Conquest API",
"version": "0.1.0"
},
"error": null,
"meta": {}
}
```
---
## Sessions ## Sessions
### List Sessions
| | |
|---|---|
| **Endpoint** | `GET /api/v1/sessions` |
| **Description** | Get all active sessions for current user |
| **Auth Required** | Yes |
**Response (200 OK):**
```json
{
"app": "Code of Conquest",
"version": "0.1.0",
"status": 200,
"timestamp": "2025-11-16T10:30:00Z",
"result": [
{
"session_id": "sess_789",
"character_id": "char_456",
"turn_number": 5,
"status": "active",
"created_at": "2025-11-16T10:00:00Z",
"last_activity": "2025-11-16T10:25:00Z",
"game_state": {
"current_location": "crossville_village",
"location_type": "town"
}
}
]
}
```
**Error Responses:**
- `401` - Not authenticated
- `500` - Internal server error
---
### Create Session ### Create Session
| | | | | |
@@ -1066,17 +1133,19 @@ The Travel API enables location-based world exploration. Locations are defined i
"timestamp": "2025-11-25T10:30:00Z", "timestamp": "2025-11-25T10:30:00Z",
"result": { "result": {
"current_location": "crossville_village", "current_location": "crossville_village",
"available_locations": [ "destinations": [
{ {
"location_id": "crossville_tavern", "location_id": "crossville_tavern",
"name": "The Rusty Anchor Tavern", "name": "The Rusty Anchor Tavern",
"location_type": "tavern", "location_type": "tavern",
"region_id": "crossville",
"description": "A cozy tavern where travelers share tales..." "description": "A cozy tavern where travelers share tales..."
}, },
{ {
"location_id": "crossville_forest", "location_id": "crossville_forest",
"name": "Whispering Woods", "name": "Whispering Woods",
"location_type": "wilderness", "location_type": "wilderness",
"region_id": "crossville",
"description": "A dense forest on the outskirts of town..." "description": "A dense forest on the outskirts of town..."
} }
] ]
@@ -1084,6 +1153,11 @@ The Travel API enables location-based world exploration. Locations are defined i
} }
``` ```
**Error Responses:**
- `400` - Missing session_id parameter
- `404` - Session or character not found
- `500` - Internal server error
### Travel to Location ### Travel to Location
| | | | | |
@@ -1100,20 +1174,40 @@ The Travel API enables location-based world exploration. Locations are defined i
} }
``` ```
**Response (202 Accepted):** **Response (200 OK):**
```json ```json
{ {
"app": "Code of Conquest", "app": "Code of Conquest",
"version": "0.1.0", "version": "0.1.0",
"status": 202, "status": 200,
"timestamp": "2025-11-25T10:30:00Z", "timestamp": "2025-11-25T10:30:00Z",
"result": { "result": {
"job_id": "ai_travel_abc123", "location": {
"status": "queued",
"message": "Traveling to The Rusty Anchor Tavern...",
"destination": {
"location_id": "crossville_tavern", "location_id": "crossville_tavern",
"name": "The Rusty Anchor Tavern" "name": "The Rusty Anchor Tavern",
"location_type": "tavern",
"region_id": "crossville",
"description": "A cozy tavern where travelers share tales...",
"lore": "Founded decades ago by a retired adventurer...",
"ambient_description": "The scent of ale and roasting meat fills the air...",
"available_quests": ["quest_missing_trader"],
"npc_ids": ["npc_grom_ironbeard"],
"discoverable_locations": ["crossville_forest"],
"is_starting_location": false,
"tags": ["tavern", "social", "merchant", "safe"]
},
"npcs_present": [
{
"npc_id": "npc_grom_ironbeard",
"name": "Grom Ironbeard",
"role": "bartender",
"appearance": "Stout dwarf with a braided grey beard"
}
],
"game_state": {
"current_location": "crossville_tavern",
"location_type": "tavern",
"active_quests": []
} }
} }
} }
@@ -1121,7 +1215,9 @@ The Travel API enables location-based world exploration. Locations are defined i
**Error Responses:** **Error Responses:**
- `400` - Location not discovered - `400` - Location not discovered
- `403` - Location not discovered
- `404` - Session or location not found - `404` - Session or location not found
- `500` - Internal server error
### Get Location Details ### Get Location Details
@@ -1139,22 +1235,36 @@ The Travel API enables location-based world exploration. Locations are defined i
"status": 200, "status": 200,
"timestamp": "2025-11-25T10:30:00Z", "timestamp": "2025-11-25T10:30:00Z",
"result": { "result": {
"location_id": "crossville_village", "location": {
"name": "Crossville Village", "location_id": "crossville_village",
"location_type": "town", "name": "Crossville Village",
"region_id": "crossville", "location_type": "town",
"description": "A modest farming village built around a central square...", "region_id": "crossville",
"lore": "Founded two centuries ago by settlers from the eastern kingdoms...", "description": "A modest farming village built around a central square...",
"ambient_description": "The village square bustles with activity...", "lore": "Founded two centuries ago by settlers from the eastern kingdoms...",
"available_quests": ["quest_mayors_request"], "ambient_description": "The village square bustles with activity...",
"npc_ids": ["npc_mayor_aldric", "npc_blacksmith_hilda"], "available_quests": ["quest_mayors_request"],
"discoverable_locations": ["crossville_tavern", "crossville_forest"], "npc_ids": ["npc_mayor_aldric", "npc_blacksmith_hilda"],
"is_starting_location": true, "discoverable_locations": ["crossville_tavern", "crossville_forest"],
"tags": ["town", "social", "merchant", "safe"] "is_starting_location": true,
"tags": ["town", "social", "merchant", "safe"]
},
"npcs_present": [
{
"npc_id": "npc_mayor_aldric",
"name": "Mayor Aldric",
"role": "village mayor",
"appearance": "A portly man in fine robes"
}
]
} }
} }
``` ```
**Error Responses:**
- `404` - Location not found
- `500` - Internal server error
### Get Current Location ### Get Current Location
| | | | | |
@@ -1225,6 +1335,7 @@ The NPC API enables interaction with persistent NPCs. NPCs have personalities, k
"name": "Grom Ironbeard", "name": "Grom Ironbeard",
"role": "bartender", "role": "bartender",
"location_id": "crossville_tavern", "location_id": "crossville_tavern",
"image_url": "/static/images/npcs/crossville/grom_ironbeard.png",
"personality": { "personality": {
"traits": ["gruff", "observant", "secretly kind"], "traits": ["gruff", "observant", "secretly kind"],
"speech_style": "Uses dwarven expressions, speaks in short sentences", "speech_style": "Uses dwarven expressions, speaks in short sentences",
@@ -1304,7 +1415,7 @@ The NPC API enables interaction with persistent NPCs. NPCs have personalities, k
"dialogue": "*polishes mug thoughtfully* \"Ah, another adventurer. What'll it be?\"", "dialogue": "*polishes mug thoughtfully* \"Ah, another adventurer. What'll it be?\"",
"tokens_used": 728, "tokens_used": 728,
"npc_name": "Grom Ironbeard", "npc_name": "Grom Ironbeard",
"npc_id": "npc_grom_001", "npc_id": "npc_grom_ironbeard",
"character_name": "Thorin", "character_name": "Thorin",
"player_line": "greeting", "player_line": "greeting",
"conversation_history": [ "conversation_history": [
@@ -1327,6 +1438,11 @@ The NPC API enables interaction with persistent NPCs. NPCs have personalities, k
- The AI receives the last 3 exchanges as context for continuity - The AI receives the last 3 exchanges as context for continuity
- The job result includes prior `conversation_history` for UI display - The job result includes prior `conversation_history` for UI display
**Bidirectional Dialogue:**
- If `player_response` is provided in the request, it overrides `topic` and enables full bidirectional conversation
- The player's response is stored in the conversation history
- The NPC's reply takes into account the full conversation context
**Error Responses:** **Error Responses:**
- `400` - NPC not at current location - `400` - NPC not at current location
- `404` - NPC or session not found - `404` - NPC or session not found
@@ -1354,14 +1470,16 @@ The NPC API enables interaction with persistent NPCs. NPCs have personalities, k
"name": "Grom Ironbeard", "name": "Grom Ironbeard",
"role": "bartender", "role": "bartender",
"appearance": "Stout dwarf with a braided grey beard", "appearance": "Stout dwarf with a braided grey beard",
"tags": ["merchant", "quest_giver"] "tags": ["merchant", "quest_giver"],
"image_url": "/static/images/npcs/crossville/grom_ironbeard.png"
}, },
{ {
"npc_id": "npc_mira_swiftfoot", "npc_id": "npc_mira_swiftfoot",
"name": "Mira Swiftfoot", "name": "Mira Swiftfoot",
"role": "traveling rogue", "role": "traveling rogue",
"appearance": "Lithe half-elf with sharp eyes", "appearance": "Lithe half-elf with sharp eyes",
"tags": ["information", "secret_keeper"] "tags": ["information", "secret_keeper"],
"image_url": "/static/images/npcs/crossville/mira_swiftfoot.png"
} }
] ]
} }

View File

@@ -225,6 +225,7 @@ Main NPC definition with personality and dialogue data for AI generation.
| `location_id` | str | ID of location where NPC resides | | `location_id` | str | ID of location where NPC resides |
| `personality` | NPCPersonality | Personality traits and speech patterns | | `personality` | NPCPersonality | Personality traits and speech patterns |
| `appearance` | NPCAppearance | Physical description | | `appearance` | NPCAppearance | Physical description |
| `image_url` | Optional[str] | URL path to NPC portrait image (e.g., "/static/images/npcs/crossville/grom_ironbeard.png") |
| `knowledge` | Optional[NPCKnowledge] | What the NPC knows (public and secret) | | `knowledge` | Optional[NPCKnowledge] | What the NPC knows (public and secret) |
| `relationships` | List[NPCRelationship] | How NPC feels about other NPCs | | `relationships` | List[NPCRelationship] | How NPC feels about other NPCs |
| `inventory_for_sale` | List[NPCInventoryItem] | Items NPC sells (if merchant) | | `inventory_for_sale` | List[NPCInventoryItem] | Items NPC sells (if merchant) |
@@ -346,6 +347,7 @@ npc_id: "npc_grom_001"
name: "Grom Ironbeard" name: "Grom Ironbeard"
role: "bartender" role: "bartender"
location_id: "crossville_tavern" location_id: "crossville_tavern"
image_url: "/static/images/npcs/crossville/grom_ironbeard.png"
personality: personality:
traits: traits:
- "gruff" - "gruff"

View File

@@ -704,7 +704,8 @@ def npc_chat_modal(session_id: str, npc_id: str):
'name': npc_data.get('name'), 'name': npc_data.get('name'),
'role': npc_data.get('role'), 'role': npc_data.get('role'),
'appearance': npc_data.get('appearance', {}).get('brief', ''), 'appearance': npc_data.get('appearance', {}).get('brief', ''),
'tags': npc_data.get('tags', []) 'tags': npc_data.get('tags', []),
'image_url': npc_data.get('image_url')
} }
# Get relationship info # Get relationship info

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB