* update relation
* feat: add relation_type to avatar info structure and update related components
- Added `relation_type` to the avatar structured info in `info_presenter.py`.
- Updated `AvatarDetail.vue` to utilize the new `relation_type` for displaying avatar relationships.
- Modified `RelationRow.vue` to accept `type` as a prop for enhanced relationship representation.
- Updated `core.ts` to include `relation_type` in the `RelationInfo` interface.
Closes #
- Added `owned_regions` attribute to the Avatar class to track regions owned by avatars.
- Introduced `occupy_region` and `release_region` methods for managing region ownership and ensuring proper relationship handling.
- Updated AvatarManager to clear relationships when an avatar is released, ensuring no lingering references.
- Refactored region ownership logic in the Occupy action and Simulator to utilize the new methods for better clarity and maintainability.
- Enhanced game loading process to establish ownership relationships correctly during game state restoration.
* feat: add avatar metrics tracking feature (#110)
Add AvatarMetrics dataclass for tracking avatar state snapshots
- Add AvatarMetrics dataclass for recording monthly snapshots
- Add metrics_history field to Avatar with opt-in tracking
- Implement automatic monthly snapshot recording in Simulator
- Add backward compatibility support for existing save files
- Set default tracking limit to 1200 months (100 years)
- Add comprehensive tests with 100% coverage
- Move documentation to specs directory with simplified chinese
* fix: convert Traditional Chinese comments to Simplified Chinese
修正程式碼中的繁體中文註解為簡體中文,以符合專案規範。
Fix Traditional Chinese comments to Simplified Chinese in codebase.
* fix: CSV column name mismatches in data loading
- sect.py: Fix headquarter_name/headquarter_desc -> name/desc when reading sect_region.csv
- sect.py: Move sid initialization before technique lookup to fix unbound variable bug
- technique.py: Change sect (name) to sect_id (int) to match technique.csv column
- elixir.py: Remove redundant get_int(row, "id") that reads non-existent column
These fixes ensure:
1. Sect headquarters display correct location names (e.g., "大千光极城" instead of "不夜城")
2. Sect techniques are correctly associated and displayed
3. Technique sect restrictions work properly
* fix: update main.py to use sect_id, add CSV loading tests
- main.py: Change technique.sect to technique.sect_id in API response
- Add tests/test_csv_loading.py to verify CSV column names match code
* test: add API test for /api/meta/game_data endpoint
Verify that techniques in API response use sect_id field (not sect)
* fix: add None check for hq_region in AvatarFactory
Remove redundant code that adds sect headquarters to known_regions.
This logic is already handled by Avatar._init_known_regions() which
uses sect_id matching (more reliable than name-based lookup).
The removed code was causing a flaky test (~1% failure rate) because
resolve_query returns None in test environments with simplified maps.
- Fix load_game to use World.create_with_db() for SQLite event storage
- Add get_events_db_path() to compute event database path from save path
- Add JSON to SQLite migration for backward compatibility with old saves
- Close old EventManager before loading new save to prevent connection leaks
- Add events_db metadata to save file
- Add comprehensive tests for database switching bug and save/load cycle
Implement SQLite-based event persistence as specified in sqlite-event-manager.md.
## Changes
### Backend
- **EventStorage** (`src/classes/event_storage.py`): New SQLite storage layer
- Cursor-based pagination with compound cursor `{month_stamp}_{rowid}`
- Avatar filtering (single and pair queries)
- Major/minor event separation
- Cleanup API with `keep_major` and `before_month_stamp` filters
- **EventManager** (`src/classes/event_manager.py`): Refactored to use SQLite
- Delegates to EventStorage for persistence
- Memory fallback mode for testing
- New `get_events_paginated()` method
- **API** (`src/server/main.py`):
- `GET /api/events` - Paginated event retrieval with filtering
- `DELETE /api/events/cleanup` - User-triggered cleanup
### Frontend
- **EventPanel.vue**: Scroll-to-load pagination, dual-person filter UI
- **world.ts**: Event state management with pagination
- **game.ts**: New API client methods
### Testing
- 81 new tests for EventStorage, EventManager, and API
- Added `pytest-asyncio` and `httpx` to requirements.txt
## Known Issues: Save/Load is Currently Broken
After loading a saved game, the following issues occur:
1. **Wrong database used**: API returns events from the startup database instead
of the loaded save's `_events.db` file
2. **Events from wrong time period**: Shows events from year 115 when loaded
save is at year 114
3. **Pagination broken after load**: `has_more` returns `False` despite hundreds
of events in the saved database
4. **Filter functionality broken**: Character selection filter stops working
after loading a game
Root cause: `load_game.py` does not properly switch the EventManager's database
connection to the loaded save's events database.
LLM sometimes returns null instead of {} for action_params when an
action doesn't require parameters (e.g., ["Cultivate", null]). This
caused AttributeError when calling .items() on None.
Changes:
- Add defensive check in ai.py to convert null to {}
- Update prompt to explicitly require {} instead of null