Files
cat-haven/INSTRUCTIONS.md
2026-07-23 21:23:01 +00:00

1879 lines
43 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="Cat Haven Tests">
<testsuite name="Core Module" tests="16" failures="0" errors="0">
<testcase name="should initialize game state" classname="core_spec.lua"/>
<testcase name="should spawn cats" classname="core_spec.lua"/>
</testsuite>
</testsuites>
```
**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:**
```
<type>: <description>
[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