diff options
| author | Indrajith K L | 2025-11-03 17:48:56 +0530 |
|---|---|---|
| committer | Indrajith K L | 2025-11-03 17:48:56 +0530 |
| commit | 737214b71be8fe5fdf51155ad50bb064b3156bd3 (patch) | |
| tree | 21a2713d03830b21ee2b3ffd919708b054728e40 | |
| parent | 3afcbd32001fc2ab2dcee1553268dbb39dabf070 (diff) | |
| download | reilua-enhanced-737214b71be8fe5fdf51155ad50bb064b3156bd3.tar.gz reilua-enhanced-737214b71be8fe5fdf51155ad50bb064b3156bd3.tar.bz2 reilua-enhanced-737214b71be8fe5fdf51155ad50bb064b3156bd3.zip | |
Add embedded assets, splash screens, and asset loading support
Features added:
- Embedded main.lua and Lua files support (EMBED_MAIN option)
- Embedded assets support (EMBED_ASSETS option)
- Splash screens with dual logo display (always embedded)
- Asset loading progress tracking API (BeginAssetLoading, UpdateAssetLoading, EndAssetLoading)
- Custom font embedding for splash/loading screens
- --log flag for Windows console control
- --no-logo flag to skip splash screens in development
- Python scripts for embedding (embed_lua.py, embed_assets.py, embed_logo.py, embed_font.py)
- Documentation (EMBEDDING.md, ASSET_LOADING.md, SPLASH_SCREENS.md)
This allows building single-executable releases with all Lua code and assets embedded.
| -rw-r--r-- | ASSET_LOADING.md | 285 | ||||
| -rw-r--r-- | CMakeLists.txt | 70 | ||||
| -rw-r--r-- | EMBEDDING.md | 290 | ||||
| -rw-r--r-- | SPLASH_SCREENS.md | 230 | ||||
| -rw-r--r-- | build/.gitignore | 1 | ||||
| -rw-r--r-- | embed_assets.py | 116 | ||||
| -rw-r--r-- | embed_font.py | 57 | ||||
| -rw-r--r-- | embed_logo.py | 64 | ||||
| -rw-r--r-- | embed_lua.py | 92 | ||||
| -rw-r--r-- | fonts/Oleaguid.ttf | bin | 0 -> 112828 bytes | |||
| -rw-r--r-- | include/core.h | 3 | ||||
| -rw-r--r-- | include/lua_core.h | 2 | ||||
| -rw-r--r-- | include/splash.h | 6 | ||||
| -rw-r--r-- | logo/raylib_logo.png | bin | 0 -> 2466 bytes | |||
| -rw-r--r-- | logo/reilua_logo.png | bin | 0 -> 1191 bytes | |||
| -rw-r--r-- | src/core.c | 61 | ||||
| -rw-r--r-- | src/lua_core.c | 236 | ||||
| -rw-r--r-- | src/main.c | 114 | ||||
| -rw-r--r-- | src/splash.c | 198 |
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 Binary files differnew file mode 100644 index 0000000..034af36 --- /dev/null +++ b/fonts/Oleaguid.ttf 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 Binary files differnew file mode 100644 index 0000000..0edd29a --- /dev/null +++ b/logo/raylib_logo.png diff --git a/logo/reilua_logo.png b/logo/reilua_logo.png Binary files differnew file mode 100644 index 0000000..9a4eb53 --- /dev/null +++ b/logo/reilua_logo.png @@ -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 ); @@ -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(); +} |
