From a104a12cc4857ba7b166ce18d154f481f8ffe623 Mon Sep 17 00:00:00 2001 From: Miloslav Ciz Date: Thu, 19 Jun 2025 02:28:51 +0200 Subject: [PATCH] Clean up a bit --- assets.h | 15 ++-- frontend_csfml.c | 3 +- frontend_espboy/frontend_espboy.ino | 4 +- frontend_helper.h | 5 +- frontend_pokitto.cpp | 118 ++++++++++++++++++++++++++++ frontend_ringo/frontend_ringo.ino | 7 -- frontend_saf.c | 3 +- frontend_sdl.c | 9 ++- frontend_x11.c | 3 +- general.h | 6 +- map.h | 4 +- media/manual.txt | 75 ++++++++++++------ racing.h | 15 ++-- renderer.h | 2 +- 14 files changed, 207 insertions(+), 62 deletions(-) create mode 100644 frontend_pokitto.cpp diff --git a/assets.h b/assets.h index f65e6b0..d8495f5 100644 --- a/assets.h +++ b/assets.h @@ -12,9 +12,10 @@ supplied via the external data file. NOTES: - - All images are 64x64, stored in an indexed mode (8bits pery pixel), the - image starts with a 256 color palette in RGB565 format (i.e. the palette - takes 512 bytes, then 64x64 bytes follow). + + - All images are 64x64, stored in an indexed mode (8bits per pixel), the image + starts with a 256 color palette in RGB565 format (i.e. the palette takes 512 + bytes, then 64x64 bytes follow). - Each sky (background) image is composed of 4 regular images so they have higher resolution. The partial images are stored in the above format, one sky part consists of interlaced lines, so it effectively stores a 128x32 @@ -30,17 +31,17 @@ #define LCR_MODULE_NAME "asset" +#define LCR_VERSION "0.1d" + static const char *LCR_texts[] = { #define LCR_TEXTS_VERSION 0 - "0.5d", // version string - + LCR_VERSION, // version string #define LCR_TEXTS_TABS 1 "main menu", "play map", "view repl", "race repl", - #define LCR_TEXTS_MAIN_MENU 5 "camera", "music", @@ -55,8 +56,6 @@ static const char *LCR_texts[] = "failed" }; -#define LCR_VERSION "0.1d" - /// String that can be used by frontends to offer help about CLI arguments. #define LCR_ARG_HELP_STR \ " -cN set camera mode to N\n" \ diff --git a/frontend_csfml.c b/frontend_csfml.c index d9554d4..16e2f84 100644 --- a/frontend_csfml.c +++ b/frontend_csfml.c @@ -1,5 +1,6 @@ /** @file frontend_csfml.c - CSFML frontend for Licar. + + CSFML frontend for Licar, alternative to SDL. */ #include diff --git a/frontend_espboy/frontend_espboy.ino b/frontend_espboy/frontend_espboy.ino index f02d6d4..b620896 100644 --- a/frontend_espboy/frontend_espboy.ino +++ b/frontend_espboy/frontend_espboy.ino @@ -80,9 +80,7 @@ void LCR_appendDataStr(const char *str) void setup() { -// *((volatile uint32_t *) 0x60000900) &= ~(1); // disable watchdog - -wdt_disable(); + wdt_disable(); // disable watchdog dac.begin(MCP4725address); delay(100); diff --git a/frontend_helper.h b/frontend_helper.h index 1e80b13..f95b078 100644 --- a/frontend_helper.h +++ b/frontend_helper.h @@ -1,12 +1,13 @@ /** @file frontend_helper.h Helper file for generic PC frontends that can use the stdio library, to avoid - duplication of code. + duplication of code. Just include it. */ #include #include +// Before including this file, it's possible to define a quality preset: #if PRESET_QUALITY == 1 // ultra low #define LCR_SETTING_RESOLUTION_X 256 #define LCR_SETTING_RESOLUTION_Y 200 @@ -86,7 +87,7 @@ void LCR_appendDataStr(const char *str) else { #if LCR_SETTING_LOG_LEVEL > 1 - printf("SDL: appending data \"%s\"\n",str); + printf("appending data: \"%s\"\n",str); #endif fclose(dataFile); diff --git a/frontend_pokitto.cpp b/frontend_pokitto.cpp new file mode 100644 index 0000000..2892b20 --- /dev/null +++ b/frontend_pokitto.cpp @@ -0,0 +1,118 @@ +/** @file frontend_pokitto.cpp + + Experimental Pokitto frontend, very limited. +*/ + +#include "../PokittoLib/Pokitto/Pokitto.h" + +#define LCR_SETTING_RESOLUTION_X 110 +#define LCR_SETTING_RESOLUTION_Y 88 +#define LCR_SETTING_RESOLUTION_SUBDIVIDE 2 +#define LCR_SETTING_MUSIC 0 +#define LCR_SETTING_GHOST_MAX_SAMPLES 0 +#define LCR_SETTING_332_COLOR 1 +#define LCR_SETTING_MAP_MAX_BLOCKS 280 +#define LCR_SETTING_MAX_MAP_VERTICES 200 +#define LCR_SETTING_MAX_MAP_TRIANGLES 300 +#define LCR_SETTING_REPLAY_MAX_SIZE 0 +#define LCR_SETTING_CAR_SHADOW 0 +#define LCR_SETTING_PARTICLES 0 +#define LCR_SETTING_LOD_DISTANCE 100 +#define LCR_SETTING_CAR_ANIMATION_SUBDIVIDE 0 +#define LCR_SETTING_FPS 20 +#define LCR_SETTING_ENABLE_DATA_FILE 0 +#define LCR_SETTING_ONLY_SMALL_MAPS 1 +#define S3L_SORT 0 +#define S3L_Z_BUFFER 0 +#define S3L_MAX_TRIANGES_DRAWN 16 +#define LCR_SETTING_POTATO_GRAPHICS 1 + +static const uint16_t palette[256] = // 332 palette as 565 +{ +0x0000, 0x000a, 0x0015, 0x001f, 0x0120, 0x012a, 0x0135, 0x013f, 0x0240, 0x024a, +0x0255, 0x025f, 0x0360, 0x036a, 0x0375, 0x037f, 0x0480, 0x048a, 0x0495, 0x049f, +0x05a0, 0x05aa, 0x05b5, 0x05bf, 0x06c0, 0x06ca, 0x06d5, 0x06df, 0x07e0, 0x07ea, +0x07f5, 0x07ff, 0x2000, 0x200a, 0x2015, 0x201f, 0x2120, 0x212a, 0x2135, 0x213f, +0x2240, 0x224a, 0x2255, 0x225f, 0x2360, 0x236a, 0x2375, 0x237f, 0x2480, 0x248a, +0x2495, 0x249f, 0x25a0, 0x25aa, 0x25b5, 0x25bf, 0x26c0, 0x26ca, 0x26d5, 0x26df, +0x27e0, 0x27ea, 0x27f5, 0x27ff, 0x4800, 0x480a, 0x4815, 0x481f, 0x4920, 0x492a, +0x4935, 0x493f, 0x4a40, 0x4a4a, 0x4a55, 0x4a5f, 0x4b60, 0x4b6a, 0x4b75, 0x4b7f, +0x4c80, 0x4c8a, 0x4c95, 0x4c9f, 0x4da0, 0x4daa, 0x4db5, 0x4dbf, 0x4ec0, 0x4eca, +0x4ed5, 0x4edf, 0x4fe0, 0x4fea, 0x4ff5, 0x4fff, 0x6800, 0x680a, 0x6815, 0x681f, +0x6920, 0x692a, 0x6935, 0x693f, 0x6a40, 0x6a4a, 0x6a55, 0x6a5f, 0x6b60, 0x6b6a, +0x6b75, 0x6b7f, 0x6c80, 0x6c8a, 0x6c95, 0x6c9f, 0x6da0, 0x6daa, 0x6db5, 0x6dbf, +0x6ec0, 0x6eca, 0x6ed5, 0x6edf, 0x6fe0, 0x6fea, 0x6ff5, 0x6fff, 0x9000, 0x900a, +0x9015, 0x901f, 0x9120, 0x912a, 0x9135, 0x913f, 0x9240, 0x924a, 0x9255, 0x925f, +0x9360, 0x936a, 0x9375, 0x937f, 0x9480, 0x948a, 0x9495, 0x949f, 0x95a0, 0x95aa, +0x95b5, 0x95bf, 0x96c0, 0x96ca, 0x96d5, 0x96df, 0x97e0, 0x97ea, 0x97f5, 0x97ff, +0xb000, 0xb00a, 0xb015, 0xb01f, 0xb120, 0xb12a, 0xb135, 0xb13f, 0xb240, 0xb24a, +0xb255, 0xb25f, 0xb360, 0xb36a, 0xb375, 0xb37f, 0xb480, 0xb48a, 0xb495, 0xb49f, +0xb5a0, 0xb5aa, 0xb5b5, 0xb5bf, 0xb6c0, 0xb6ca, 0xb6d5, 0xb6df, 0xb7e0, 0xb7ea, +0xb7f5, 0xb7ff, 0xd800, 0xd80a, 0xd815, 0xd81f, 0xd920, 0xd92a, 0xd935, 0xd93f, +0xda40, 0xda4a, 0xda55, 0xda5f, 0xdb60, 0xdb6a, 0xdb75, 0xdb7f, 0xdc80, 0xdc8a, +0xdc95, 0xdc9f, 0xdda0, 0xddaa, 0xddb5, 0xddbf, 0xdec0, 0xdeca, 0xded5, 0xdedf, +0xdfe0, 0xdfea, 0xdff5, 0xdfff, 0xf800, 0xf80a, 0xf815, 0xf81f, 0xf920, 0xf92a, +0xf935, 0xf93f, 0xfa40, 0xfa4a, 0xfa55, 0xfa5f, 0xfb60, 0xfb6a, 0xfb75, 0xfb7f, +0xfc80, 0xfc8a, 0xfc95, 0xfc9f, 0xfda0, 0xfdaa, 0xfdb5, 0xfdbf, 0xfec0, 0xfeca, +0xfed5, 0xfedf, 0xffe0, 0xffea, 0xfff5, 0xffff +}; + +#include "game.h" + +Pokitto::Core pokitto; +uint8_t *pokittoScreen; + +void LCR_drawPixel(unsigned long index, uint16_t color) +{ + pokittoScreen[index] = color; +} + +void LCR_sleep(uint16_t timeMs) +{ +} + +uint8_t LCR_keyPressed(uint8_t key) +{ + switch (key) + { + case LCR_KEY_UP: return pokitto.upBtn(); break; + case LCR_KEY_DOWN: return pokitto.downBtn(); break; + case LCR_KEY_RIGHT: return pokitto.rightBtn(); break; + case LCR_KEY_LEFT: return pokitto.leftBtn(); break; + case LCR_KEY_A: return pokitto.aBtn(); break; + case LCR_KEY_B: return pokitto.bBtn() || pokitto.cBtn(); break; + default: return 0; break; + } +} + +char LCR_getNextDataFileChar(void) +{ + return 0; +} + +void LCR_appendDataStr(const char *str) +{ + return; +} + +int main(void) +{ + pokitto.begin(); + pokitto.setFrameRate(255); + pokitto.display.setFont(fontTiny); + pokitto.display.persistence = 1; + pokitto.display.setInvisibleColor(-1); + pokitto.display.load565Palette(palette); + pokittoScreen = pokitto.display.screenbuffer; + + LCR_gameInit(0,0); + + while (pokitto.isRunning()) + if (pokitto.update()) + if (!LCR_gameStep(pokitto.getTime())) + break; + + LCR_gameEnd(); + pokitto.quit(); + return 0; +} diff --git a/frontend_ringo/frontend_ringo.ino b/frontend_ringo/frontend_ringo.ino index 37dbfec..0456aef 100644 --- a/frontend_ringo/frontend_ringo.ino +++ b/frontend_ringo/frontend_ringo.ino @@ -72,14 +72,9 @@ void LCR_appendDataStr(const char *str) return; } -//Oscillator *osc; - void setup() { mp.begin(); -// osc = new Oscillator(SINE); -// addOscillator(osc); -// SFG_init(); LCR_gameInit(0,0); } @@ -91,8 +86,6 @@ void loop() ((mp.buttons.getJoystickY() < 200)) << 2 | ((mp.buttons.getJoystickY() > 900)) << 3; -// SFG_mainLoopBody(); - LCR_gameStep(millis()); mp.update(); diff --git a/frontend_saf.c b/frontend_saf.c index 0054740..e392124 100644 --- a/frontend_saf.c +++ b/frontend_saf.c @@ -1,5 +1,6 @@ /** @file frontend_saf.c - SAF frontend for Licar. + + SAF frontend for Licar, just for fun (probably not seriously playable). */ #define SAF_PLATFORM_SDL2 diff --git a/frontend_sdl.c b/frontend_sdl.c index 949d6a9..6469449 100644 --- a/frontend_sdl.c +++ b/frontend_sdl.c @@ -1,5 +1,8 @@ /** @file frontend_sdl.c - SDL2 frontend for Licar. + + SDL2 frontend for Licar. This is the number one frontend, most advanced and + with most features -- if a platform supports SDL2, this should probably be + used. */ #include @@ -8,7 +11,9 @@ #define LCR_SETTING_LOG_LEVEL 2 #define LCR_LOADING_COMMAND SDL_PumpEvents(); -#define LCR_FPS_GET_MS SDL_GetTicks() // uncomment for FPS measuring +#if 0 +#define LCR_FPS_GET_MS SDL_GetTicks() // enabling will log FPS in console +#endif #ifdef __EMSCRIPTEN__ #define LCR_SETTING_RESOLUTION_X 512 diff --git a/frontend_x11.c b/frontend_x11.c index fbdfdcd..d1d1102 100644 --- a/frontend_x11.c +++ b/frontend_x11.c @@ -1,5 +1,6 @@ /** @file frontend_sdl.c - X11 frontend for Licar. + + X11 frontend for Licar, has no sound. */ #include diff --git a/general.h b/general.h index 7dc09ad..c999bf1 100644 --- a/general.h +++ b/general.h @@ -5,10 +5,10 @@ Licar: general - This file holds general definitions used by all modules. + This file holds general definitions used by all Licar modules. All Licar code uses LCR_ (or _LCR_) prefix as a kind of namespace preventing - collision with 3rd party identifiers. + collisions with 3rd party identifiers. */ #include @@ -74,7 +74,7 @@ int _LCR_hexDigitVal(char c) } /** - Computes simple hash of a string represented by a function returning next + Computes a simple hash of a string represented by a function returning next string character, ending at 0 or endChar. This is intended for simple (but not 100% reliable) string comparison. */ diff --git a/map.h b/map.h index aa13565..13e0699 100644 --- a/map.h +++ b/map.h @@ -31,8 +31,8 @@ following are characters signifying numbers 0 to 63: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$@ - M is optional block material ('0' to '3', 0 is default). - - T is an optinal transform string (for more detail see the binary format) - consisting from 0 to 3 characters, which may be: + - T is an optional transform string (for more detail see the binary + format) consisting of 0 to 3 characters, which may be: - '|': flip horizontally - '-': flip vertically - 'L': rotate 90 degrees diff --git a/media/manual.txt b/media/manual.txt index d0b34b2..aa8e7e2 100644 --- a/media/manual.txt +++ b/media/manual.txt @@ -1,5 +1,3 @@ -WORK IN PROGRESS - '-._-'"'-_.-'"'-._.-'"'-._.-'"'- LICAR MANUAL -'"'-._.-'"'-._.-'"'-._.-'"'-._.-' libre racing video game by drummyfish (drummyfish@disroot.org) @@ -83,9 +81,9 @@ right). There are two additional keys, A and B (depending on your platform these may be e.g. K and L keys, RETURN and ESCAPE etc.), for restarting runs and handling menu. -~~~~~ GAMPLAY TIPS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~ GAMEPLAY TIPS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Here are some tips: +Here are some tips for playing: - It's possible to stop the car from rotating mid-air by pressing brake. - Similarly, holding down brake in air will slow down the car's horizontal @@ -150,30 +148,34 @@ different settings. ~~~~~ REPLAYS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If your platform supports it, Licar can save and play back recorded runs as -"replays" (sometimes also called "demos"), which are just exact records of game -inputs (so that replays, unlike videos, take very little space). Replays can -also be used to spawn a ghost car to race against. +"replays" (sometimes also called "demos") which are nothing else than exact +records of game input keys (so that replays, unlike videos, take very little +space). Replays can also be used to spawn a ghost car to race against. There +are special tabs in the menu to view a replay or race against a replay ghost. -Replays are saved in the data file. +Replays are saved in the data file (see the section about data file for +details). Whenever a map's target time is beaten, replay is automatically saved. It's also possible to save a replay manually by opening the menu and selecting -"save replay". +"save replay" after finishing a map. To share or add replays you can simply copy +replay strings to/from the data file with any text editor. Similarly to delete a +replay you just delete it in the data file. ~~~~~ GHOSTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The game supports ghost cars to race against (note that this may be unsupported -on some weaker platforms). A ghost car is created from a replay and will race -in real time against the player, without being able to collide with him. This is +on some weaker platforms). A ghost car is created from a replay and will race in +real time against the player, without being able to collide with him. This is very useful when attacking someone else's (or one's own) achieved time, to see where exactly time losses against the opponent occur. ~~~~~ MAKING CUSTOM MAPS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Maps are stored in the data file. For its simplicity the original game doesn't -come with a user-friendly map editor, the maps are hand-written directly in the -text format that's stored in the data file, however there is a helper file for -Blender that facilitates this process. +come with a user-friendly map editor (perhaps some will be made over time), the +maps are hand-written directly in the text format that's stored in the data +file, however there is a helper file for Blender that facilitates this process. In the game a map is represented as a 64x64x64 grid, with each cell one unit in width, one unit in depth and HALF a unit in height. In the cell blocks can be @@ -195,12 +197,12 @@ details you won't find here. In summary: First, if the map is preceded by another data string in the data file, remember to start it with the `#` character (to separate it from the previous string). -Then there must be the letter `M` (for map), and the immediately the map's name +Then there must be the letter `M` (for map), and then immediately the map's name terminated with `;`. For example a map named "mymap" will in the data file start with "#Mmymap;". Then the map string itself follows. This string starts with a decimal number saying the target time in physics -ticks, then a space (` `) follows, then a single digit saying the map +ticks, then a space (` `) follows, then a single digit specifying the map environment (`0`, `1` or `2`), and then the string of map blocks follows. Each map block starts with `:`, then immediately follows the type of the block @@ -213,9 +215,9 @@ this: I.e. number 11 will be encoded as `b` etc. After the coordinates an optional decimal digit MAY follow, which defines the block material (`0`, `1`, `2` or `3`, `0` being default). Finally there MAY follow the transformation string, -which may consists of characters `|` (flip horizontally), `-` (flip vertically), +which may consist of characters `|` (flip horizontally), `-` (flip vertically), `L` (rotate by 90 degrees), `I` (rotate by 180 degrees and `J` (rotate by 270 -degrees). For example ":^5dA1-L" represents a ramp (`^`) placed on coordinates +degrees). For example ":^5dA1-L" represents a ramp (`^`) placed at coordinates 5, 13, 36, having material 1 (grass), flipped vertically (`-`) and rotated by 180 degrees (`I`). @@ -282,15 +284,40 @@ A few tips for making maps: ~~~~~ COMPILING AND MODIFYING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Mods are highly recommended to be distributed as diff files that will apply -changes to the source code. This is inspired by suckless programs. +Mods are highly recommended to be distributed as context-aware diff files that +will apply changes to the base source code. This is inspired by suckless +programs. Distributing software in source form (preferably licensed as free +software), as opposed to compiled binaries, is highly advised, in Licar and +elsewhere :) -TODO +Compiling the code depends on platform, but in general is done by simply +compiling the platform's front end. For example the SDL2 frontend is compiled +just by handing frontend_sdl.c to any C99 compiler (of course with appropriate +flags, e.g. linking the SDL library). There is a Unix script that helps with +compiling: make.sh. On many systems you can compile the game just by running +the script. Otherwise read the script to learn about the details (flags etc.). + +Understanding the code: the code is highly commented and meant to serve as its +own documentation, so first read the code. There is also a Doxygen file that can +help you easily generate reference pages. Otherwise the code is pretty +straightforward and shouldn't be too hard to understand (there is no OOP, no +3rd party libraries, ...). It is in some ways unique, but always in a way that +makes it easier to work with: for example the whole game is implemented solely +in header files so that you compile only a single file: your frontend. I.e. no +headaches with linking etc. The code follows the suckless philosophy and also +what's called LRS (less retarded software): check these out to learn more. + +Porting and making your own frontends is quite easy, you just write a program +in C (or a compatible language such as C++) in which you include the game's +backend code (literally just #include "game.h") and implement a few basic +functions such as a function for drawing pixels or reading keyboard. Exact +details about this are documented in game.h. ~~~~~ CREDITS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Everything was created by drummyfish. Special thanks goes to my friends Blitz -and Ramon for help with testing <3 +Everything was created by drummyfish, down to making own photo textures and +recording own samples for music in the soundtrack. Special thanks goes to my +friends Blitz and Ramon for help with testing and providing some ideas <3 ~~~~~ FAQ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -314,7 +341,7 @@ that's meant to help the people. Q: Can I still voluntarily donate money? A: Currently it is possible, donation info is somewhere on my website, but I am -not dependant on donations and am sometimes doubting if it's a good thing to +not dependent on donations and am sometimes doubting if it's a good thing to accept them, but if you still insist, I'll be very grateful, thank you very much. diff --git a/racing.h b/racing.h index c20d281..1f9615c 100644 --- a/racing.h +++ b/racing.h @@ -41,8 +41,8 @@ i.e. the series of 16 bit words in hexadecimal, each preceded by ':'. The events (but nothing else) may otherwise be preceded or followed by other characters (possible comments). All hexadecimal letters must be lowercase. - The word 00000000 may optinally be used to terminate the replay, the rest of - the string will be ignored. + The word 00000000 may optionally be used to terminate the replay, the rest + of the string will be ignored. - Physics engine version: LCR_RACING_VERSION1 and LCR_RACING_VERSION2 define a two-character version string of this module that determines compatibility of replays. Whenever a change is made to this module that changes the @@ -81,11 +81,12 @@ typedef int32_t LCR_GameUnit; ///< Abstract game unit. #define LCR_PHYSICS_UNIT 4096 ///< Len. of square for phys. engine. -/* The combination of values TPE_RESHAPE_TENSION_LIMIT and - TPE_RESHAPE_ITERATIONS has crucial effect on the bugginess and feel of car - physics, especially when driving onto ramps in high speed etc., the values - were chosen empirically by testing, these are the ones that showed to - subjectively feel the best. */ +/* + The combination of values TPE_RESHAPE_TENSION_LIMIT and TPE_RESHAPE_ITERATIONS + has crucial effect on the bugginess and feel of car physics, especially when + driving onto ramps in high speed etc., the values were chosen empirically by + testing, these are the ones that showed to subjectively feel the best. +*/ #define TPE_RESHAPE_TENSION_LIMIT 7 #define TPE_RESHAPE_ITERATIONS 18 diff --git a/renderer.h b/renderer.h index 0b3b9f2..09d2e15 100644 --- a/renderer.h +++ b/renderer.h @@ -150,7 +150,7 @@ struct polygons will be drawn without texture, with color stored in this variable. */ #if LCR_SETTING_PARTICLES - uint_fast16_t particleColor; /**< 0x0000 means no particles active. */ + uint_fast16_t particleColor; ///< 0x0000 means no particles active. #endif } LCR_renderer;