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