|
| 1 | +# Design Document |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +The Controls Menu Dialog feature adds an in-game reference for all keyboard controls accessible from the main game menu. The implementation follows the established dialog pattern used by LanguageDialog and WorldSaveDialog, ensuring visual and architectural consistency. The dialog displays all control bindings organized into logical categories, supports multi-language localization, and integrates seamlessly with the existing menu system. |
| 6 | + |
| 7 | +## Architecture |
| 8 | + |
| 9 | +The feature consists of three main components: |
| 10 | + |
| 11 | +1. **ControlsDialog Class**: A new dialog class that manages the display and interaction logic for the controls reference |
| 12 | +2. **GameMenu Integration**: Modifications to GameMenu to add the Controls menu item and handle dialog lifecycle |
| 13 | +3. **Localization Resources**: Translation keys added to all supported language files (en, de, nl, pl, pt) |
| 14 | + |
| 15 | +The architecture follows the Model-View-Controller pattern where: |
| 16 | +- **Model**: Translation keys and control binding data |
| 17 | +- **View**: ControlsDialog rendering logic |
| 18 | +- **Controller**: GameMenu input handling and dialog state management |
| 19 | + |
| 20 | +## Components and Interfaces |
| 21 | + |
| 22 | +### ControlsDialog |
| 23 | + |
| 24 | +**Location**: `src/main/java/wagemaker/uk/ui/ControlsDialog.java` |
| 25 | + |
| 26 | +**Responsibilities**: |
| 27 | +- Render the controls reference dialog with wooden plank background |
| 28 | +- Display all control bindings organized by category |
| 29 | +- Handle ESC key input to close the dialog |
| 30 | +- Implement LanguageChangeListener for localization support |
| 31 | +- Manage dialog visibility state |
| 32 | + |
| 33 | +**Key Methods**: |
| 34 | +```java |
| 35 | +public ControlsDialog() // Constructor |
| 36 | +public void show() // Display the dialog |
| 37 | +public void hide() // Hide the dialog |
| 38 | +public boolean isVisible() // Check visibility state |
| 39 | +public void handleInput() // Process keyboard input |
| 40 | +public void render(SpriteBatch, ShapeRenderer, float, float) // Render dialog |
| 41 | +public void onLanguageChanged(String) // Handle language changes |
| 42 | +public void dispose() // Clean up resources |
| 43 | +``` |
| 44 | + |
| 45 | +**Dependencies**: |
| 46 | +- LocalizationManager: For retrieving translated text |
| 47 | +- LibGDX Graphics: For texture and font rendering |
| 48 | +- LanguageChangeListener: Interface implementation |
| 49 | + |
| 50 | +### GameMenu Modifications |
| 51 | + |
| 52 | +**Location**: `src/main/java/wagemaker/uk/ui/GameMenu.java` |
| 53 | + |
| 54 | +**Changes Required**: |
| 55 | +1. Add ControlsDialog field and initialization |
| 56 | +2. Add "Controls" to menu items arrays (singleplayer and multiplayer) |
| 57 | +3. Increase MENU_HEIGHT constant to accommodate new menu item |
| 58 | +4. Add dialog visibility check in update() method |
| 59 | +5. Add dialog rendering in render methods |
| 60 | +6. Add menu selection handler for Controls option |
| 61 | +7. Add dialog disposal in dispose() method |
| 62 | + |
| 63 | +### Localization Resources |
| 64 | + |
| 65 | +**Location**: `assets/localization/*.json` |
| 66 | + |
| 67 | +**New Translation Keys**: |
| 68 | +``` |
| 69 | +controls_dialog.title |
| 70 | +controls_dialog.movement_header |
| 71 | +controls_dialog.movement_up |
| 72 | +controls_dialog.movement_down |
| 73 | +controls_dialog.movement_left |
| 74 | +controls_dialog.movement_right |
| 75 | +controls_dialog.inventory_header |
| 76 | +controls_dialog.inventory_open |
| 77 | +controls_dialog.inventory_navigate_left |
| 78 | +controls_dialog.inventory_navigate_right |
| 79 | +controls_dialog.item_header |
| 80 | +controls_dialog.item_plant_p |
| 81 | +controls_dialog.item_plant_space |
| 82 | +controls_dialog.item_consume |
| 83 | +controls_dialog.targeting_header |
| 84 | +controls_dialog.targeting_up |
| 85 | +controls_dialog.targeting_down |
| 86 | +controls_dialog.targeting_left |
| 87 | +controls_dialog.targeting_right |
| 88 | +controls_dialog.combat_header |
| 89 | +controls_dialog.combat_attack |
| 90 | +controls_dialog.system_header |
| 91 | +controls_dialog.system_menu |
| 92 | +controls_dialog.system_delete_world |
| 93 | +controls_dialog.system_compass_target |
| 94 | +controls_dialog.close_instruction |
| 95 | +menu.controls |
| 96 | +``` |
| 97 | + |
| 98 | +## Data Models |
| 99 | + |
| 100 | +### Control Binding Structure |
| 101 | + |
| 102 | +Controls are organized into logical categories for display: |
| 103 | + |
| 104 | +1. **Movement Controls** |
| 105 | + - UP Arrow: Move up |
| 106 | + - DOWN Arrow: Move down |
| 107 | + - LEFT Arrow: Move left |
| 108 | + - RIGHT Arrow: Move right |
| 109 | + |
| 110 | +2. **Inventory Controls** |
| 111 | + - I: Open/close inventory |
| 112 | + - LEFT Arrow: Navigate inventory left |
| 113 | + - RIGHT Arrow: Navigate inventory right |
| 114 | + |
| 115 | +3. **Item Controls** |
| 116 | + - P: Plant item |
| 117 | + - SPACE: Plant/consume item |
| 118 | + - SPACE: Consume item |
| 119 | + |
| 120 | +4. **Targeting Controls** |
| 121 | + - W: Target up |
| 122 | + - A: Target left |
| 123 | + - S: Target down |
| 124 | + - D: Target right |
| 125 | + |
| 126 | +5. **Combat Controls** |
| 127 | + - SPACE: Attack |
| 128 | + |
| 129 | +6. **System Controls** |
| 130 | + - ESC: Open menu |
| 131 | + - X: Delete world |
| 132 | + - TAB: Set compass target |
| 133 | + |
| 134 | +### Dialog Dimensions |
| 135 | + |
| 136 | +```java |
| 137 | +private static final float DIALOG_WIDTH = 840; // Increased by 20% to prevent text overlap in wider languages |
| 138 | +private static final float DIALOG_HEIGHT = 480; // Reduced by 20% for better proportions |
| 139 | +``` |
| 140 | + |
| 141 | +### Menu Dimensions Update |
| 142 | + |
| 143 | +```java |
| 144 | +// In GameMenu.java |
| 145 | +private static final float MENU_HEIGHT = 340; // Increased from 310 to fit Controls item |
| 146 | +``` |
| 147 | + |
| 148 | +## Correctness Properties |
| 149 | + |
| 150 | +*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.* |
| 151 | + |
| 152 | +### Property 1: Dialog visibility toggles correctly |
| 153 | +*For any* dialog state, when show() is called the dialog should become visible, and when hide() is called the dialog should become not visible |
| 154 | +**Validates: Requirements 1.2, 1.4** |
| 155 | + |
| 156 | +### Property 2: ESC key closes dialog |
| 157 | +*For any* dialog state where isVisible() returns true, simulating ESC key input should result in isVisible() returning false |
| 158 | +**Validates: Requirements 1.4** |
| 159 | + |
| 160 | +### Property 3: Dialog prevents game input |
| 161 | +*For any* game state where the Controls Dialog is visible, game input handlers should not process player movement or action inputs |
| 162 | +**Validates: Requirements 1.3** |
| 163 | + |
| 164 | +### Property 4: All controls are displayed |
| 165 | +*For any* rendering of the Controls Dialog, the rendered text should contain descriptions for all defined control bindings (movement, inventory, items, targeting, combat, system) |
| 166 | +**Validates: Requirements 2.1-2.9** |
| 167 | + |
| 168 | +### Property 5: Dialog centers on camera |
| 169 | +*For any* camera position (camX, camY), the dialog X position should equal camX - DIALOG_WIDTH/2 and dialog Y position should equal camY - DIALOG_HEIGHT/2 |
| 170 | +**Validates: Requirements 3.4** |
| 171 | + |
| 172 | +### Property 6: Language changes update text |
| 173 | +*For any* supported language, when setLanguage() is called on LocalizationManager, the Controls Dialog should retrieve and display text in the new language |
| 174 | +**Validates: Requirements 1.5, 4.5** |
| 175 | + |
| 176 | +### Property 7: Translation keys exist in all languages |
| 177 | +*For any* translation key defined for the Controls Dialog, that key should exist in all supported language files (en.json, de.json, nl.json, pl.json, pt.json) |
| 178 | +**Validates: Requirements 5.1, 5.2-5.6** |
| 179 | + |
| 180 | +### Property 8: No hardcoded strings in rendering |
| 181 | +*For any* text displayed in the Controls Dialog, that text should be retrieved from LocalizationManager using a translation key, not hardcoded as a string literal |
| 182 | +**Validates: Requirements 5.7** |
| 183 | + |
| 184 | +### Property 9: Listener registration lifecycle |
| 185 | +*For any* ControlsDialog instance, after construction the dialog should be registered with LocalizationManager, and after dispose() the dialog should be unregistered |
| 186 | +**Validates: Requirements 4.3, 4.4** |
| 187 | + |
| 188 | +## Error Handling |
| 189 | + |
| 190 | +### Input Validation |
| 191 | +- No user input validation required (dialog is read-only) |
| 192 | +- ESC key handling is fail-safe (always closes dialog) |
| 193 | + |
| 194 | +### Resource Loading |
| 195 | +- Font loading failure: Fall back to default LibGDX BitmapFont |
| 196 | +- Texture creation failure: Log error and use fallback solid color background |
| 197 | +- Translation key missing: LocalizationManager returns key wrapped in brackets [key] |
| 198 | + |
| 199 | +### State Management |
| 200 | +- Dialog visibility state is boolean (no invalid states possible) |
| 201 | +- Multiple show() calls are idempotent (no side effects) |
| 202 | +- Multiple hide() calls are idempotent (no side effects) |
| 203 | + |
| 204 | +### Disposal |
| 205 | +- Texture disposal is guarded by null checks |
| 206 | +- Font disposal is guarded by null checks |
| 207 | +- Listener unregistration is safe even if not registered |
| 208 | + |
| 209 | +## Testing Strategy |
| 210 | + |
| 211 | +### Unit Tests |
| 212 | + |
| 213 | +Unit tests will verify specific examples and edge cases: |
| 214 | + |
| 215 | +1. **Dialog Construction Test** |
| 216 | + - Verify dialog initializes with isVisible() = false |
| 217 | + - Verify wooden plank texture is created |
| 218 | + - Verify font is loaded or fallback is used |
| 219 | + |
| 220 | +2. **Show/Hide Test** |
| 221 | + - Verify show() sets isVisible() to true |
| 222 | + - Verify hide() sets isVisible() to false |
| 223 | + - Verify multiple show() calls are idempotent |
| 224 | + |
| 225 | +3. **Translation Key Existence Test** |
| 226 | + - Verify all required translation keys exist in en.json |
| 227 | + - Verify all required translation keys exist in all language files |
| 228 | + - Verify no translation keys are missing |
| 229 | + |
| 230 | +4. **Menu Integration Test** |
| 231 | + - Verify "Controls" menu item is added to singleplayer menu |
| 232 | + - Verify "Controls" menu item is added to multiplayer menu |
| 233 | + - Verify MENU_HEIGHT is increased appropriately |
| 234 | + |
| 235 | +### Property-Based Tests |
| 236 | + |
| 237 | +Property-based tests will verify universal properties across all inputs: |
| 238 | + |
| 239 | +**Testing Framework**: We will use [junit-quickcheck](https://github.com/pholser/junit-quickcheck) for Java property-based testing, which integrates with JUnit and provides generators for common types. |
| 240 | + |
| 241 | +**Configuration**: Each property-based test will run a minimum of 100 iterations to ensure thorough coverage of the input space. |
| 242 | + |
| 243 | +**Test Tagging**: Each property-based test will include a comment tag in the format: |
| 244 | +```java |
| 245 | +// Feature: controls-menu-dialog, Property N: [property description] |
| 246 | +``` |
| 247 | + |
| 248 | +1. **Property Test: Dialog Visibility Toggle** |
| 249 | + - Generate random sequences of show()/hide() calls |
| 250 | + - Verify isVisible() state matches expected state after each call |
| 251 | + - **Validates Property 1** |
| 252 | + |
| 253 | +2. **Property Test: ESC Key Closes Dialog** |
| 254 | + - Generate random dialog states (visible/hidden) |
| 255 | + - When visible, simulate ESC input and verify dialog closes |
| 256 | + - **Validates Property 2** |
| 257 | + |
| 258 | +3. **Property Test: Dialog Centering** |
| 259 | + - Generate random camera positions |
| 260 | + - Verify dialog X/Y coordinates are correctly centered |
| 261 | + - **Validates Property 5** |
| 262 | + |
| 263 | +4. **Property Test: Language Change Updates** |
| 264 | + - Generate random language selections from supported languages |
| 265 | + - Verify dialog text changes when language changes |
| 266 | + - **Validates Property 6** |
| 267 | + |
| 268 | +5. **Property Test: Translation Key Completeness** |
| 269 | + - For each required translation key |
| 270 | + - Verify key exists in all language files |
| 271 | + - **Validates Property 7** |
| 272 | + |
| 273 | +6. **Property Test: No Hardcoded Strings** |
| 274 | + - Parse ControlsDialog source code |
| 275 | + - Verify all displayed text uses LocalizationManager.getText() |
| 276 | + - **Validates Property 8** |
| 277 | + |
| 278 | +### Integration Tests |
| 279 | + |
| 280 | +Integration tests will verify the feature works correctly with other system components: |
| 281 | + |
| 282 | +1. **Menu Navigation Integration Test** |
| 283 | + - Open game menu |
| 284 | + - Navigate to Controls item |
| 285 | + - Press Enter and verify dialog opens |
| 286 | + - Press ESC and verify dialog closes |
| 287 | + |
| 288 | +2. **Localization Integration Test** |
| 289 | + - Open Controls Dialog |
| 290 | + - Change language via Language Dialog |
| 291 | + - Verify Controls Dialog text updates automatically |
| 292 | + |
| 293 | +3. **Multi-Dialog Integration Test** |
| 294 | + - Open Controls Dialog |
| 295 | + - Verify other dialogs cannot be opened simultaneously |
| 296 | + - Verify game menu remains accessible after closing dialog |
| 297 | + |
| 298 | +## Implementation Notes |
| 299 | + |
| 300 | +### Visual Consistency |
| 301 | + |
| 302 | +The ControlsDialog must match the visual style of existing dialogs: |
| 303 | +- Use same wooden plank texture generation algorithm |
| 304 | +- Use same font (Sancreek-Regular.ttf) with size 16 |
| 305 | +- Use same color scheme (white for labels, yellow for highlights, light gray for instructions) |
| 306 | +- Use same border styling (dark brown, 2px thick) |
| 307 | + |
| 308 | +### Performance Considerations |
| 309 | + |
| 310 | +- Texture and font are created once in constructor, not per-frame |
| 311 | +- Translation text is retrieved once per render, cached in local variables |
| 312 | +- No complex calculations in render loop |
| 313 | +- Dialog only renders when visible |
| 314 | + |
| 315 | +### Accessibility |
| 316 | + |
| 317 | +- All text uses high-contrast colors (white/yellow on dark brown) |
| 318 | +- Font size is 16pt for readability |
| 319 | +- Controls are organized into clear categories with headers |
| 320 | +- Simple keyboard navigation (only ESC key required) |
| 321 | + |
| 322 | +### Future Extensibility |
| 323 | + |
| 324 | +The design allows for future enhancements: |
| 325 | +- Adding rebindable controls (would require additional UI for key binding) |
| 326 | +- Adding controller support (would add new control categories) |
| 327 | +- Adding tooltips or help text for complex controls |
| 328 | +- Adding search/filter functionality for large control lists |
0 commit comments