43 KiB
Cat Haven - Comprehensive Instructions
Table of Contents
- Codebase Overview
- Build Instructions
- Testing Instructions
- Development Workflow
- Architecture Documentation
- API Reference
- 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 compatibilitycore.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:
lovr.load()→ core.init() → loads all moduleslovr.update(dt)→ core.update(dt) → updates cats, checks departures, calculates happinesslovr.draw(pass)→ renders voxel world, furniture, cats, UIlovr.mousepressed()→ UI handles interactionlovr.keypressed()→ Save/Load with RThumb+L/S, Pause with ESClovr.quit()→ autosave game state
Configuration and State Management
Configuration (core.CONFIG)
{
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)
{
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:
- Lone Wolf - Independent, quiet, territorial
- High comfort need, low social need
- Compatible with other lone wolves
- Social Butterfly - Friendly, outgoing, curious
- High social and fun needs
- Compatible with social and clown personalities
- Playful Clown - Mischievous, energetic, attention seeker
- High social, hunger, and fun needs
- Compatible with social and clown
- Greedy Eater - Foodie, persistent, demanding
- Very high hunger need
- Moderate compatibility with others
- 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:
- Cat Tree - High comfort, social, and fun bonus
- Scratching Post - High fun bonus
- Cozy Bed - High comfort bonus
- Window Seat - Good comfort and social bonus
- Food Bowl - Base happiness only (hunger handled separately)
Build Instructions
Prerequisites
Required Software
-
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)
-
Lua 5.4
- Required for running tests
- Available on most Linux distributions
- Windows: Download from https://luabinaries.sourceforge.net/
- macOS:
brew install lua
-
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
# From project root
lovr ./
# Or specify path explicitly
lovr /path/to/cat_game/
Method 2: Using LOVR API
# If you have LOVR installed as a library
lua -e "require('lovr').boot('main.lua')"
Method 3: Development mode
# Edit and run without restarting
# Just press R in LOVR to reload (if supported)
Running Tests
All tests:
lua test/run_tests.lua
Specific test file:
lua test/spec/json_spec.lua
lua test/spec/core_spec.lua
With LuaJIT (faster):
luajit test/run_tests.lua
With Busted directly:
busted test/spec/
busted test/integration/
busted test/e2e/
Development Setup
IDE Configuration
VS Code (Recommended):
// .vscode/settings.json
{
"lua.diagnostics.globals": [
"lovr",
"describe",
"it",
"assert",
"assert_truthy",
"assert_falsy"
],
"lua.workspace.library": [
"/path/to/lovr/include",
"test"
]
}
Neovim:
" 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:
# 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:
busted --version
Environment Variables
No environment variables required, but you can set:
# 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 oflua main.lua - Check LOVR version:
lovr --version
Issue 2: "busted: command not found"
Solution:
# 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:
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:
lua test/run_tests.lua 2>&1 | grep -A 1 "describe\|it\|passed\|failed"
Running Specific Test Suites
Unit tests only:
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:
lua test/integration/game_flow_spec.lua
End-to-end tests:
lua test/e2e/edge_cases_spec.lua
With Busted filters:
# 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:
# 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
# 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
-- 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)
-- test/run_tests.lua
local testDirs = {
'test/spec',
'test/integration',
'test/e2e',
'test/new_tests' -- Add new directory here
}
Step 4: Run and verify
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 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:
# .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
-- 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
# Run specific test
lua test/spec/newfeature_spec.lua
# Run all tests
lua test/run_tests.lua
Modifying Existing Modules
Guidelines:
-
Read existing code first
- Understand the current implementation
- Identify side effects
- Check test coverage
-
Update tests before code
- Add tests for new behavior
- Update existing tests if behavior changes
-
Maintain consistency
- Follow naming conventions
- Use existing patterns
- Preserve error handling
-
Test thoroughly
# 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
-- Debug module loading
print('Loading core module')
-- Debug values
print('Current state:', json.encode(core.state))
2. LOVR console output
# Enable verbose output
lovr . --verbose
# Check for errors
lovr . 2>&1 | grep -i error
3. Test helper utilities
-- Use test helper for debugging
local utils = test_helper.utils
utils.assertDeepEqual(actual, expected, 'Debug message')
4. Check module loading
-- Verify module is loaded
assert(core, 'Core module not loaded')
assert(cat, 'Cat module not loaded')
5. Validate data structures
-- 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:
-- 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:
-- 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 featurefix: Bug fixdocs: Documentation changestest: Test additions/changesrefactor: Code refactoringperf: Performance improvementschore: 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:
-- 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:
-- 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:
-- 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:
-- 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:
-- 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:
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:
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:
-- 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:
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:
core.update(0.016) -- ~60 FPS
core.spawnCat()
Spawns a new cat with random personality.
Returns: nil
Example:
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:
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 IDposition(table): {x, y, z} position
Returns: table|false - Furniture object or false
Example:
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:
local success = core.saveGame()
core.loadGame()
Loads a saved game state.
Returns: boolean - true if load successful
Example:
local success = core.loadGame()
core.togglePause()
Toggles pause state.
Returns: nil
Example:
core.togglePause()
core.getHappinessPercentage()
Gets current house happiness as percentage.
Returns: number - 0-100
Example:
local happiness = core.getHappinessPercentage()
print(string.format('Happiness: %d%%', happiness))
core.calculateHouseHappiness()
Calculates overall house happiness.
Returns: nil
Example:
core.calculateHouseHappiness()
print(core.state.house.totalHappiness)
core.checkCatDepartures()
Checks for cats that should leave (low happiness).
Returns: nil
Example:
core.checkCatDepartures()
Cat Module Functions
cat.createCat(id, name, personality)
Creates a new cat object.
Parameters:
id(number): Unique cat IDname(string): Cat namepersonality(table): Personality definition
Returns: table - Cat object
Example:
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:
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:
local cat = cat.deserialize(savedData)
cat.getCatAnimation(catObj, time)
Gets animation values for a cat.
Parameters:
catObj(table): Cat objecttime(number): Animation time
Returns: table - {bounce, rotate}
Example:
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 definitionposition(table): {x, y, z} position
Returns: table|nil - Furniture object or nil
Example:
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} positionsize(table): {w, h, d} size
Returns: boolean, string - Can place, reason
Example:
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 IDposition(table): {x, y, z} position
Returns: table|nil - Furniture object or nil
Example:
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} positionsize(table): {w, h, d} size
Returns: boolean - true if collision
Example:
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:
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:
local furn = house.deserializeFurniture(savedData)
Voxel Module Functions
voxel.init()
Initializes voxel textures.
Returns: nil
Example:
voxel.init()
voxel.renderWorld(pass)
Renders the voxel world.
Parameters:
pass(table): LOVR render pass
Returns: nil
Example:
function lovr.draw(pass)
voxel.renderWorld(pass)
end
voxel.renderFurniture(pass)
Renders all furniture.
Parameters:
pass(table): LOVR render pass
Returns: nil
Example:
function lovr.draw(pass)
voxel.renderFurniture(pass)
end
voxel.renderCats(pass)
Renders all cats.
Parameters:
pass(table): LOVR render pass
Returns: nil
Example:
function lovr.draw(pass)
voxel.renderCats(pass)
end
voxel.raycast(start, direction, maxDistance)
Performs raycast through world.
Parameters:
start(table): {x, y, z} ray startdirection(table): {x, y, z} ray directionmaxDistance(number): Maximum distance
Returns: table - Array of hit results
Example:
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:
function lovr.draw(pass)
ui.renderHUD(pass)
end
ui.handleMousePress(x, y, button)
Handles mouse click.
Parameters:
x(number): Mouse X positiony(number): Mouse Y positionbutton(number): Button index
Returns: nil
Example:
function lovr.mousepressed(x, y, button)
ui.handleMousePress(x, y, button)
end
ui.updateHUD()
Updates dynamic HUD elements.
Returns: nil
Example:
function core.update(dt)
ui.updateHUD()
end
ui.toggleInventory()
Toggles inventory visibility.
Returns: nil
Example:
ui.toggleInventory()
ui.toggleCatLog()
Toggles cat log visibility.
Returns: nil
Example:
ui.toggleCatLog()
Audio Module Functions
audio.init()
Initializes audio system.
Returns: nil
Example:
audio.init()
audio.createPurrSound()
Creates purr sound source.
Returns: table|nil - Sound source or nil
Example:
local purr = audio.createPurrSound()
audio.createMeowSound()
Creates meow sound source.
Returns: table|nil - Sound source or nil
Example:
local meow = audio.createMeowSound()
audio.createClickSound()
Creates click sound source.
Returns: table|nil - Sound source or nil
Example:
local click = audio.createClickSound()
audio.createAmbientSound()
Creates ambient room sound.
Returns: table|nil - Sound source or nil
Example:
local ambient = audio.createAmbientSound()
audio.playPurr(position)
Plays purr at position.
Parameters:
position(table): {x, y, z} position
Returns: nil
Example:
audio.playPurr(cat.position)
audio.playMeow(position)
Plays meow at position.
Parameters:
position(table): {x, y, z} position
Returns: nil
Example:
audio.playMeow(cat.position)
audio.playClick()
Plays click sound.
Returns: nil
Example:
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:
-- 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:
-- 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:
-- 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:
-- 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:
-- 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
lovr . --verbose
2. Add Debug Logging
-- 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
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
local stats = lovr.graphics.getStats()
print('Draw calls:', stats.drawCalls)
print('Textures:', stats.textures)
print('Meshes:', stats.meshes)
Performance Optimization
1. Reduce Draw Calls
-- Batch similar objects
-- Use instanced rendering if available
-- Combine meshes when possible
2. Optimize Texture Usage
-- Use smaller textures
-- Reuse textures between objects
-- Compress textures if supported
3. Limit Object Count
-- Cap furniture count
-- Use object pooling for cats
-- Remove off-screen objects
4. Profile Code
-- 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
-- Check memory
local mem = collectgarbage('count')
print('Memory:', mem, 'KB')
2. Clean Up Unused Data
-- Clear texture cache
voxel.textures = {}
-- Reset state
core.state = {
cats = {},
furniture = {},
-- ... reset other fields
}
3. Use Weak References
-- For caches
local cache = setmetatable({}, {__mode = 'v'})
4. Avoid Memory Leaks
-- Always remove event listeners
-- Clear timers
-- Close file handles
Additional Resources
LOVR Documentation
Lua Programming
Busted Testing
JSON Specification
Last Updated: July 2026
Version: 1.0.0
Maintainer: Cat Haven Team