# Cat Haven - Comprehensive Instructions ## Table of Contents 1. [Codebase Overview](#codebase-overview) 2. [Build Instructions](#build-instructions) 3. [Testing Instructions](#testing-instructions) 4. [Development Workflow](#development-workflow) 5. [Architecture Documentation](#architecture-documentation) 6. [API Reference](#api-reference) 7. [Troubleshooting](#troubleshooting) --- ## Codebase Overview ### Project Structure ``` cat_game/ ├── main.lua # LOVR entry point - initializes all modules ├── README.md # Project overview and quick start ├── INSTRUCTIONS.md # This file - comprehensive documentation ├── src/ │ ├── core.lua # Game state, configuration, save/load system │ ├── json.lua # Custom JSON encoder/decoder (DKJSON) │ ├── voxel/ │ │ └── init.lua # Voxel rendering, procedural textures, mesh generation │ ├── cat/ │ │ └── init.lua # Cat AI, behavior, personality logic, state machine │ ├── house/ │ │ └── init.lua # Grid system, furniture placement, collision detection │ ├── ui/ │ │ └── init.lua # HUD rendering, inventory, cat log, interaction │ └── audio/ │ └── init.lua # Procedural audio generation (purr, meow, ambient) ├── test/ │ ├── test_helper.lua # LOVR API mocks and test utilities │ ├── run_tests.lua # Test runner with TAP reporter and XML generation │ ├── spec/ # Unit tests (per module) │ │ ├── core_spec.lua │ │ ├── json_spec.lua │ │ ├── voxel_spec.lua │ │ ├── cat_spec.lua │ │ ├── house_spec.lua │ │ ├── ui_spec.lua │ │ └── audio_spec.lua │ ├── integration/ # Integration tests │ │ └── game_flow_spec.lua │ └── e2e/ # End-to-end edge case tests │ └── edge_cases_spec.lua └── test-report.xml # Generated JUnit XML report (after tests) ``` ### Module Descriptions #### Core Module (`src/core.lua`) **Responsibilities:** - Global game state management - Configuration constants and defaults - Personality definitions (5 types) - Furniture type definitions (5 types) - Cat spawning and removal - Save/load system using JSON - Happiness calculation - Game loop updates - Pause state management **Key Components:** - `core.CONFIG` - Game configuration (grid size, voxel size, spawn intervals) - `core.state` - Mutable game state (cats, furniture, inventory, cat log) - `core.personalities` - 5 personality definitions with traits and compatibility - `core.furnitureTypes` - 5 furniture types with properties and bonuses #### JSON Module (`src/json.lua`) **Responsibilities:** - Custom JSON encoding/decoding - Support for all Lua data types (nil, number, string, boolean, table) - Round-trip validation - Error handling for malformed JSON **Features:** - Deterministic encoding (sorted keys) - Handles nested tables - Proper string escaping - Robust parsing with position tracking #### Voxel Module (`src/voxel/init.lua`) **Responsibilities:** - Procedural texture generation with noise - Voxel cube rendering - Furniture and cat rendering - Raycast for interaction detection - Mesh generation with proper normals and UVs **Features:** - 64×64 pixel procedural textures - Texture caching by ID - AABB collision detection for raycasting - Support for multiple pass rendering #### Cat Module (`src/cat/init.lua`) **Responsibilities:** - Cat object creation with personality - AI behavior state machine - Need management (comfort, social, hunger, fun) - Animation and state updates - Serialization for save/load - Personality compatibility checking **AI Behavior:** - Seeks furniture that satisfies lowest need - Avoids incompatible personalities - Sleeps when comfort is low - Moves toward targets based on needs #### House Module (`src/house/init.lua`) **Responsibilities:** - Grid-based placement system - Furniture creation and management - Collision detection - Furniture upgrades - Serialization/deserialization **Features:** - 10×6×10 grid system - Voxel-based collision detection - 3-level upgrade system - Occupant tracking #### UI Module (`src/ui/init.lua`) **Responsibilities:** - HUD rendering (happiness meter, cat count, time) - Inventory display and interaction - Cat log display - Placement preview - Mouse interaction handling **Features:** - Dynamic scaling based on window size - 3D world raycasting for placement - Interactive inventory selection - Real-time updates #### Audio Module (`src/audio/init.lua`) **Responsibilities:** - Procedural sound generation - Purr, meow, click, and ambient sound creation - Spatial audio support - Sound caching and playback **Features:** - 44.1kHz sample rate - Frequency modulation for realistic sounds - Spatial positioning - Looping ambient sound ### Key Components and Responsibilities | Component | File | Primary Responsibility | |-----------|------|----------------------| | Game State | `core.lua` | Global state, configuration, save/load | | Rendering | `voxel/init.lua` | 3D voxel rendering, textures, meshes | | AI System | `cat/init.lua` | Cat behavior, state machine, needs | | Environment | `house/init.lua` | Grid, furniture, collision | | Interface | `ui/init.lua` | HUD, inventory, interaction | | Audio | `audio/init.lua` | Procedural sound generation | | Serialization | `json.lua` | JSON encoding/decoding | ### Data Flow and Module Interactions ``` ┌─────────────────────────────────────────────────────────────────┐ │ LOVR Engine │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ lovr.load │ │ lovr.update │ │ lovr.draw │ │ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ │ │ │ │ │ └──────────────────┴──────────────────┘ │ │ │ │ │ ┌───────▼───────┐ │ │ │ main.lua │ │ │ └───────┬───────┘ │ │ │ │ │ ┌──────────────────┼──────────────────┐ │ │ │ │ │ │ │ ┌────▼────┐ ┌──────▼──────┐ ┌────▼────┐ │ │ │ core │◄─────┤ house │◄─────┤ cat │ │ │ └────┬────┘ └─────────────┘ └────┬────┘ │ │ │ │ │ │ ┌────▼────┐ ┌─────────────┐ ┌────▼────┐ │ │ │ audio │ │ voxel │ │ ui │ │ │ └─────────┘ └─────────────┘ └─────────┘ │ │ │ │ │ ┌───────▼───────┐ │ │ │ json.lua │ │ │ └───────────────┘ │ └─────────────────────────────────────────────────────────────────┘ ``` **Data Flow:** 1. `lovr.load()` → core.init() → loads all modules 2. `lovr.update(dt)` → core.update(dt) → updates cats, checks departures, calculates happiness 3. `lovr.draw(pass)` → renders voxel world, furniture, cats, UI 4. `lovr.mousepressed()` → UI handles interaction 5. `lovr.keypressed()` → Save/Load with RThumb+L/S, Pause with ESC 6. `lovr.quit()` → autosave game state ### Configuration and State Management #### Configuration (`core.CONFIG`) ```lua { version = '1.0.0', voxelSize = 0.5, -- Size of each voxel cube gridSize = { x = 10, y = 6, z = 10 }, -- Grid dimensions catSpawnInterval = 30, -- Seconds between spawn attempts maxCats = 8, -- Maximum concurrent cats roomDimensions = { width = 5, height = 3, depth = 5 }, lighting = { ambient = { 0.8, 0.75, 0.7 }, sunbeamIntensity = 0.6, warmColor = { 1.0, 0.95, 0.85 } }, happinessFormulas = { comfortWeight = 0.3, socialWeight = 0.2, hungerWeight = 0.2, funWeight = 0.3 } } ``` #### Game State (`core.state`) ```lua { cats = {}, -- Array of cat objects furniture = {}, -- Array of furniture objects house = { rooms = {}, -- 3D grid (x,y,z) totalHappiness = 0, -- Current happiness score totalCats = 0, -- Current cat count history = {} -- Historical events }, inventory = {}, -- Inventory items catLog = {}, -- Cat arrival/departure log gameTime = 0, -- Elapsed game time isPaused = false, -- Pause state camera = { position = { 0, 2, 0 }, orientation = { 0, 0, 0, 1 }, fov = 90 }, selectedFurniture = nil, -- Currently selected inventory item placementMode = false -- Whether in placement mode } ``` #### Personality System **5 Personalities:** 1. **Lone Wolf** - Independent, quiet, territorial - High comfort need, low social need - Compatible with other lone wolves 2. **Social Butterfly** - Friendly, outgoing, curious - High social and fun needs - Compatible with social and clown personalities 3. **Playful Clown** - Mischievous, energetic, attention seeker - High social, hunger, and fun needs - Compatible with social and clown 4. **Greedy Eater** - Foodie, persistent, demanding - Very high hunger need - Moderate compatibility with others 5. **Lazy Sleeper** - Relaxed, comfort lover, slow - Very high comfort need, low social and fun needs - Compatible with other lazy cats #### Furniture System **5 Furniture Types:** 1. **Cat Tree** - High comfort, social, and fun bonus 2. **Scratching Post** - High fun bonus 3. **Cozy Bed** - High comfort bonus 4. **Window Seat** - Good comfort and social bonus 5. **Food Bowl** - Base happiness only (hunger handled separately) --- ## Build Instructions ### Prerequisites #### Required Software 1. **LOVR 2023 (v1.4)** - Download: https://lovr.org/ - Required for running the game - Must support Lua 5.4 - Required for VR features (optional but recommended) 2. **Lua 5.4** - Required for running tests - Available on most Linux distributions - Windows: Download from https://luabinaries.sourceforge.net/ - macOS: `brew install lua` 3. **Busted (Test Framework)** - Required for running tests - Install via LuaRocks: `luarocks install busted` - Or download from https://lunarmodules.github.io/busted/ #### Optional Dependencies - **LuaRocks** - Package manager for Lua modules - **LuaJIT** - Faster Lua execution (optional, for testing) ### Building and Running #### Running the Game **Method 1: Using LOVR executable** ```bash # From project root lovr ./ # Or specify path explicitly lovr /path/to/cat_game/ ``` **Method 2: Using LOVR API** ```bash # If you have LOVR installed as a library lua -e "require('lovr').boot('main.lua')" ``` **Method 3: Development mode** ```bash # Edit and run without restarting # Just press R in LOVR to reload (if supported) ``` #### Running Tests **All tests:** ```bash lua test/run_tests.lua ``` **Specific test file:** ```bash lua test/spec/json_spec.lua lua test/spec/core_spec.lua ``` **With LuaJIT (faster):** ```bash luajit test/run_tests.lua ``` **With Busted directly:** ```bash busted test/spec/ busted test/integration/ busted test/e2e/ ``` ### Development Setup #### IDE Configuration **VS Code (Recommended):** ```json // .vscode/settings.json { "lua.diagnostics.globals": [ "lovr", "describe", "it", "assert", "assert_truthy", "assert_falsy" ], "lua.workspace.library": [ "/path/to/lovr/include", "test" ] } ``` **Neovim:** ```vim " Add to init.vim set omnifunc=lua#complete#Complete let g:lua_language_server = { \ 'workspace': {'library': [ \ expand('$VIMRUNTIME'), \ 'test', \ 'src' \ ]}, \ 'runtime': {'version': 'LuaJIT'}, \ } ``` #### Testing Setup **Install Busted:** ```bash # Using LuaRocks luarocks install busted # Or download manually wget https://github.com/Olivine-Labs/busted/archive/master.zip unzip master.zip cd busted-master sudo make install ``` **Verify installation:** ```bash busted --version ``` #### Environment Variables No environment variables required, but you can set: ```bash # For debugging export LOVR_DEBUG=1 # For specific graphics backend export LOVR_BACKEND=vulkan ``` ### Common Build Issues and Solutions #### Issue 1: "module 'lovr' not found" **Error:** ``` no field package.preload['lovr'] no file './lovr.lua' no file '/usr/local/share/lua/5.4/lovr.lua' ``` **Solution:** - Ensure LOVR is installed and in PATH - Run with `lovr .` instead of `lua main.lua` - Check LOVR version: `lovr --version` #### Issue 2: "busted: command not found" **Solution:** ```bash # Install LuaRocks first sudo apt install luarocks # Ubuntu/Debian brew install luarocks # macOS # Then install busted luarocks install busted # Or use Lua directly lua test/run_tests.lua ``` #### Issue 3: "attempt to call field 'newImage' (a nil value)" **Solution:** - This indicates LOVR API mismatch - Ensure you're using LOVR 2023 (v1.4) - Update LOVR: `lovr update` #### Issue 4: Tests fail with "attempt to index global 'core'" **Solution:** - This is expected in some edge case tests - Tests should handle nil core gracefully - Check test_helper.lua for proper mocking #### Issue 5: Audio not working **Solution:** - Check audio device is selected - Verify LOVR audio is enabled - Test with: `lovr.audio.getSources()` #### Issue 6: Graphics rendering issues **Solution:** - Update graphics drivers - Try different backend: `lovr --backend opengl` - Check GPU supports OpenGL 3.3+ --- ## Testing Instructions ### Running All Tests **Basic test run:** ```bash lua test/run_tests.lua ``` **Expected output:** ``` ======================================== Test Summary ======================================== Suites: 7 Tests: 150 Passed: 150 Failed: 0 Errors: 0 Skipped: 0 ✅ All tests passed ``` **With verbose output:** ```bash lua test/run_tests.lua 2>&1 | grep -A 1 "describe\|it\|passed\|failed" ``` ### Running Specific Test Suites **Unit tests only:** ```bash lua test/spec/core_spec.lua lua test/spec/cat_spec.lua lua test/spec/house_spec.lua lua test/spec/ui_spec.lua lua test/spec/audio_spec.lua lua test/spec/voxel_spec.lua lua test/spec/json_spec.lua ``` **Integration tests:** ```bash lua test/integration/game_flow_spec.lua ``` **End-to-end tests:** ```bash lua test/e2e/edge_cases_spec.lua ``` **With Busted filters:** ```bash # Run tests matching pattern busted test/spec/ -m "cat.*spawn" # Run specific test file busted test/spec/core_spec.lua # Run with coverage busted test/ --coverage ``` ### Test Coverage Expectations **Coverage Areas:** | Module | Coverage Focus | Expected Coverage | |--------|---------------|-------------------| | JSON | Encoding/decoding, edge cases, round-trip | 100% | | Voxel | Texture generation, mesh creation, raycast | 95% | | Cat | Creation, AI, needs, serialization | 95% | | House | Grid system, collision, upgrades | 95% | | UI | HUD, inventory, interaction | 90% | | Audio | Sound generation, playback | 90% | | Core | State management, save/load, happiness | 95% | **Coverage Tools:** ```bash # Install luacov for coverage analysis luarocks install luacov # Run with coverage luacov test/run_tests.lua # Generate report lua -luacov ``` ### How to Add New Tests **Step 1: Create test file** ```bash # For new module touch test/spec/newmodule_spec.lua # For new feature touch test/integration/new_feature_spec.lua ``` **Step 2: Write test using Busted syntax** ```lua -- test/spec/newmodule_spec.lua local test_helper = require('test.test_helper') local mocks = test_helper.mocks local utils = test_helper.utils describe('New Module', function() local newmodule setup(function() mocks.setupFilesystem() newmodule = require('src.newmodule') end) it('should do something', function() local result = newmodule.doSomething() assert.equals('expected', result) end) it('should handle edge cases', function() local result = newmodule.doSomething(nil) assert_falsy(result) end) it('should integrate with other modules', function() local core = require('src.core') core.init() local result = newmodule.interactWithCore() assert_truthy(result) end) end) ``` **Step 3: Add to test runner (if needed)** ```lua -- test/run_tests.lua local testDirs = { 'test/spec', 'test/integration', 'test/e2e', 'test/new_tests' -- Add new directory here } ``` **Step 4: Run and verify** ```bash lua test/spec/newmodule_spec.lua ``` ### Test Reporting and Interpretation **Test Output Format:** ``` describe('Core Module') it('should initialize game state')............. passed (0.001s) it('should spawn cats')........................ passed (0.001s) it('should handle nil values')................. passed (0.001s) it('should handle edge cases')................. passed (0.001s) Suites: 7 Tests: 150 Passed: 150 Failed: 0 Errors: 0 Skipped: 0 ``` **XML Report Structure:** ```xml ``` **Interpreting Results:** | Status | Meaning | Action | |--------|---------|--------| | Passed | Test executed successfully | No action needed | | Failed | Assertion failed | Fix code or test | | Error | Exception thrown | Fix code or test setup | | Skipped | Test explicitly skipped | Review if intentional | **CI/CD Integration:** ```yaml # .github/workflows/test.yml name: Run Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install Lua uses: leafo/gh-actions-lua@v9 - name: Install Busted uses: leafo/gh-actions-luarocks@v4 - name: Run Tests run: lua test/run_tests.lua - name: Upload Test Report uses: actions/upload-artifact@v3 with: name: test-report path: test-report.xml ``` --- ## Development Workflow ### Adding New Features **Step 1: Plan the feature** - Determine which module(s) need modification - Define the feature's scope and requirements - Consider test coverage needs **Step 2: Create test-first** ```lua -- Write tests first describe('New Feature', function() it('should implement feature X') it('should handle edge cases') end) ``` **Step 3: Implement in module** - Follow existing code patterns - Use proper error handling - Add comments only if requested **Step 4: Update documentation** - Update this INSTRUCTIONS.md if needed - Update README.md for user-facing changes **Step 5: Test and iterate** ```bash # Run specific test lua test/spec/newfeature_spec.lua # Run all tests lua test/run_tests.lua ``` ### Modifying Existing Modules **Guidelines:** 1. **Read existing code first** - Understand the current implementation - Identify side effects - Check test coverage 2. **Update tests before code** - Add tests for new behavior - Update existing tests if behavior changes 3. **Maintain consistency** - Follow naming conventions - Use existing patterns - Preserve error handling 4. **Test thoroughly** ```bash # Test the specific module lua test/spec/core_spec.lua # Test integration lua test/integration/game_flow_spec.lua # Test edge cases lua test/e2e/edge_cases_spec.lua ``` ### Debugging Tips **1. Use print statements** ```lua -- Debug module loading print('Loading core module') -- Debug values print('Current state:', json.encode(core.state)) ``` **2. LOVR console output** ```bash # Enable verbose output lovr . --verbose # Check for errors lovr . 2>&1 | grep -i error ``` **3. Test helper utilities** ```lua -- Use test helper for debugging local utils = test_helper.utils utils.assertDeepEqual(actual, expected, 'Debug message') ``` **4. Check module loading** ```lua -- Verify module is loaded assert(core, 'Core module not loaded') assert(cat, 'Cat module not loaded') ``` **5. Validate data structures** ```lua -- Check table structure for k, v in pairs(core.state) do print(string.format('%s: %s', k, type(v))) end ``` ### Code Style Guidelines **Naming Conventions:** - **Modules**: lowercase with underscores (`core.lua`, `json.lua`) - **Functions**: camelCase (`updateCats`, `createFurniture`) - **Constants**: UPPER_SNAKE_CASE (`MAX_CATS`, `VOXEL_SIZE`) - **Variables**: camelCase (`catCount`, `happinessScore`) - **Classes**: PascalCase (`HouseFurniture`, `Cat`) **File Structure:** ```lua -- 1. Module declaration local moduleName = {} -- 2. Private variables (local) local privateVar = nil -- 3. Class definitions local ClassName = {} ClassName.__index = ClassName -- 4. Public functions function moduleName.publicFunction() -- implementation end -- 5. Return module return moduleName ``` **Error Handling:** ```lua -- Check for nil if not core then return end -- Check parameters if not position then return nil end -- Return error info return false, 'error message' -- Handle gracefully local result, err = someOperation() if not result then print('Operation failed:', err) return nil end ``` **Comments:** - Use comments only when requested - Explain why, not what - Keep comments up to date **Line Length:** - Maximum 100 characters - Break long lines logically ### Commit Conventions **Format:** ``` : [optional body] [optional footer] ``` **Types:** - `feat`: New feature - `fix`: Bug fix - `docs`: Documentation changes - `test`: Test additions/changes - `refactor`: Code refactoring - `perf`: Performance improvements - `chore`: Maintenance tasks **Examples:** ``` feat: add new furniture type fix: handle nil cat in happiness calculation test: add edge case tests for audio docs: update INSTRUCTIONS.md with testing info refactor: simplify JSON encoding logic ``` --- ## Architecture Documentation ### Module Dependency Diagram ``` ┌─────────────────────────────────────────────────────────────────────┐ │ LOVR Runtime │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ lovr.load() │ │lovr.update()│ │ lovr.draw() │ │lovr.quit() │ │ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ │ │ │ └─────────────────┴─────────────────┴─────────────────┘ │ │ │ │ │ ┌──────▼───────┐ │ │ │ main.lua │ │ │ └──────┬───────┘ │ └───────────────────────────┼───────────────────────────────────────────┘ │ ┌───────────────────┼───────────────────┐ │ │ │ ┌────▼────┐ ┌─────▼─────┐ ┌────▼────┐ │ core │◄───────┤ house │◄───────┤ cat │ └────┬────┘ └─────┬─────┘ └────┬────┘ │ │ │ ┌────▼────┐ ┌─────▼─────┐ ┌────▼────┐ │ audio │ │ voxel │ │ ui │ └─────────┘ └─────┬─────┘ └────┬────┘ │ │ ┌───────▼───────┐ │ │ json.lua │◄──────────┘ └───────────────┘ ``` **Dependency Directions:** - **core** → house, cat, ui, voxel, audio (loads them) - **house** → core (reads furniture types, state) - **cat** → core (reads personalities, config), house (placement) - **ui** → core (reads state, config), voxel (raycast), house (interaction) - **voxel** → core (reads furniture, cats, config) - **audio** → core (reads position, config) - **json** → standalone (no dependencies) ### State Management Patterns **Global State (`core.state`):** - Single source of truth for game data - Mutable but controlled via functions - Saved to disk using JSON **State Access Pattern:** ```lua -- Read state local catCount = #core.state.cats -- Modify state via functions core.spawnCat() core.addFurniture('bed', position) -- Access nested state local happiness = core.state.house.totalHappiness ``` **State Isolation:** - Each module has its own state table - Modules don't share state directly - Communication via core module **State Persistence:** ```lua -- Save core.saveGame() → JSON.encode() → file write -- Load file read → JSON.decode() → core.restoreState() ``` ### Rendering Pipeline **LOVR Rendering Flow:** ``` 1. lovr.load() └─> core.init() └─> voxel.init() -- Create textures └─> audio.init() -- Create sounds 2. lovr.update(dt) └─> core.update(dt) └─> cat.updateCats() -- Update cat positions └─> house.update() -- Update house state 3. lovr.draw(pass) └─> voxel.renderWorld(pass) -- Floor, walls └─> house.renderFurniture(pass) -- Furniture └─> cat.renderCats(pass) -- Cats └─> ui.renderHUD(pass) -- HUD overlay 4. lovr.quit() └─> core.saveGame() -- Autosave ``` **Voxel Rendering Details:** ```lua -- Texture generation createVoxelTexture(color) → 64×64 RGBA image → LOVR texture -- Mesh generation createCubeMesh() → 24 vertices, 36 indices → LOVR mesh -- Rendering pass:box(mode, x, y, z, w, h, d, color, thickness) ``` ### AI Behavior Flow **Cat AI State Machine:** ``` ┌─────────────┐ │ Sleeping │ │ (comfort │ │ < 0.3) │ └──────┬──────┘ │ │ Timer expires ▼ ┌─────────────┐ │ Idle │ │ (waking │ │ up) │ └──────┬──────┘ │ │ Need unmet ▼ ┌─────────────┐ │ Moving │ │ (seeking │ │ furniture) │ └──────┬──────┘ │ │ Reached target ▼ ┌─────────────┐ │ Active │ │ (interacting│ │ with │ │ furniture) │ └─────────────┘ ``` **Need-Based Behavior:** ```lua -- Update needs over time comfort = comfort - dt * 0.02 social = social - dt * 0.015 hunger = hunger - dt * 0.025 fun = fun - dt * 0.02 -- AI seeks furniture that satisfies lowest need if comfort < 0.5 then seek comfort furniture elseif fun < 0.5 then seek fun furniture elseif social < 0.5 then seek social furniture end ``` **Personality Compatibility:** ```lua -- Check nearby cats for each nearby cat: compatibility = personality.compatibility[other.personality] if compatibility < -0.3: reduce social need (stress) elseif compatibility > 0.3: increase social need (pleasure) ``` ### Save/Load Mechanism **Save Process:** ```lua 1. core.saveGame() ├─> Serialize cats (cat:serialize()) ├─> Serialize furniture (furn:serialize()) ├─> Encode to JSON (json.encode()) └─> Write to file (lovr.filesystem) 2. JSON structure: { "version": "1.0.0", "gameTime": 1234.56, "cats": [...], "furniture": [...], "house": {...}, "catLog": [...] } ``` **Load Process:** ```lua 1. core.loadGame() ├─> Read file (lovr.filesystem) ├─> Decode JSON (json.decode()) ├─> Deserialize cats (cat.deserialize()) ├─> Deserialize furniture (house.deserializeFurniture()) └─> Restore state 2. Validation: - Check version compatibility - Handle missing fields - Default values for old saves ``` **Data Serialization:** ```lua -- Cat serialization function Cat:serialize() return { id = self.id, name = self.name, personalityId = self.personality.id, position = self.position, state = self.state, mood = self.mood, needs = self.needs, -- ... other fields } end -- Furniture serialization function HouseFurniture:serialize() return { typeId = self.type.id, position = self.position, upgradeLevel = self.upgradeLevel, -- ... other fields } end ``` --- ## API Reference ### Core Module Functions #### `core.init()` Initializes the game state and all modules. **Returns:** `nil` **Side Effects:** - Initializes house grid - Spawns initial cats - Creates voxel textures - Loads audio **Example:** ```lua core.init() -- Game is now ready to play ``` #### `core.update(dt)` Updates the game state each frame. **Parameters:** - `dt` (number): Delta time in seconds **Returns:** `nil` **Example:** ```lua core.update(0.016) -- ~60 FPS ``` #### `core.spawnCat()` Spawns a new cat with random personality. **Returns:** `nil` **Example:** ```lua core.spawnCat() -- New cat added to core.state.cats ``` #### `core.removeCat(catId)` Removes a cat by ID. **Parameters:** - `catId` (number): Cat ID to remove **Returns:** `boolean` - true if cat was removed **Example:** ```lua local success = core.removeCat(1) if success then print('Cat removed') end ``` #### `core.addFurniture(typeId, position)` Adds furniture to the game. **Parameters:** - `typeId` (string): Furniture type ID - `position` (table): {x, y, z} position **Returns:** `table|false` - Furniture object or false **Example:** ```lua local furn = core.addFurniture('bed', {x=0, y=0, z=0}) if furn then print('Furniture added') end ``` #### `core.saveGame()` Saves the current game state. **Returns:** `boolean` - true if save successful **Example:** ```lua local success = core.saveGame() ``` #### `core.loadGame()` Loads a saved game state. **Returns:** `boolean` - true if load successful **Example:** ```lua local success = core.loadGame() ``` #### `core.togglePause()` Toggles pause state. **Returns:** `nil` **Example:** ```lua core.togglePause() ``` #### `core.getHappinessPercentage()` Gets current house happiness as percentage. **Returns:** `number` - 0-100 **Example:** ```lua local happiness = core.getHappinessPercentage() print(string.format('Happiness: %d%%', happiness)) ``` #### `core.calculateHouseHappiness()` Calculates overall house happiness. **Returns:** `nil` **Example:** ```lua core.calculateHouseHappiness() print(core.state.house.totalHappiness) ``` #### `core.checkCatDepartures()` Checks for cats that should leave (low happiness). **Returns:** `nil` **Example:** ```lua core.checkCatDepartures() ``` ### Cat Module Functions #### `cat.createCat(id, name, personality)` Creates a new cat object. **Parameters:** - `id` (number): Unique cat ID - `name` (string): Cat name - `personality` (table): Personality definition **Returns:** `table` - Cat object **Example:** ```lua local personality = core.personalities[1] local cat = cat.createCat(1, 'Mittens', personality) ``` #### `cat.updateCats(dt)` Updates all cats. **Parameters:** - `dt` (number): Delta time **Returns:** `nil` **Example:** ```lua cat.updateCats(0.016) ``` #### `cat.deserialize(data)` Deserializes a cat from save data. **Parameters:** - `data` (table): Serialized cat data **Returns:** `table|nil` - Cat object or nil **Example:** ```lua local cat = cat.deserialize(savedData) ``` #### `cat.getCatAnimation(catObj, time)` Gets animation values for a cat. **Parameters:** - `catObj` (table): Cat object - `time` (number): Animation time **Returns:** `table` - {bounce, rotate} **Example:** ```lua local anim = cat.getCatAnimation(cat, lovr.timer.getTime()) cat.position.y = baseY + anim.bounce ``` ### House Module Functions #### `house.createFurniture(type, position)` Creates a furniture object. **Parameters:** - `type` (table): Furniture type definition - `position` (table): {x, y, z} position **Returns:** `table|nil` - Furniture object or nil **Example:** ```lua local type = core.furnitureTypes[1] local furn = house.createFurniture(type, {x=0, y=0, z=0}) ``` #### `house.canPlaceFurniture(position, size)` Checks if furniture can be placed at position. **Parameters:** - `position` (table): {x, y, z} position - `size` (table): {w, h, d} size **Returns:** `boolean, string` - Can place, reason **Example:** ```lua local canPlace, reason = house.canPlaceFurniture(pos, size) if not canPlace then print('Cannot place:', reason) end ``` #### `house.placeFurniture(typeId, position)` Places furniture at position. **Parameters:** - `typeId` (string): Furniture type ID - `position` (table): {x, y, z} position **Returns:** `table|nil` - Furniture object or nil **Example:** ```lua local furn = house.placeFurniture('bed', {x=0, y=0, z=0}) ``` #### `house.checkCollision(position, size)` Checks for collision with existing furniture. **Parameters:** - `position` (table): {x, y, z} position - `size` (table): {w, h, d} size **Returns:** `boolean` - true if collision **Example:** ```lua local collision = house.checkCollision(pos, size) ``` #### `house.interactWithFurniture(position)` Gets furniture at position. **Parameters:** - `position` (table): {x, y, z} position **Returns:** `table|nil` - Furniture object or nil **Example:** ```lua local furn = house.interactWithFurniture(pos) if furn then print('Clicked:', furn.type.name) end ``` #### `house.deserializeFurniture(data)` Deserializes furniture from save data. **Parameters:** - `data` (table): Serialized furniture data **Returns:** `table|nil` - Furniture object or nil **Example:** ```lua local furn = house.deserializeFurniture(savedData) ``` ### Voxel Module Functions #### `voxel.init()` Initializes voxel textures. **Returns:** `nil` **Example:** ```lua voxel.init() ``` #### `voxel.renderWorld(pass)` Renders the voxel world. **Parameters:** - `pass` (table): LOVR render pass **Returns:** `nil` **Example:** ```lua function lovr.draw(pass) voxel.renderWorld(pass) end ``` #### `voxel.renderFurniture(pass)` Renders all furniture. **Parameters:** - `pass` (table): LOVR render pass **Returns:** `nil` **Example:** ```lua function lovr.draw(pass) voxel.renderFurniture(pass) end ``` #### `voxel.renderCats(pass)` Renders all cats. **Parameters:** - `pass` (table): LOVR render pass **Returns:** `nil` **Example:** ```lua function lovr.draw(pass) voxel.renderCats(pass) end ``` #### `voxel.raycast(start, direction, maxDistance)` Performs raycast through world. **Parameters:** - `start` (table): {x, y, z} ray start - `direction` (table): {x, y, z} ray direction - `maxDistance` (number): Maximum distance **Returns:** `table` - Array of hit results **Example:** ```lua local results = voxel.raycast(start, direction, 10) if #results > 0 then local hit = results[1] print('Hit:', hit.type) end ``` ### UI Module Functions #### `ui.renderHUD(pass)` Renders the HUD. **Parameters:** - `pass` (table): LOVR render pass **Returns:** `nil` **Example:** ```lua function lovr.draw(pass) ui.renderHUD(pass) end ``` #### `ui.handleMousePress(x, y, button)` Handles mouse click. **Parameters:** - `x` (number): Mouse X position - `y` (number): Mouse Y position - `button` (number): Button index **Returns:** `nil` **Example:** ```lua function lovr.mousepressed(x, y, button) ui.handleMousePress(x, y, button) end ``` #### `ui.updateHUD()` Updates dynamic HUD elements. **Returns:** `nil` **Example:** ```lua function core.update(dt) ui.updateHUD() end ``` #### `ui.toggleInventory()` Toggles inventory visibility. **Returns:** `nil` **Example:** ```lua ui.toggleInventory() ``` #### `ui.toggleCatLog()` Toggles cat log visibility. **Returns:** `nil` **Example:** ```lua ui.toggleCatLog() ``` ### Audio Module Functions #### `audio.init()` Initializes audio system. **Returns:** `nil` **Example:** ```lua audio.init() ``` #### `audio.createPurrSound()` Creates purr sound source. **Returns:** `table|nil` - Sound source or nil **Example:** ```lua local purr = audio.createPurrSound() ``` #### `audio.createMeowSound()` Creates meow sound source. **Returns:** `table|nil` - Sound source or nil **Example:** ```lua local meow = audio.createMeowSound() ``` #### `audio.createClickSound()` Creates click sound source. **Returns:** `table|nil` - Sound source or nil **Example:** ```lua local click = audio.createClickSound() ``` #### `audio.createAmbientSound()` Creates ambient room sound. **Returns:** `table|nil` - Sound source or nil **Example:** ```lua local ambient = audio.createAmbientSound() ``` #### `audio.playPurr(position)` Plays purr at position. **Parameters:** - `position` (table): {x, y, z} position **Returns:** `nil` **Example:** ```lua audio.playPurr(cat.position) ``` #### `audio.playMeow(position)` Plays meow at position. **Parameters:** - `position` (table): {x, y, z} position **Returns:** `nil` **Example:** ```lua audio.playMeow(cat.position) ``` #### `audio.playClick()` Plays click sound. **Returns:** `nil` **Example:** ```lua audio.playClick() ``` --- ## Troubleshooting ### Common Errors and Solutions #### Error: "attempt to index global 'core' (a nil value)" **Cause:** Module not loaded or initialization order issue **Solution:** ```lua -- Check module is loaded if not core then core = require('src.core') core.init() end ``` #### Error: "invalid key to 'next'" **Cause:** Modifying table while iterating **Solution:** ```lua -- Iterate backwards for removal for i = #array, 1, -1 do if shouldRemove(array[i]) then table.remove(array, i) end end ``` #### Error: "out of memory" **Cause:** Texture or mesh allocation failure **Solution:** ```lua -- Check texture limits local maxTextures = lovr.graphics.getStats().maxTextures print('Max textures:', maxTextures) -- Free unused textures voxel.textures = nil ``` #### Error: "mesh has too many vertices" **Cause:** Mesh exceeds GPU limits **Solution:** ```lua -- Use smaller meshes local maxVertices = lovr.graphics.getStats().maxVertices print('Max vertices:', maxVertices) -- Split large meshes ``` #### Error: "file not found: savegame.json" **Cause:** No save file exists **Solution:** ```lua -- Check before loading local file = lovr.filesystem.newFile('savegame.json', 'r') if file then file:close() core.loadGame() else print('No save file found') end ``` ### Debugging Tips **1. Enable LOVR Debug Mode** ```bash lovr . --verbose ``` **2. Add Debug Logging** ```lua -- Debug module loading print('Loading:', debug.info(2, 's')) -- Debug function calls print('Function:', debug.info(2, 'n')) -- Debug variable values print('Value:', type(value), json.encode(value)) ``` **3. Use Test Helper** ```lua local utils = test_helper.utils -- Deep equality check utils.assertDeepEqual(actual, expected, 'Debug message') -- Mock pass for rendering tests local pass = utils.createMockPass() ``` **4. Check LOVR Stats** ```lua local stats = lovr.graphics.getStats() print('Draw calls:', stats.drawCalls) print('Textures:', stats.textures) print('Meshes:', stats.meshes) ``` ### Performance Optimization **1. Reduce Draw Calls** ```lua -- Batch similar objects -- Use instanced rendering if available -- Combine meshes when possible ``` **2. Optimize Texture Usage** ```lua -- Use smaller textures -- Reuse textures between objects -- Compress textures if supported ``` **3. Limit Object Count** ```lua -- Cap furniture count -- Use object pooling for cats -- Remove off-screen objects ``` **4. Profile Code** ```lua -- Simple timing local start = lovr.timer.getTime() -- ... code ... local elapsed = lovr.timer.getTime() - start print(string.format('Took %.3fms', elapsed * 1000)) ``` ### Memory Management **1. Monitor Memory Usage** ```lua -- Check memory local mem = collectgarbage('count') print('Memory:', mem, 'KB') ``` **2. Clean Up Unused Data** ```lua -- Clear texture cache voxel.textures = {} -- Reset state core.state = { cats = {}, furniture = {}, -- ... reset other fields } ``` **3. Use Weak References** ```lua -- For caches local cache = setmetatable({}, {__mode = 'v'}) ``` **4. Avoid Memory Leaks** ```lua -- Always remove event listeners -- Clear timers -- Close file handles ``` --- ## Additional Resources ### LOVR Documentation - https://lovr.org/docs - https://lovr.org/guide ### Lua Programming - https://www.lua.org/manual/5.4/ - http://lua-users.org/wiki/ ### Busted Testing - https://lunarmodules.github.io/busted/ - https://github.com/Olivine-Labs/busted ### JSON Specification - https://www.json.org/ - https://datatracker.ietf.org/doc/html/rfc8259 --- **Last Updated:** July 2026 **Version:** 1.0.0 **Maintainer:** Cat Haven Team