# 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