|
| 1 | +# **Harbour Masters C Mod Template** |
| 2 | + |
| 3 | +A streamlined template for creating C-based mods for games utilizing the libultraship engine (e.g., Super Mario 64 PC ports). |
| 4 | + |
| 5 | +This template utilizes a Unity Build (amalgamation) pipeline. It allows you to write clean, modular C code organized across multiple directories, and automatically compiles them down into a single, portable mod.c file. This single-file output is perfectly formatted for dynamic compilation and loading via TCC (Tiny C Compiler) or for easy distribution. |
| 6 | + |
| 7 | +## **🌟 Features** |
| 8 | + |
| 9 | +* **Automated Amalgamation:** Uses a custom Python script hooked into CMake to safely merge all your .c and .h files, preventing redefinition errors. |
| 10 | +* **Cross-Platform Exports:** Includes HM_API macros to handle the exact ABI export rules so the engine's dynamic linker can always find your ModInit function. |
| 11 | +* **Event Hooking:** Includes a working boilerplate for tapping into the engine's event system (e.g., GameFrameUpdate, RenderGamePost). |
| 12 | + |
| 13 | +## **📂 Directory Structure** |
| 14 | + |
| 15 | +``` |
| 16 | +├── CMakeLists.txt # The build configuration |
| 17 | +├── tools/ |
| 18 | +│ └── merge.py # The Python script that amalgamates the source code |
| 19 | +├── include/ |
| 20 | +│ └── mod.h # Core macros (MOD_INIT, MOD_EXIT, HM_API) |
| 21 | +├── src/ |
| 22 | +│ └── demo.c # Your actual mod logic and event listeners |
| 23 | +└── output/ |
| 24 | + ├── mod.c # (Generated) The final, single-file amalgamated mod |
| 25 | + └── manifest.json # Metadata for the mod loader |
| 26 | +``` |
| 27 | + |
| 28 | +## **🚀 Getting Started** |
| 29 | + |
| 30 | +### **Prerequisites** |
| 31 | + |
| 32 | +* **CMake** (3.10 or higher) |
| 33 | +* **Python 3** (used for the amalgamation step) |
| 34 | +* The libultraship source headers available in your parent directory structure. |
| 35 | + |
| 36 | +### **Building the Mod** |
| 37 | + |
| 38 | +We use an out-of-source build to keep the working directory clean. The CMake script will automatically grab all files in the src/ directory and generate the single mod.c in the output/ folder. |
| 39 | + |
| 40 | +1. Open your terminal in the project root. |
| 41 | +2. Create a build directory and configure the project: |
| 42 | + ```Bash |
| 43 | + mkdir build |
| 44 | + cd build |
| 45 | + cmake .. |
| 46 | + ``` |
| 47 | + |
| 48 | +3. Run the build process: |
| 49 | + ```Bash |
| 50 | + cmake --build . |
| 51 | + ``` |
| 52 | + |
| 53 | +4. Your fully amalgamated mod will be available at output/mod.c, ready to be packaged alongside manifest.json. |
| 54 | + |
| 55 | +## **🛠️ Writing Your Mod** |
| 56 | + |
| 57 | +### **The Entry and Exit Points** |
| 58 | + |
| 59 | +Every mod requires an entry point so the engine knows how to initialize it. Use the provided macros from mod.h: |
| 60 | + |
| 61 | +```C |
| 62 | +#include "mod.h" |
| 63 | + |
| 64 | +MOD_INIT() { |
| 65 | + // Called when the mod is dynamically loaded. |
| 66 | + // Register your engine listeners here. |
| 67 | +} |
| 68 | + |
| 69 | +MOD_EXIT() { |
| 70 | + // Called when the mod is unloaded or the game closes. |
| 71 | + // Unregister your listeners and free memory here. |
| 72 | +} |
| 73 | +``` |
| 74 | + |
| 75 | +### **Hooking into Engine Events** |
| 76 | + |
| 77 | +The template includes a demo.c file showing how to hook into the engine's game loop. |
| 78 | + |
| 79 | +* **GameFrameUpdate**: Ideal for gameplay logic (e.g., modifying gMarioState). |
| 80 | +* **RenderGamePost**: Ideal for drawing custom UI (e.g., using print_text_centered). |
| 81 | + |
| 82 | +Make sure to store your ListenerIDs globally so you can properly unregister them in MOD_EXIT(). |
| 83 | + |
| 84 | +### **⚠️ Important Note on Unity Builds (Amalgamation)** |
| 85 | + |
| 86 | +Because all of your .c files are ultimately merged into a single mod.c file, everything shares the same global scope. |
| 87 | + |
| 88 | +* **Use static:** If you write a helper function that should only be used internally, **always** prefix it with static. This ensures it won't accidentally collide with functions of the same name in the game engine or other mods once loaded into memory. |
0 commit comments