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

43 KiB
Raw Blame History

Cat Haven - Comprehensive Instructions

Table of Contents

  1. Codebase Overview
  2. Build Instructions
  3. Testing Instructions
  4. Development Workflow
  5. Architecture Documentation
  6. API Reference
  7. 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)

{
  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:

  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

  3. Busted (Test Framework)

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 of lua 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:

  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

    # 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 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:

-- 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 ID
  • position (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 ID
  • name (string): Cat name
  • personality (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 object
  • time (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 definition
  • position (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} position
  • size (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 ID
  • position (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} position
  • size (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 start
  • direction (table): {x, y, z} ray direction
  • maxDistance (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 position
  • y (number): Mouse Y position
  • button (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