summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ASSET_LOADING.md285
-rw-r--r--CMakeLists.txt70
-rw-r--r--EMBEDDING.md290
-rw-r--r--SPLASH_SCREENS.md230
-rw-r--r--build/.gitignore1
-rw-r--r--embed_assets.py116
-rw-r--r--embed_font.py57
-rw-r--r--embed_logo.py64
-rw-r--r--embed_lua.py92
-rw-r--r--fonts/Oleaguid.ttfbin0 -> 112828 bytes
-rw-r--r--include/core.h3
-rw-r--r--include/lua_core.h2
-rw-r--r--include/splash.h6
-rw-r--r--logo/raylib_logo.pngbin0 -> 2466 bytes
-rw-r--r--logo/reilua_logo.pngbin0 -> 1191 bytes
-rw-r--r--src/core.c61
-rw-r--r--src/lua_core.c236
-rw-r--r--src/main.c114
-rw-r--r--src/splash.c198
19 files changed, 1801 insertions, 24 deletions
diff --git a/ASSET_LOADING.md b/ASSET_LOADING.md
new file mode 100644
index 0000000..67a3702
--- /dev/null
+++ b/ASSET_LOADING.md
@@ -0,0 +1,285 @@
+# Asset Loading System
+
+ReiLua includes a built-in asset loading system with a nice loading screen UI that automatically shows progress while assets are being loaded.
+
+## 🎨 Features
+
+- **Automatic Progress Tracking** - Tracks how many assets have been loaded
+- **Beautiful Loading UI** - Modern, minimal loading screen with:
+ - Animated "Loading..." text with dots
+ - Smooth progress bar with shimmer effect
+ - Progress percentage (e.g., "3 / 10")
+ - Current asset name being loaded
+ - Dark, professional color scheme
+- **Easy to Use** - Just 3 functions to show loading progress
+- **Works Everywhere** - Development and release builds
+
+## 📝 API Functions
+
+### RL.BeginAssetLoading(totalAssets)
+
+Initialize asset loading progress tracking and show the loading screen.
+
+**Parameters:**
+- `totalAssets` (integer) - Total number of assets to load
+
+**Example:**
+```lua
+RL.BeginAssetLoading(10) -- We're loading 10 assets
+```
+
+---
+
+### RL.UpdateAssetLoading(assetName)
+
+Update the loading progress and display current asset being loaded.
+
+**Parameters:**
+- `assetName` (string) - Name of the asset currently being loaded
+
+**Example:**
+```lua
+RL.UpdateAssetLoading("player.png")
+```
+
+Call this **after** each asset is loaded to update the progress bar.
+
+---
+
+### RL.EndAssetLoading()
+
+Finish asset loading and hide the loading screen.
+
+**Example:**
+```lua
+RL.EndAssetLoading()
+```
+
+## 🚀 Quick Example
+
+```lua
+function RL.init()
+ -- List of assets to load
+ local assetsToLoad = {
+ "assets/player.png",
+ "assets/enemy.png",
+ "assets/background.png",
+ "assets/music.wav",
+ }
+
+ -- Begin loading
+ RL.BeginAssetLoading(#assetsToLoad)
+
+ -- Load each asset
+ for i, path in ipairs(assetsToLoad) do
+ RL.UpdateAssetLoading(path) -- Update progress
+
+ -- Load the actual asset
+ if path:match("%.png$") or path:match("%.jpg$") then
+ textures[i] = RL.LoadTexture(path)
+ elseif path:match("%.wav$") or path:match("%.ogg$") then
+ sounds[i] = RL.LoadSound(path)
+ end
+ end
+
+ -- Done!
+ RL.EndAssetLoading()
+end
+```
+
+## 💡 Complete Example
+
+```lua
+local assets = {}
+
+local assetsToLoad = {
+ {type="texture", name="player", path="assets/player.png"},
+ {type="texture", name="enemy", path="assets/enemy.png"},
+ {type="texture", name="background", path="assets/background.png"},
+ {type="sound", name="music", path="assets/music.wav"},
+ {type="sound", name="shoot", path="assets/shoot.wav"},
+ {type="font", name="title", path="assets/title.ttf"},
+}
+
+function RL.init()
+ RL.SetWindowTitle("My Game")
+
+ -- Start loading with progress
+ RL.BeginAssetLoading(#assetsToLoad)
+
+ -- Load all assets
+ for i, asset in ipairs(assetsToLoad) do
+ -- Show current asset name on loading screen
+ RL.UpdateAssetLoading(asset.name)
+
+ -- Load based on type
+ if asset.type == "texture" then
+ assets[asset.name] = RL.LoadTexture(asset.path)
+ elseif asset.type == "sound" then
+ assets[asset.name] = RL.LoadSound(asset.path)
+ elseif asset.type == "font" then
+ assets[asset.name] = RL.LoadFont(asset.path)
+ end
+ end
+
+ -- Loading complete!
+ RL.EndAssetLoading()
+
+ print("Game ready!")
+end
+
+function RL.update(delta)
+ -- Your game logic
+end
+
+function RL.draw()
+ RL.ClearBackground(RL.RAYWHITE)
+
+ -- Use loaded assets
+ if assets.background then
+ RL.DrawTexture(assets.background, {0, 0}, RL.WHITE)
+ end
+
+ if assets.player then
+ RL.DrawTexture(assets.player, {100, 100}, RL.WHITE)
+ end
+end
+```
+
+## 🎨 Loading Screen Appearance
+
+The loading screen features a clean 1-bit pixel art style:
+
+**Design:**
+- Pure black and white aesthetic
+- Retro pixel art styling
+- Minimal and clean design
+
+**Elements:**
+- **Title**: "LOADING" in bold white pixel text
+- **Animated Dots**: White pixelated dots (4x4 squares) that cycle
+- **Progress Bar**:
+ - 200px wide, 16px tall
+ - Thick 2px white border (pixel art style)
+ - White fill with black dithering pattern
+ - Retro/Classic terminal aesthetic
+- **Progress Text**: "3/10" in white pixel font style
+- **Asset Name**: Current loading asset in small white text
+- **Corner Decorations**: White pixel art L-shaped corners in all 4 corners
+
+**Background:**
+- Pure black background (#000000)
+- High contrast for maximum clarity
+
+**Color Palette:**
+- White text and UI (#FFFFFF)
+- Black background (#000000)
+- Pure 1-bit aesthetic (inverted terminal style)
+
+**Visual Layout:**
+```
+[Black Background]
+
+┌─┐ ┌─┐
+│ │ LOADING □ □ │ │
+│ │ │ │
+│ │ ┌──────────────────┐ │ │
+│ │ │████████░░░░░░░░░░│ 3/10 │ │
+│ │ └──────────────────┘ │ │
+│ │ │ │
+│ │ player.png │ │
+│ │ │ │
+└─┘ └─┘
+
+[All text and UI elements in WHITE]
+```
+
+**Style Inspiration:**
+- Classic terminal / console aesthetic
+- MS-DOS loading screens
+- 1-bit dithering patterns
+- Chunky pixel borders
+- Retro computing / CRT monitor style
+
+## 🔧 Customization
+
+If you want to customize the loading screen appearance, you can modify the colors and sizes in `src/lua_core.c` in the `drawLoadingScreen()` function.
+
+## ⚡ Performance Tips
+
+1. **Call UpdateAssetLoading AFTER loading** - This ensures the progress updates at the right time
+2. **Load assets in order of importance** - Load critical assets first
+3. **Group similar assets** - Load all textures, then sounds, etc.
+4. **Use descriptive names** - Shows better feedback to users
+
+## 📋 Example Asset Loading Patterns
+
+### Pattern 1: Simple List
+```lua
+local files = {"player.png", "enemy.png", "music.wav"}
+RL.BeginAssetLoading(#files)
+for i, file in ipairs(files) do
+ RL.UpdateAssetLoading(file)
+ -- load file
+end
+RL.EndAssetLoading()
+```
+
+### Pattern 2: With Types
+```lua
+local assets = {
+ textures = {"player.png", "enemy.png"},
+ sounds = {"music.wav", "shoot.wav"},
+}
+local total = #assets.textures + #assets.sounds
+
+RL.BeginAssetLoading(total)
+for _, file in ipairs(assets.textures) do
+ RL.UpdateAssetLoading(file)
+ -- load texture
+end
+for _, file in ipairs(assets.sounds) do
+ RL.UpdateAssetLoading(file)
+ -- load sound
+end
+RL.EndAssetLoading()
+```
+
+### Pattern 3: Error Handling
+```lua
+RL.BeginAssetLoading(#files)
+for i, file in ipairs(files) do
+ RL.UpdateAssetLoading(file)
+
+ if RL.FileExists(file) then
+ -- load file
+ else
+ print("Warning: " .. file .. " not found")
+ end
+end
+RL.EndAssetLoading()
+```
+
+## 🎮 When to Use
+
+**Use the loading system when:**
+- You have more than 5-10 assets to load
+- Assets are large (images, sounds, fonts)
+- Loading might take more than 1 second
+- You want professional loading feedback
+
+**You can skip it when:**
+- You have very few, small assets
+- Loading is nearly instant
+- You prefer immediate game start
+
+## ✨ Benefits
+
+- ✅ Professional user experience
+- ✅ User knows the game is loading, not frozen
+- ✅ Shows progress for large asset sets
+- ✅ Works with embedded assets
+- ✅ Minimal code required
+- ✅ Beautiful default UI
+
+The loading system makes your game feel polished and professional with just a few lines of code!
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4cfba5b..2df11ad 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -15,6 +15,8 @@ option( LUAJIT "Use LuaJIT." off )
option( LUA_EVENTS "Enable Lua event callbacks (RL.event)." off )
option( DYNAMIC_SYMBOLS "Expose all dynamic symbols with rdynamic." off )
option( EXPOSE_API_SYMBOLS "Expose dynamic symbols only for get and push functions of variable types." off )
+option( EMBED_MAIN "Embed all Lua files from build directory into executable." off )
+option( EMBED_ASSETS "Embed all files from assets folder into executable." off )
enum_option( PLATFORM "Desktop;Desktop_SDL2;Desktop_SDL3;Web" "Platform to build for." )
@@ -25,7 +27,75 @@ endif()
file( GLOB SOURCES src/*.c )
+# Always embed logo files for splash screens
+set( LOGO_FILES
+ "${CMAKE_SOURCE_DIR}/logo/raylib_logo.png"
+ "${CMAKE_SOURCE_DIR}/logo/reilua_logo.png"
+)
+
+add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/embedded_logo.h
+ COMMAND python ${CMAKE_SOURCE_DIR}/embed_logo.py
+ ${CMAKE_CURRENT_BINARY_DIR}/embedded_logo.h
+ ${CMAKE_SOURCE_DIR}/logo/raylib_logo.png
+ ${CMAKE_SOURCE_DIR}/logo/reilua_logo.png
+ DEPENDS ${LOGO_FILES}
+ COMMENT "Embedding logo files for splash screens..."
+)
+list( APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/embedded_logo.h )
+set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DEMBED_LOGO" )
+
+# Always embed font file
+set( FONT_FILE "${CMAKE_SOURCE_DIR}/fonts/Oleaguid.ttf" )
+
+add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/embedded_font.h
+ COMMAND python ${CMAKE_SOURCE_DIR}/embed_font.py
+ ${CMAKE_CURRENT_BINARY_DIR}/embedded_font.h
+ ${CMAKE_SOURCE_DIR}/fonts/Oleaguid.ttf
+ DEPENDS ${FONT_FILE}
+ COMMENT "Embedding font file..."
+)
+list( APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/embedded_font.h )
+set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DEMBED_FONT" )
+
+include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
include_directories( include )
+
+# Embed Lua files if EMBED_MAIN is ON
+if( EMBED_MAIN )
+ file( GLOB LUA_FILES "${CMAKE_CURRENT_BINARY_DIR}/*.lua" )
+ if( LUA_FILES )
+ add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/embedded_main.h
+ COMMAND python ${CMAKE_SOURCE_DIR}/embed_lua.py ${CMAKE_CURRENT_BINARY_DIR}/embedded_main.h ${LUA_FILES}
+ DEPENDS ${LUA_FILES}
+ COMMENT "Embedding Lua files into executable..."
+ )
+ list( APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/embedded_main.h )
+ set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DEMBED_MAIN" )
+ else()
+ message( WARNING "EMBED_MAIN is ON but no .lua files found in build directory!" )
+ endif()
+endif()
+
+# Embed asset files if EMBED_ASSETS is ON
+if( EMBED_ASSETS )
+ file( GLOB_RECURSE ASSET_FILES "${CMAKE_CURRENT_BINARY_DIR}/assets/*" )
+ if( ASSET_FILES )
+ add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/embedded_assets.h
+ COMMAND python ${CMAKE_SOURCE_DIR}/embed_assets.py ${CMAKE_CURRENT_BINARY_DIR}/embedded_assets.h ${ASSET_FILES}
+ DEPENDS ${ASSET_FILES}
+ COMMENT "Embedding asset files into executable..."
+ )
+ list( APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/embedded_assets.h )
+ set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DEMBED_ASSETS" )
+ else()
+ message( WARNING "EMBED_ASSETS is ON but no files found in build/assets/ directory!" )
+ endif()
+endif()
+
add_executable( ${PROJECT_NAME} ${SOURCES} )
if( PLATFORM STREQUAL "Desktop" )
diff --git a/EMBEDDING.md b/EMBEDDING.md
new file mode 100644
index 0000000..c496059
--- /dev/null
+++ b/EMBEDDING.md
@@ -0,0 +1,290 @@
+# Embedding main.lua into Executable
+
+When you're ready to ship your game, you can embed all Lua files and asset files directly into the executable.
+
+## Development vs Release Workflow
+
+### 🔧 Development Build (Fast Iteration)
+
+During development, use external files for quick iteration:
+
+**Setup:**
+```
+GameFolder/
+├── ReiLua.exe
+├── main.lua
+├── player.lua
+└── assets/
+ ├── player.png
+ └── music.wav
+```
+
+**Build:**
+```bash
+cd build
+cmake ..
+cmake --build .
+```
+
+**Benefits:**
+- ✅ Edit Lua files and re-run immediately
+- ✅ Edit assets and reload
+- ✅ Fast development cycle
+- ✅ Debug with `--log` flag
+
+### 📦 Release Build (Single Executable)
+
+For distribution, embed everything into one file:
+
+**Setup:**
+```bash
+cd build
+
+# Copy Lua files to build directory
+copy ..\main.lua .
+copy ..\player.lua .
+
+# Create assets folder and copy files
+mkdir assets
+copy ..\player.png assets\
+copy ..\music.wav assets\
+```
+
+**Build:**
+```bash
+# Configure with embedding
+cmake .. -DEMBED_MAIN=ON -DEMBED_ASSETS=ON
+
+# Build release
+cmake --build . --config Release
+```
+
+**Result:**
+```
+Distribution/
+└── YourGame.exe (Everything embedded!)
+```
+
+**Benefits:**
+- ✅ Single executable file
+- ✅ No external dependencies
+- ✅ Users can't modify game files
+- ✅ Professional distribution
+- ✅ Smaller download (no separate files)
+
+## Quick Start
+
+### Embedding Lua Files
+
+1. **Copy your Lua files to the build directory**:
+ ```bash
+ copy main.lua build\main.lua
+ copy player.lua build\player.lua
+ ```
+
+2. **Build with EMBED_MAIN option**:
+ ```bash
+ cd build
+ cmake .. -DEMBED_MAIN=ON
+ cmake --build . --config Release
+ ```
+
+## Command Line Options
+
+ReiLua supports several command-line options:
+
+```bash
+ReiLua [Options] [Directory to main.lua or main]
+
+Options:
+ -h, --help Show help message
+ -v, --version Show ReiLua version
+ -i, --interpret Interpret mode [File name]
+ --log Show console window for logging (Windows only)
+```
+
+### Console/Logging
+
+By default, ReiLua runs **without a console window** for a clean user experience. To enable console output for debugging:
+
+```bash
+# Run with console for debugging
+ReiLua.exe --log
+
+# You can also combine with other options
+ReiLua.exe --log path/to/game
+
+# Or with interpret mode
+ReiLua.exe --log -i script.lua
+```
+
+This is useful during development to see:
+- TraceLog output
+- Print statements
+- Lua errors
+- Debug information
+
+## Complete Release Workflow
+
+Here's a complete step-by-step guide to prepare your game for release:
+
+### Step 1: Organize Your Project
+
+Ensure your project has this structure:
+```
+MyGame/
+├── main.lua
+├── player.lua
+├── enemy.lua
+├── player.png
+├── enemy.png
+├── music.wav
+└── icon.ico (optional)
+```
+
+### Step 2: Customize Branding (Optional)
+
+**Change executable icon:**
+```bash
+# Replace ReiLua's icon with yours
+copy MyGame\icon.ico ReiLua\icon.ico
+```
+
+**Edit executable properties:**
+Open `ReiLua\resources.rc` and modify:
+```rc
+VALUE "CompanyName", "Your Studio Name"
+VALUE "FileDescription", "Your Game Description"
+VALUE "ProductName", "Your Game Name"
+VALUE "LegalCopyright", "Copyright (C) Your Name, 2025"
+```
+
+**Change executable name:**
+Edit `ReiLua\CMakeLists.txt`:
+```cmake
+project( YourGameName ) # Change from "ReiLua"
+```
+
+See [CUSTOMIZATION.md](CUSTOMIZATION.md) for full details.
+
+### Important: Asset Paths
+
+**Keep your paths consistent!** The embedding system now preserves the `assets/` prefix, so use the same paths in both development and release:
+
+```lua
+-- ✅ Correct - works in both dev and release
+playerImage = RL.LoadTexture("assets/player.png")
+backgroundImg = RL.LoadTexture("assets/background.png")
+musicSound = RL.LoadSound("assets/music.wav")
+```
+
+Your Lua code doesn't need to change between development and release builds!
+
+### Step 3: Prepare Build Directory
+
+```bash
+cd ReiLua\build
+
+# Copy all Lua files
+copy ..\MyGame\*.lua .
+
+# Create assets folder
+mkdir assets
+
+# Copy all asset files (images, sounds, etc.)
+copy ..\MyGame\*.png assets\
+copy ..\MyGame\*.wav assets\
+copy ..\MyGame\*.ogg assets\
+# Or copy entire folders
+xcopy /E /I ..\MyGame\images assets\images
+xcopy /E /I ..\MyGame\sounds assets\sounds
+```
+
+### Step 4: Build Release
+
+```bash
+# Configure with embedding enabled
+cmake .. -DEMBED_MAIN=ON -DEMBED_ASSETS=ON
+
+# Build in release mode
+cmake --build . --config Release
+```
+
+### Step 5: Test Release Build
+
+```bash
+# Test with console to verify everything loaded
+YourGameName.exe --log
+
+# Test production mode (no console)
+YourGameName.exe
+```
+
+Check console output for:
+- ✅ "ReiLua x.x.x" version info
+- ✅ No file loading errors
+- ✅ Game runs correctly
+
+### Step 6: Package for Distribution
+
+```bash
+# Create distribution folder
+mkdir ..\Distribution
+copy YourGameName.exe ..\Distribution\
+
+# Optional: Add README, LICENSE, etc.
+copy ..\README.txt ..\Distribution\
+```
+
+Your game is now ready to distribute as a single executable!
+
+### Workflow Summary
+
+| Stage | Build Command | Files Needed | Result |
+|-------|--------------|--------------|--------|
+| **Development** | `cmake .. && cmake --build .` | Lua + assets external | Fast iteration |
+| **Testing** | `cmake .. -DEMBED_MAIN=ON && cmake --build .` | Lua in build/ | Test embedding |
+| **Release** | `cmake .. -DEMBED_MAIN=ON -DEMBED_ASSETS=ON && cmake --build . --config Release` | Lua + assets in build/ | Single .exe |
+
+### Troubleshooting
+
+**Problem: "No .lua files found in build directory"**
+```bash
+# Solution: Copy Lua files to build directory
+copy ..\*.lua .
+```
+
+**Problem: "No files found in assets folder"**
+```bash
+# Solution: Create assets folder and copy files
+mkdir assets
+copy ..\*.png assets\
+```
+
+**Problem: Game crashes on startup**
+```bash
+# Solution: Run with --log to see error messages
+YourGameName.exe --log
+```
+
+**Problem: Assets not loading**
+- Verify assets are in `build/assets/` before building
+- Check asset filenames match in your Lua code
+- Use `--log` to see loading errors
+
+### Notes
+
+- `.lua` files in the **build directory root** are embedded when using `EMBED_MAIN=ON`
+- Asset files in **build/assets/** folder (and subfolders) are embedded when using `EMBED_ASSETS=ON`
+- `main.lua` must exist and is always the entry point
+- Asset embedding works with subdirectories: `assets/images/player.png` → Load with `LoadImage("player.png")`
+- Both Lua and asset embedding can be used independently or together
+- The system falls back to file system if embedded file is not found
+- No code changes needed - all raylib functions work automatically with embedded assets
+
+## Customizing Your Executable
+
+Want to add your own icon and version info to the executable? See [CUSTOMIZATION.md](CUSTOMIZATION.md) for details on:
+- Adding a custom icon
+- Setting exe properties (company name, version, description)
+- Renaming the executable
diff --git a/SPLASH_SCREENS.md b/SPLASH_SCREENS.md
new file mode 100644
index 0000000..8fb9b51
--- /dev/null
+++ b/SPLASH_SCREENS.md
@@ -0,0 +1,230 @@
+# Splash Screens
+
+ReiLua includes a built-in splash screen system that displays three professional splash screens before your game loads. This gives your game a polished, professional appearance right from startup.
+
+## Overview
+
+When you run your ReiLua game, it automatically shows two splash screens in sequence:
+
+1. **"INDRAJITH MAKES GAMES"** - Clean, bold text on Raylib red background (similar to Squid Game style)
+2. **"Made using"** - Text with Raylib and ReiLua logos displayed side-by-side
+
+Each splash screen:
+- Fades in over 0.8 seconds
+- Displays for 2.5 seconds
+- Fades out over 0.8 seconds
+- Total display time: 8.2 seconds for both screens
+
+## Features
+
+### Always Embedded
+
+The logo images are **always embedded** into the executable in both development and release builds. This means:
+
+- ✅ No external logo files needed
+- ✅ Consistent splash screens across all builds
+- ✅ No risk of missing logo files
+- ✅ Professional appearance from the start
+
+### Asset Loading Integration
+
+The splash screens display **before** your game's asset loading begins. This means:
+
+1. User starts your game
+2. Splash screens play (~8 seconds)
+3. Your `RL.init()` function runs
+4. Asset loading with progress indicator (if you use it)
+5. Your game starts
+
+This creates a smooth, professional startup experience.
+
+## Skipping Splash Screens (Development)
+
+During development, you often need to test your game repeatedly. Waiting for splash screens every time can slow down your workflow. Use the `--no-logo` flag to skip them:
+
+```bash
+# Windows
+ReiLua.exe --no-logo
+
+# Linux/Mac
+./ReiLua --no-logo
+
+# With other options
+ReiLua.exe --log --no-logo
+./ReiLua --no-logo path/to/game/
+```
+
+**Note:** The `--no-logo` flag only works in development. In release builds, users should see the full splash screen sequence.
+
+## Technical Details
+
+### How It Works
+
+The splash screen system is implemented in C and runs before any Lua code executes:
+
+1. **Logo Embedding**: During build, `embed_logo.py` converts PNG files to C byte arrays
+2. **Initialization**: Before calling `RL.init()`, the engine initializes splash screens
+3. **Display Loop**: A dedicated loop handles timing, fading, and rendering
+4. **Cleanup**: After completion, resources are freed and Lua code begins
+
+### Files
+
+- `src/splash.c` - Splash screen implementation
+- `include/splash.h` - Header file
+- `embed_logo.py` - Python script to embed logo images
+- `logo/raylib_logo.png` - Raylib logo (embedded)
+- `logo/reilua_logo.png` - ReiLua logo (embedded)
+
+### Build Integration
+
+The CMakeLists.txt automatically:
+
+1. Runs `embed_logo.py` during build
+2. Generates `embedded_logo.h` with logo data
+3. Defines `EMBED_LOGO` flag
+4. Compiles `splash.c` with the project
+
+No manual steps required - it just works!
+
+## Customization
+
+### Changing Splash Screen Text
+
+To change "INDRAJITH MAKES GAMES" to your studio name:
+
+1. Open `src/splash.c`
+2. Find the `drawIndrajithSplash()` function
+3. Change this line:
+ ```c
+ const char* text = "INDRAJITH MAKES GAMES";
+ ```
+4. Rebuild the project
+
+**Note:** Use ALL CAPS for the Squid Game-style aesthetic.
+
+### Changing Logos
+
+To use different logos:
+
+1. Replace `logo/raylib_logo.png` and/or `logo/reilua_logo.png` with your images
+2. Recommended size: 256x256 or smaller (logos are auto-scaled to max 200px)
+3. Format: PNG with transparency support
+4. Rebuild the project - logos will be automatically embedded
+
+### Changing Timing
+
+To adjust how long each screen displays:
+
+1. Open `src/splash.c`
+2. Modify these constants at the top:
+ ```c
+ #define FADE_IN_TIME 0.8f // Seconds to fade in
+ #define DISPLAY_TIME 2.5f // Seconds to display fully
+ #define FADE_OUT_TIME 0.8f // Seconds to fade out
+ ```
+3. Rebuild the project
+
+### Removing Splash Screens Entirely
+
+If you don't want any splash screens:
+
+1. Open `src/main.c`
+2. Find this block:
+ ```c
+ /* Show splash screens if not skipped */
+ if ( !skip_splash ) {
+ splashInit();
+ // ... splash code ...
+ splashCleanup();
+ }
+ ```
+3. Comment out or remove the entire block
+4. Rebuild the project
+
+## Example: Complete Startup Sequence
+
+Here's what a typical game startup looks like with everything enabled:
+
+```bash
+ReiLua.exe MyGame/
+```
+
+**User Experience:**
+
+1. **Splash Screen 1** (4.1 seconds)
+ - "INDRAJITH MAKES GAMES" bold text
+ - Red background (Raylib color #E62937)
+ - Subtle zoom effect
+
+2. **Splash Screen 2** (4.1 seconds)
+ - "Made using" text at top
+ - Raylib + ReiLua logos side-by-side (max 200px each)
+ - Black background
+
+3. **Asset Loading** (varies)
+ - Your loading screen with progress bar
+ - Shows "Loading texture1.png", "3/10", etc.
+
+4. **Game Start**
+ - Your game's main screen appears
+ - Player can interact
+
+## Best Practices
+
+1. **Keep --no-logo for Development**: Always use `--no-logo` during active development
+2. **Test Without Flag**: Occasionally test without `--no-logo` to ensure splash screens work
+3. **Customize for Your Studio**: Change the text and logos to match your branding
+4. **Consider Total Time**: Splash (~8s) + Loading (varies) = Total startup time
+5. **Optimize Loading**: Keep asset loading fast to maintain a good first impression
+
+## Troubleshooting
+
+### Splash Screens Don't Show
+
+**Problem**: Game starts immediately without splash screens
+
+**Solutions**:
+- Check you're not using `--no-logo` flag
+- Verify logos exist in `logo/` folder before building
+- Check console output for embedding errors
+- Rebuild project completely: `cmake .. && make clean && make`
+
+### Logos Appear Corrupted
+
+**Problem**: Logos display incorrectly or not at all
+
+**Solutions**:
+- Verify PNG files are valid (open in image viewer)
+- Check file sizes aren't too large (keep under 1MB each)
+- Ensure PNGs use standard format (not progressive or exotic encoding)
+- Rebuild project to regenerate embedded data
+
+### Compilation Errors
+
+**Problem**: Build fails with logo-related errors
+
+**Solutions**:
+- Ensure Python 3 is installed and in PATH
+- Check `embed_logo.py` has correct paths
+- Verify `logo/` folder exists with both PNG files
+- Check CMake output for specific error messages
+
+## Command Reference
+
+```bash
+# Development (skip splash)
+ReiLua --no-logo
+
+# Development with logging
+ReiLua --log --no-logo
+
+# Production/testing (full splash)
+ReiLua
+
+# Help
+ReiLua --help
+```
+
+---
+
+The splash screen system adds a professional touch to your ReiLua games with minimal effort. Customize it to match your studio's branding and give players a great first impression!
diff --git a/build/.gitignore b/build/.gitignore
deleted file mode 100644
index f59ec20..0000000
--- a/build/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-* \ No newline at end of file
diff --git a/embed_assets.py b/embed_assets.py
new file mode 100644
index 0000000..52309ef
--- /dev/null
+++ b/embed_assets.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python3
+"""
+Embed asset files (images, sounds, fonts, etc.) into a C header file.
+Usage: python embed_assets.py <output.h> <file1.png> [file2.wav] [file3.ttf] ...
+
+Embeds all specified asset files into a C header for inclusion in the executable.
+"""
+import sys
+import os
+
+def sanitize_name(filename):
+ """Convert filename to valid C identifier"""
+ name = os.path.basename(filename)
+ # Remove or replace all non-alphanumeric characters (except underscore)
+ valid_chars = []
+ for char in name:
+ if char.isalnum() or char == '_':
+ valid_chars.append(char)
+ else:
+ valid_chars.append('_')
+ name = ''.join(valid_chars)
+ # Ensure it doesn't start with a digit
+ if name and name[0].isdigit():
+ name = '_' + name
+ return name
+
+def get_file_extension(filename):
+ """Get the file extension"""
+ return os.path.splitext(filename)[1].lower()
+
+def embed_files(output_file, input_files):
+ with open(output_file, 'w') as f:
+ f.write('#ifndef EMBEDDED_ASSETS_H\n')
+ f.write('#define EMBEDDED_ASSETS_H\n\n')
+ f.write('/* Auto-generated file - do not edit manually */\n\n')
+
+ # Embed each file as a separate array
+ for idx, input_file in enumerate(input_files):
+ with open(input_file, 'rb') as inf:
+ data = inf.read()
+
+ var_name = sanitize_name(input_file)
+ # Extract relative path from 'assets/' onwards if present
+ if 'assets' in input_file.replace('\\', '/'):
+ parts = input_file.replace('\\', '/').split('assets/')
+ if len(parts) > 1:
+ relative_name = 'assets/' + parts[-1]
+ else:
+ relative_name = os.path.basename(input_file)
+ else:
+ relative_name = os.path.basename(input_file)
+
+ f.write(f'/* Embedded asset: {input_file} ({len(data)} bytes) */\n')
+ f.write(f'static const unsigned char embedded_asset_{idx}_{var_name}[] = {{\n')
+
+ for i, byte in enumerate(data):
+ if i % 12 == 0:
+ f.write(' ')
+ f.write(f'0x{byte:02x}')
+ if i < len(data) - 1:
+ f.write(',')
+ if (i + 1) % 12 == 0:
+ f.write('\n')
+ else:
+ f.write(' ')
+
+ f.write('\n};\n')
+ f.write(f'static const unsigned int embedded_asset_{idx}_{var_name}_len = {len(data)};\n\n')
+
+ # Create the asset table
+ f.write('/* Asset table for virtual filesystem */\n')
+ f.write('typedef struct {\n')
+ f.write(' const char* name;\n')
+ f.write(' const unsigned char* data;\n')
+ f.write(' unsigned int size;\n')
+ f.write('} EmbeddedAsset;\n\n')
+
+ f.write('static const EmbeddedAsset embedded_assets[] = {\n')
+ for idx, input_file in enumerate(input_files):
+ var_name = sanitize_name(input_file)
+ # Extract relative path from 'assets/' onwards if present
+ if 'assets' in input_file.replace('\\', '/'):
+ parts = input_file.replace('\\', '/').split('assets/')
+ if len(parts) > 1:
+ relative_name = 'assets/' + parts[-1]
+ else:
+ relative_name = os.path.basename(input_file)
+ else:
+ relative_name = os.path.basename(input_file)
+ f.write(f' {{ "{relative_name}", embedded_asset_{idx}_{var_name}, embedded_asset_{idx}_{var_name}_len }},\n')
+ f.write('};\n\n')
+
+ f.write(f'static const int embedded_asset_count = {len(input_files)};\n\n')
+ f.write('#endif /* EMBEDDED_ASSETS_H */\n')
+
+if __name__ == '__main__':
+ if len(sys.argv) < 3:
+ print('Usage: python embed_assets.py <output.h> <asset1> [asset2] ...')
+ print(' Embeds images, sounds, fonts, and other asset files into a C header.')
+ print(' Supported: .png, .jpg, .wav, .ogg, .mp3, .ttf, .otf, etc.')
+ sys.exit(1)
+
+ output_file = sys.argv[1]
+ input_files = sys.argv[2:]
+
+ # Check all input files exist
+ for f in input_files:
+ if not os.path.exists(f):
+ print(f'Error: File not found: {f}')
+ sys.exit(1)
+
+ embed_files(output_file, input_files)
+ print(f'Embedded {len(input_files)} asset file(s) into {output_file}')
+ for f in input_files:
+ size = os.path.getsize(f)
+ print(f' - {f} ({size} bytes)')
diff --git a/embed_font.py b/embed_font.py
new file mode 100644
index 0000000..8144718
--- /dev/null
+++ b/embed_font.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+"""
+Embed font file into C header.
+Usage: python embed_font.py <output.h> <font.ttf>
+"""
+
+import sys
+import os
+
+def embed_file(file_path, var_name):
+ """Convert a file to a C byte array"""
+ with open(file_path, 'rb') as f:
+ data = f.read()
+
+ output = f"/* {os.path.basename(file_path)} */\n"
+ output += f"static const unsigned char {var_name}[] = {{\n"
+
+ # Write bytes in rows of 16
+ for i in range(0, len(data), 16):
+ chunk = data[i:i+16]
+ hex_values = ', '.join(f'0x{b:02x}' for b in chunk)
+ output += f" {hex_values},\n"
+
+ output += "};\n"
+ output += f"static const unsigned int {var_name}_size = {len(data)};\n\n"
+
+ return output
+
+def main():
+ if len(sys.argv) != 3:
+ print("Usage: python embed_font.py <output.h> <font.ttf>")
+ sys.exit(1)
+
+ output_file = sys.argv[1]
+ font_file = sys.argv[2]
+
+ # Check if file exists
+ if not os.path.exists(font_file):
+ print(f"Error: {font_file} not found!")
+ sys.exit(1)
+
+ # Generate header content
+ header_content = "/* Auto-generated embedded font file */\n"
+ header_content += "#pragma once\n\n"
+
+ # Embed font file
+ header_content += embed_file(font_file, "embedded_font_data")
+
+ # Write to output file
+ with open(output_file, 'w') as f:
+ f.write(header_content)
+
+ print(f"Generated {output_file}")
+ print(f" - Embedded {font_file} ({os.path.getsize(font_file)} bytes)")
+
+if __name__ == "__main__":
+ main()
diff --git a/embed_logo.py b/embed_logo.py
new file mode 100644
index 0000000..e6b645e
--- /dev/null
+++ b/embed_logo.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python3
+"""
+Embed logo image files into C header for splash screens.
+Usage: python embed_logo.py <output.h> <raylib_logo.png> <reilua_logo.png>
+"""
+
+import sys
+import os
+
+def embed_file(file_path, var_name):
+ """Convert a file to a C byte array"""
+ with open(file_path, 'rb') as f:
+ data = f.read()
+
+ output = f"/* {os.path.basename(file_path)} */\n"
+ output += f"static const unsigned char {var_name}[] = {{\n"
+
+ # Write bytes in rows of 16
+ for i in range(0, len(data), 16):
+ chunk = data[i:i+16]
+ hex_values = ', '.join(f'0x{b:02x}' for b in chunk)
+ output += f" {hex_values},\n"
+
+ output += "};\n"
+ output += f"static const unsigned int {var_name}_size = {len(data)};\n\n"
+
+ return output
+
+def main():
+ if len(sys.argv) != 4:
+ print("Usage: python embed_logo.py <output.h> <raylib_logo.png> <reilua_logo.png>")
+ sys.exit(1)
+
+ output_file = sys.argv[1]
+ raylib_logo = sys.argv[2]
+ reilua_logo = sys.argv[3]
+
+ # Check if files exist
+ if not os.path.exists(raylib_logo):
+ print(f"Error: {raylib_logo} not found!")
+ sys.exit(1)
+
+ if not os.path.exists(reilua_logo):
+ print(f"Error: {reilua_logo} not found!")
+ sys.exit(1)
+
+ # Generate header content
+ header_content = "/* Auto-generated embedded logo files */\n"
+ header_content += "#pragma once\n\n"
+
+ # Embed both logo files
+ header_content += embed_file(raylib_logo, "embedded_raylib_logo")
+ header_content += embed_file(reilua_logo, "embedded_reilua_logo")
+
+ # Write to output file
+ with open(output_file, 'w') as f:
+ f.write(header_content)
+
+ print(f"Generated {output_file}")
+ print(f" - Embedded {raylib_logo} ({os.path.getsize(raylib_logo)} bytes)")
+ print(f" - Embedded {reilua_logo} ({os.path.getsize(reilua_logo)} bytes)")
+
+if __name__ == "__main__":
+ main()
diff --git a/embed_lua.py b/embed_lua.py
new file mode 100644
index 0000000..9562354
--- /dev/null
+++ b/embed_lua.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+"""
+Embed multiple Lua files into a C header file for inclusion in the executable.
+Usage: python embed_lua.py <output.h> <file1.lua> [file2.lua] [file3.lua] ...
+
+Embeds all specified Lua files into a C header with a virtual filesystem.
+The first file is treated as main.lua (entry point).
+"""
+import sys
+import os
+
+def sanitize_name(filename):
+ """Convert filename to valid C identifier"""
+ name = os.path.basename(filename)
+ name = name.replace('.', '_').replace('-', '_').replace('/', '_').replace('\\', '_')
+ return name
+
+def embed_files(output_file, input_files):
+ with open(output_file, 'w') as f:
+ f.write('#ifndef EMBEDDED_MAIN_H\n')
+ f.write('#define EMBEDDED_MAIN_H\n\n')
+ f.write('/* Auto-generated file - do not edit manually */\n\n')
+
+ # Embed each file as a separate array
+ for idx, input_file in enumerate(input_files):
+ with open(input_file, 'rb') as inf:
+ data = inf.read()
+
+ var_name = sanitize_name(input_file)
+ f.write(f'/* Embedded file: {input_file} */\n')
+ f.write(f'static const unsigned char embedded_lua_{idx}_{var_name}[] = {{\n')
+
+ for i, byte in enumerate(data):
+ if i % 12 == 0:
+ f.write(' ')
+ f.write(f'0x{byte:02x}')
+ if i < len(data) - 1:
+ f.write(',')
+ if (i + 1) % 12 == 0:
+ f.write('\n')
+ else:
+ f.write(' ')
+
+ f.write('\n};\n')
+ f.write(f'static const unsigned int embedded_lua_{idx}_{var_name}_len = {len(data)};\n\n')
+
+ # Create the file table
+ f.write('/* File table for virtual filesystem */\n')
+ f.write('typedef struct {\n')
+ f.write(' const char* name;\n')
+ f.write(' const unsigned char* data;\n')
+ f.write(' unsigned int size;\n')
+ f.write('} EmbeddedLuaFile;\n\n')
+
+ f.write('static const EmbeddedLuaFile embedded_lua_files[] = {\n')
+ for idx, input_file in enumerate(input_files):
+ var_name = sanitize_name(input_file)
+ # Store both original filename and basename for require compatibility
+ basename = os.path.basename(input_file)
+ f.write(f' {{ "{basename}", embedded_lua_{idx}_{var_name}, embedded_lua_{idx}_{var_name}_len }},\n')
+ f.write('};\n\n')
+
+ f.write(f'static const int embedded_lua_file_count = {len(input_files)};\n\n')
+
+ # Main entry point (first file)
+ var_name = sanitize_name(input_files[0])
+ f.write('/* Main entry point */\n')
+ f.write(f'#define embedded_main_lua embedded_lua_0_{var_name}\n')
+ f.write(f'#define embedded_main_lua_len embedded_lua_0_{var_name}_len\n\n')
+
+ f.write('#endif /* EMBEDDED_MAIN_H */\n')
+
+if __name__ == '__main__':
+ if len(sys.argv) < 3:
+ print('Usage: python embed_lua.py <output.h> <file1.lua> [file2.lua] ...')
+ print(' The first Lua file is treated as the main entry point.')
+ sys.exit(1)
+
+ output_file = sys.argv[1]
+ input_files = sys.argv[2:]
+
+ # Check all input files exist
+ for f in input_files:
+ if not os.path.exists(f):
+ print(f'Error: File not found: {f}')
+ sys.exit(1)
+
+ embed_files(output_file, input_files)
+ print(f'Embedded {len(input_files)} file(s) into {output_file}')
+ for f in input_files:
+ print(f' - {f}')
+
diff --git a/fonts/Oleaguid.ttf b/fonts/Oleaguid.ttf
new file mode 100644
index 0000000..034af36
--- /dev/null
+++ b/fonts/Oleaguid.ttf
Binary files differ
diff --git a/include/core.h b/include/core.h
index ab115ae..d608d0a 100644
--- a/include/core.h
+++ b/include/core.h
@@ -140,6 +140,9 @@ int lcoreGetDirectoryPath( lua_State* L );
int lcoreGetPrevDirectoryPath( lua_State* L );
int lcoreGetWorkingDirectory( lua_State* L );
int lcoreGetApplicationDirectory( lua_State* L );
+int lcoreBeginAssetLoading( lua_State* L );
+int lcoreUpdateAssetLoading( lua_State* L );
+int lcoreEndAssetLoading( lua_State* L );
int lcoreMakeDirectory( lua_State* L );
int lcoreChangeDirectory( lua_State* L );
int lcoreIsPathFile( lua_State* L );
diff --git a/include/lua_core.h b/include/lua_core.h
index 8204b49..acbae3f 100644
--- a/include/lua_core.h
+++ b/include/lua_core.h
@@ -50,7 +50,7 @@ void assingGlobalFunction( const char* name, int ( *functionPtr )( lua_State* )
bool luaInit( int argn, const char** argc );
int luaTraceback( lua_State* L );
-void luaCallMain();
+bool luaCallMain();
void luaCallInit();
void luaCallUpdate();
void luaCallDraw();
diff --git a/include/splash.h b/include/splash.h
new file mode 100644
index 0000000..da5e762
--- /dev/null
+++ b/include/splash.h
@@ -0,0 +1,6 @@
+#pragma once
+
+void splashInit();
+bool splashUpdate( float delta );
+void splashDraw();
+void splashCleanup();
diff --git a/logo/raylib_logo.png b/logo/raylib_logo.png
new file mode 100644
index 0000000..0edd29a
--- /dev/null
+++ b/logo/raylib_logo.png
Binary files differ
diff --git a/logo/reilua_logo.png b/logo/reilua_logo.png
new file mode 100644
index 0000000..9a4eb53
--- /dev/null
+++ b/logo/reilua_logo.png
Binary files differ
diff --git a/src/core.c b/src/core.c
index 338778e..0238432 100644
--- a/src/core.c
+++ b/src/core.c
@@ -4,6 +4,14 @@
#include "textures.h"
#include "lua_core.h"
+/* Forward declarations from lua_core.c for asset loading */
+extern int g_totalAssets;
+extern int g_loadedAssets;
+extern char g_currentAssetName[256];
+extern bool g_showLoadingScreen;
+extern float g_loadingProgress;
+extern void drawLoadingScreen();
+
static size_t getBufferElementSize( Buffer* buffer ) {
switch ( buffer->type ) {
case BUFFER_UNSIGNED_CHAR: return sizeof( unsigned char );
@@ -1956,6 +1964,59 @@ int lcoreGetApplicationDirectory( lua_State* L ) {
}
/*
+> RL.BeginAssetLoading( int totalAssets )
+
+Initialize asset loading progress tracking
+
+- totalAssets: Total number of assets to load
+*/
+int lcoreBeginAssetLoading( lua_State* L ) {
+ g_totalAssets = luaL_checkinteger( L, 1 );
+ g_loadedAssets = 0;
+ g_showLoadingScreen = true;
+ g_loadingProgress = 0.0f;
+ g_currentAssetName[0] = '\0';
+
+ return 0;
+}
+
+/*
+> RL.UpdateAssetLoading( string assetName )
+
+Update loading progress for current asset
+
+- assetName: Name of the asset currently being loaded
+*/
+int lcoreUpdateAssetLoading( lua_State* L ) {
+ const char* assetName = luaL_checkstring( L, 1 );
+ strncpy( g_currentAssetName, assetName, sizeof(g_currentAssetName) - 1 );
+ g_currentAssetName[sizeof(g_currentAssetName) - 1] = '\0';
+
+ g_loadedAssets++;
+ g_loadingProgress = (float)g_loadedAssets / (float)g_totalAssets;
+
+ if ( g_showLoadingScreen ) {
+ drawLoadingScreen();
+ }
+
+ return 0;
+}
+
+/*
+> RL.EndAssetLoading()
+
+Finish asset loading and hide loading screen
+*/
+int lcoreEndAssetLoading( lua_State* L ) {
+ g_showLoadingScreen = false;
+ g_totalAssets = 0;
+ g_loadedAssets = 0;
+ g_currentAssetName[0] = '\0';
+
+ return 0;
+}
+
+/*
> success = RL.MakeDirectory( string dirPath )
Create directories (including full path requested), returns 0 on success
diff --git a/src/lua_core.c b/src/lua_core.c
index c619b9c..a6c2ef7 100644
--- a/src/lua_core.c
+++ b/src/lua_core.c
@@ -15,6 +15,21 @@
#include "reasings.h"
#include "bitwiseOp.h"
+#ifdef EMBED_MAIN
+ #include "embedded_main.h"
+#endif
+
+#ifdef EMBED_ASSETS
+ #include "embedded_assets.h"
+#endif
+
+/* Asset loading progress tracking (non-static so core.c can access) */
+int g_totalAssets = 0;
+int g_loadedAssets = 0;
+char g_currentAssetName[256] = { '\0' };
+bool g_showLoadingScreen = false;
+float g_loadingProgress = 0.0f;
+
#ifdef PLATFORM_DESKTOP
#include "platforms/core_desktop_glfw.c"
#elif PLATFORM_DESKTOP_SDL2
@@ -25,6 +40,152 @@
#include "platforms/core_web.c"
#endif
+/* Draw a nice loading screen with progress bar (non-static so core.c can call it) */
+void drawLoadingScreen() {
+ int screenWidth = GetScreenWidth();
+ int screenHeight = GetScreenHeight();
+
+ BeginDrawing();
+ ClearBackground( BLACK );
+
+ int centerX = screenWidth / 2;
+ int centerY = screenHeight / 2;
+
+ const char* title = "LOADING";
+ int titleSize = 32;
+ int titleWidth = MeasureText( title, titleSize );
+
+ DrawText( title, centerX - titleWidth / 2, centerY - 80, titleSize, WHITE );
+
+ static float dotTime = 0.0f;
+ dotTime += 0.016f;
+ int dotCount = (int)(dotTime * 2.0f) % 4;
+
+ int dotStartX = centerX + titleWidth / 2 + 10;
+ int dotY = centerY - 80 + titleSize - 12;
+ for ( int i = 0; i < dotCount; i++ ) {
+ DrawRectangle( dotStartX + i * 8, dotY, 4, 4, WHITE );
+ }
+
+ int barWidth = 200;
+ int barHeight = 16;
+ int barX = centerX - barWidth / 2;
+ int barY = centerY;
+
+ DrawRectangle( barX - 2, barY - 2, barWidth + 4, barHeight + 4, WHITE );
+ DrawRectangle( barX, barY, barWidth, barHeight, BLACK );
+
+ int fillWidth = (int)(barWidth * g_loadingProgress);
+ if ( fillWidth > 0 ) {
+ DrawRectangle( barX, barY, fillWidth, barHeight, WHITE );
+
+ for ( int y = 0; y < barHeight; y += 2 ) {
+ for ( int x = 0; x < fillWidth; x += 4 ) {
+ if ( (x + y) % 4 == 0 ) {
+ DrawRectangle( barX + x, barY + y, 1, 1, BLACK );
+ }
+ }
+ }
+ }
+
+ if ( g_totalAssets > 0 ) {
+ char progressText[32];
+ sprintf( progressText, "%d/%d", g_loadedAssets, g_totalAssets );
+ int progressWidth = MeasureText( progressText, 16 );
+ DrawText( progressText, centerX - progressWidth / 2, barY + barHeight + 12, 16, WHITE );
+ }
+
+ if ( g_currentAssetName[0] != '\0' ) {
+ int assetNameWidth = MeasureText( g_currentAssetName, 10 );
+ DrawText( g_currentAssetName, centerX - assetNameWidth / 2, barY + barHeight + 36, 10, WHITE );
+ }
+
+ int cornerSize = 8;
+ int margin = 40;
+
+ DrawRectangle( margin, margin, cornerSize, 2, WHITE );
+ DrawRectangle( margin, margin, 2, cornerSize, WHITE );
+
+ DrawRectangle( screenWidth - margin - cornerSize, margin, cornerSize, 2, WHITE );
+ DrawRectangle( screenWidth - margin - 2, margin, 2, cornerSize, WHITE );
+
+ DrawRectangle( margin, screenHeight - margin - cornerSize, 2, cornerSize, WHITE );
+ DrawRectangle( margin, screenHeight - margin - 2, cornerSize, 2, WHITE );
+
+ DrawRectangle( screenWidth - margin - cornerSize, screenHeight - margin - 2, cornerSize, 2, WHITE );
+ DrawRectangle( screenWidth - margin - 2, screenHeight - margin - cornerSize, 2, cornerSize, WHITE );
+
+ EndDrawing();
+}
+
+#ifdef EMBED_MAIN
+/* Custom loader for embedded Lua files */
+static int embedded_lua_loader( lua_State* L ) {
+ const char* name = lua_tostring( L, 1 );
+ if ( name == NULL ) return 0;
+
+ for ( int i = 0; i < embedded_lua_file_count; i++ ) {
+ const EmbeddedLuaFile* file = &embedded_lua_files[i];
+
+ const char* basename = file->name;
+ size_t name_len = strlen( name );
+ size_t base_len = strlen( basename );
+
+ if ( strcmp( basename, name ) == 0 ||
+ ( base_len > 4 && strcmp( basename + base_len - 4, ".lua" ) == 0 &&
+ strncmp( basename, name, base_len - 4 ) == 0 && name_len == base_len - 4 ) ) {
+
+ if ( luaL_loadbuffer( L, (const char*)file->data, file->size, file->name ) == 0 ) {
+ return 1;
+ }
+ else {
+ lua_pushfstring( L, "\n\tembedded loader error: %s", lua_tostring( L, -1 ) );
+ return 1;
+ }
+ }
+ }
+
+ lua_pushfstring( L, "\n\tno embedded file '%s'", name );
+ return 1;
+}
+#endif
+
+#ifdef EMBED_ASSETS
+/* Helper function to find embedded asset by name */
+static const EmbeddedAsset* find_embedded_asset( const char* name ) {
+ if ( name == NULL ) return NULL;
+
+ for ( int i = 0; i < embedded_asset_count; i++ ) {
+ if ( strcmp( embedded_assets[i].name, name ) == 0 ) {
+ return &embedded_assets[i];
+ }
+ }
+ return NULL;
+}
+
+/* Override LoadFileData to check embedded assets first */
+unsigned char* LoadFileData_Embedded( const char* fileName, int* dataSize ) {
+ const EmbeddedAsset* asset = find_embedded_asset( fileName );
+ if ( asset != NULL ) {
+ *dataSize = asset->size;
+ unsigned char* data = (unsigned char*)malloc( asset->size );
+ if ( data != NULL ) {
+ memcpy( data, asset->data, asset->size );
+ }
+ return data;
+ }
+ return LoadFileData( fileName, dataSize );
+}
+
+/* Check if file exists in embedded assets */
+bool FileExists_Embedded( const char* fileName ) {
+ if ( find_embedded_asset( fileName ) != NULL ) {
+ return true;
+ }
+ return FileExists( fileName );
+}
+#endif
+
/* Custom implementation since LuaJIT doesn't have lua_geti. */
static void lua_getiCustom( lua_State* L, int index, int i ) {
lua_pushinteger( L, i ); // Push the index onto the stack
@@ -1446,11 +1607,51 @@ int luaTraceback( lua_State* L ) {
return 1;
}
-void luaCallMain() {
+bool luaCallMain() {
lua_State* L = state->luaState;
char path[ STRING_LEN ] = { '\0' };
+ /* Show loading screen */
+ BeginDrawing();
+ ClearBackground( RAYWHITE );
+ const char* loadingText = "Loading...";
+ int fontSize = 40;
+ int textWidth = MeasureText( loadingText, fontSize );
+ DrawText( loadingText, ( GetScreenWidth() - textWidth ) / 2, GetScreenHeight() / 2 - fontSize / 2, fontSize, DARKGRAY );
+ EndDrawing();
+
+#ifdef EMBED_MAIN
+ /* Register custom loader for embedded files */
+ lua_getglobal( L, "package" );
+ lua_getfield( L, -1, "loaders" );
+ if ( lua_isnil( L, -1 ) ) {
+ lua_pop( L, 1 );
+ lua_getfield( L, -1, "searchers" ); /* Lua 5.2+ uses 'searchers' */
+ }
+
+ /* Insert our loader at position 2 (before file loaders) */
+ lua_len( L, -1 );
+ int num_loaders = lua_tointeger( L, -1 );
+ lua_pop( L, 1 );
+ for ( int i = num_loaders; i >= 2; i-- ) {
+ lua_rawgeti( L, -1, i );
+ lua_rawseti( L, -2, i + 1 );
+ }
+ lua_pushcfunction( L, embedded_lua_loader );
+ lua_rawseti( L, -2, 2 );
+ lua_pop( L, 2 ); /* Pop loaders/searchers and package */
+
+ /* Load from embedded data */
+ if ( luaL_loadbuffer( L, (const char*)embedded_main_lua, embedded_main_lua_len, "main.lua" ) != 0 ) {
+ TraceLog( LOG_ERROR, "Lua error loading embedded main.lua: %s\n", lua_tostring( L, -1 ) );
+ return false;
+ }
+ if ( lua_pcall( L, 0, 0, 0 ) != 0 ) {
+ TraceLog( LOG_ERROR, "Lua error executing embedded main.lua: %s\n", lua_tostring( L, -1 ) );
+ return false;
+ }
+#else
/* If web, set path to resources folder. */
#ifdef PLATFORM_WEB
snprintf( path, STRING_LEN, "main.lua" );
@@ -1467,17 +1668,16 @@ void luaCallMain() {
#endif
if ( !FileExists( path ) ) {
TraceLog( LOG_ERROR, "Cannot find file: %s\n", path );
- state->run = false;
- return;
+ return false;
}
luaL_dofile( L, path );
/* Check errors in main.lua */
if ( lua_tostring( L, -1 ) ) {
TraceLog( LOG_ERROR, "Lua error: %s\n", lua_tostring( L, -1 ) );
- state->run = false;
- return;
+ return false;
}
+#endif
lua_pushcfunction( L, luaTraceback );
int tracebackidx = lua_gettop( L );
/* Apply custom callback here. */
@@ -1489,8 +1689,7 @@ void luaCallMain() {
if ( lua_isfunction( L, -1 ) ) {
if ( lua_pcall( L, 0, 0, tracebackidx ) != 0 ) {
TraceLog( LOG_ERROR, "Lua error: %s", lua_tostring( L, -1 ) );
- state->run = false;
- return;
+ return false;
}
}
lua_pop( L, -1 );
@@ -1502,8 +1701,25 @@ void luaCallMain() {
stateContextInit();
}
else {
- state->run = false;
+ return false;
}
+
+ lua_getglobal( L, "RL" );
+ lua_getfield( L, -1, "init" );
+
+ if ( lua_isfunction( L, -1 ) ) {
+ if ( lua_pcall( L, 0, 0, tracebackidx ) != 0 ) {
+ TraceLog( LOG_ERROR, "Lua error: %s", lua_tostring( L, -1 ) );
+ return false;
+ }
+ }
+ else {
+ TraceLog( LOG_ERROR, "%s", "No Lua init found!" );
+ return false;
+ }
+ lua_pop( L, -1 );
+
+ return state->run;
}
void luaCallInit() {
@@ -1789,6 +2005,10 @@ void luaRegister() {
assingGlobalFunction( "GetPrevDirectoryPath", lcoreGetPrevDirectoryPath );
assingGlobalFunction( "GetWorkingDirectory", lcoreGetWorkingDirectory );
assingGlobalFunction( "GetApplicationDirectory", lcoreGetApplicationDirectory );
+ /* Asset loading functions. */
+ assingGlobalFunction( "BeginAssetLoading", lcoreBeginAssetLoading );
+ assingGlobalFunction( "UpdateAssetLoading", lcoreUpdateAssetLoading );
+ assingGlobalFunction( "EndAssetLoading", lcoreEndAssetLoading );
assingGlobalFunction( "MakeDirectory", lcoreMakeDirectory );
assingGlobalFunction( "ChangeDirectory", lcoreChangeDirectory );
assingGlobalFunction( "IsPathFile", lcoreIsPathFile );
diff --git a/src/main.c b/src/main.c
index f35d2bf..d4d8fd2 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,6 +1,15 @@
#include "main.h"
#include "state.h"
#include "lua_core.h"
+#include "splash.h"
+
+#ifdef _WIN32
+// Forward declarations for Windows console functions
+extern __declspec(dllimport) int __stdcall AllocConsole(void);
+extern __declspec(dllimport) int __stdcall FreeConsole(void);
+extern __declspec(dllimport) void* __stdcall GetStdHandle(unsigned long nStdHandle);
+extern __declspec(dllimport) int __stdcall SetStdHandle(unsigned long nStdHandle, void* hHandle);
+#endif
static inline void printVersion() {
if ( VERSION_DEV ) {
@@ -22,30 +31,92 @@ static inline void printVersion() {
int main( int argn, const char** argc ) {
char basePath[ STRING_LEN ] = { '\0' };
bool interpret_mode = false;
+ bool show_console = false;
+ bool skip_splash = false;
+
+#ifdef _WIN32
+ /* Check for --log and --no-logo arguments */
+ for ( int i = 1; i < argn; i++ ) {
+ if ( strcmp( argc[i], "--log" ) == 0 ) {
+ show_console = true;
+ }
+ if ( strcmp( argc[i], "--no-logo" ) == 0 ) {
+ skip_splash = true;
+ }
+ }
+
+ /* Show or hide console based on --log argument */
+ if ( show_console ) {
+ /* Allocate a console if we don't have one */
+ if ( AllocConsole() ) {
+ freopen( "CONOUT$", "w", stdout );
+ freopen( "CONOUT$", "w", stderr );
+ freopen( "CONIN$", "r", stdin );
+ }
+ }
+ else {
+ /* Hide console window */
+ FreeConsole();
+ }
+#else
+ /* Check for --no-logo on non-Windows platforms */
+ for ( int i = 1; i < argn; i++ ) {
+ if ( strcmp( argc[i], "--no-logo" ) == 0 ) {
+ skip_splash = true;
+ break;
+ }
+ }
+#endif
if ( 1 < argn ) {
- if ( strcmp( argc[1], "--version" ) == 0 || strcmp( argc[1], "-v" ) == 0 ) {
+ /* Skip --log and --no-logo flags to find the actual command */
+ int arg_index = 1;
+ while ( arg_index < argn && ( strcmp( argc[arg_index], "--log" ) == 0 || strcmp( argc[arg_index], "--no-logo" ) == 0 ) ) {
+ arg_index++;
+ }
+
+ if ( arg_index < argn && ( strcmp( argc[arg_index], "--version" ) == 0 || strcmp( argc[arg_index], "-v" ) == 0 ) ) {
printVersion();
return 1;
}
- else if ( strcmp( argc[1], "--help" ) == 0 || strcmp( argc[1], "-h" ) == 0 ) {
- printf( "Usage: ReiLua [Options] [Directory to main.lua or main]\nOptions:\n-h --help\tThis help\n-v --version\tShow ReiLua version\n-i --interpret\tInterpret mode [File name]\n" );
+ else if ( arg_index < argn && ( strcmp( argc[arg_index], "--help" ) == 0 || strcmp( argc[arg_index], "-h" ) == 0 ) ) {
+ printf( "Usage: ReiLua [Options] [Directory to main.lua or main]\nOptions:\n-h --help\tThis help\n-v --version\tShow ReiLua version\n-i --interpret\tInterpret mode [File name]\n--log\t\tShow console for logging\n--no-logo\tSkip splash screens (development)\n" );
return 1;
}
- else if ( strcmp( argc[1], "--interpret" ) == 0 || strcmp( argc[1], "-i" ) == 0 ) {
+ else if ( arg_index < argn && ( strcmp( argc[arg_index], "--interpret" ) == 0 || strcmp( argc[arg_index], "-i" ) == 0 ) ) {
interpret_mode = true;
- if ( 2 < argn ) {
- sprintf( basePath, "%s/%s", GetWorkingDirectory(), argc[2] );
+ if ( arg_index + 1 < argn ) {
+ sprintf( basePath, "%s/%s", GetWorkingDirectory(), argc[arg_index + 1] );
}
}
- else{
- sprintf( basePath, "%s/%s", GetWorkingDirectory(), argc[1] );
+ else if ( arg_index < argn ) {
+ sprintf( basePath, "%s/%s", GetWorkingDirectory(), argc[arg_index] );
+ }
+ else {
+ /* Only flags were provided, use default path search */
+ char testPath[ STRING_LEN ] = { '\0' };
+ sprintf( testPath, "%s/main.lua", GetWorkingDirectory() );
+
+ if ( FileExists( testPath ) ) {
+ sprintf( basePath, "%s", GetWorkingDirectory() );
+ }
+ else {
+ sprintf( basePath, "%s", GetApplicationDirectory() );
+ }
}
}
- /* If no argument given, assume main.lua is in exe directory. */
+ /* If no argument given, check current directory first, then exe directory. */
else {
- sprintf( basePath, "%s", GetApplicationDirectory() );
+ char testPath[ STRING_LEN ] = { '\0' };
+ sprintf( testPath, "%s/main.lua", GetWorkingDirectory() );
+
+ if ( FileExists( testPath ) ) {
+ sprintf( basePath, "%s", GetWorkingDirectory() );
+ }
+ else {
+ sprintf( basePath, "%s", GetApplicationDirectory() );
+ }
}
if ( interpret_mode ) {
@@ -65,15 +136,30 @@ int main( int argn, const char** argc ) {
else {
printVersion();
stateInit( argn, argc, basePath );
- luaCallMain();
- luaCallInit();
+
+ /* Show splash screens if not skipped */
+ if ( !skip_splash ) {
+ splashInit();
+ bool splashDone = false;
+
+ while ( !splashDone && !WindowShouldClose() ) {
+ float delta = GetFrameTime();
+ splashDone = splashUpdate( delta );
+ splashDraw();
+ }
+
+ splashCleanup();
+ }
+
+ /* Now run the main Lua program */
+ state->run = luaCallMain();
while ( state->run ) {
- luaCallUpdate();
- luaCallDraw();
if ( WindowShouldClose() ) {
state->run = false;
}
+ luaCallUpdate();
+ luaCallDraw();
}
luaCallExit();
}
diff --git a/src/splash.c b/src/splash.c
new file mode 100644
index 0000000..185db0c
--- /dev/null
+++ b/src/splash.c
@@ -0,0 +1,198 @@
+#include "main.h"
+#include "state.h"
+#include "splash.h"
+
+#ifdef EMBED_LOGO
+ #include "embedded_logo.h"
+#endif
+
+#define FADE_IN_TIME 0.8f
+#define DISPLAY_TIME 2.5f
+#define FADE_OUT_TIME 0.8f
+#define SPLASH_TOTAL_TIME (FADE_IN_TIME + DISPLAY_TIME + FADE_OUT_TIME)
+
+typedef enum {
+ SPLASH_INDRAJITH,
+ SPLASH_MADE_WITH,
+ SPLASH_DONE
+} SplashState;
+
+static SplashState currentSplash = SPLASH_INDRAJITH;
+static float splashTimer = 0.0f;
+static Texture2D raylibLogo = { 0 };
+static Texture2D reiluaLogo = { 0 };
+static bool logosLoaded = false;
+
+static float getSplashAlpha( float timer ) {
+ if ( timer < FADE_IN_TIME ) {
+ return timer / FADE_IN_TIME;
+ }
+ else if ( timer < FADE_IN_TIME + DISPLAY_TIME ) {
+ return 1.0f;
+ }
+ else {
+ float fadeOut = timer - FADE_IN_TIME - DISPLAY_TIME;
+ return 1.0f - ( fadeOut / FADE_OUT_TIME );
+ }
+}
+
+static void loadSplashLogos() {
+ if ( logosLoaded ) return;
+
+#ifdef EMBED_LOGO
+ /* Load from embedded data */
+ Image raylib_img = LoadImageFromMemory( ".png", embedded_raylib_logo, embedded_raylib_logo_size );
+ raylibLogo = LoadTextureFromImage( raylib_img );
+ UnloadImage( raylib_img );
+
+ Image reilua_img = LoadImageFromMemory( ".png", embedded_reilua_logo, embedded_reilua_logo_size );
+ reiluaLogo = LoadTextureFromImage( reilua_img );
+ UnloadImage( reilua_img );
+#else
+ /* Load from files (development mode) */
+ if ( FileExists( "logo/raylib_logo.png" ) ) {
+ raylibLogo = LoadTexture( "logo/raylib_logo.png" );
+ }
+ if ( FileExists( "logo/reilua_logo.png" ) ) {
+ reiluaLogo = LoadTexture( "logo/reilua_logo.png" );
+ }
+#endif
+
+ logosLoaded = true;
+}
+
+static void unloadSplashLogos() {
+ if ( !logosLoaded ) return;
+
+ UnloadTexture( raylibLogo );
+ UnloadTexture( reiluaLogo );
+ logosLoaded = false;
+}
+
+static void drawIndrajithSplash( float alpha ) {
+ int screenWidth = GetScreenWidth();
+ int screenHeight = GetScreenHeight();
+
+ ClearBackground( (Color){ 230, 41, 55, 255 } ); // Raylib red
+
+ const char* text = "INDRAJITH MAKES GAMES";
+ int fontSize = 48;
+ float spacing = 2.0f;
+
+ Color textColor = WHITE;
+ textColor.a = (unsigned char)(255 * alpha);
+
+ /* Draw text with slight expansion effect during fade in */
+ float scale = 0.95f + (alpha * 0.05f); // Subtle scale from 0.95 to 1.0
+
+ /* Measure text with proper spacing for accurate centering */
+ Vector2 textSize = MeasureTextEx( state->defaultFont, text, fontSize * scale, spacing );
+
+ /* Calculate centered position */
+ Vector2 position = {
+ (float)(screenWidth / 2) - (textSize.x / 2),
+ (float)(screenHeight / 2) - (textSize.y / 2)
+ };
+
+ /* Draw with proper kerning */
+ DrawTextEx( state->defaultFont, text, position, fontSize * scale, spacing, textColor );
+}
+
+static void drawMadeWithSplash( float alpha ) {
+ int screenWidth = GetScreenWidth();
+ int screenHeight = GetScreenHeight();
+
+ ClearBackground( BLACK );
+
+ /* "Made using" text at top */
+ const char* madeText = "Made using";
+ int madeSize = 32;
+ int madeWidth = MeasureText( madeText, madeSize );
+
+ Color textColor = WHITE;
+ textColor.a = (unsigned char)(255 * alpha);
+
+ int textY = screenHeight / 2 - 100;
+ DrawText( madeText, screenWidth / 2 - madeWidth / 2, textY, madeSize, textColor );
+
+ /* Calculate logo sizes and positions - scale down if too large */
+ int maxLogoSize = 200;
+ float raylibScale = 1.0f;
+ float reiluaScale = 1.0f;
+
+ if ( raylibLogo.id > 0 && raylibLogo.width > maxLogoSize ) {
+ raylibScale = (float)maxLogoSize / (float)raylibLogo.width;
+ }
+ if ( reiluaLogo.id > 0 && reiluaLogo.width > maxLogoSize ) {
+ reiluaScale = (float)maxLogoSize / (float)reiluaLogo.width;
+ }
+
+ int raylibWidth = (int)(raylibLogo.width * raylibScale);
+ int raylibHeight = (int)(raylibLogo.height * raylibScale);
+ int reiluaWidth = (int)(reiluaLogo.width * reiluaScale);
+ int reiluaHeight = (int)(reiluaLogo.height * reiluaScale);
+
+ /* Position logos side by side with spacing */
+ int spacing = 40;
+ int totalWidth = raylibWidth + spacing + reiluaWidth;
+ int startX = screenWidth / 2 - totalWidth / 2;
+ int logoY = screenHeight / 2 - 20;
+
+ Color tint = WHITE;
+ tint.a = (unsigned char)(255 * alpha);
+
+ /* Draw Raylib logo */
+ if ( raylibLogo.id > 0 ) {
+ Rectangle source = { 0, 0, (float)raylibLogo.width, (float)raylibLogo.height };
+ Rectangle dest = { (float)startX, (float)logoY, (float)raylibWidth, (float)raylibHeight };
+ DrawTexturePro( raylibLogo, source, dest, (Vector2){ 0, 0 }, 0.0f, tint );
+ }
+
+ /* Draw ReiLua logo */
+ if ( reiluaLogo.id > 0 ) {
+ int reiluaX = startX + raylibWidth + spacing;
+ Rectangle source = { 0, 0, (float)reiluaLogo.width, (float)reiluaLogo.height };
+ Rectangle dest = { (float)reiluaX, (float)logoY, (float)reiluaWidth, (float)reiluaHeight };
+ DrawTexturePro( reiluaLogo, source, dest, (Vector2){ 0, 0 }, 0.0f, tint );
+ }
+}
+
+void splashInit() {
+ loadSplashLogos();
+ currentSplash = SPLASH_INDRAJITH;
+ splashTimer = 0.0f;
+}
+
+bool splashUpdate( float delta ) {
+ splashTimer += delta;
+
+ if ( splashTimer >= SPLASH_TOTAL_TIME ) {
+ splashTimer = 0.0f;
+ currentSplash++;
+ }
+
+ return currentSplash >= SPLASH_DONE;
+}
+
+void splashDraw() {
+ float alpha = getSplashAlpha( splashTimer );
+
+ BeginDrawing();
+
+ switch ( currentSplash ) {
+ case SPLASH_INDRAJITH:
+ drawIndrajithSplash( alpha );
+ break;
+ case SPLASH_MADE_WITH:
+ drawMadeWithSplash( alpha );
+ break;
+ default:
+ break;
+ }
+
+ EndDrawing();
+}
+
+void splashCleanup() {
+ unloadSplashLogos();
+}