feat/playscreen #1
@@ -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({
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
BIN
public_web/static/images/npcs/crossville/blacksmith_hilda.png
Normal file
BIN
public_web/static/images/npcs/crossville/blacksmith_hilda.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 111 KiB |
BIN
public_web/static/images/npcs/crossville/grom_ironbeard.png
Normal file
BIN
public_web/static/images/npcs/crossville/grom_ironbeard.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 114 KiB |
BIN
public_web/static/images/npcs/crossville/mayor_aldric.png
Normal file
BIN
public_web/static/images/npcs/crossville/mayor_aldric.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 103 KiB |
BIN
public_web/static/images/npcs/crossville/mira_swiftfoot.png
Normal file
BIN
public_web/static/images/npcs/crossville/mira_swiftfoot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
Reference in New Issue
Block a user