feat: Add Codex screen for recording discovered living entities#349
Conversation
- Create src/codex/codex.py with Codex class (@component) - Create src/codex/codexJsonReaderWriter.py for persistence - Create schemas/codex.json JSON schema - Create src/screen/codexScreen.py with scrollable entity list - Add CODEX_SCREEN to ScreenType - Add 'codex' keybinding (L) to KeyBindings - Integrate discovery in WorldScreen (room transitions, initialization) - Handle codex screen in Roam.run() - Save/load codex alongside stats and tick count - Add L (Codex) to README Controls table - Add 18 new tests for Codex and CodexScreen - Update CHANGELOG.md Agent-Logs-Url: https://github.com/Preponderous-Software/roam/sessions/3d54f664-2b84-4573-8909-b57272f8c9dc Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a new “Codex” feature that records which living entity types the player has encountered, persists that data in saves, and provides a dedicated Codex UI screen accessible via a configurable keybinding.
Changes:
- Introduces
Codexmodel + JSON persistence + schema validation for save/load. - Adds
CodexScreenUI and wires it into the screen routing / keybinding system. - Hooks entity discovery into
WorldScreen(initialization + room transitions) and persists codex state on exit / first discovery.
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
src/codex/codex.py |
New DI-registered Codex model tracking discovered entity types. |
src/codex/codexJsonReaderWriter.py |
Adds codex save/load JSON persistence with schema validation. |
schemas/codex.json |
New JSON schema for codex save data. |
src/screen/codexScreen.py |
New Codex UI screen with scrollable list and entity textures. |
src/screen/screenType.py |
Adds CODEX_SCREEN screen type constant. |
src/screen/worldScreen.py |
Discovers living entities on init/room change; loads/saves codex with the save set. |
src/roam.py |
Resolves and routes to CodexScreen based on ScreenType.CODEX_SCREEN. |
src/config/keyBindings.py |
Adds codex action default binding (K_l) and label for remapping UI. |
tests/codex/test_codex.py |
Unit tests for Codex behavior and persistence round-trip. |
tests/codex/test_codexScreen.py |
Unit tests for CodexScreen basic behavior and scrolling. |
README.md |
Documents the new default Codex keybinding. |
CHANGELOG.md |
Records the new Codex feature and adds a learning-log entry. |
src/codex/__init__.py |
Package init file for the new codex module. |
| # All known living entity types for the codex display. | ||
| ALL_LIVING_ENTITY_TYPES = ["Bear", "Chicken"] | ||
|
|
||
| # Maps entity class names to their asset image paths. | ||
| ENTITY_IMAGE_PATHS = { | ||
| "Bear": "assets/images/bear.png", | ||
| "Chicken": "assets/images/chicken.png", | ||
| } |
There was a problem hiding this comment.
ALL_LIVING_ENTITY_TYPES / ENTITY_IMAGE_PATHS are hardcoded in the screen, so adding a new living entity type requires updating multiple places and the codex UI can easily fall out of sync with the actual entity roster. Consider centralizing the roster (e.g., in codex module) or deriving it from an existing registry used for living entities so there’s a single source of truth.
| with open(path) as f: | ||
| data = json.load(f) | ||
|
|
||
| with open("schemas/codex.json") as f: | ||
| schema = json.load(f) | ||
| jsonschema.validate(data, schema) | ||
|
|
There was a problem hiding this comment.
load() performs schema validation without handling validation errors. A corrupted/edited codex.json will currently raise and prevent the world from loading. Consider catching jsonschema.exceptions.ValidationError / json.JSONDecodeError, logging the problem, and returning None (or an empty list) so the game can still start.
| @@ -0,0 +1,79 @@ | |||
| import pygame | |||
| import pytest | |||
| from unittest.mock import MagicMock, patch | |||
There was a problem hiding this comment.
Unused import: patch is imported but never referenced. Removing it keeps the test module clean and avoids lint/static-analysis noise.
| from unittest.mock import MagicMock, patch | |
| from unittest.mock import MagicMock |
| imageX = x * 0.25 | ||
| nameX = x * 0.25 + 45 | ||
|
|
||
| visibleRows = int((y - startY - 80) / rowHeight) |
There was a problem hiding this comment.
visibleRows can become 0 (or negative) if the window height is small, which makes maxOffset/scrollInfo calculations inconsistent (e.g., showing ranges like 3-2 of 2). Guard by ensuring visibleRows is at least 1, or skip rendering/clamping logic when there’s no vertical space to show rows.
| visibleRows = int((y - startY - 80) / rowHeight) | |
| visibleRows = max(1, int((y - startY - 80) / rowHeight)) |
|
@copilot You are a coding agent tasked with resolving all open comments and review threads Instructions1. Discover all comments
2. For each comment, do the following:
3. Types of issues to handle:
4. Do NOT:
After making changes
Context
|
…ing, fix imports - Move ALL_LIVING_ENTITY_TYPES and ENTITY_IMAGE_PATHS from codexScreen.py to codex.py as the single source of truth - Add error handling for jsonschema validation in CodexJsonReaderWriter: save() catches ValidationError and logs instead of crashing; load() catches ValidationError/JSONDecodeError and returns None - Replace blanket Exception catch in _getEntityImage() with specific (pygame.error, FileNotFoundError) and log via getLogger() - Guard visibleRows with max(1, ...) to prevent layout issues on small windows - Remove unused 'patch' import from test_codexScreen.py Agent-Logs-Url: https://github.com/Preponderous-Software/roam/sessions/5762f4f8-0ed1-412f-bb57-d80c3429e98c Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
All 6 review comments addressed in f094246:
All 381 tests pass. |
|
@copilot The Codex should be reachable via the options menu. Additionally, please ensure that the entity names and entity sprites don't overlap in the Codex screen. |
…ligned text - Add "Codex" button to OptionsScreen between Controls and Back - CodexScreen now tracks returnScreen: navigates back to Options when opened from Options, back to World when opened via keybinding - Fix sprite/text overlap by left-aligning entity names after the image using _drawTextLeftAligned() instead of centered drawText() - Cache font objects to avoid repeated creation per frame - Add test_set_return_screen test for the new routing behavior Agent-Logs-Url: https://github.com/Preponderous-Software/roam/sessions/f211acb6-dde9-481b-9b4f-ffda21865e99 Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Both changes addressed in b59acc2: Options menu access — Added a "Codex" button to the options menu (between Controls and Back). Navigation is context-aware: pressing Back/Escape returns to Options when opened from the menu, or to the World when opened via the Sprite/text overlap fix — Entity names are now left-aligned starting 16px after the right edge of the sprite image, using a new |
Adds a Codex that tracks which living entity types (Bear, Chicken) the player has encountered, with persistence across save/load and a dedicated screen accessible via configurable keybinding and the options menu.
Codex data model
src/codex/codex.py—@componentclass maintaining asetof discovered entity class names withdiscover(),hasDiscovered(),getDiscoveredEntities()(sorted)src/codex/codex.pyalso centralizesALL_LIVING_ENTITY_TYPESandENTITY_IMAGE_PATHSas the single source of truth for the entity rostersrc/codex/codexJsonReaderWriter.py— JSON persistence following theStatspattern, with graceful error handling for corrupted save filesschemas/codex.json— validation schemaDiscovery trigger
WorldScreenresolvesCodexvia DI, callsdiscoverLivingEntitiesInRoom()after room transitions and on initialization"New codex entry: Bear"viastatus.set()and saves immediatelyKeybinding & navigation
codexaction added toKeyBindings.DEFAULT_BINDINGS(defaultpygame.K_l) with label for Controls screen remappingCODEX_SCREENadded toScreenType, handled inWorldScreen.handleKeyDownEvent()andRoam.run()Codex screen
src/screen/codexScreen.py—@componentscreen consistent withControlsScreenlayout: scrollable entity list with textures for discovered entries,"???"dimmed for undiscovered, Back button, mouse wheel scrollingPersistence integration
WorldScreen.initialize()alongside stats/tick/inventorysaveSynchronous()on world exitTests