瀏覽代碼

Develop branch integration (#1091)

* [core] REDESIGNED: Implement global context

* [rlgl] REDESIGNED: Implement global context

* Reviewed globals for Android

* Review Android globals usage

* Update Android globals

* Bump raylib version to 3.0 !!!

* [raudio] REDESIGNED: Implement global context

* [raudio] Reorder functions

* [core] Tweaks on descriptions

* Issues with SUPPORT_MOUSE_GESTURES

* [camera] Use global context

* REDESIGN: Move shapes drawing texture/rec to RLGL context

* Review some issues on standalone mode

* Update to use global context

* [GAME] Upload RE-PAIR game from GGJ2020 -WIP-

* Update game: RE-PAIR

* [utils] TRACELOG macros proposal

* Update config.h
Ray 5 年之前
父節點
當前提交
40b73a8a91

+ 20 - 0
games/repair/LICENSE.txt

@@ -0,0 +1,20 @@
+
+This game sources are licensed under an unmodified zlib/libpng license, 
+which is an OSI-certified, BSD-like license:
+	
+Copyright (c) 2020 Ramon Santamaria (@raysan5)
+
+This software is provided "as-is", without any express or implied warranty. In no event 
+will the authors be held liable for any damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose, including commercial 
+applications, and to alter it and redistribute it freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not claim that you 
+  wrote the original software. If you use this software in a product, an acknowledgment 
+  in the product documentation would be appreciated but is not required.
+
+  2. Altered source versions must be plainly marked as such, and must not be misrepresented
+  as being the original software.
+
+  3. This notice may not be removed or altered from any source distribution.

+ 406 - 0
games/repair/Makefile

@@ -0,0 +1,406 @@
+#**************************************************************************************************
+#
+#   raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5
+#
+#   Copyright (c) 2013-2020 Ramon Santamaria (@raysan5)
+#
+#   This software is provided "as-is", without any express or implied warranty. In no event
+#   will the authors be held liable for any damages arising from the use of this software.
+#
+#   Permission is granted to anyone to use this software for any purpose, including commercial
+#   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+#
+#     1. The origin of this software must not be misrepresented; you must not claim that you
+#     wrote the original software. If you use this software in a product, an acknowledgment
+#     in the product documentation would be appreciated but is not required.
+#
+#     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+#     as being the original software.
+#
+#     3. This notice may not be removed or altered from any source distribution.
+#
+#**************************************************************************************************
+
+.PHONY: all clean
+
+# Define required raylib variables
+PROJECT_NAME       ?= repair
+RAYLIB_VERSION     ?= 3.0.0
+RAYLIB_API_VERSION ?= 3
+RAYLIB_PATH        ?= C:\GitHub\raylib
+
+# Define default options
+
+# One of PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB
+PLATFORM           ?= PLATFORM_DESKTOP
+
+# Locations of your newly installed library and associated headers. See ../src/Makefile
+# On Linux, if you have installed raylib but cannot compile the examples, check that
+# the *_INSTALL_PATH values here are the same as those in src/Makefile or point to known locations.
+# To enable system-wide compile-time and runtime linking to libraylib.so, run ../src/$ sudo make install RAYLIB_LIBTYPE_SHARED.
+# To enable compile-time linking to a special version of libraylib.so, change these variables here.
+# To enable runtime linking to a special version of libraylib.so, see EXAMPLE_RUNTIME_PATH below.
+# If there is a libraylib in both EXAMPLE_RUNTIME_PATH and RAYLIB_INSTALL_PATH, at runtime,
+# the library at EXAMPLE_RUNTIME_PATH, if present, will take precedence over the one at RAYLIB_INSTALL_PATH.
+# RAYLIB_INSTALL_PATH should be the desired full path to libraylib. No relative paths.
+DESTDIR ?= /usr/local
+RAYLIB_INSTALL_PATH ?= $(DESTDIR)/lib
+# RAYLIB_H_INSTALL_PATH locates the installed raylib header and associated source files.
+RAYLIB_H_INSTALL_PATH ?= $(DESTDIR)/include
+
+# Library type used for raylib: STATIC (.a) or SHARED (.so/.dll)
+RAYLIB_LIBTYPE        ?= STATIC
+
+# Build mode for project: DEBUG or RELEASE
+BUILD_MODE            ?= RELEASE
+
+# Use external GLFW library instead of rglfw module
+# TODO: Review usage on Linux. Target version of choice. Switch on -lglfw or -lglfw3
+USE_EXTERNAL_GLFW     ?= FALSE
+
+# Use Wayland display server protocol on Linux desktop
+# by default it uses X11 windowing system
+USE_WAYLAND_DISPLAY   ?= FALSE
+
+# Determine PLATFORM_OS in case PLATFORM_DESKTOP selected
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+    # No uname.exe on MinGW!, but OS=Windows_NT on Windows!
+    # ifeq ($(UNAME),Msys) -> Windows
+    ifeq ($(OS),Windows_NT)
+        PLATFORM_OS=WINDOWS
+    else
+        UNAMEOS=$(shell uname)
+        ifeq ($(UNAMEOS),Linux)
+            PLATFORM_OS=LINUX
+        endif
+        ifeq ($(UNAMEOS),FreeBSD)
+            PLATFORM_OS=BSD
+        endif
+        ifeq ($(UNAMEOS),OpenBSD)
+            PLATFORM_OS=BSD
+        endif
+        ifeq ($(UNAMEOS),NetBSD)
+            PLATFORM_OS=BSD
+        endif
+        ifeq ($(UNAMEOS),DragonFly)
+            PLATFORM_OS=BSD
+        endif
+        ifeq ($(UNAMEOS),Darwin)
+            PLATFORM_OS=OSX
+        endif
+    endif
+endif
+ifeq ($(PLATFORM),PLATFORM_RPI)
+    UNAMEOS=$(shell uname)
+    ifeq ($(UNAMEOS),Linux)
+        PLATFORM_OS=LINUX
+    endif
+endif
+
+# RAYLIB_PATH adjustment for different platforms.
+# If using GNU make, we can get the full path to the top of the tree. Windows? BSD?
+# Required for ldconfig or other tools that do not perform path expansion.
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+    ifeq ($(PLATFORM_OS),LINUX)
+        RAYLIB_PREFIX ?= ..
+        RAYLIB_PATH    = $(realpath $(RAYLIB_PREFIX))
+    endif
+endif
+# Default path for raylib on Raspberry Pi, if installed in different path, update it!
+# This is not currently used by src/Makefile. Not sure of its origin or usage. Refer to wiki.
+# TODO: update install: target in src/Makefile for RPI, consider relation to LINUX.
+ifeq ($(PLATFORM),PLATFORM_RPI)
+    RAYLIB_PATH       ?= /home/pi/raylib
+endif
+
+ifeq ($(PLATFORM),PLATFORM_WEB)
+    # Emscripten required variables
+    EMSDK_PATH         ?= C:/emsdk
+    EMSCRIPTEN_PATH    ?= $(EMSDK_PATH)/fastcomp/emscripten
+    CLANG_PATH          = $(EMSDK_PATH)/fastcomp/bin
+    PYTHON_PATH         = $(EMSDK_PATH)/python/2.7.13.1_64bit/python-2.7.13.amd64
+    NODE_PATH           = $(EMSDK_PATH)/node/12.9.1_64bit/bin
+    export PATH         = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH);C:\raylib\MinGW\bin:$$(PATH)
+endif
+
+# Define raylib release directory for compiled library.
+# RAYLIB_RELEASE_PATH points to provided binaries or your freshly built version
+RAYLIB_RELEASE_PATH 	?= $(RAYLIB_PATH)/src
+
+# EXAMPLE_RUNTIME_PATH embeds a custom runtime location of libraylib.so or other desired libraries
+# into each example binary compiled with RAYLIB_LIBTYPE=SHARED. It defaults to RAYLIB_RELEASE_PATH
+# so that these examples link at runtime with your version of libraylib.so in ../release/libs/linux
+# without formal installation from ../src/Makefile. It aids portability and is useful if you have
+# multiple versions of raylib, have raylib installed to a non-standard location, or want to
+# bundle libraylib.so with your game. Change it to your liking.
+# NOTE: If, at runtime, there is a libraylib.so at both EXAMPLE_RUNTIME_PATH and RAYLIB_INSTALL_PATH,
+# The library at EXAMPLE_RUNTIME_PATH, if present, will take precedence over RAYLIB_INSTALL_PATH,
+# Implemented for LINUX below with CFLAGS += -Wl,-rpath,$(EXAMPLE_RUNTIME_PATH)
+# To see the result, run readelf -d core/core_basic_window; looking at the RPATH or RUNPATH attribute.
+# To see which libraries a built example is linking to, ldd core/core_basic_window;
+# Look for libraylib.so.1 => $(RAYLIB_INSTALL_PATH)/libraylib.so.1 or similar listing.
+EXAMPLE_RUNTIME_PATH   ?= $(RAYLIB_RELEASE_PATH)
+
+# Define default C compiler: gcc
+# NOTE: define g++ compiler if using C++
+CC = gcc
+
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+    ifeq ($(PLATFORM_OS),OSX)
+        # OSX default compiler
+        CC = clang
+    endif
+    ifeq ($(PLATFORM_OS),BSD)
+        # FreeBSD, OpenBSD, NetBSD, DragonFly default compiler
+        CC = clang
+    endif
+endif
+ifeq ($(PLATFORM),PLATFORM_RPI)
+    ifeq ($(USE_RPI_CROSS_COMPILER),TRUE)
+        # Define RPI cross-compiler
+        #CC = armv6j-hardfloat-linux-gnueabi-gcc
+        CC = $(RPI_TOOLCHAIN)/bin/arm-linux-gnueabihf-gcc
+    endif
+endif
+ifeq ($(PLATFORM),PLATFORM_WEB)
+    # HTML5 emscripten compiler
+    # WARNING: To compile to HTML5, code must be redesigned
+    # to use emscripten.h and emscripten_set_main_loop()
+    CC = emcc
+endif
+
+# Define default make program: Mingw32-make
+MAKE = mingw32-make
+
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+    ifeq ($(PLATFORM_OS),LINUX)
+        MAKE = make
+    endif
+endif
+
+# Define compiler flags:
+#  -O1                  defines optimization level
+#  -g                   include debug information on compilation
+#  -s                   strip unnecessary data from build
+#  -Wall                turns on most, but not all, compiler warnings
+#  -std=c99             defines C language mode (standard C from 1999 revision)
+#  -std=gnu99           defines C language mode (GNU C from 1999 revision)
+#  -Wno-missing-braces  ignore invalid warning (GCC bug 53119)
+#  -D_DEFAULT_SOURCE    use with -std=c99 on Linux and PLATFORM_WEB, required for timespec
+CFLAGS += -Wall -std=c99 -D_DEFAULT_SOURCE -Wno-missing-braces
+
+ifeq ($(BUILD_MODE),DEBUG)
+    CFLAGS += -g
+    ifeq ($(PLATFORM),PLATFORM_WEB)
+        CFLAGS += -s ASSERTIONS=1 --profiling
+    endif
+else
+    ifeq ($(PLATFORM),PLATFORM_WEB)
+        CFLAGS += -Os
+    else
+        CFLAGS += -s -O1
+    endif
+endif
+
+# Additional flags for compiler (if desired)
+#CFLAGS += -Wextra -Wmissing-prototypes -Wstrict-prototypes
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+    ifeq ($(PLATFORM_OS),WINDOWS)
+        # resource file contains windows executable icon and properties
+        # -Wl,--subsystem,windows hides the console window
+        CFLAGS += $(RAYLIB_PATH)/src/raylib.rc.data 
+        ifeq ($(BUILD_MODE), RELEASE)
+            CFLAGS += -Wl,--subsystem,windows
+        endif
+    endif
+    ifeq ($(PLATFORM_OS),LINUX)
+        ifeq ($(RAYLIB_LIBTYPE),STATIC)
+            CFLAGS += -D_DEFAULT_SOURCE
+        endif
+        ifeq ($(RAYLIB_LIBTYPE),SHARED)
+            # Explicitly enable runtime link to libraylib.so
+            CFLAGS += -Wl,-rpath,$(EXAMPLE_RUNTIME_PATH)
+        endif
+    endif
+endif
+ifeq ($(PLATFORM),PLATFORM_RPI)
+    CFLAGS += -std=gnu99
+endif
+ifeq ($(PLATFORM),PLATFORM_WEB)
+    # -Os                        # size optimization
+    # -O2                        # optimization level 2, if used, also set --memory-init-file 0
+    # -s USE_GLFW=3              # Use glfw3 library (context/input management)
+    # -s ALLOW_MEMORY_GROWTH=1   # to allow memory resizing -> WARNING: Audio buffers could FAIL!
+    # -s TOTAL_MEMORY=16777216   # to specify heap memory size (default = 16MB)
+    # -s USE_PTHREADS=1          # multithreading support
+    # -s WASM=0                  # disable Web Assembly, emitted by default
+    # -s EMTERPRETIFY=1          # enable emscripten code interpreter (very slow)
+    # -s EMTERPRETIFY_ASYNC=1    # support synchronous loops by emterpreter
+    # -s FORCE_FILESYSTEM=1      # force filesystem to load/save files data
+    # -s ASSERTIONS=1            # enable runtime checks for common memory allocation errors (-O1 and above turn it off)
+    # --profiling                # include information for code profiling
+    # --memory-init-file 0       # to avoid an external memory initialization code file (.mem)
+    # --preload-file resources   # specify a resources folder for data compilation
+    CFLAGS += -s USE_GLFW=3 -s TOTAL_MEMORY=67108864 --preload-file resources
+
+    # Define a custom shell .html and output extension
+    CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html
+    EXT = .html
+endif
+
+# Define include paths for required headers
+# NOTE: Several external required libraries (stb and others)
+INCLUDE_PATHS = -I. -I$(RAYLIB_PATH)/src -I$(RAYLIB_PATH)/src/external
+
+# Define additional directories containing required header files
+ifeq ($(PLATFORM),PLATFORM_RPI)
+    # RPI required libraries
+    INCLUDE_PATHS += -I/opt/vc/include
+    INCLUDE_PATHS += -I/opt/vc/include/interface/vmcs_host/linux
+    INCLUDE_PATHS += -I/opt/vc/include/interface/vcos/pthreads
+endif
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+    ifeq ($(PLATFORM_OS),BSD)
+        # Consider -L$(RAYLIB_H_INSTALL_PATH)
+        INCLUDE_PATHS += -I/usr/local/include
+    endif
+    ifeq ($(PLATFORM_OS),LINUX)
+        # Reset everything.
+        # Precedence: immediately local, installed version, raysan5 provided libs -I$(RAYLIB_H_INSTALL_PATH) -I$(RAYLIB_PATH)/release/include
+        INCLUDE_PATHS = -I$(RAYLIB_H_INSTALL_PATH) -isystem. -isystem$(RAYLIB_PATH)/src -isystem$(RAYLIB_PATH)/release/include -isystem$(RAYLIB_PATH)/src/external
+    endif
+endif
+
+# Define library paths containing required libs.
+LDFLAGS = -L. -L$(RAYLIB_RELEASE_PATH) -L$(RAYLIB_PATH)/src
+
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+    ifeq ($(PLATFORM_OS),BSD)
+        # Consider -L$(RAYLIB_INSTALL_PATH)
+        LDFLAGS += -L. -Lsrc -L/usr/local/lib
+    endif
+    ifeq ($(PLATFORM_OS),LINUX)
+        # Reset everything.
+        # Precedence: immediately local, installed version, raysan5 provided libs
+        LDFLAGS = -L. -L$(RAYLIB_INSTALL_PATH) -L$(RAYLIB_RELEASE_PATH)
+    endif
+endif
+
+ifeq ($(PLATFORM),PLATFORM_RPI)
+    LDFLAGS += -L/opt/vc/lib
+endif
+
+# Define any libraries required on linking
+# if you want to link libraries (libname.so or libname.a), use the -lname
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+    ifeq ($(PLATFORM_OS),WINDOWS)
+        # Libraries for Windows desktop compilation
+        # NOTE: WinMM library required to set high-res timer resolution
+        LDLIBS = -lraylib -lopengl32 -lgdi32 -lwinmm
+        # Required for physac examples
+        LDLIBS += -static -lpthread
+    endif
+    ifeq ($(PLATFORM_OS),LINUX)
+        # Libraries for Debian GNU/Linux desktop compiling
+        # NOTE: Required packages: libegl1-mesa-dev
+        LDLIBS = -lraylib -lGL -lm -lpthread -ldl -lrt
+
+        # On X11 requires also below libraries
+        LDLIBS += -lX11
+        # NOTE: It seems additional libraries are not required any more, latest GLFW just dlopen them
+        #LDLIBS += -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor
+
+        # On Wayland windowing system, additional libraries requires
+        ifeq ($(USE_WAYLAND_DISPLAY),TRUE)
+            LDLIBS += -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon
+        endif
+        # Explicit link to libc
+        ifeq ($(RAYLIB_LIBTYPE),SHARED)
+            LDLIBS += -lc
+        endif
+    endif
+    ifeq ($(PLATFORM_OS),OSX)
+        # Libraries for OSX 10.9 desktop compiling
+        # NOTE: Required packages: libopenal-dev libegl1-mesa-dev
+        LDLIBS = -lraylib -framework OpenGL -framework Cocoa -framework IOKit -framework CoreAudio -framework CoreVideo 
+    endif
+    ifeq ($(PLATFORM_OS),BSD)
+        # Libraries for FreeBSD, OpenBSD, NetBSD, DragonFly desktop compiling
+        # NOTE: Required packages: mesa-libs
+        LDLIBS = -lraylib -lGL -lpthread -lm
+
+        # On XWindow requires also below libraries
+        LDLIBS += -lX11 -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor
+    endif
+    ifeq ($(USE_EXTERNAL_GLFW),TRUE)
+        # NOTE: It could require additional packages installed: libglfw3-dev
+        LDLIBS += -lglfw
+    endif
+endif
+ifeq ($(PLATFORM),PLATFORM_RPI)
+    # Libraries for Raspberry Pi compiling
+    # NOTE: Required packages: libasound2-dev (ALSA)
+    LDLIBS = -lraylib -lbrcmGLESv2 -lbrcmEGL -lpthread -lrt -lm -lbcm_host -ldl
+endif
+ifeq ($(PLATFORM),PLATFORM_WEB)
+    # Libraries for web (HTML5) compiling
+    LDLIBS = $(RAYLIB_RELEASE_PATH)/libraylib.bc
+endif
+
+# Define all source files required
+PROJECT_SOURCE_FILES ?= \
+    repair.c \
+    screens/screen_logo.c \
+    screens/screen_title.c \
+    screens/screen_gameplay.c \
+    screens/screen_ending.c
+
+# Define all object files from source files
+OBJS = $(patsubst %.c, %.o, $(PROJECT_SOURCE_FILES))
+
+# For Android platform we call a custom Makefile.Android
+ifeq ($(PLATFORM),PLATFORM_ANDROID)
+    MAKEFILE_PARAMS = -f Makefile.Android 
+    export PROJECT_NAME
+    export PROJECT_SOURCE_FILES
+else
+    MAKEFILE_PARAMS = $(PROJECT_NAME)
+endif
+
+# Default target entry
+# NOTE: We call this Makefile target or Makefile.Android target
+all:
+	$(MAKE) $(MAKEFILE_PARAMS)
+
+# Project target defined by PROJECT_NAME
+$(PROJECT_NAME): $(OBJS)
+	$(CC) -o $(PROJECT_NAME)$(EXT) $(OBJS) $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
+
+# Compile source files
+# NOTE: This pattern will compile every module defined on $(OBJS)
+%.o: %.c
+	$(CC) -c $< -o $@ $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM)
+
+# Clean everything
+clean:
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+    ifeq ($(PLATFORM_OS),WINDOWS)
+		del *.o *.exe /s
+    endif
+    ifeq ($(PLATFORM_OS),LINUX)
+	find -type f -executable | xargs file -i | grep -E 'x-object|x-archive|x-sharedlib|x-executable|x-pie-executable' | rev | cut -d ':' -f 2- | rev | xargs rm -fv
+    endif
+    ifeq ($(PLATFORM_OS),OSX)
+		find . -type f -perm +ugo+x -delete
+		rm -f *.o
+    endif
+endif
+ifeq ($(PLATFORM),PLATFORM_RPI)
+	find . -type f -executable -delete
+	rm -fv *.o
+endif
+ifeq ($(PLATFORM),PLATFORM_WEB)
+	del *.o *.html *.js
+endif
+	@echo Cleaning done
+

+ 416 - 0
games/repair/repair.c

@@ -0,0 +1,416 @@
+/*******************************************************************************************
+*
+*   raylib - Advance Game template
+*
+*   <Game title>
+*   <Game description>
+*
+*   This game has been created using raylib (www.raylib.com)
+*   raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
+*
+*   Copyright (c) 2014-2020 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include "screens/screens.h"    // NOTE: Defines global variable: currentScreen
+
+#if defined(PLATFORM_WEB)
+    #include <emscripten/emscripten.h>
+#endif
+
+GameScreen currentScreen = 0;
+Font font = { 0 };
+Music music = { 0 };
+Sound fxCoin = { 0 };
+Texture2D background = { 0 };
+Texture2D texNPatch = { 0 };
+NPatchInfo npInfo = { 0 };
+
+Texture2D texHead, texHair, texNose, texMouth, texEyes, texComp;
+Texture2D texMakeup = { 0 };
+
+Character playerBase = { 0 };
+Character datingBase = { 0 };
+
+Character player = { 0 };
+Character dating = { 0 };
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition (local to this module)
+//----------------------------------------------------------------------------------
+const int screenWidth = 1280;
+const int screenHeight = 720;
+
+// Required variables to manage screen transitions (fade-in, fade-out)
+static float transAlpha = 0.0f;
+static bool onTransition = false;
+static bool transFadeOut = false;
+static int transFromScreen = -1;
+static int transToScreen = -1;
+
+// NOTE: Some global variables that require to be visible for all screens,
+// are defined in screens.h (i.e. currentScreen)
+    
+//----------------------------------------------------------------------------------
+// Local Functions Declaration
+//----------------------------------------------------------------------------------
+static void ChangeToScreen(int screen);     // No transition effect
+
+static void TransitionToScreen(int screen);
+static void UpdateTransition(void);
+static void DrawTransition(void);
+
+static void UpdateDrawFrame(void);          // Update and Draw one frame
+
+//----------------------------------------------------------------------------------
+// Main entry point
+//----------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization (Note windowTitle is unused on Android)
+    //---------------------------------------------------------
+    InitWindow(screenWidth, screenHeight, "raylib template - advance game");
+
+    // Global data loading (assets that must be available in all screens, i.e. fonts)
+    InitAudioDevice();
+
+    font = LoadFont("resources/font.png");
+    SetTextureFilter(font.texture, FILTER_BILINEAR);
+    
+    music = LoadMusicStream("resources/elevator_romance.ogg");
+    fxCoin = LoadSound("resources/coin.wav");
+    
+    background = LoadTexture("resources/background.png");
+    
+    texNPatch = LoadTexture("resources/npatch.png");
+    npInfo.sourceRec = (Rectangle){ 0, 0, 80, texNPatch.height },
+    npInfo.left = 24;
+    npInfo.top = 24;
+    npInfo.right = 24;
+    npInfo.bottom = 24;
+    
+    // Load required textures
+    texHead = LoadTexture("resources/head_models.png");
+    texHair = LoadTexture("resources/hair_models.png");
+    texNose = LoadTexture("resources/nose_models.png");
+    texMouth = LoadTexture("resources/mouth_models.png");
+    texEyes = LoadTexture("resources/eyes_models.png");
+    //texComp = LoadTexture("resources/comp_models.png");
+    texMakeup = LoadTexture("resources/makeup.png");
+    
+    SetMusicVolume(music, 0.5f);
+    //PlayMusicStream(music);
+
+    // Setup and Init first screen
+    currentScreen = LOGO;
+    InitLogoScreen();
+
+#if defined(PLATFORM_WEB)
+    emscripten_set_main_loop(UpdateDrawFrame, 0, 1);
+#else
+    SetTargetFPS(60);   // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        UpdateDrawFrame();
+    }
+#endif
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    
+    // Unload current screen data before closing
+    switch (currentScreen)
+    {
+        case LOGO: UnloadLogoScreen(); break;
+        case TITLE: UnloadTitleScreen(); break;
+        case GAMEPLAY: UnloadGameplayScreen(); break;
+        case ENDING: UnloadEndingScreen(); break;
+        default: break;
+    }
+    
+    // Unload all global loaded data (i.e. fonts) here!
+    UnloadFont(font);
+    UnloadMusicStream(music);
+    UnloadSound(fxCoin);
+    UnloadTexture(background);
+    UnloadTexture(texNPatch);
+    
+    UnloadTexture(texHead);
+    UnloadTexture(texHair);
+    UnloadTexture(texNose);
+    UnloadTexture(texMouth);
+    UnloadTexture(texEyes);
+    //UnloadTexture(texComp);
+    UnloadTexture(texMakeup);
+
+    CloseAudioDevice();     // Close audio context
+    
+    CloseWindow();          // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Public Functions Definition
+//----------------------------------------------------------------------------------
+
+Character GenerateCharacter(void)
+{
+    Character character = { 0 };
+    
+    // Generate player character!
+    character.head = GetRandomValue(0, texHead.width/BASE_HEAD_WIDTH - 1);
+    character.colHead = headColors[GetRandomValue(0, 5)];
+    character.hair = GetRandomValue(0, texHair.width/BASE_HAIR_WIDTH - 1);
+    character.colHair = hairColors[GetRandomValue(0, 9)];
+    character.eyes = GetRandomValue(0, texEyes.width/BASE_EYES_WIDTH - 1);
+    character.nose = GetRandomValue(0, texNose.width/BASE_NOSE_WIDTH - 1);
+    character.mouth = GetRandomValue(0, texMouth.width/BASE_MOUTH_WIDTH - 1);
+    
+    // NOTE: No character customization at this point
+    
+    return character;
+}
+
+void CustomizeCharacter(Character *character)
+{
+    if (GetRandomValue(0, 1)) character->hair = GetRandomValue(0, texHair.width/BASE_HAIR_WIDTH - 1);
+    if (GetRandomValue(0, 1)) character->colHair = hairColors[GetRandomValue(0, 9)];
+    if (GetRandomValue(0, 1)) character->eyes = GetRandomValue(0, texEyes.width/BASE_EYES_WIDTH - 1);
+    if (GetRandomValue(0, 1)) character->nose = GetRandomValue(0, texNose.width/BASE_NOSE_WIDTH - 1);
+    if (GetRandomValue(0, 1)) character->mouth = GetRandomValue(0, texMouth.width/BASE_MOUTH_WIDTH - 1);
+}
+
+void DrawCharacter(Character character, Vector2 position)
+{
+    DrawTextureRec(texHair, (Rectangle){ BASE_HAIR_WIDTH*character.hair, 240, BASE_HAIR_WIDTH, texHair.height - 240 }, (Vector2){ position.x + (250 - BASE_HAIR_WIDTH)/2, position.y + 240 }, GetColor(character.colHair));
+    DrawTextureRec(texHead, (Rectangle){ BASE_HEAD_WIDTH*character.head, 0, BASE_HEAD_WIDTH, texHead.height }, (Vector2){ position.x + (250 - BASE_HEAD_WIDTH)/2, position.y + 60 }, GetColor(character.colHead));
+    DrawTextureRec(texHair, (Rectangle){ BASE_HAIR_WIDTH*character.hair, 0, BASE_HAIR_WIDTH, 240 }, (Vector2){ position.x + (250 - BASE_HAIR_WIDTH)/2, position.y }, GetColor(character.colHair));
+    DrawTextureRec(texEyes, (Rectangle){ BASE_EYES_WIDTH*character.eyes, 0, BASE_EYES_WIDTH, texEyes.height }, (Vector2){ position.x + (250 - BASE_EYES_WIDTH)/2, position.y + 190 }, WHITE);
+    DrawTextureRec(texNose, (Rectangle){ BASE_NOSE_WIDTH*character.nose, 0, BASE_NOSE_WIDTH, texNose.height }, (Vector2){ position.x + (250 - BASE_NOSE_WIDTH)/2, position.y + 275 }, GetColor(character.colHead));
+    DrawTextureRec(texMouth, (Rectangle){ BASE_MOUTH_WIDTH*character.mouth, 0, BASE_MOUTH_WIDTH, texMouth.height }, (Vector2){ position.x + (250 - BASE_MOUTH_WIDTH)/2, position.y + 370 }, GetColor(character.colHead));
+}
+
+// Gui Button
+bool GuiButton(Rectangle bounds, const char *text, int forcedState)
+{
+    static const int textColor[4] = { 0xeff6ffff, 0x78e782ff, 0xb04d5fff, 0xd6d6d6ff };
+    
+    int state = (forcedState >= 0)? forcedState : 0;                // NORMAL
+    bool pressed = false;
+    Vector2 textSize = MeasureTextEx(font, text, font.baseSize, 1);
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state < 3) && (forcedState < 0))
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check button state
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = 2;    // PRESSED
+            else state = 1;                                         // FOCUSED
+
+            if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
+            {
+                pressed = true;
+                PlaySound(fxCoin);
+            }
+        }
+    }
+            
+    npInfo.sourceRec.x = 80*state;
+
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    //DrawRectangleRec(bounds, GREEN);
+    //DrawRectangleLinesEx(bounds, 4, DARKGREEN);
+    DrawTextureNPatch(texNPatch, npInfo, bounds, (Vector2){ 0.0f, 0.0f }, 0.0f, WHITE);
+    DrawTextEx(font, text, (Vector2){ bounds.x + bounds.width/2 - textSize.x/2, bounds.y + bounds.height/2 - textSize.y/2 + 4 }, font.baseSize, 1, GetColor(textColor[state]));
+    //------------------------------------------------------------------
+    
+    return pressed;
+}
+
+//----------------------------------------------------------------------------------
+// Module specific Functions Definition
+//----------------------------------------------------------------------------------
+
+// Change to next screen, no transition
+static void ChangeToScreen(int screen)
+{
+    // Unload current screen
+    switch (currentScreen)
+    {
+        case LOGO: UnloadLogoScreen(); break;
+        case TITLE: UnloadTitleScreen(); break;
+        case GAMEPLAY: UnloadGameplayScreen(); break;
+        case ENDING: UnloadEndingScreen(); break;
+        default: break;
+    }
+    
+    // Init next screen
+    switch (screen)
+    {
+        case LOGO: InitLogoScreen(); break;
+        case TITLE: InitTitleScreen(); break;
+        case GAMEPLAY: InitGameplayScreen(); break;
+        case ENDING: InitEndingScreen(); break;
+        default: break;
+    }
+    
+    currentScreen = screen;
+}
+
+// Define transition to next screen
+static void TransitionToScreen(int screen)
+{
+    onTransition = true;
+    transFadeOut = false;
+    transFromScreen = currentScreen;
+    transToScreen = screen;
+    transAlpha = 0.0f;
+}
+
+// Update transition effect
+static void UpdateTransition(void)
+{
+    if (!transFadeOut)
+    {
+        transAlpha += 0.05f;
+        
+        // NOTE: Due to float internal representation, condition jumps on 1.0f instead of 1.05f
+        // For that reason we compare against 1.01f, to avoid last frame loading stop
+        if (transAlpha > 1.01f)
+        {
+            transAlpha = 1.0f;
+        
+            // Unload current screen
+            switch (transFromScreen)
+            {
+                case LOGO: UnloadLogoScreen(); break;
+                case TITLE: UnloadTitleScreen(); break;
+                case GAMEPLAY: UnloadGameplayScreen(); break;
+                case ENDING: UnloadEndingScreen(); break;
+                default: break;
+            }
+            
+            // Load next screen
+            switch (transToScreen)
+            {
+                case LOGO: InitLogoScreen(); break;
+                case TITLE: InitTitleScreen(); break;
+                case GAMEPLAY: InitGameplayScreen(); break;
+                case ENDING: InitEndingScreen(); break;
+                default: break;
+            }
+            
+            currentScreen = transToScreen;
+            
+            // Activate fade out effect to next loaded screen
+            transFadeOut = true;
+        }
+    }
+    else  // Transition fade out logic
+    {
+        transAlpha -= 0.02f;
+        
+        if (transAlpha < -0.01f)
+        {
+            transAlpha = 0.0f;
+            transFadeOut = false;
+            onTransition = false;
+            transFromScreen = -1;
+            transToScreen = -1;
+        }
+    }
+}
+
+// Draw transition effect (full-screen rectangle)
+static void DrawTransition(void)
+{
+    DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Fade(BLACK, transAlpha));
+}
+
+// Update and draw game frame
+static void UpdateDrawFrame(void)
+{
+    // Update
+    //----------------------------------------------------------------------------------
+    UpdateMusicStream(music);       // NOTE: Music keeps playing between screens
+    
+    if (!onTransition)
+    {
+        switch(currentScreen) 
+        {
+            case LOGO: 
+            {
+                UpdateLogoScreen();
+                
+                if (FinishLogoScreen())
+                {
+                    TransitionToScreen(TITLE);
+                    PlayMusicStream(music);
+                }
+
+            } break;
+            case TITLE: 
+            {
+                UpdateTitleScreen();
+                    
+                if (FinishTitleScreen() == 1) TransitionToScreen(GAMEPLAY);
+                //else if (FinishTitleScreen() == 2) TransitionToScreen(GAMEPLAY);
+
+            } break;
+            case GAMEPLAY:
+            {
+                UpdateGameplayScreen();
+                
+                if (FinishGameplayScreen() == 1) TransitionToScreen(ENDING);
+                //else if (FinishGameplayScreen() == 2) TransitionToScreen(TITLE);
+
+            } break;
+            case ENDING:
+            { 
+                UpdateEndingScreen();
+                
+                if (FinishEndingScreen() == 1) TransitionToScreen(TITLE);
+
+            } break;
+            default: break;
+        }
+    }
+    else UpdateTransition();    // Update transition (fade-in, fade-out)
+    //----------------------------------------------------------------------------------
+    
+    // Draw
+    //----------------------------------------------------------------------------------
+    BeginDrawing();
+        
+        ClearBackground(RAYWHITE);
+            
+        switch(currentScreen) 
+        {
+            case LOGO: DrawLogoScreen(); break;
+            case TITLE: DrawTitleScreen(); break;
+            case GAMEPLAY: DrawGameplayScreen(); break;
+            case ENDING: DrawEndingScreen(); break;
+            default: break;
+        }
+         
+        // Draw full screen rectangle in front of everything
+        if (onTransition) DrawTransition();
+        
+        //DrawFPS(10, 10);
+        
+    EndDrawing();
+    //----------------------------------------------------------------------------------
+}

二進制
games/repair/resources/background.png


二進制
games/repair/resources/coin.wav


二進制
games/repair/resources/elevator_romance.ogg


二進制
games/repair/resources/eyes_models.png


二進制
games/repair/resources/font.png


二進制
games/repair/resources/hair_models.png


二進制
games/repair/resources/head_models.png


二進制
games/repair/resources/makeup.png


二進制
games/repair/resources/match.png


二進制
games/repair/resources/mouth_models.png


二進制
games/repair/resources/nose_models.png


二進制
games/repair/resources/npatch.png


二進制
games/repair/resources/qmark.png


二進制
games/repair/resources/raylib_logo.png


二進制
games/repair/resources/title.png


+ 246 - 0
games/repair/screens/screen_ending.c

@@ -0,0 +1,246 @@
+/**********************************************************************************************
+*
+*   raylib - Advance Game template
+*
+*   Ending Screen Functions Definitions (Init, Update, Draw, Unload)
+*
+*   Copyright (c) 2014-2020 Ramon Santamaria (@raysan5)
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#include "raylib.h"
+#include "screens.h"
+
+typedef struct {
+    int hair;
+    int colHair;
+    int eyes;
+    int nose;
+    int mouth;
+    //int glasses;
+    //int piercing;
+} CharLikes;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition (local to this module)
+//----------------------------------------------------------------------------------
+
+// Ending screen global variables
+static int framesCounter = 0;
+static int finishScreen = 0;
+
+static Texture2D texQmark = { 0 };
+static Texture2D texMatch = { 0 };
+
+static int state = 0;
+static int matchValue = 0;
+
+static CharLikes playerLikes = { 0 };
+static CharLikes playerBaseLikes = { 0 };
+
+//----------------------------------------------------------------------------------
+// Ending Screen Functions Definition
+//----------------------------------------------------------------------------------
+
+// Ending Screen Initialization logic
+void InitEndingScreen(void)
+{
+    framesCounter = 0;
+    finishScreen = 0;
+    state = 0;
+    
+    CustomizeCharacter(&dating);
+    
+    texQmark = LoadTexture("resources/qmark.png");
+    texMatch = LoadTexture("resources/match.png");
+}
+
+// Ending Screen Update logic
+void UpdateEndingScreen(void)
+{
+    if (state == 0)
+    {
+        framesCounter++;
+    
+        if (framesCounter > 200)
+        {
+            state = 1;
+            
+            // Check like percentatge for player base (playerBaseLikes)
+            if (playerBase.hair == dating.hair) playerBaseLikes.hair = GetRandomValue(70, 100);
+            else if (playerBase.hair == datingBase.hair) playerBaseLikes.hair = GetRandomValue(0, 30);
+            else playerBaseLikes.hair = GetRandomValue(0, 100);
+            
+            if (playerBase.colHair == dating.colHair) playerBaseLikes.colHair = GetRandomValue(70, 100);
+            else if (playerBase.colHair == datingBase.colHair) playerBaseLikes.colHair = GetRandomValue(0, 30);
+            else playerBaseLikes.colHair = GetRandomValue(0, 100);
+            
+            if (playerBase.eyes == dating.eyes) playerBaseLikes.eyes = GetRandomValue(70, 100);
+            else if (playerBase.eyes == datingBase.eyes) playerBaseLikes.eyes = GetRandomValue(0, 30);
+            else playerBaseLikes.eyes = GetRandomValue(0, 100);
+            
+            if (playerBase.nose == dating.nose) playerBaseLikes.nose = GetRandomValue(70, 100);
+            else if (playerBase.nose == datingBase.nose) playerBaseLikes.nose = GetRandomValue(0, 30);
+            else playerBaseLikes.nose = GetRandomValue(0, 100);
+            
+            if (playerBase.mouth == dating.mouth) playerBaseLikes.mouth = GetRandomValue(70, 100);
+            else if (playerBase.mouth == datingBase.mouth) playerBaseLikes.mouth = GetRandomValue(0, 30);
+            else playerBaseLikes.mouth = GetRandomValue(0, 100);
+            
+            
+            // Check like percentatge for player (playerLikes)
+            if (player.hair == dating.hair) playerLikes.hair = GetRandomValue(70, 100);
+            else if (player.hair == datingBase.hair) playerLikes.hair = GetRandomValue(0, 30);
+            else playerLikes.hair = GetRandomValue(0, 100);
+            
+            if (player.colHair == dating.colHair) playerLikes.colHair = GetRandomValue(70, 100);
+            else if (player.colHair == datingBase.colHair) playerLikes.colHair = GetRandomValue(0, 30);
+            else playerLikes.colHair = GetRandomValue(0, 100);
+            
+            if (player.eyes == dating.eyes) playerLikes.eyes = GetRandomValue(70, 100);
+            else if (player.eyes == datingBase.eyes) playerLikes.eyes = GetRandomValue(0, 30);
+            else playerLikes.eyes = GetRandomValue(0, 100);
+            
+            if (player.nose == dating.nose) playerLikes.nose = GetRandomValue(70, 100);
+            else if (player.nose == datingBase.nose) playerLikes.nose = GetRandomValue(0, 30);
+            else playerLikes.nose = GetRandomValue(0, 100);
+            
+            if (player.mouth == dating.mouth) playerLikes.mouth = GetRandomValue(70, 100);
+            else if (player.mouth == datingBase.mouth) playerLikes.mouth = GetRandomValue(0, 30);
+            else playerLikes.mouth = GetRandomValue(0, 100);
+            
+            // NOTE: Max possible points to get 5*100 = 500
+            // If getting > 250 player likes! :D
+            matchValue = playerLikes.hair + playerLikes.colHair + playerLikes.eyes + playerLikes.nose + playerLikes.mouth;
+        }
+    }
+    else if (state == 1)
+    {
+        // Levels animation?
+    }
+    
+    // Press enter or tap to return to TITLE screen
+    if (IsKeyPressed(KEY_ENTER) || IsGestureDetected(GESTURE_TAP))
+    {
+        finishScreen = 1;
+        PlaySound(fxCoin);
+    }
+}
+
+// Ending Screen Draw logic
+void DrawEndingScreen(void)
+{
+    // Draw background
+    DrawTexture(background, 0, 0, GetColor(0xf6aa60ff));
+    
+    DrawCharacter(player, (Vector2){ 180, 40 });
+    
+    DrawCharacter(dating, (Vector2){ 820, 40 });
+    
+    if (state == 0)
+    {
+        if ((framesCounter/15)%2 == 1) DrawTexture(texQmark, GetScreenWidth()/2 - texQmark.width/2, 180, WHITE);
+    }
+    else if (state == 1)
+    {
+        DrawTextEx(font, TextFormat("MATCH: %i%%", (int)(((float)matchValue/500.0f)*100.0f)), (Vector2){ 420, 40 }, font.baseSize*2, 1, SKYBLUE);
+        
+        DrawTextureRec(texMatch, (Rectangle){ 0, (matchValue > 250)? 0 : texMatch.height/2, texMatch.width, texMatch.height/2 }, (Vector2){ GetScreenWidth()/2 - texMatch.width/2, 240 }, WHITE);
+
+        int barsPositionX = 80;
+        
+        //DrawRectangle(0, 530, GetScreenWidth(), GetScreenHeight() - 530, Fade(GRAY, 0.6f));
+        //DrawRectangleLines(0, 530, GetScreenWidth(), GetScreenHeight() - 530, Fade(DARKGRAY, 0.8f));
+    
+        // Draw like values: player base
+        DrawTextEx(font, "HAIR:", (Vector2){ barsPositionX, 550 }, font.baseSize/2, 1, WHITE);
+        DrawRectangle(barsPositionX + 80, 550 + 6, 400, font.baseSize/4, GRAY);
+        DrawRectangle(barsPositionX + 80, 550 + 6, playerBaseLikes.hair*4, font.baseSize/4, RED);
+        
+        DrawTextEx(font, "TINT:", (Vector2){ barsPositionX, 580 }, font.baseSize/2, 1, WHITE);
+        DrawRectangle(barsPositionX + 80, 580 + 6, 400, font.baseSize/4, GRAY);
+        DrawRectangle(barsPositionX + 80, 580 + 6, playerBaseLikes.colHair*4, font.baseSize/4, RED);
+        
+        DrawTextEx(font, "EYES:", (Vector2){ barsPositionX, 610 }, font.baseSize/2, 1, WHITE);
+        DrawRectangle(barsPositionX + 80, 610 + 6, 400, font.baseSize/4, GRAY);
+        DrawRectangle(barsPositionX + 80, 610 + 6, playerBaseLikes.eyes*4, font.baseSize/4, RED);
+        
+        DrawTextEx(font, "NOSE:", (Vector2){ barsPositionX, 640 }, font.baseSize/2, 1, WHITE);
+        DrawRectangle(barsPositionX + 80, 640 + 6, 400, font.baseSize/4, GRAY);
+        DrawRectangle(barsPositionX + 80, 640 + 6, playerBaseLikes.nose*4, font.baseSize/4, RED);
+        
+        DrawTextEx(font, "LIPS:", (Vector2){ barsPositionX, 670 }, font.baseSize/2, 1, WHITE);
+        DrawRectangle(barsPositionX + 80, 670 + 6, 400, font.baseSize/4, GRAY);
+        DrawRectangle(barsPositionX + 80, 670 + 6, playerBaseLikes.mouth*4, font.baseSize/4, RED);
+        
+        // Draw like values: player
+        if (player.hair != playerBase.hair)
+        {
+            DrawTextEx(font, "after re-touch:", (Vector2){ barsPositionX + 80 + 400 + 20, 550 }, font.baseSize/2, 1, WHITE);
+            DrawRectangle(barsPositionX + 80 + 400 + 100 + 90, 550 + 6, 400, font.baseSize/4, GRAY);
+            DrawRectangle(barsPositionX + 80 + 400 + 100 + 90, 550 + 6, playerLikes.hair*4, font.baseSize/4, RED);
+        }
+        
+        if (player.colHair != playerBase.colHair)
+        {
+            DrawTextEx(font, "after re-touch:", (Vector2){ barsPositionX + 80 + 400 + 20, 580 }, font.baseSize/2, 1, WHITE);
+            DrawRectangle(barsPositionX + 80 + 400 + 100 + 90, 580 + 6, 400, font.baseSize/4, GRAY);
+            DrawRectangle(barsPositionX + 80 + 400 + 100 + 90, 580 + 6, playerLikes.colHair*4, font.baseSize/4, RED);
+        }
+        
+        if (player.eyes != playerBase.eyes)
+        {
+            DrawTextEx(font, "after re-touch:", (Vector2){ barsPositionX + 80 + 400 + 20, 610 }, font.baseSize/2, 1, WHITE);
+            DrawRectangle(barsPositionX + 80 + 400 + 100 + 90, 610 + 6, 400, font.baseSize/4, GRAY);
+            DrawRectangle(barsPositionX + 80 + 400 + 100 + 90, 610 + 6, playerLikes.eyes*4, font.baseSize/4, RED);
+        }
+        
+        if (player.nose != playerBase.nose)
+        {
+            DrawTextEx(font, "after re-touch:", (Vector2){ barsPositionX + 80 + 400 + 20, 640 }, font.baseSize/2, 1, WHITE);
+            DrawRectangle(barsPositionX + 80 + 400 + 100 + 90, 640 + 6, 400, font.baseSize/4, GRAY);
+            DrawRectangle(barsPositionX + 80 + 400 + 100 + 90, 640 + 6, playerLikes.nose*4, font.baseSize/4, RED);
+        }
+        
+        if (player.mouth != playerBase.mouth)
+        {
+            DrawTextEx(font, "after re-touch:", (Vector2){ barsPositionX + 80 + 400 + 20, 670 }, font.baseSize/2, 1, WHITE);
+            DrawRectangle(barsPositionX + 80 + 400 + 100 + 90, 670 + 6, 400, font.baseSize/4, GRAY);
+            DrawRectangle(barsPositionX + 80 + 400 + 100 + 90, 670 + 6, playerLikes.mouth*4, font.baseSize/4, RED);
+        }
+        
+        // Draw left button: date!
+        if (GuiButton((Rectangle){ GetScreenWidth() - 280, 60, 260, 80 }, "AGAIN!", -1))
+        {
+            finishScreen = 1;
+        }
+    }
+}
+
+// Ending Screen Unload logic
+void UnloadEndingScreen(void)
+{
+    UnloadTexture(texQmark);
+    UnloadTexture(texMatch);
+}
+
+// Ending Screen should finish?
+int FinishEndingScreen(void)
+{
+    return finishScreen;
+}

+ 169 - 0
games/repair/screens/screen_gameplay.c

@@ -0,0 +1,169 @@
+/**********************************************************************************************
+*
+*   raylib - Advance Game template
+*
+*   Gameplay Screen Functions Definitions (Init, Update, Draw, Unload)
+*
+*   Copyright (c) 2014-2020 Ramon Santamaria (@raysan5)
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#include "raylib.h"
+#include "screens.h"
+
+static bool doHairCut = false;
+static bool doHairTint = false;
+static bool doEyeLiner = false;
+static bool doLipStick = false;
+static bool doNose = false;
+static bool doGlasses = false;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition (local to this module)
+//----------------------------------------------------------------------------------
+
+const unsigned int headColors[6] = { 0xffe29bff, 0xfed5a8ff, 0xad8962ff, 0xfff1b8ff, 0xffd6c4ff, 0xd49c8dff };
+const unsigned int hairColors[10] = { 0xf5bf60ff, 0xaa754aff, 0x974e14ff, 0xf36347ff, 0x87f347ff, 0xfc48d0ff, 0x3b435dff, 0x5f5e60ff, 0xe7e7e7ff, 0xfb386bff };
+
+// Gameplay screen global variables
+static int framesCounter = 0;
+static int finishScreen = 0;
+
+static RenderTexture target = { 0 };
+
+//----------------------------------------------------------------------------------
+// Gameplay Screen Functions Definition
+//----------------------------------------------------------------------------------
+
+// Gameplay Screen Initialization logic
+void InitGameplayScreen(void)
+{
+    // Initialize GAMEPLAY screen variables
+    framesCounter = 0;
+    finishScreen = 0;
+       
+    target = LoadRenderTexture(720, 720);
+    SetTextureFilter(target.texture, FILTER_BILINEAR);
+       
+    // Generate player character!
+    //player = GenerateCharacter();
+    playerBase = player;
+    
+    // Generate dating character!
+    dating = GenerateCharacter();
+    datingBase = dating;
+    
+    // TODO: Generate dating character likes
+    // For the different types of properties we assign random like values: 0% (total-dislike) -> 100% (total-like)
+    
+    // The total match point will be the (like accumulated amount)/(num properties)
+    // Some of the elements add points or remove points
+    
+    // At the end we can show the like percentadge of every element
+    
+    doHairCut = false;
+    doHairTint = false;
+    doEyeLiner = false;
+    doLipStick = false;
+    doNose = false;
+    doGlasses = false;
+}
+
+// Gameplay Screen Update logic
+void UpdateGameplayScreen(void)
+{
+    if (IsKeyPressed(KEY_SPACE))
+    {
+        player = GenerateCharacter();
+        playerBase = player;
+    }
+    
+    if (IsKeyPressed(KEY_ENTER)) finishScreen = 1;
+}
+
+// Gameplay Screen Draw logic
+void DrawGameplayScreen(void)
+{
+    // Draw background
+    DrawTexture(background, 0, 0, GetColor(0xf6aa60ff));
+    
+    // Draw left menu buttons
+    GuiButton((Rectangle){ 20, 40, 300, 60 }, "RE-TOUCH:", 2);
+    
+    if (GuiButton((Rectangle){ 20, 40 + 90, 300, 80 }, "HAIR TINT", doHairTint? 3 : -1))
+    {
+        doHairTint = true;
+        player.colHair = hairColors[GetRandomValue(0, 9)];
+    }
+    if (GuiButton((Rectangle){ 20, 40 + 180, 300, 80 }, "HAIR", doHairCut? 3 : -1))
+    {
+        doHairCut = true;
+        player.hair = GetRandomValue(0, texHair.width/BASE_HAIR_WIDTH);
+
+    }
+    if (GuiButton((Rectangle){ 20, 40 + 270, 300, 80 }, "EYES", doEyeLiner? 3 : -1))
+    {
+        doEyeLiner = true;
+        player.eyes = GetRandomValue(0, texEyes.width/BASE_EYES_WIDTH - 1);
+    }
+    if (GuiButton((Rectangle){ 20, 40 + 360, 300, 80 }, "NOSE", doNose? 3 : -1))
+    {
+        doNose = true;
+        player.nose = GetRandomValue(0, texNose.width/BASE_NOSE_WIDTH - 1);
+    }
+    if (GuiButton((Rectangle){ 20, 40 + 450, 300, 80 }, "LIPS", doLipStick? 3 : -1))
+    {
+        doLipStick = true;
+        player.mouth = GetRandomValue(0, texMouth.width/BASE_MOUTH_WIDTH - 1);
+    }
+    if (GuiButton((Rectangle){ 20, 40 + 540, 300, 80 }, "GLASSES", 3))
+    {
+        doGlasses = true;
+    }
+    
+    // Draw player
+    DrawCharacter(player, (Vector2){ GetScreenWidth()/2 - 125, 80 });
+
+    // Draw dating view
+    GuiButton((Rectangle){ 970, 40, 260, 60 }, "DATING:", 2);
+    GuiButton((Rectangle){ 970, 40 + 70, 260, 260 }, " ", 0);
+    
+    BeginTextureMode(target);
+        DrawCharacter(dating, (Vector2){ (720 - 250)/2, (720 - 500)/2 });
+    EndTextureMode();
+    
+    DrawTexturePro(target.texture, (Rectangle){ 0.0f, 0.0f, (float)target.texture.width, (float)-target.texture.height }, (Rectangle){ 970, 40 + 70, 260, 260 }, (Vector2){ 0, 0 }, 0.0f, WHITE);
+   
+    // Draw left button: date!
+    if (GuiButton((Rectangle){ 970, 580, 260, 90 }, "GO DATE!", -1))
+    {
+        finishScreen = 1;
+    }
+}
+
+// Gameplay Screen Unload logic
+void UnloadGameplayScreen(void)
+{
+    // Unload required textures
+}
+
+// Gameplay Screen should finish?
+int FinishGameplayScreen(void)
+{
+    return finishScreen;
+}

+ 211 - 0
games/repair/screens/screen_logo.c

@@ -0,0 +1,211 @@
+/**********************************************************************************************
+*
+*   raylib - Advance Game template
+*
+*   Logo Screen Functions Definitions (Init, Update, Draw, Unload)
+*
+*   Copyright (c) 2014-2019 Ramon Santamaria (@raysan5)
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#include "raylib.h"
+#include "screens.h"
+
+#define LOGO_RECS_SIDE  16
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition (local to this module)
+//----------------------------------------------------------------------------------
+
+// Logo screen global variables
+static int framesCounter = 0;
+static int finishScreen = 0;
+
+static int logoPositionX = 0;
+static int logoPositionY = 0;
+
+static int lettersCount = 0;
+
+static int topSideRecWidth = 0;
+static int leftSideRecHeight = 0;
+
+static int bottomSideRecWidth = 0;
+static int rightSideRecHeight = 0;
+
+static char raylib[8] = { 0 };  // raylib text array, max 8 letters
+static int state = 0;           // Tracking animation states (State Machine)
+static float alpha = 1.0f;      // Useful for fading
+
+//----------------------------------------------------------------------------------
+// Logo Screen Functions Definition
+//----------------------------------------------------------------------------------
+
+// Logo Screen Initialization logic
+void InitLogoScreen(void)
+{
+    // Initialize LOGO screen variables here!
+    finishScreen = 0;
+    framesCounter = 0;
+    lettersCount = 0;
+    
+    logoPositionX = GetScreenWidth()/2 - 128;
+    logoPositionY = GetScreenHeight()/2 - 128;
+    
+    topSideRecWidth = LOGO_RECS_SIDE;
+    leftSideRecHeight = LOGO_RECS_SIDE;
+    bottomSideRecWidth = LOGO_RECS_SIDE;
+    rightSideRecHeight = LOGO_RECS_SIDE;
+    
+    for (int i = 0; i < 8; i++) raylib[i] = '\0';
+    
+    state = 0;
+    alpha = 1.0f;
+}
+
+// Logo Screen Update logic
+void UpdateLogoScreen(void)
+{
+    // Update LOGO screen variables here!
+    if (state == 0)                 // State 0: Small box blinking
+    {
+        framesCounter++;
+
+        if (framesCounter == 80)
+        {
+            state = 1;
+            framesCounter = 0;      // Reset counter... will be used later...
+        }
+    }
+    else if (state == 1)            // State 1: Top and left bars growing
+    {
+        topSideRecWidth += 8;
+        leftSideRecHeight += 8;
+
+        if (topSideRecWidth == 256) state = 2;
+    }
+    else if (state == 2)            // State 2: Bottom and right bars growing
+    {
+        bottomSideRecWidth += 8;
+        rightSideRecHeight += 8;
+
+        if (bottomSideRecWidth == 256) state = 3;
+    }
+    else if (state == 3)            // State 3: Letters appearing (one by one)
+    {
+        framesCounter++;
+
+        if (framesCounter/10)       // Every 12 frames, one more letter!
+        {
+            lettersCount++;
+            framesCounter = 0;
+        }
+
+        switch (lettersCount)
+        {
+            case 1: raylib[0] = 'r'; break;
+            case 2: raylib[1] = 'a'; break;
+            case 3: raylib[2] = 'y'; break;
+            case 4: raylib[3] = 'l'; break;
+            case 5: raylib[4] = 'i'; break;
+            case 6: raylib[5] = 'b'; break;
+            default: break;
+        }
+
+        // When all letters have appeared...
+        if (lettersCount >= 10)
+        {
+            state = 4;
+            framesCounter = 0;
+        }
+    }
+    else if (state == 4)
+    {
+        framesCounter++;
+        
+        if (framesCounter > 100)
+        {
+            alpha -= 0.02f;
+
+            if (alpha <= 0.0f)
+            {
+                alpha = 0.0f;
+                finishScreen = 1;
+            }
+        }
+    }
+}
+
+// Logo Screen Draw logic
+void DrawLogoScreen(void)
+{
+    if (state == 0)
+    {
+        if ((framesCounter/10)%2) DrawRectangle(logoPositionX, logoPositionY, 16, 16, BLACK);
+    }
+    else if (state == 1)
+    {
+        DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, BLACK);
+        DrawRectangle(logoPositionX, logoPositionY, 16, leftSideRecHeight, BLACK);
+    }
+    else if (state == 2)
+    {
+        DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, BLACK);
+        DrawRectangle(logoPositionX, logoPositionY, 16, leftSideRecHeight, BLACK);
+
+        DrawRectangle(logoPositionX + 240, logoPositionY, 16, rightSideRecHeight, BLACK);
+        DrawRectangle(logoPositionX, logoPositionY + 240, bottomSideRecWidth, 16, BLACK);
+    }
+    else if (state == 3)
+    {
+        DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, Fade(BLACK, alpha));
+        DrawRectangle(logoPositionX, logoPositionY + 16, 16, leftSideRecHeight - 32, Fade(BLACK, alpha));
+
+        DrawRectangle(logoPositionX + 240, logoPositionY + 16, 16, rightSideRecHeight - 32, Fade(BLACK, alpha));
+        DrawRectangle(logoPositionX, logoPositionY + 240, bottomSideRecWidth, 16, Fade(BLACK, alpha));
+
+        DrawRectangle(GetScreenWidth()/2 - 112, GetScreenHeight()/2 - 112, 224, 224, Fade(RAYWHITE, alpha));
+
+        DrawText(raylib, GetScreenWidth()/2 - 44, GetScreenHeight()/2 + 48, 50, Fade(BLACK, alpha));
+    }
+    else if (state == 4)
+    {
+        DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, Fade(BLACK, alpha));
+        DrawRectangle(logoPositionX, logoPositionY + 16, 16, leftSideRecHeight - 32, Fade(BLACK, alpha));
+
+        DrawRectangle(logoPositionX + 240, logoPositionY + 16, 16, rightSideRecHeight - 32, Fade(BLACK, alpha));
+        DrawRectangle(logoPositionX, logoPositionY + 240, bottomSideRecWidth, 16, Fade(BLACK, alpha));
+
+        DrawRectangle(GetScreenWidth()/2 - 112, GetScreenHeight()/2 - 112, 224, 224, Fade(RAYWHITE, alpha));
+
+        DrawText(raylib, GetScreenWidth()/2 - 44, GetScreenHeight()/2 + 48, 50, Fade(BLACK, alpha));
+        
+        if (framesCounter > 20) DrawText("powered by", logoPositionX, logoPositionY - 27, 20, Fade(DARKGRAY, alpha));
+    }
+}
+
+// Logo Screen Unload logic
+void UnloadLogoScreen(void)
+{
+    // Unload LOGO screen variables here!
+}
+
+// Logo Screen should finish?
+int FinishLogoScreen(void)
+{
+    return finishScreen;
+}

+ 134 - 0
games/repair/screens/screen_title.c

@@ -0,0 +1,134 @@
+/**********************************************************************************************
+*
+*   raylib - Advance Game template
+*
+*   Title Screen Functions Definitions (Init, Update, Draw, Unload)
+*
+*   Copyright (c) 2014-2020 Ramon Santamaria (@raysan5)
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#include "raylib.h"
+#include "screens.h"
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition (local to this module)
+//----------------------------------------------------------------------------------
+
+// Title screen global variables
+static int framesCounter = 0;
+static int finishScreen = 0;
+
+static Texture2D texTitle = { 0 };
+static Texture2D texLogo = { 0 };
+
+static int titlePositionY = 0;
+static int titleCounter = 0;
+
+//----------------------------------------------------------------------------------
+// Title Screen Functions Definition
+//----------------------------------------------------------------------------------
+
+// Title Screen Initialization logic
+void InitTitleScreen(void)
+{
+    framesCounter = 0;
+    finishScreen = 0;
+    
+    texTitle = LoadTexture("resources/title.png");
+    texLogo = LoadTexture("resources/raylib_logo.png");
+    
+    player = GenerateCharacter();
+    
+    titlePositionY = -200;
+}
+
+// Title Screen Update logic
+void UpdateTitleScreen(void)
+{
+    framesCounter++;
+
+    if (framesCounter > 5)
+    {
+        int partToChange = GetRandomValue(0, 4);
+        
+        if (partToChange == 0)
+        {
+            player.head = GetRandomValue(0, texHead.width/BASE_HEAD_WIDTH - 1);
+            player.colHead = headColors[GetRandomValue(0, 5)];
+        }
+        else if (partToChange == 1) player.eyes = GetRandomValue(0, texEyes.width/BASE_EYES_WIDTH - 1);
+        else if (partToChange == 2) player.nose = GetRandomValue(0, texNose.width/BASE_NOSE_WIDTH - 1);
+        else if (partToChange == 3) player.mouth = GetRandomValue(0, texMouth.width/BASE_MOUTH_WIDTH - 1);
+        else if (partToChange == 4)
+        {
+            player.hair = GetRandomValue(0, texHair.width/BASE_HAIR_WIDTH - 1);
+            player.colHair = hairColors[GetRandomValue(0, 9)];
+        }
+
+        framesCounter = 0;
+    }
+    
+    titlePositionY += 3;
+    if (titlePositionY > 40) titlePositionY = 40;
+    
+    titleCounter++;
+    
+    if (IsKeyPressed(KEY_ENTER)) finishScreen = 1;
+}
+
+// Title Screen Draw logic
+void DrawTitleScreen(void)
+{
+    DrawTexture(background, 0, 0, GetColor(0xf6aa60ff));
+    
+    // Draw face, parts keep changing ranomly
+    DrawCharacter(player, (Vector2){ GetScreenWidth()/2 - 125, 80 });
+    
+    // Draw face rectangles
+    //DrawRectangleRec((Rectangle){ GetScreenWidth()/2 - BASE_EYES_WIDTH/2, 270, BASE_EYES_WIDTH, texEyes.height }, Fade(GREEN, 0.3f));
+    //DrawRectangleRec((Rectangle){ GetScreenWidth()/2 - BASE_NOSE_WIDTH/2, 355, BASE_NOSE_WIDTH, texNose.height }, Fade(SKYBLUE, 0.3f));
+    //DrawRectangleRec((Rectangle){ GetScreenWidth()/2 - BASE_MOUTH_WIDTH/2, 450, BASE_MOUTH_WIDTH, texMouth.height }, Fade(RED, 0.3f));
+    
+    DrawTexture(texTitle, GetScreenWidth()/2 - texTitle.width/2, titlePositionY, WHITE);
+
+    if (titleCounter > 180)
+    {
+        if (GuiButton((Rectangle){ GetScreenWidth()/2 - 440/2, 580, 440, 80 }, "START DATE!", -1))
+        {
+            finishScreen = 1;   // GAMEPLAY
+            PlaySound(fxCoin);
+        }
+    }
+    
+    DrawText("powered by", 20, GetScreenHeight() - texLogo.height - 35, 10, BLACK);
+    DrawTexture(texLogo, 20, GetScreenHeight() - texLogo.height - 20, WHITE);
+}
+
+// Title Screen Unload logic
+void UnloadTitleScreen(void)
+{
+    UnloadTexture(texTitle);
+    UnloadTexture(texLogo);
+}
+
+// Title Screen should finish?
+int FinishTitleScreen(void)
+{
+    return finishScreen;
+}

+ 134 - 0
games/repair/screens/screens.h

@@ -0,0 +1,134 @@
+/**********************************************************************************************
+*
+*   raylib - Advance Game template
+*
+*   Screens Functions Declarations (Init, Update, Draw, Unload)
+*
+*   Copyright (c) 2014-2020 Ramon Santamaria (@raysan5)
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#ifndef SCREENS_H
+#define SCREENS_H
+
+#define BASE_HEAD_WIDTH     400
+#define BASE_HAIR_WIDTH     500
+#define BASE_NOSE_WIDTH      80
+#define BASE_MOUTH_WIDTH    170
+#define BASE_EYES_WIDTH     240
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef enum GameScreen { LOGO = 0, TITLE, OPTIONS, GAMEPLAY, ENDING } GameScreen;
+
+typedef struct {
+    int head;
+    int colHead;
+    int eyes;           // Config
+    int nose;           // Config
+    int mouth;          // Config
+    int hair;           // Config
+    int colHair;        // Config
+    int glasses;        // Config
+    //int piercing;
+    //int freckles;
+} Character;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+extern const unsigned int headColors[6];
+extern const unsigned int hairColors[10];
+
+extern GameScreen currentScreen;
+extern Font font;
+extern Music music;
+extern Sound fxCoin;
+extern Texture2D background;
+extern Texture2D texNPatch;
+extern NPatchInfo npInfo;
+extern Texture2D texHead, texHair, texNose, texMouth, texEyes, texComp;
+extern Texture2D texMakeup;
+
+extern Character player;
+extern Character playerBase;
+extern Character dating;
+extern Character datingBase;
+
+#ifdef __cplusplus
+extern "C" {            // Prevents name mangling of functions
+#endif
+
+// Gui Button
+bool GuiButton(Rectangle rec, const char *text, int forcedState);
+
+Character GenerateCharacter(void);
+void CustomizeCharacter(Character *character);
+void DrawCharacter(Character character, Vector2 position);
+
+//----------------------------------------------------------------------------------
+// Logo Screen Functions Declaration
+//----------------------------------------------------------------------------------
+void InitLogoScreen(void);
+void UpdateLogoScreen(void);
+void DrawLogoScreen(void);
+void UnloadLogoScreen(void);
+int FinishLogoScreen(void);
+
+//----------------------------------------------------------------------------------
+// Title Screen Functions Declaration
+//----------------------------------------------------------------------------------
+void InitTitleScreen(void);
+void UpdateTitleScreen(void);
+void DrawTitleScreen(void);
+void UnloadTitleScreen(void);
+int FinishTitleScreen(void);
+
+//----------------------------------------------------------------------------------
+// Options Screen Functions Declaration
+//----------------------------------------------------------------------------------
+void InitOptionsScreen(void);
+void UpdateOptionsScreen(void);
+void DrawOptionsScreen(void);
+void UnloadOptionsScreen(void);
+int FinishOptionsScreen(void);
+
+//----------------------------------------------------------------------------------
+// Gameplay Screen Functions Declaration
+//----------------------------------------------------------------------------------
+void InitGameplayScreen(void);
+void UpdateGameplayScreen(void);
+void DrawGameplayScreen(void);
+void UnloadGameplayScreen(void);
+int FinishGameplayScreen(void);
+
+//----------------------------------------------------------------------------------
+// Ending Screen Functions Declaration
+//----------------------------------------------------------------------------------
+void InitEndingScreen(void);
+void UpdateEndingScreen(void);
+void DrawEndingScreen(void);
+void UnloadEndingScreen(void);
+int FinishEndingScreen(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // SCREENS_H

+ 125 - 113
src/camera.h

@@ -197,19 +197,31 @@ typedef enum {
     MOVE_DOWN
 } CameraMove;
 
+typedef struct {
+    int mode;                     // Current camera mode
+    float targetDistance;         // Camera distance from position to target
+    float playerEyesPosition;     // Default player eyes position from ground (in meters)
+    Vector2 angle;                // Camera angle in plane XZ
+
+    int moveControl[6];
+    int smoothZoomControl;        // raylib: KEY_LEFT_CONTROL
+    int altControl;               // raylib: KEY_LEFT_ALT
+    int panControl;               // raylib: MOUSE_MIDDLE_BUTTON
+} CameraData;
+
 //----------------------------------------------------------------------------------
 // Global Variables Definition
 //----------------------------------------------------------------------------------
-static Vector2 cameraAngle = { 0.0f, 0.0f };          // Camera angle in plane XZ
-static float cameraTargetDistance = 0.0f;             // Camera distance from position to target
-static float playerEyesPosition = 1.85f;              // Default player eyes position from ground (in meters)
-
-static int cameraMoveControl[6]  = { 'W', 'S', 'D', 'A', 'E', 'Q' };
-static int cameraPanControlKey = 2;                   // raylib: MOUSE_MIDDLE_BUTTON
-static int cameraAltControlKey = 342;                 // raylib: KEY_LEFT_ALT
-static int cameraSmoothZoomControlKey = 341;          // raylib: KEY_LEFT_CONTROL
-
-static int cameraMode = CAMERA_CUSTOM;                // Current camera mode
+static CameraData CAMERA = { 
+    .mode = 0,
+    .targetDistance = 0,
+    .playerEyesPosition = 1.85f,
+    .angle = { 0 },
+    .moveControl = { 'W', 'S', 'D', 'A', 'E', 'Q' },
+    .smoothZoomControl = 341,
+    .altControl = 342,
+    .panControl = 2
+};
 
 //----------------------------------------------------------------------------------
 // Module specific Functions Declaration
@@ -241,19 +253,19 @@ void SetCameraMode(Camera camera, int mode)
     float dy = v2.y - v1.y;
     float dz = v2.z - v1.z;
 
-    cameraTargetDistance = sqrtf(dx*dx + dy*dy + dz*dz);
+    CAMERA.targetDistance = sqrtf(dx*dx + dy*dy + dz*dz);
 
     // Camera angle calculation
-    cameraAngle.x = atan2f(dx, dz);                   // Camera angle in plane XZ (0 aligned with Z, move positive CCW)
-    cameraAngle.y = atan2f(dy, sqrtf(dx*dx + dz*dz)); // Camera angle in plane XY (0 aligned with X, move positive CW)
+    CAMERA.angle.x = atan2f(dx, dz);                   // Camera angle in plane XZ (0 aligned with Z, move positive CCW)
+    CAMERA.angle.y = atan2f(dy, sqrtf(dx*dx + dz*dz)); // Camera angle in plane XY (0 aligned with X, move positive CW)
 
-    playerEyesPosition = camera.position.y;
+    CAMERA.playerEyesPosition = camera.position.y;
 
     // Lock cursor for first person and third person cameras
     if ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON)) DisableCursor();
     else EnableCursor();
 
-    cameraMode = mode;
+    CAMERA.mode = mode;
 }
 
 // Update camera depending on selected mode
@@ -267,7 +279,7 @@ void UpdateCamera(Camera *camera)
     static int swingCounter = 0;    // Used for 1st person swinging movement
     static Vector2 previousMousePosition = { 0.0f, 0.0f };
 
-    // TODO: Compute cameraTargetDistance and cameraAngle here
+    // TODO: Compute CAMERA.targetDistance and CAMERA.angle here
 
     // Mouse movement detection
     Vector2 mousePositionDelta = { 0.0f, 0.0f };
@@ -275,20 +287,20 @@ void UpdateCamera(Camera *camera)
     int mouseWheelMove = GetMouseWheelMove();
 
     // Keys input detection
-    bool panKey = IsMouseButtonDown(cameraPanControlKey);
-    bool altKey = IsKeyDown(cameraAltControlKey);
-    bool szoomKey = IsKeyDown(cameraSmoothZoomControlKey);
+    bool panKey = IsMouseButtonDown(CAMERA.panControl);
+    bool altKey = IsKeyDown(CAMERA.altControl);
+    bool szoomKey = IsKeyDown(CAMERA.smoothZoomControl);
 
-    bool direction[6] = { IsKeyDown(cameraMoveControl[MOVE_FRONT]),
-                          IsKeyDown(cameraMoveControl[MOVE_BACK]),
-                          IsKeyDown(cameraMoveControl[MOVE_RIGHT]),
-                          IsKeyDown(cameraMoveControl[MOVE_LEFT]),
-                          IsKeyDown(cameraMoveControl[MOVE_UP]),
-                          IsKeyDown(cameraMoveControl[MOVE_DOWN]) };
+    bool direction[6] = { IsKeyDown(CAMERA.moveControl[MOVE_FRONT]),
+                          IsKeyDown(CAMERA.moveControl[MOVE_BACK]),
+                          IsKeyDown(CAMERA.moveControl[MOVE_RIGHT]),
+                          IsKeyDown(CAMERA.moveControl[MOVE_LEFT]),
+                          IsKeyDown(CAMERA.moveControl[MOVE_UP]),
+                          IsKeyDown(CAMERA.moveControl[MOVE_DOWN]) };
 
     // TODO: Consider touch inputs for camera
 
-    if (cameraMode != CAMERA_CUSTOM)
+    if (CAMERA.mode != CAMERA_CUSTOM)
     {
         mousePositionDelta.x = mousePosition.x - previousMousePosition.x;
         mousePositionDelta.y = mousePosition.y - previousMousePosition.y;
@@ -297,58 +309,58 @@ void UpdateCamera(Camera *camera)
     }
 
     // Support for multiple automatic camera modes
-    switch (cameraMode)
+    switch (CAMERA.mode)
     {
         case CAMERA_FREE:
         {
             // Camera zoom
-            if ((cameraTargetDistance < CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0))
+            if ((CAMERA.targetDistance < CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0))
             {
-                cameraTargetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY);
+                CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY);
 
-                if (cameraTargetDistance > CAMERA_FREE_DISTANCE_MAX_CLAMP) cameraTargetDistance = CAMERA_FREE_DISTANCE_MAX_CLAMP;
+                if (CAMERA.targetDistance > CAMERA_FREE_DISTANCE_MAX_CLAMP) CAMERA.targetDistance = CAMERA_FREE_DISTANCE_MAX_CLAMP;
             }
             // Camera looking down
-            // TODO: Review, weird comparisson of cameraTargetDistance == 120.0f?
-            else if ((camera->position.y > camera->target.y) && (cameraTargetDistance == CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0))
+            // TODO: Review, weird comparisson of CAMERA.targetDistance == 120.0f?
+            else if ((camera->position.y > camera->target.y) && (CAMERA.targetDistance == CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0))
             {
-                camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
-                camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
-                camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
+                camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance;
+                camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance;
+                camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance;
             }
             else if ((camera->position.y > camera->target.y) && (camera->target.y >= 0))
             {
-                camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
-                camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
-                camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
+                camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance;
+                camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance;
+                camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance;
 
                 // if (camera->target.y < 0) camera->target.y = -0.001;
             }
             else if ((camera->position.y > camera->target.y) && (camera->target.y < 0) && (mouseWheelMove > 0))
             {
-                cameraTargetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY);
-                if (cameraTargetDistance < CAMERA_FREE_DISTANCE_MIN_CLAMP) cameraTargetDistance = CAMERA_FREE_DISTANCE_MIN_CLAMP;
+                CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY);
+                if (CAMERA.targetDistance < CAMERA_FREE_DISTANCE_MIN_CLAMP) CAMERA.targetDistance = CAMERA_FREE_DISTANCE_MIN_CLAMP;
             }
             // Camera looking up
-            // TODO: Review, weird comparisson of cameraTargetDistance == 120.0f?
-            else if ((camera->position.y < camera->target.y) && (cameraTargetDistance == CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0))
+            // TODO: Review, weird comparisson of CAMERA.targetDistance == 120.0f?
+            else if ((camera->position.y < camera->target.y) && (CAMERA.targetDistance == CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0))
             {
-                camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
-                camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
-                camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
+                camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance;
+                camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance;
+                camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance;
             }
             else if ((camera->position.y < camera->target.y) && (camera->target.y <= 0))
             {
-                camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
-                camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
-                camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
+                camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance;
+                camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance;
+                camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance;
 
                 // if (camera->target.y > 0) camera->target.y = 0.001;
             }
             else if ((camera->position.y < camera->target.y) && (camera->target.y > 0) && (mouseWheelMove > 0))
             {
-                cameraTargetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY);
-                if (cameraTargetDistance < CAMERA_FREE_DISTANCE_MIN_CLAMP) cameraTargetDistance = CAMERA_FREE_DISTANCE_MIN_CLAMP;
+                CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY);
+                if (CAMERA.targetDistance < CAMERA_FREE_DISTANCE_MIN_CLAMP) CAMERA.targetDistance = CAMERA_FREE_DISTANCE_MIN_CLAMP;
             }
 
             // Input keys checks
@@ -359,78 +371,78 @@ void UpdateCamera(Camera *camera)
                     if (szoomKey)
                     {
                         // Camera smooth zoom
-                        cameraTargetDistance += (mousePositionDelta.y*CAMERA_FREE_SMOOTH_ZOOM_SENSITIVITY);
+                        CAMERA.targetDistance += (mousePositionDelta.y*CAMERA_FREE_SMOOTH_ZOOM_SENSITIVITY);
                     }
                     else
                     {
                         // Camera rotation
-                        cameraAngle.x += mousePositionDelta.x*-CAMERA_FREE_MOUSE_SENSITIVITY;
-                        cameraAngle.y += mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY;
+                        CAMERA.angle.x += mousePositionDelta.x*-CAMERA_FREE_MOUSE_SENSITIVITY;
+                        CAMERA.angle.y += mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY;
 
                         // Angle clamp
-                        if (cameraAngle.y > CAMERA_FREE_MIN_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_FREE_MIN_CLAMP*DEG2RAD;
-                        else if (cameraAngle.y < CAMERA_FREE_MAX_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_FREE_MAX_CLAMP*DEG2RAD;
+                        if (CAMERA.angle.y > CAMERA_FREE_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FREE_MIN_CLAMP*DEG2RAD;
+                        else if (CAMERA.angle.y < CAMERA_FREE_MAX_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FREE_MAX_CLAMP*DEG2RAD;
                     }
                 }
                 else
                 {
                     // Camera panning
-                    camera->target.x += ((mousePositionDelta.x*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(cameraAngle.x) + (mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(cameraAngle.x)*sinf(cameraAngle.y))*(cameraTargetDistance/CAMERA_FREE_PANNING_DIVIDER);
-                    camera->target.y += ((mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(cameraAngle.y))*(cameraTargetDistance/CAMERA_FREE_PANNING_DIVIDER);
-                    camera->target.z += ((mousePositionDelta.x*-CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(cameraAngle.x) + (mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(cameraAngle.x)*sinf(cameraAngle.y))*(cameraTargetDistance/CAMERA_FREE_PANNING_DIVIDER);
+                    camera->target.x += ((mousePositionDelta.x*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.x) + (mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(CAMERA.angle.x)*sinf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER);
+                    camera->target.y += ((mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER);
+                    camera->target.z += ((mousePositionDelta.x*-CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(CAMERA.angle.x) + (mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.x)*sinf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER);
                 }
             }
 
             // Update camera position with changes
-            camera->position.x = -sinf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.x;
-            camera->position.y = -sinf(cameraAngle.y)*cameraTargetDistance + camera->target.y;
-            camera->position.z = -cosf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.z;
+            camera->position.x = -sinf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.x;
+            camera->position.y = -sinf(CAMERA.angle.y)*CAMERA.targetDistance + camera->target.y;
+            camera->position.z = -cosf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.z;
         } break;
         case CAMERA_ORBITAL:
         {
-            cameraAngle.x += CAMERA_ORBITAL_SPEED;      // Camera orbit angle
-            cameraTargetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY);   // Camera zoom
+            CAMERA.angle.x += CAMERA_ORBITAL_SPEED;      // Camera orbit angle
+            CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY);   // Camera zoom
 
             // Camera distance clamp
-            if (cameraTargetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP;
+            if (CAMERA.targetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) CAMERA.targetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP;
 
             // Update camera position with changes
-            camera->position.x = sinf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.x;
-            camera->position.y = ((cameraAngle.y <= 0.0f)? 1 : -1)*sinf(cameraAngle.y)*cameraTargetDistance*sinf(cameraAngle.y) + camera->target.y;
-            camera->position.z = cosf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.z;
+            camera->position.x = sinf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.x;
+            camera->position.y = ((CAMERA.angle.y <= 0.0f)? 1 : -1)*sinf(CAMERA.angle.y)*CAMERA.targetDistance*sinf(CAMERA.angle.y) + camera->target.y;
+            camera->position.z = cosf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.z;
 
         } break;
         case CAMERA_FIRST_PERSON:
         {
-            camera->position.x += (sinf(cameraAngle.x)*direction[MOVE_BACK] -
-                                   sinf(cameraAngle.x)*direction[MOVE_FRONT] -
-                                   cosf(cameraAngle.x)*direction[MOVE_LEFT] +
-                                   cosf(cameraAngle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY;
+            camera->position.x += (sinf(CAMERA.angle.x)*direction[MOVE_BACK] -
+                                   sinf(CAMERA.angle.x)*direction[MOVE_FRONT] -
+                                   cosf(CAMERA.angle.x)*direction[MOVE_LEFT] +
+                                   cosf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY;
 
-            camera->position.y += (sinf(cameraAngle.y)*direction[MOVE_FRONT] -
-                                   sinf(cameraAngle.y)*direction[MOVE_BACK] +
+            camera->position.y += (sinf(CAMERA.angle.y)*direction[MOVE_FRONT] -
+                                   sinf(CAMERA.angle.y)*direction[MOVE_BACK] +
                                    1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])/PLAYER_MOVEMENT_SENSITIVITY;
 
-            camera->position.z += (cosf(cameraAngle.x)*direction[MOVE_BACK] -
-                                   cosf(cameraAngle.x)*direction[MOVE_FRONT] +
-                                   sinf(cameraAngle.x)*direction[MOVE_LEFT] -
-                                   sinf(cameraAngle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY;
+            camera->position.z += (cosf(CAMERA.angle.x)*direction[MOVE_BACK] -
+                                   cosf(CAMERA.angle.x)*direction[MOVE_FRONT] +
+                                   sinf(CAMERA.angle.x)*direction[MOVE_LEFT] -
+                                   sinf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY;
 
             bool isMoving = false;  // Required for swinging
 
             for (int i = 0; i < 6; i++) if (direction[i]) { isMoving = true; break; }
 
             // Camera orientation calculation
-            cameraAngle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY);
-            cameraAngle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY);
+            CAMERA.angle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY);
+            CAMERA.angle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY);
 
             // Angle clamp
-            if (cameraAngle.y > CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD;
-            else if (cameraAngle.y < CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD;
+            if (CAMERA.angle.y > CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD;
+            else if (CAMERA.angle.y < CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD;
 
             // Recalculate camera target considering translation and rotation
-            Matrix translation = MatrixTranslate(0, 0, (cameraTargetDistance/CAMERA_FREE_PANNING_DIVIDER));
-            Matrix rotation = MatrixRotateXYZ((Vector3){ PI*2 - cameraAngle.y, PI*2 - cameraAngle.x, 0 });
+            Matrix translation = MatrixTranslate(0, 0, (CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER));
+            Matrix rotation = MatrixRotateXYZ((Vector3){ PI*2 - CAMERA.angle.y, PI*2 - CAMERA.angle.x, 0 });
             Matrix transform = MatrixMultiply(translation, rotation);
             
             camera->target.x = camera->position.x - transform.m12;
@@ -441,7 +453,7 @@ void UpdateCamera(Camera *camera)
 
             // Camera position update
             // NOTE: On CAMERA_FIRST_PERSON player Y-movement is limited to player 'eyes position'
-            camera->position.y = playerEyesPosition - sinf(swingCounter/CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER)/CAMERA_FIRST_PERSON_STEP_DIVIDER;
+            camera->position.y = CAMERA.playerEyesPosition - sinf(swingCounter/CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER)/CAMERA_FIRST_PERSON_STEP_DIVIDER;
 
             camera->up.x = sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER;
             camera->up.z = -sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER;
@@ -450,39 +462,39 @@ void UpdateCamera(Camera *camera)
         } break;
         case CAMERA_THIRD_PERSON:
         {
-            camera->position.x += (sinf(cameraAngle.x)*direction[MOVE_BACK] -
-                                   sinf(cameraAngle.x)*direction[MOVE_FRONT] -
-                                   cosf(cameraAngle.x)*direction[MOVE_LEFT] +
-                                   cosf(cameraAngle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY;
+            camera->position.x += (sinf(CAMERA.angle.x)*direction[MOVE_BACK] -
+                                   sinf(CAMERA.angle.x)*direction[MOVE_FRONT] -
+                                   cosf(CAMERA.angle.x)*direction[MOVE_LEFT] +
+                                   cosf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY;
 
-            camera->position.y += (sinf(cameraAngle.y)*direction[MOVE_FRONT] -
-                                   sinf(cameraAngle.y)*direction[MOVE_BACK] +
+            camera->position.y += (sinf(CAMERA.angle.y)*direction[MOVE_FRONT] -
+                                   sinf(CAMERA.angle.y)*direction[MOVE_BACK] +
                                    1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])/PLAYER_MOVEMENT_SENSITIVITY;
 
-            camera->position.z += (cosf(cameraAngle.x)*direction[MOVE_BACK] -
-                                   cosf(cameraAngle.x)*direction[MOVE_FRONT] +
-                                   sinf(cameraAngle.x)*direction[MOVE_LEFT] -
-                                   sinf(cameraAngle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY;
+            camera->position.z += (cosf(CAMERA.angle.x)*direction[MOVE_BACK] -
+                                   cosf(CAMERA.angle.x)*direction[MOVE_FRONT] +
+                                   sinf(CAMERA.angle.x)*direction[MOVE_LEFT] -
+                                   sinf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY;
 
             // Camera orientation calculation
-            cameraAngle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY);
-            cameraAngle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY);
+            CAMERA.angle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY);
+            CAMERA.angle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY);
 
             // Angle clamp
-            if (cameraAngle.y > CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD;
-            else if (cameraAngle.y < CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD;
+            if (CAMERA.angle.y > CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD;
+            else if (CAMERA.angle.y < CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD;
 
             // Camera zoom
-            cameraTargetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY);
+            CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY);
 
             // Camera distance clamp
-            if (cameraTargetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP;
+            if (CAMERA.targetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) CAMERA.targetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP;
 
             // TODO: It seems camera->position is not correctly updated or some rounding issue makes the camera move straight to camera->target...
-            camera->position.x = sinf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.x;
-            if (cameraAngle.y <= 0.0f) camera->position.y = sinf(cameraAngle.y)*cameraTargetDistance*sinf(cameraAngle.y) + camera->target.y;
-            else camera->position.y = -sinf(cameraAngle.y)*cameraTargetDistance*sinf(cameraAngle.y) + camera->target.y;
-            camera->position.z = cosf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.z;
+            camera->position.x = sinf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.x;
+            if (CAMERA.angle.y <= 0.0f) camera->position.y = sinf(CAMERA.angle.y)*CAMERA.targetDistance*sinf(CAMERA.angle.y) + camera->target.y;
+            else camera->position.y = -sinf(CAMERA.angle.y)*CAMERA.targetDistance*sinf(CAMERA.angle.y) + camera->target.y;
+            camera->position.z = cosf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.z;
 
         } break;
         default: break;
@@ -490,23 +502,23 @@ void UpdateCamera(Camera *camera)
 }
 
 // Set camera pan key to combine with mouse movement (free camera)
-void SetCameraPanControl(int panKey) { cameraPanControlKey = panKey; }
+void SetCameraPanControl(int panKey) { CAMERA.panControl = panKey; }
 
 // Set camera alt key to combine with mouse movement (free camera)
-void SetCameraAltControl(int altKey) { cameraAltControlKey = altKey; }
+void SetCameraAltControl(int altKey) { CAMERA.altControl = altKey; }
 
 // Set camera smooth zoom key to combine with mouse (free camera)
-void SetCameraSmoothZoomControl(int szoomKey) { cameraSmoothZoomControlKey = szoomKey; }
+void SetCameraSmoothZoomControl(int szoomKey) { CAMERA.smoothZoomControl = szoomKey; }
 
 // Set camera move controls (1st person and 3rd person cameras)
 void SetCameraMoveControls(int frontKey, int backKey, int rightKey, int leftKey, int upKey, int downKey)
 {
-    cameraMoveControl[MOVE_FRONT] = frontKey;
-    cameraMoveControl[MOVE_BACK] = backKey;
-    cameraMoveControl[MOVE_RIGHT] = rightKey;
-    cameraMoveControl[MOVE_LEFT] = leftKey;
-    cameraMoveControl[MOVE_UP] = upKey;
-    cameraMoveControl[MOVE_DOWN] = downKey;
+    CAMERA.moveControl[MOVE_FRONT] = frontKey;
+    CAMERA.moveControl[MOVE_BACK] = backKey;
+    CAMERA.moveControl[MOVE_RIGHT] = rightKey;
+    CAMERA.moveControl[MOVE_LEFT] = leftKey;
+    CAMERA.moveControl[MOVE_UP] = upKey;
+    CAMERA.moveControl[MOVE_DOWN] = downKey;
 }
 
 #endif // CAMERA_IMPLEMENTATION

+ 1 - 1
src/config.h

@@ -25,7 +25,7 @@
 *
 **********************************************************************************************/
 
-#define RAYLIB_VERSION  "2.6-dev"
+#define RAYLIB_VERSION  "3.0"
 
 // Edit to control what features Makefile'd raylib is compiled with
 #if defined(RAYLIB_CMAKE)

File diff suppressed because it is too large
+ 272 - 262
src/core.c


+ 143 - 142
src/gestures.h

@@ -149,75 +149,76 @@ float GetGesturePinchAngle(void);                       // Get gesture pinch ang
         #undef _POSIX_C_SOURCE
         #define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext.
     #endif
-    #include <sys/time.h>           // Required for: timespec
-    #include <time.h>               // Required for: clock_gettime()
+    #include <sys/time.h>               // Required for: timespec
+    #include <time.h>                   // Required for: clock_gettime()
 
-    #include <math.h>               // Required for: atan2(), sqrt()
-    #include <stdint.h>             // Required for: uint64_t
+    #include <math.h>                   // Required for: atan2(), sqrt()
+    #include <stdint.h>                 // Required for: uint64_t
 #endif
 
-#if defined(__APPLE__)              // macOS also defines __MACH__
-    #include <mach/clock.h>         // Required for: clock_get_time()
-    #include <mach/mach.h>          // Required for: mach_timespec_t
+#if defined(__APPLE__)                  // macOS also defines __MACH__
+    #include <mach/clock.h>             // Required for: clock_get_time()
+    #include <mach/mach.h>              // Required for: mach_timespec_t
 #endif
 
 //----------------------------------------------------------------------------------
 // Defines and Macros
 //----------------------------------------------------------------------------------
-#define FORCE_TO_SWIPE          0.0005f     // Measured in normalized screen units/time
-#define MINIMUM_DRAG            0.015f      // Measured in normalized screen units (0.0f to 1.0f)
-#define MINIMUM_PINCH           0.005f      // Measured in normalized screen units (0.0f to 1.0f)
-#define TAP_TIMEOUT             300         // Time in milliseconds
-#define PINCH_TIMEOUT           300         // Time in milliseconds
-#define DOUBLETAP_RANGE         0.03f       // Measured in normalized screen units (0.0f to 1.0f)
+#define FORCE_TO_SWIPE      0.0005f     // Measured in normalized screen units/time
+#define MINIMUM_DRAG        0.015f      // Measured in normalized screen units (0.0f to 1.0f)
+#define MINIMUM_PINCH       0.005f      // Measured in normalized screen units (0.0f to 1.0f)
+#define TAP_TIMEOUT         300         // Time in milliseconds
+#define PINCH_TIMEOUT       300         // Time in milliseconds
+#define DOUBLETAP_RANGE     0.03f       // Measured in normalized screen units (0.0f to 1.0f)
 
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
 //----------------------------------------------------------------------------------
-// ...
+
+typedef struct {
+    int current;                        // Current detected gesture
+    unsigned int enabledFlags;          // Enabled gestures flags
+    struct {
+        int firstId;                    // Touch id for first touch point
+        int pointCount;                 // Touch points counter
+        double eventTime;               // Time stamp when an event happened
+        Vector2 upPosition;             // Touch up position
+        Vector2 downPositionA;          // First touch down position
+        Vector2 downPositionB;          // Second touch down position
+        Vector2 downDragPosition;       // Touch drag position
+        Vector2 moveDownPositionA;      // First touch down position on move
+        Vector2 moveDownPositionB;      // Second touch down position on move
+        int tapCounter;                 // TAP counter (one tap implies TOUCH_DOWN and TOUCH_UP actions)
+    } Touch;
+    struct {
+        bool resetRequired;             // HOLD reset to get first touch point again
+        double timeDuration;            // HOLD duration in milliseconds
+    } Hold;
+    struct {
+        Vector2 vector;                 // DRAG vector (between initial and current position)
+        float angle;                    // DRAG angle (relative to x-axis)
+        float distance;                 // DRAG distance (from initial touch point to final) (normalized [0..1])
+        float intensity;                // DRAG intensity, how far why did the DRAG (pixels per frame)
+    } Drag;
+    struct {
+        bool start;                     // SWIPE used to define when start measuring GESTURES.Swipe.timeDuration
+        double timeDuration;            // SWIPE time to calculate drag intensity
+    } Swipe;
+    struct {
+        Vector2 vector;                 // PINCH vector (between first and second touch points)
+        float angle;                    // PINCH angle (relative to x-axis)
+        float distance;                 // PINCH displacement distance (normalized [0..1])
+    } Pinch;
+} GesturesData;
 
 //----------------------------------------------------------------------------------
 // Global Variables Definition
 //----------------------------------------------------------------------------------
-
-// Touch gesture variables
-static Vector2 touchDownPosition = { 0.0f, 0.0f };      // First touch down position
-static Vector2 touchDownPosition2 = { 0.0f, 0.0f };     // Second touch down position
-static Vector2 touchDownDragPosition = { 0.0f, 0.0f };  // Touch drag position
-static Vector2 touchUpPosition = { 0.0f, 0.0f };        // Touch up position
-static Vector2 moveDownPosition = { 0.0f, 0.0f };       // First touch down position on move
-static Vector2 moveDownPosition2 = { 0.0f, 0.0f };      // Second touch down position on move
-
-static int pointCount = 0;                      // Touch points counter
-static int firstTouchId = -1;                   // Touch id for first touch point
-static double eventTime = 0.0;                  // Time stamp when an event happened
-
-// Tap gesture variables
-static int tapCounter = 0;                      // TAP counter (one tap implies TOUCH_DOWN and TOUCH_UP actions)
-
-// Hold gesture variables
-static bool resetHold = false;                  // HOLD reset to get first touch point again
-static double timeHold = 0.0f;                  // HOLD duration in milliseconds
-
-// Drag gesture variables
-static Vector2 dragVector = { 0.0f , 0.0f };    // DRAG vector (between initial and current position)
-static float dragAngle = 0.0f;                  // DRAG angle (relative to x-axis)
-static float dragDistance = 0.0f;               // DRAG distance (from initial touch point to final) (normalized [0..1])
-static float dragIntensity = 0.0f;              // DRAG intensity, how far why did the DRAG (pixels per frame)
-
-// Swipe gestures variables
-static bool startMoving = false;                // SWIPE used to define when start measuring swipeTime
-static double swipeTime = 0.0;                  // SWIPE time to calculate drag intensity
-
-// Pinch gesture variables
-static Vector2 pinchVector = { 0.0f , 0.0f };   // PINCH vector (between first and second touch points)
-static float pinchAngle = 0.0f;                 // PINCH angle (relative to x-axis)
-static float pinchDistance = 0.0f;              // PINCH displacement distance (normalized [0..1])
-
-static int currentGesture = GESTURE_NONE;       // Current detected gesture
-
-// Enabled gestures flags, all gestures enabled by default
-static unsigned int enabledGestures = 0b0000001111111111;
+static GesturesData GESTURES = {
+    .Touch.firstId = -1,
+    .current = GESTURE_NONE,
+    .enabledFlags = 0b0000001111111111          // All gestures enabled by default
+};
 
 //----------------------------------------------------------------------------------
 // Module specific Functions Declaration
@@ -236,13 +237,13 @@ static double GetCurrentTime(void);
 // Enable only desired getures to be detected
 void SetGesturesEnabled(unsigned int gestureFlags)
 {
-    enabledGestures = gestureFlags;
+    GESTURES.enabledFlags = gestureFlags;
 }
 
 // Check if a gesture have been detected
 bool IsGestureDetected(int gesture)
 {
-    if ((enabledGestures & currentGesture) == gesture) return true;
+    if ((GESTURES.enabledFlags & GESTURES.current) == gesture) return true;
     else return false;
 }
 
@@ -250,150 +251,150 @@ bool IsGestureDetected(int gesture)
 void ProcessGestureEvent(GestureEvent event)
 {
     // Reset required variables
-    pointCount = event.pointCount;      // Required on UpdateGestures()
+    GESTURES.Touch.pointCount = event.pointCount;      // Required on UpdateGestures()
 
-    if (pointCount < 2)
+    if (GESTURES.Touch.pointCount < 2)
     {
         if (event.touchAction == TOUCH_DOWN)
         {
-            tapCounter++;    // Tap counter
+            GESTURES.Touch.tapCounter++;    // Tap counter
 
             // Detect GESTURE_DOUBLE_TAP
-            if ((currentGesture == GESTURE_NONE) && (tapCounter >= 2) && ((GetCurrentTime() - eventTime) < TAP_TIMEOUT) && (Vector2Distance(touchDownPosition, event.position[0]) < DOUBLETAP_RANGE))
+            if ((GESTURES.current == GESTURE_NONE) && (GESTURES.Touch.tapCounter >= 2) && ((GetCurrentTime() - GESTURES.Touch.eventTime) < TAP_TIMEOUT) && (Vector2Distance(GESTURES.Touch.downPositionA, event.position[0]) < DOUBLETAP_RANGE))
             {
-                currentGesture = GESTURE_DOUBLETAP;
-                tapCounter = 0;
+                GESTURES.current = GESTURE_DOUBLETAP;
+                GESTURES.Touch.tapCounter = 0;
             }
             else    // Detect GESTURE_TAP
             {
-                tapCounter = 1;
-                currentGesture = GESTURE_TAP;
+                GESTURES.Touch.tapCounter = 1;
+                GESTURES.current = GESTURE_TAP;
             }
 
-            touchDownPosition = event.position[0];
-            touchDownDragPosition = event.position[0];
+            GESTURES.Touch.downPositionA = event.position[0];
+            GESTURES.Touch.downDragPosition = event.position[0];
 
-            touchUpPosition = touchDownPosition;
-            eventTime = GetCurrentTime();
+            GESTURES.Touch.upPosition = GESTURES.Touch.downPositionA;
+            GESTURES.Touch.eventTime = GetCurrentTime();
 
-            firstTouchId = event.pointerId[0];
+            GESTURES.Touch.firstId = event.pointerId[0];
 
-            dragVector = (Vector2){ 0.0f, 0.0f };
+            GESTURES.Drag.vector = (Vector2){ 0.0f, 0.0f };
         }
         else if (event.touchAction == TOUCH_UP)
         {
-            if (currentGesture == GESTURE_DRAG) touchUpPosition = event.position[0];
+            if (GESTURES.current == GESTURE_DRAG) GESTURES.Touch.upPosition = event.position[0];
 
-            // NOTE: dragIntensity dependend on the resolution of the screen
-            dragDistance = Vector2Distance(touchDownPosition, touchUpPosition);
-            dragIntensity = dragDistance/(float)((GetCurrentTime() - swipeTime));
+            // NOTE: GESTURES.Drag.intensity dependend on the resolution of the screen
+            GESTURES.Drag.distance = Vector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition);
+            GESTURES.Drag.intensity = GESTURES.Drag.distance/(float)((GetCurrentTime() - GESTURES.Swipe.timeDuration));
 
-            startMoving = false;
+            GESTURES.Swipe.start = false;
 
             // Detect GESTURE_SWIPE
-            if ((dragIntensity > FORCE_TO_SWIPE) && (firstTouchId == event.pointerId[0]))
+            if ((GESTURES.Drag.intensity > FORCE_TO_SWIPE) && (GESTURES.Touch.firstId == event.pointerId[0]))
             {
                 // NOTE: Angle should be inverted in Y
-                dragAngle = 360.0f - Vector2Angle(touchDownPosition, touchUpPosition);
+                GESTURES.Drag.angle = 360.0f - Vector2Angle(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition);
 
-                if ((dragAngle < 30) || (dragAngle > 330)) currentGesture = GESTURE_SWIPE_RIGHT;        // Right
-                else if ((dragAngle > 30) && (dragAngle < 120)) currentGesture = GESTURE_SWIPE_UP;      // Up
-                else if ((dragAngle > 120) && (dragAngle < 210)) currentGesture = GESTURE_SWIPE_LEFT;   // Left
-                else if ((dragAngle > 210) && (dragAngle < 300)) currentGesture = GESTURE_SWIPE_DOWN;   // Down
-                else currentGesture = GESTURE_NONE;
+                if ((GESTURES.Drag.angle < 30) || (GESTURES.Drag.angle > 330)) GESTURES.current = GESTURE_SWIPE_RIGHT;        // Right
+                else if ((GESTURES.Drag.angle > 30) && (GESTURES.Drag.angle < 120)) GESTURES.current = GESTURE_SWIPE_UP;      // Up
+                else if ((GESTURES.Drag.angle > 120) && (GESTURES.Drag.angle < 210)) GESTURES.current = GESTURE_SWIPE_LEFT;   // Left
+                else if ((GESTURES.Drag.angle > 210) && (GESTURES.Drag.angle < 300)) GESTURES.current = GESTURE_SWIPE_DOWN;   // Down
+                else GESTURES.current = GESTURE_NONE;
             }
             else
             {
-                dragDistance = 0.0f;
-                dragIntensity = 0.0f;
-                dragAngle = 0.0f;
+                GESTURES.Drag.distance = 0.0f;
+                GESTURES.Drag.intensity = 0.0f;
+                GESTURES.Drag.angle = 0.0f;
 
-                currentGesture = GESTURE_NONE;
+                GESTURES.current = GESTURE_NONE;
             }
 
-            touchDownDragPosition = (Vector2){ 0.0f, 0.0f };
-            pointCount = 0;
+            GESTURES.Touch.downDragPosition = (Vector2){ 0.0f, 0.0f };
+            GESTURES.Touch.pointCount = 0;
         }
         else if (event.touchAction == TOUCH_MOVE)
         {
-            if (currentGesture == GESTURE_DRAG) eventTime = GetCurrentTime();
+            if (GESTURES.current == GESTURE_DRAG) GESTURES.Touch.eventTime = GetCurrentTime();
 
-            if (!startMoving)
+            if (!GESTURES.Swipe.start)
             {
-                swipeTime = GetCurrentTime();
-                startMoving = true;
+                GESTURES.Swipe.timeDuration = GetCurrentTime();
+                GESTURES.Swipe.start = true;
             }
 
-            moveDownPosition = event.position[0];
+            GESTURES.Touch.moveDownPositionA = event.position[0];
 
-            if (currentGesture == GESTURE_HOLD)
+            if (GESTURES.current == GESTURE_HOLD)
             {
-                if (resetHold) touchDownPosition = event.position[0];
+                if (GESTURES.Hold.resetRequired) GESTURES.Touch.downPositionA = event.position[0];
 
-                resetHold = false;
+                GESTURES.Hold.resetRequired = false;
 
                 // Detect GESTURE_DRAG
-                if (Vector2Distance(touchDownPosition, moveDownPosition) >= MINIMUM_DRAG)
+                if (Vector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.moveDownPositionA) >= MINIMUM_DRAG)
                 {
-                    eventTime = GetCurrentTime();
-                    currentGesture = GESTURE_DRAG;
+                    GESTURES.Touch.eventTime = GetCurrentTime();
+                    GESTURES.current = GESTURE_DRAG;
                 }
             }
 
-            dragVector.x = moveDownPosition.x - touchDownDragPosition.x;
-            dragVector.y = moveDownPosition.y - touchDownDragPosition.y;
+            GESTURES.Drag.vector.x = GESTURES.Touch.moveDownPositionA.x - GESTURES.Touch.downDragPosition.x;
+            GESTURES.Drag.vector.y = GESTURES.Touch.moveDownPositionA.y - GESTURES.Touch.downDragPosition.y;
         }
     }
     else    // Two touch points
     {
         if (event.touchAction == TOUCH_DOWN)
         {
-            touchDownPosition = event.position[0];
-            touchDownPosition2 = event.position[1];
+            GESTURES.Touch.downPositionA = event.position[0];
+            GESTURES.Touch.downPositionB = event.position[1];
 
-            //pinchDistance = Vector2Distance(touchDownPosition, touchDownPosition2);
+            //GESTURES.Pinch.distance = Vector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.downPositionB);
 
-            pinchVector.x = touchDownPosition2.x - touchDownPosition.x;
-            pinchVector.y = touchDownPosition2.y - touchDownPosition.y;
+            GESTURES.Pinch.vector.x = GESTURES.Touch.downPositionB.x - GESTURES.Touch.downPositionA.x;
+            GESTURES.Pinch.vector.y = GESTURES.Touch.downPositionB.y - GESTURES.Touch.downPositionA.y;
 
-            currentGesture = GESTURE_HOLD;
-            timeHold = GetCurrentTime();
+            GESTURES.current = GESTURE_HOLD;
+            GESTURES.Hold.timeDuration = GetCurrentTime();
         }
         else if (event.touchAction == TOUCH_MOVE)
         {
-            pinchDistance = Vector2Distance(moveDownPosition, moveDownPosition2);
+            GESTURES.Pinch.distance = Vector2Distance(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB);
 
-            touchDownPosition = moveDownPosition;
-            touchDownPosition2 = moveDownPosition2;
+            GESTURES.Touch.downPositionA = GESTURES.Touch.moveDownPositionA;
+            GESTURES.Touch.downPositionB = GESTURES.Touch.moveDownPositionB;
 
-            moveDownPosition = event.position[0];
-            moveDownPosition2 = event.position[1];
+            GESTURES.Touch.moveDownPositionA = event.position[0];
+            GESTURES.Touch.moveDownPositionB = event.position[1];
 
-            pinchVector.x = moveDownPosition2.x - moveDownPosition.x;
-            pinchVector.y = moveDownPosition2.y - moveDownPosition.y;
+            GESTURES.Pinch.vector.x = GESTURES.Touch.moveDownPositionB.x - GESTURES.Touch.moveDownPositionA.x;
+            GESTURES.Pinch.vector.y = GESTURES.Touch.moveDownPositionB.y - GESTURES.Touch.moveDownPositionA.y;
 
-            if ((Vector2Distance(touchDownPosition, moveDownPosition) >= MINIMUM_PINCH) || (Vector2Distance(touchDownPosition2, moveDownPosition2) >= MINIMUM_PINCH))
+            if ((Vector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.moveDownPositionA) >= MINIMUM_PINCH) || (Vector2Distance(GESTURES.Touch.downPositionB, GESTURES.Touch.moveDownPositionB) >= MINIMUM_PINCH))
             {
-                if ((Vector2Distance(moveDownPosition, moveDownPosition2) - pinchDistance) < 0) currentGesture = GESTURE_PINCH_IN;
-                else currentGesture = GESTURE_PINCH_OUT;
+                if ((Vector2Distance(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB) - GESTURES.Pinch.distance) < 0) GESTURES.current = GESTURE_PINCH_IN;
+                else GESTURES.current = GESTURE_PINCH_OUT;
             }
             else
             {
-                currentGesture = GESTURE_HOLD;
-                timeHold = GetCurrentTime();
+                GESTURES.current = GESTURE_HOLD;
+                GESTURES.Hold.timeDuration = GetCurrentTime();
             }
 
             // NOTE: Angle should be inverted in Y
-            pinchAngle = 360.0f - Vector2Angle(moveDownPosition, moveDownPosition2);
+            GESTURES.Pinch.angle = 360.0f - Vector2Angle(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB);
         }
         else if (event.touchAction == TOUCH_UP)
         {
-            pinchDistance = 0.0f;
-            pinchAngle = 0.0f;
-            pinchVector = (Vector2){ 0.0f, 0.0f };
-            pointCount = 0;
+            GESTURES.Pinch.distance = 0.0f;
+            GESTURES.Pinch.angle = 0.0f;
+            GESTURES.Pinch.vector = (Vector2){ 0.0f, 0.0f };
+            GESTURES.Touch.pointCount = 0;
 
-            currentGesture = GESTURE_NONE;
+            GESTURES.current = GESTURE_NONE;
         }
     }
 }
@@ -404,23 +405,23 @@ void UpdateGestures(void)
     // NOTE: Gestures are processed through system callbacks on touch events
 
     // Detect GESTURE_HOLD
-    if (((currentGesture == GESTURE_TAP) || (currentGesture == GESTURE_DOUBLETAP)) && (pointCount < 2))
+    if (((GESTURES.current == GESTURE_TAP) || (GESTURES.current == GESTURE_DOUBLETAP)) && (GESTURES.Touch.pointCount < 2))
     {
-        currentGesture = GESTURE_HOLD;
-        timeHold = GetCurrentTime();
+        GESTURES.current = GESTURE_HOLD;
+        GESTURES.Hold.timeDuration = GetCurrentTime();
     }
 
-    if (((GetCurrentTime() - eventTime) > TAP_TIMEOUT) && (currentGesture == GESTURE_DRAG) && (pointCount < 2))
+    if (((GetCurrentTime() - GESTURES.Touch.eventTime) > TAP_TIMEOUT) && (GESTURES.current == GESTURE_DRAG) && (GESTURES.Touch.pointCount < 2))
     {
-        currentGesture = GESTURE_HOLD;
-        timeHold = GetCurrentTime();
-        resetHold = true;
+        GESTURES.current = GESTURE_HOLD;
+        GESTURES.Hold.timeDuration = GetCurrentTime();
+        GESTURES.Hold.resetRequired = true;
     }
 
     // Detect GESTURE_NONE
-    if ((currentGesture == GESTURE_SWIPE_RIGHT) || (currentGesture == GESTURE_SWIPE_UP) || (currentGesture == GESTURE_SWIPE_LEFT) || (currentGesture == GESTURE_SWIPE_DOWN))
+    if ((GESTURES.current == GESTURE_SWIPE_RIGHT) || (GESTURES.current == GESTURE_SWIPE_UP) || (GESTURES.current == GESTURE_SWIPE_LEFT) || (GESTURES.current == GESTURE_SWIPE_DOWN))
     {
-        currentGesture = GESTURE_NONE;
+        GESTURES.current = GESTURE_NONE;
     }
 }
 
@@ -429,14 +430,14 @@ int GetTouchPointsCount(void)
 {
     // NOTE: point count is calculated when ProcessGestureEvent(GestureEvent event) is called
 
-    return pointCount;
+    return GESTURES.Touch.pointCount;
 }
 
 // Get latest detected gesture
 int GetGestureDetected(void)
 {
     // Get current gesture only if enabled
-    return (enabledGestures & currentGesture);
+    return (GESTURES.enabledFlags & GESTURES.current);
 }
 
 // Hold time measured in ms
@@ -446,7 +447,7 @@ float GetGestureHoldDuration(void)
 
     double time = 0.0;
 
-    if (currentGesture == GESTURE_HOLD) time = GetCurrentTime() - timeHold;
+    if (GESTURES.current == GESTURE_HOLD) time = GetCurrentTime() - GESTURES.Hold.timeDuration;
 
     return (float)time;
 }
@@ -456,7 +457,7 @@ Vector2 GetGestureDragVector(void)
 {
     // NOTE: drag vector is calculated on one touch points TOUCH_MOVE
 
-    return dragVector;
+    return GESTURES.Drag.vector;
 }
 
 // Get drag angle
@@ -465,16 +466,16 @@ float GetGestureDragAngle(void)
 {
     // NOTE: drag angle is calculated on one touch points TOUCH_UP
 
-    return dragAngle;
+    return GESTURES.Drag.angle;
 }
 
 // Get distance between two pinch points
 Vector2 GetGesturePinchVector(void)
 {
-    // NOTE: The position values used for pinchDistance are not modified like the position values of [core.c]-->GetTouchPosition(int index)
+    // NOTE: The position values used for GESTURES.Pinch.distance are not modified like the position values of [core.c]-->GetTouchPosition(int index)
     // NOTE: pinch distance is calculated on two touch points TOUCH_MOVE
 
-    return pinchVector;
+    return GESTURES.Pinch.vector;
 }
 
 // Get angle beween two pinch points
@@ -483,7 +484,7 @@ float GetGesturePinchAngle(void)
 {
     // NOTE: pinch angle is calculated on two touch points TOUCH_MOVE
 
-    return pinchAngle;
+    return GESTURES.Pinch.angle;
 }
 
 //----------------------------------------------------------------------------------

+ 375 - 368
src/raudio.c

@@ -4,11 +4,11 @@
 *
 *   FEATURES:
 *       - Manage audio device (init/close)
+*       - Manage raw audio context
+*       - Manage mixing channels
 *       - Load and unload audio files
 *       - Format wave data (sample rate, size, channels)
 *       - Play/Stop/Pause/Resume loaded audio
-*       - Manage mixing channels
-*       - Manage raw audio context
 *
 *   CONFIGURATION:
 *
@@ -124,7 +124,15 @@
 // After some math, considering a sampleRate of 48000, a buffer refill rate of 1/60 seconds and a
 // standard double-buffering system, a 4096 samples buffer has been chosen, it should be enough
 // In case of music-stalls, just increase this number
-#define AUDIO_BUFFER_SIZE        4096       // PCM data samples (i.e. 16bit, Mono: 8Kb)
+#if !defined(AUDIO_BUFFER_SIZE)
+    #define AUDIO_BUFFER_SIZE 4096      // PCM data samples (i.e. 16bit, Mono: 8Kb)
+#endif
+
+#define DEVICE_FORMAT       ma_format_f32
+#define DEVICE_CHANNELS     2
+#define DEVICE_SAMPLE_RATE  44100
+
+#define MAX_AUDIO_BUFFER_POOL_CHANNELS 16
 
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
@@ -155,14 +163,74 @@ typedef enum {
 } TraceLogType;
 #endif
 
+// NOTE: Different logic is used when feeding data to the playback device 
+// depending on whether or not data is streamed (Music vs Sound)
+typedef enum { 
+    AUDIO_BUFFER_USAGE_STATIC = 0, 
+    AUDIO_BUFFER_USAGE_STREAM
+} AudioBufferUsage;
+
+// Audio buffer structure
+struct rAudioBuffer {
+    ma_pcm_converter dsp;           // PCM data converter
+
+    float volume;                   // Audio buffer volume
+    float pitch;                    // Audio buffer pitch
+
+    bool playing;                   // Audio buffer state: AUDIO_PLAYING
+    bool paused;                    // Audio buffer state: AUDIO_PAUSED
+    bool looping;                   // Audio buffer looping, always true for AudioStreams
+    int usage;                      // Audio buffer usage mode: STATIC or STREAM
+
+    bool isSubBufferProcessed[2];   // SubBuffer processed (virtual double buffer)
+    unsigned int sizeInFrames;      // Total buffer size in frames
+    unsigned int frameCursorPos;    // Frame cursor position
+    unsigned int totalFramesProcessed;  // Total frames processed in this buffer (required for play timming)
+
+    unsigned char *data;            // Data buffer, on music stream keeps filling
+
+    rAudioBuffer *next;             // Next audio buffer on the list
+    rAudioBuffer *prev;             // Previous audio buffer on the list
+};
+
+#define AudioBuffer rAudioBuffer    // HACK: To avoid CoreAudio (macOS) symbol collision
+
+// Audio data context
+typedef struct AudioData {
+    struct {
+        ma_context context;         // miniaudio context data
+        ma_device device;           // miniaudio device
+        ma_mutex lock;              // miniaudio mutex lock
+        bool isReady;               // Check if audio device is ready
+        float masterVolume;         // Master volume (multiplied on output mixing)
+    } System;
+    struct {
+        AudioBuffer *first;         // Pointer to first AudioBuffer in the list
+        AudioBuffer *last;          // Pointer to last AudioBuffer in the list
+    } Buffer;
+    struct {
+        AudioBuffer *pool[MAX_AUDIO_BUFFER_POOL_CHANNELS];      // Multichannel AudioBuffer pointers pool
+        unsigned int poolCounter;                               // AudioBuffer pointers pool counter
+        unsigned int channels[MAX_AUDIO_BUFFER_POOL_CHANNELS];  // AudioBuffer pool channels
+    } MultiChannel;
+} AudioData;
+
 //----------------------------------------------------------------------------------
 // Global Variables Definition
 //----------------------------------------------------------------------------------
-// ...
+static AudioData AUDIO = { 0 };     // Global CORE context
 
 //----------------------------------------------------------------------------------
 // Module specific Functions Declaration
 //----------------------------------------------------------------------------------
+static void OnLog(ma_context *pContext, ma_device *pDevice, ma_uint32 logLevel, const char *message);
+static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const void *pFramesInput, ma_uint32 frameCount);
+static ma_uint32 OnAudioBufferDSPRead(ma_pcm_converter *pDSP, void *pFramesOut, ma_uint32 frameCount, void *pUserData);
+static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 frameCount, float localVolume);
+
+static void InitAudioBufferPool(void);                  // Initialise the multichannel buffer pool
+static void CloseAudioBufferPool(void);                 // Close the audio buffers pool
+
 #if defined(SUPPORT_FILEFORMAT_WAV)
 static Wave LoadWAV(const char *fileName);              // Load WAV file
 static int SaveWAV(Wave wave, const char *fileName);    // Save wave data as WAV file
@@ -178,73 +246,15 @@ static Wave LoadMP3(const char *fileName);              // Load MP3 file
 #endif
 
 #if defined(RAUDIO_STANDALONE)
-bool IsFileExtension(const char *fileName, const char *ext);    // Check file extension
-void TraceLog(int msgType, const char *text, ...);              // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG)
+bool IsFileExtension(const char *fileName, const char *ext);// Check file extension
+void TraceLog(int msgType, const char *text, ...);      // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG)
 #endif
 
 //----------------------------------------------------------------------------------
-// AudioBuffer Functionality
-//----------------------------------------------------------------------------------
-#define DEVICE_FORMAT       ma_format_f32
-#define DEVICE_CHANNELS     2
-#define DEVICE_SAMPLE_RATE  44100
-
-#define MAX_AUDIO_BUFFER_POOL_CHANNELS 16
-
-typedef enum { AUDIO_BUFFER_USAGE_STATIC = 0, AUDIO_BUFFER_USAGE_STREAM } AudioBufferUsage;
-
-// Audio buffer structure
-// NOTE: Slightly different logic is used when feeding data to the
-// playback device depending on whether or not data is streamed
-struct rAudioBuffer {
-    ma_pcm_converter dsp;   // PCM data converter
-
-    float volume;           // Audio buffer volume
-    float pitch;            // Audio buffer pitch
-
-    bool playing;           // Audio buffer state: AUDIO_PLAYING
-    bool paused;            // Audio buffer state: AUDIO_PAUSED
-    bool looping;           // Audio buffer looping, always true for AudioStreams
-    int usage;              // Audio buffer usage mode: STATIC or STREAM
-
-    bool isSubBufferProcessed[2];       // SubBuffer processed (virtual double buffer)
-    unsigned int frameCursorPos;        // Frame cursor position
-    unsigned int bufferSizeInFrames;    // Total buffer size in frames
-    unsigned int totalFramesProcessed;  // Total frames processed in this buffer (required for play timming)
-
-    unsigned char *buffer;              // Data buffer, on music stream keeps filling
-
-    rAudioBuffer *next;     // Next audio buffer on the list
-    rAudioBuffer *prev;     // Previous audio buffer on the list
-};
-
-#define AudioBuffer rAudioBuffer        // HACK: To avoid CoreAudio (macOS) symbol collision
-
-// Audio buffers are tracked in a linked list
-static AudioBuffer *firstAudioBuffer = NULL;    // Pointer to first AudioBuffer in the list
-static AudioBuffer *lastAudioBuffer = NULL;     // Pointer to last AudioBuffer in the list
-
-// miniaudio global variables
-static ma_context context;                      // miniaudio context data
-static ma_device device;                        // miniaudio device
-static ma_mutex audioLock;                      // miniaudio mutex lock
-static bool isAudioInitialized = false;         // Check if audio device is initialized
-static float masterVolume = 1.0f;               // Master volume (multiplied on output mixing)
-
-// Multi channel playback global variables
-static AudioBuffer *audioBufferPool[MAX_AUDIO_BUFFER_POOL_CHANNELS] = { 0 };         // Multichannel AudioBuffer pointers pool
-static unsigned int audioBufferPoolCounter = 0;                                      // AudioBuffer pointers pool counter
-static unsigned int audioBufferPoolChannels[MAX_AUDIO_BUFFER_POOL_CHANNELS] = { 0 }; // AudioBuffer pool channels
-
-// miniaudio functions declaration
-static void OnLog(ma_context *pContext, ma_device *pDevice, ma_uint32 logLevel, const char *message);
-static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const void *pFramesInput, ma_uint32 frameCount);
-static ma_uint32 OnAudioBufferDSPRead(ma_pcm_converter *pDSP, void *pFramesOut, ma_uint32 frameCount, void *pUserData);
-static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 frameCount, float localVolume);
-
 // AudioBuffer management functions declaration
 // NOTE: Those functions are not exposed by raylib... for the moment
-AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 bufferSizeInFrames, int usage);
+//----------------------------------------------------------------------------------
+AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 sizeInFrames, int usage);
 void CloseAudioBuffer(AudioBuffer *buffer);
 bool IsAudioBufferPlaying(AudioBuffer *buffer);
 void PlayAudioBuffer(AudioBuffer *buffer);
@@ -256,248 +266,20 @@ void SetAudioBufferPitch(AudioBuffer *buffer, float pitch);
 void TrackAudioBuffer(AudioBuffer *buffer);
 void UntrackAudioBuffer(AudioBuffer *buffer);
 
-
-//----------------------------------------------------------------------------------
-// miniaudio functions definitions
-//----------------------------------------------------------------------------------
-
-// Log callback function
-static void OnLog(ma_context *pContext, ma_device *pDevice, ma_uint32 logLevel, const char *message)
-{
-    (void)pContext;
-    (void)pDevice;
-
-    TraceLog(LOG_ERROR, message);   // All log messages from miniaudio are errors
-}
-
-// Sending audio data to device callback function
-// NOTE: All the mixing takes place here
-static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const void *pFramesInput, ma_uint32 frameCount)
-{
-    (void)pDevice;
-
-    // Mixing is basically just an accumulation, we need to initialize the output buffer to 0
-    memset(pFramesOut, 0, frameCount*pDevice->playback.channels*ma_get_bytes_per_sample(pDevice->playback.format));
-
-    // Using a mutex here for thread-safety which makes things not real-time
-    // This is unlikely to be necessary for this project, but may want to consider how you might want to avoid this
-    ma_mutex_lock(&audioLock);
-    {
-        for (AudioBuffer *audioBuffer = firstAudioBuffer; audioBuffer != NULL; audioBuffer = audioBuffer->next)
-        {
-            // Ignore stopped or paused sounds
-            if (!audioBuffer->playing || audioBuffer->paused) continue;
-
-            ma_uint32 framesRead = 0;
-
-            while (1)
-            {
-                if (framesRead > frameCount)
-                {
-                    TraceLog(LOG_DEBUG, "Mixed too many frames from audio buffer");
-                    break;
-                }
-
-                if (framesRead == frameCount) break;
-
-                // Just read as much data as we can from the stream
-                ma_uint32 framesToRead = (frameCount - framesRead);
-
-                while (framesToRead > 0)
-                {
-                    float tempBuffer[1024]; // 512 frames for stereo
-
-                    ma_uint32 framesToReadRightNow = framesToRead;
-                    if (framesToReadRightNow > sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS)
-                    {
-                        framesToReadRightNow = sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS;
-                    }
-
-                    ma_uint32 framesJustRead = (ma_uint32)ma_pcm_converter_read(&audioBuffer->dsp, tempBuffer, framesToReadRightNow);
-                    if (framesJustRead > 0)
-                    {
-                        float *framesOut = (float *)pFramesOut + (framesRead*device.playback.channels);
-                        float *framesIn  = tempBuffer;
-
-                        MixAudioFrames(framesOut, framesIn, framesJustRead, audioBuffer->volume);
-
-                        framesToRead -= framesJustRead;
-                        framesRead += framesJustRead;
-                    }
-                    
-                    if (!audioBuffer->playing)
-                    {
-                        framesRead = frameCount;
-                        break;
-                    }
-
-                    // If we weren't able to read all the frames we requested, break
-                    if (framesJustRead < framesToReadRightNow)
-                    {
-                        if (!audioBuffer->looping)
-                        {
-                            StopAudioBuffer(audioBuffer);
-                            break;
-                        }
-                        else
-                        {
-                            // Should never get here, but just for safety,
-                            // move the cursor position back to the start and continue the loop
-                            audioBuffer->frameCursorPos = 0;
-                            continue;
-                        }
-                    }
-                }
-
-                // If for some reason we weren't able to read every frame we'll need to break from the loop
-                // Not doing this could theoretically put us into an infinite loop
-                if (framesToRead > 0) break;
-            }
-        }
-    }
-
-    ma_mutex_unlock(&audioLock);
-}
-
-// DSP read from audio buffer callback function
-static ma_uint32 OnAudioBufferDSPRead(ma_pcm_converter *pDSP, void *pFramesOut, ma_uint32 frameCount, void *pUserData)
-{
-    AudioBuffer *audioBuffer = (AudioBuffer *)pUserData;
-
-    ma_uint32 subBufferSizeInFrames = (audioBuffer->bufferSizeInFrames > 1)? audioBuffer->bufferSizeInFrames/2 : audioBuffer->bufferSizeInFrames;
-    ma_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos/subBufferSizeInFrames;
-
-    if (currentSubBufferIndex > 1)
-    {
-        TraceLog(LOG_DEBUG, "Frame cursor position moved too far forward in audio stream");
-        return 0;
-    }
-
-    // Another thread can update the processed state of buffers so
-    // we just take a copy here to try and avoid potential synchronization problems
-    bool isSubBufferProcessed[2];
-    isSubBufferProcessed[0] = audioBuffer->isSubBufferProcessed[0];
-    isSubBufferProcessed[1] = audioBuffer->isSubBufferProcessed[1];
-
-    ma_uint32 frameSizeInBytes = ma_get_bytes_per_sample(audioBuffer->dsp.formatConverterIn.config.formatIn)*audioBuffer->dsp.formatConverterIn.config.channels;
-
-    // Fill out every frame until we find a buffer that's marked as processed. Then fill the remainder with 0
-    ma_uint32 framesRead = 0;
-    while (1)
-    {
-        // We break from this loop differently depending on the buffer's usage
-        //  - For static buffers, we simply fill as much data as we can
-        //  - For streaming buffers we only fill the halves of the buffer that are processed
-        //    Unprocessed halves must keep their audio data in-tact
-        if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC)
-        {
-            if (framesRead >= frameCount) break;
-        }
-        else
-        {
-            if (isSubBufferProcessed[currentSubBufferIndex]) break;
-        }
-
-        ma_uint32 totalFramesRemaining = (frameCount - framesRead);
-        if (totalFramesRemaining == 0) break;
-
-        ma_uint32 framesRemainingInOutputBuffer;
-        if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC)
-        {
-            framesRemainingInOutputBuffer = audioBuffer->bufferSizeInFrames - audioBuffer->frameCursorPos;
-        }
-        else
-        {
-            ma_uint32 firstFrameIndexOfThisSubBuffer = subBufferSizeInFrames*currentSubBufferIndex;
-            framesRemainingInOutputBuffer = subBufferSizeInFrames - (audioBuffer->frameCursorPos - firstFrameIndexOfThisSubBuffer);
-        }
-
-        ma_uint32 framesToRead = totalFramesRemaining;
-        if (framesToRead > framesRemainingInOutputBuffer) framesToRead = framesRemainingInOutputBuffer;
-
-        memcpy((unsigned char *)pFramesOut + (framesRead*frameSizeInBytes), audioBuffer->buffer + (audioBuffer->frameCursorPos*frameSizeInBytes), framesToRead*frameSizeInBytes);
-        audioBuffer->frameCursorPos = (audioBuffer->frameCursorPos + framesToRead)%audioBuffer->bufferSizeInFrames;
-        framesRead += framesToRead;
-
-        // If we've read to the end of the buffer, mark it as processed
-        if (framesToRead == framesRemainingInOutputBuffer)
-        {
-            audioBuffer->isSubBufferProcessed[currentSubBufferIndex] = true;
-            isSubBufferProcessed[currentSubBufferIndex] = true;
-
-            currentSubBufferIndex = (currentSubBufferIndex + 1)%2;
-
-            // We need to break from this loop if we're not looping
-            if (!audioBuffer->looping)
-            {
-                StopAudioBuffer(audioBuffer);
-                break;
-            }
-        }
-    }
-
-    // Zero-fill excess
-    ma_uint32 totalFramesRemaining = (frameCount - framesRead);
-    if (totalFramesRemaining > 0)
-    {
-        memset((unsigned char *)pFramesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes);
-
-        // For static buffers we can fill the remaining frames with silence for safety, but we don't want
-        // to report those frames as "read". The reason for this is that the caller uses the return value
-        // to know whether or not a non-looping sound has finished playback.
-        if (audioBuffer->usage != AUDIO_BUFFER_USAGE_STATIC) framesRead += totalFramesRemaining;
-    }
-
-    return framesRead;
-}
-
-// This is the main mixing function. Mixing is pretty simple in this project - it's just an accumulation.
-// NOTE: framesOut is both an input and an output. It will be initially filled with zeros outside of this function.
-static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 frameCount, float localVolume)
-{
-    for (ma_uint32 iFrame = 0; iFrame < frameCount; ++iFrame)
-    {
-        for (ma_uint32 iChannel = 0; iChannel < device.playback.channels; ++iChannel)
-        {
-            float *frameOut = framesOut + (iFrame*device.playback.channels);
-            const float *frameIn  = framesIn  + (iFrame*device.playback.channels);
-
-            frameOut[iChannel] += (frameIn[iChannel]*masterVolume*localVolume);
-        }
-    }
-}
-
-// Initialise the multichannel buffer pool
-static void InitAudioBufferPool()
-{
-    // Dummy buffers
-    for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++)
-    {
-        audioBufferPool[i] = InitAudioBuffer(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, 0, AUDIO_BUFFER_USAGE_STATIC);
-    }
-}
-
-// Close the audio buffers pool
-static void CloseAudioBufferPool()
-{
-    for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) 
-    {
-        RL_FREE(audioBufferPool[i]->buffer);
-        RL_FREE(audioBufferPool[i]);
-    }
-}
-
 //----------------------------------------------------------------------------------
 // Module Functions Definition - Audio Device initialization and Closing
 //----------------------------------------------------------------------------------
 // Initialize audio device
 void InitAudioDevice(void)
 {
+    // TODO: Load AUDIO context memory dynamically?
+    AUDIO.System.masterVolume = 1.0f;
+    
     // Init audio context
-    ma_context_config contextConfig = ma_context_config_init();
-    contextConfig.logCallback = OnLog;
+    ma_context_config ctxConfig = ma_context_config_init();
+    ctxConfig.logCallback = OnLog;
 
-    ma_result result = ma_context_init(NULL, 0, &contextConfig, &context);
+    ma_result result = ma_context_init(NULL, 0, &ctxConfig, &AUDIO.System.context);
     if (result != MA_SUCCESS)
     {
         TraceLog(LOG_ERROR, "Failed to initialize audio context");
@@ -507,78 +289,78 @@ void InitAudioDevice(void)
     // Init audio device
     // NOTE: Using the default device. Format is floating point because it simplifies mixing.
     ma_device_config config = ma_device_config_init(ma_device_type_playback);
-    config.playback.pDeviceID = NULL;  // NULL for the default playback device.
+    config.playback.pDeviceID = NULL;  // NULL for the default playback AUDIO.System.device.
     config.playback.format    = DEVICE_FORMAT;
     config.playback.channels  = DEVICE_CHANNELS;
-    config.capture.pDeviceID  = NULL;  // NULL for the default capture device.
+    config.capture.pDeviceID  = NULL;  // NULL for the default capture AUDIO.System.device.
     config.capture.format     = ma_format_s16;
     config.capture.channels   = 1;
     config.sampleRate         = DEVICE_SAMPLE_RATE;
     config.dataCallback       = OnSendAudioDataToDevice;
     config.pUserData          = NULL;
 
-    result = ma_device_init(&context, &config, &device);
+    result = ma_device_init(&AUDIO.System.context, &config, &AUDIO.System.device);
     if (result != MA_SUCCESS)
     {
-        TraceLog(LOG_ERROR, "Failed to initialize audio playback device");
-        ma_context_uninit(&context);
+        TraceLog(LOG_ERROR, "Failed to initialize audio playback AUDIO.System.device");
+        ma_context_uninit(&AUDIO.System.context);
         return;
     }
 
     // Keep the device running the whole time. May want to consider doing something a bit smarter and only have the device running
     // while there's at least one sound being played.
-    result = ma_device_start(&device);
+    result = ma_device_start(&AUDIO.System.device);
     if (result != MA_SUCCESS)
     {
-        TraceLog(LOG_ERROR, "Failed to start audio playback device");
-        ma_device_uninit(&device);
-        ma_context_uninit(&context);
+        TraceLog(LOG_ERROR, "Failed to start audio playback AUDIO.System.device");
+        ma_device_uninit(&AUDIO.System.device);
+        ma_context_uninit(&AUDIO.System.context);
         return;
     }
 
     // Mixing happens on a seperate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may
     // want to look at something a bit smarter later on to keep everything real-time, if that's necessary.
-    if (ma_mutex_init(&context, &audioLock) != MA_SUCCESS)
+    if (ma_mutex_init(&AUDIO.System.context, &AUDIO.System.lock) != MA_SUCCESS)
     {
         TraceLog(LOG_ERROR, "Failed to create mutex for audio mixing");
-        ma_device_uninit(&device);
-        ma_context_uninit(&context);
+        ma_device_uninit(&AUDIO.System.device);
+        ma_context_uninit(&AUDIO.System.context);
         return;
     }
 
     TraceLog(LOG_INFO, "Audio device initialized successfully");
-    TraceLog(LOG_INFO, "Audio backend: miniaudio / %s", ma_get_backend_name(context.backend));
-    TraceLog(LOG_INFO, "Audio format: %s -> %s", ma_get_format_name(device.playback.format), ma_get_format_name(device.playback.internalFormat));
-    TraceLog(LOG_INFO, "Audio channels: %d -> %d", device.playback.channels, device.playback.internalChannels);
-    TraceLog(LOG_INFO, "Audio sample rate: %d -> %d", device.sampleRate, device.playback.internalSampleRate);
-    TraceLog(LOG_INFO, "Audio buffer size: %d", device.playback.internalBufferSizeInFrames);
+    TraceLog(LOG_INFO, "Audio backend: miniaudio / %s", ma_get_backend_name(AUDIO.System.context.backend));
+    TraceLog(LOG_INFO, "Audio format: %s -> %s", ma_get_format_name(AUDIO.System.device.playback.format), ma_get_format_name(AUDIO.System.device.playback.internalFormat));
+    TraceLog(LOG_INFO, "Audio channels: %d -> %d", AUDIO.System.device.playback.channels, AUDIO.System.device.playback.internalChannels);
+    TraceLog(LOG_INFO, "Audio sample rate: %d -> %d", AUDIO.System.device.sampleRate, AUDIO.System.device.playback.internalSampleRate);
+    TraceLog(LOG_INFO, "Audio buffer size: %d", AUDIO.System.device.playback.internalBufferSizeInFrames);
 
     InitAudioBufferPool();
     TraceLog(LOG_INFO, "Audio multichannel pool size: %i", MAX_AUDIO_BUFFER_POOL_CHANNELS);
 
-    isAudioInitialized = true;
+    AUDIO.System.isReady = true;
 }
 
 // Close the audio device for all contexts
 void CloseAudioDevice(void)
 {
-    if (isAudioInitialized)
+    if (AUDIO.System.isReady)
     {
-        ma_mutex_uninit(&audioLock);
-        ma_device_uninit(&device);
-        ma_context_uninit(&context);
+        ma_mutex_uninit(&AUDIO.System.lock);
+        ma_device_uninit(&AUDIO.System.device);
+        ma_context_uninit(&AUDIO.System.context);
 
         CloseAudioBufferPool();
 
-        TraceLog(LOG_INFO, "Audio device closed successfully");
+        TraceLog(LOG_INFO, "Audio AUDIO.System.device closed successfully");
     }
-    else TraceLog(LOG_WARNING, "Could not close audio device because it is not currently initialized");
+    else TraceLog(LOG_WARNING, "Could not close audio AUDIO.System.device because it is not currently initialized");
 }
 
 // Check if device has been initialized successfully
 bool IsAudioDeviceReady(void)
 {
-    return isAudioInitialized;
+    return AUDIO.System.isReady;
 }
 
 // Set master volume (listener)
@@ -587,7 +369,7 @@ void SetMasterVolume(float volume)
     if (volume < 0.0f) volume = 0.0f;
     else if (volume > 1.0f) volume = 1.0f;
 
-    masterVolume = volume;
+    AUDIO.System.masterVolume = volume;
 }
 
 //----------------------------------------------------------------------------------
@@ -595,7 +377,7 @@ void SetMasterVolume(float volume)
 //----------------------------------------------------------------------------------
 
 // Initialize a new audio buffer (filled with silence)
-AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 bufferSizeInFrames, int usage)
+AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 sizeInFrames, int usage)
 {
     AudioBuffer *audioBuffer = (AudioBuffer *)RL_CALLOC(1, sizeof(AudioBuffer));
 
@@ -605,7 +387,7 @@ AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam
         return NULL;
     }
     
-    audioBuffer->buffer = RL_CALLOC(bufferSizeInFrames*channels*ma_get_bytes_per_sample(format), 1);
+    audioBuffer->data = RL_CALLOC(sizeInFrames*channels*ma_get_bytes_per_sample(format), 1);
 
     // Audio data runs through a format converter
     ma_pcm_converter_config dspConfig;
@@ -637,7 +419,7 @@ AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam
     audioBuffer->looping = false;
     audioBuffer->usage = usage;
     audioBuffer->frameCursorPos = 0;
-    audioBuffer->bufferSizeInFrames = bufferSizeInFrames;
+    audioBuffer->sizeInFrames = sizeInFrames;
 
     // Buffers should be marked as processed by default so that a call to
     // UpdateAudioStream() immediately after initialization works correctly
@@ -656,7 +438,7 @@ void CloseAudioBuffer(AudioBuffer *buffer)
     if (buffer != NULL)
     {
         UntrackAudioBuffer(buffer);
-        RL_FREE(buffer->buffer);
+        RL_FREE(buffer->data);
         RL_FREE(buffer);
     }
     else TraceLog(LOG_ERROR, "CloseAudioBuffer() : No audio buffer");
@@ -748,35 +530,35 @@ void SetAudioBufferPitch(AudioBuffer *buffer, float pitch)
 // Track audio buffer to linked list next position
 void TrackAudioBuffer(AudioBuffer *buffer)
 {
-    ma_mutex_lock(&audioLock);
+    ma_mutex_lock(&AUDIO.System.lock);
     {
-        if (firstAudioBuffer == NULL) firstAudioBuffer = buffer;
+        if (AUDIO.Buffer.first == NULL) AUDIO.Buffer.first = buffer;
         else
         {
-            lastAudioBuffer->next = buffer;
-            buffer->prev = lastAudioBuffer;
+            AUDIO.Buffer.last->next = buffer;
+            buffer->prev = AUDIO.Buffer.last;
         }
 
-        lastAudioBuffer = buffer;
+        AUDIO.Buffer.last = buffer;
     }
-    ma_mutex_unlock(&audioLock);
+    ma_mutex_unlock(&AUDIO.System.lock);
 }
 
 // Untrack audio buffer from linked list
 void UntrackAudioBuffer(AudioBuffer *buffer)
 {
-    ma_mutex_lock(&audioLock);
+    ma_mutex_lock(&AUDIO.System.lock);
     {
-        if (buffer->prev == NULL) firstAudioBuffer = buffer->next;
+        if (buffer->prev == NULL) AUDIO.Buffer.first = buffer->next;
         else buffer->prev->next = buffer->next;
 
-        if (buffer->next == NULL) lastAudioBuffer = buffer->prev;
+        if (buffer->next == NULL) AUDIO.Buffer.last = buffer->prev;
         else buffer->next->prev = buffer->prev;
 
         buffer->prev = NULL;
         buffer->next = NULL;
     }
-    ma_mutex_unlock(&audioLock);
+    ma_mutex_unlock(&AUDIO.System.lock);
 }
 
 //----------------------------------------------------------------------------------
@@ -829,7 +611,7 @@ Sound LoadSoundFromWave(Wave wave)
     {
         // When using miniaudio we need to do our own mixing.
         // To simplify this we need convert the format of each sound to be consistent with
-        // the format used to open the playback device. We can do this two ways:
+        // the format used to open the playback AUDIO.System.device. We can do this two ways:
         //
         //   1) Convert the whole sound in one go at load time (here).
         //   2) Convert the audio data in chunks at mixing time.
@@ -845,7 +627,7 @@ Sound LoadSoundFromWave(Wave wave)
         AudioBuffer *audioBuffer = InitAudioBuffer(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, frameCount, AUDIO_BUFFER_USAGE_STATIC);
         if (audioBuffer == NULL) TraceLog(LOG_WARNING, "LoadSoundFromWave() : Failed to create audio buffer");
 
-        frameCount = (ma_uint32)ma_convert_frames(audioBuffer->buffer, audioBuffer->dsp.formatConverterIn.config.formatIn, audioBuffer->dsp.formatConverterIn.config.channels, audioBuffer->dsp.src.config.sampleRateIn, wave.data, formatIn, wave.channels, wave.sampleRate, frameCountIn);
+        frameCount = (ma_uint32)ma_convert_frames(audioBuffer->data, audioBuffer->dsp.formatConverterIn.config.formatIn, audioBuffer->dsp.formatConverterIn.config.channels, audioBuffer->dsp.src.config.sampleRateIn, wave.data, formatIn, wave.channels, wave.sampleRate, frameCountIn);
         if (frameCount == 0) TraceLog(LOG_WARNING, "LoadSoundFromWave() : Format conversion failed");
 
         sound.sampleCount = frameCount*DEVICE_CHANNELS;
@@ -884,7 +666,7 @@ void UpdateSound(Sound sound, const void *data, int samplesCount)
         StopAudioBuffer(audioBuffer);
 
         // TODO: May want to lock/unlock this since this data buffer is read at mixing time
-        memcpy(audioBuffer->buffer, data, samplesCount*audioBuffer->dsp.formatConverterIn.config.channels*ma_get_bytes_per_sample(audioBuffer->dsp.formatConverterIn.config.formatIn));
+        memcpy(audioBuffer->data, data, samplesCount*audioBuffer->dsp.formatConverterIn.config.channels*ma_get_bytes_per_sample(audioBuffer->dsp.formatConverterIn.config.formatIn));
     }
     else TraceLog(LOG_ERROR, "UpdateSound() : Invalid sound - no audio buffer");
 }
@@ -973,13 +755,13 @@ void PlaySoundMulti(Sound sound)
     // find the first non playing pool entry
     for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++)
     {
-        if (audioBufferPoolChannels[i] > oldAge)
+        if (AUDIO.MultiChannel.channels[i] > oldAge)
         {
-            oldAge = audioBufferPoolChannels[i];
+            oldAge = AUDIO.MultiChannel.channels[i];
             oldIndex = i;
         }
 
-        if (!IsAudioBufferPlaying(audioBufferPool[i]))
+        if (!IsAudioBufferPlaying(AUDIO.MultiChannel.pool[i]))
         {
             index = i;
             break;
@@ -989,7 +771,7 @@ void PlaySoundMulti(Sound sound)
     // If no none playing pool members can be index choose the oldest
     if (index == -1)
     {
-        TraceLog(LOG_WARNING,"pool age %i ended a sound early no room in buffer pool", audioBufferPoolCounter);
+        TraceLog(LOG_WARNING,"pool age %i ended a sound early no room in buffer pool", AUDIO.MultiChannel.poolCounter);
 
         if (oldIndex == -1)
         {
@@ -1002,32 +784,32 @@ void PlaySoundMulti(Sound sound)
         index = oldIndex;
 
         // Just in case...
-        StopAudioBuffer(audioBufferPool[index]);
+        StopAudioBuffer(AUDIO.MultiChannel.pool[index]);
     }
 
     // Experimentally mutex lock doesn't seem to be needed this makes sense
-    // as audioBufferPool[index] isn't playing and the only stuff we're copying
+    // as AUDIO.MultiChannel.pool[index] isn't playing and the only stuff we're copying
     // shouldn't be changing...
 
-    audioBufferPoolChannels[index] = audioBufferPoolCounter;
-    audioBufferPoolCounter++;
+    AUDIO.MultiChannel.channels[index] = AUDIO.MultiChannel.poolCounter;
+    AUDIO.MultiChannel.poolCounter++;
 
-    audioBufferPool[index]->volume = sound.stream.buffer->volume;
-    audioBufferPool[index]->pitch = sound.stream.buffer->pitch;
-    audioBufferPool[index]->looping = sound.stream.buffer->looping;
-    audioBufferPool[index]->usage = sound.stream.buffer->usage;
-    audioBufferPool[index]->isSubBufferProcessed[0] = false;
-    audioBufferPool[index]->isSubBufferProcessed[1] = false;
-    audioBufferPool[index]->bufferSizeInFrames = sound.stream.buffer->bufferSizeInFrames;
-    audioBufferPool[index]->buffer = sound.stream.buffer->buffer;
+    AUDIO.MultiChannel.pool[index]->volume = sound.stream.buffer->volume;
+    AUDIO.MultiChannel.pool[index]->pitch = sound.stream.buffer->pitch;
+    AUDIO.MultiChannel.pool[index]->looping = sound.stream.buffer->looping;
+    AUDIO.MultiChannel.pool[index]->usage = sound.stream.buffer->usage;
+    AUDIO.MultiChannel.pool[index]->isSubBufferProcessed[0] = false;
+    AUDIO.MultiChannel.pool[index]->isSubBufferProcessed[1] = false;
+    AUDIO.MultiChannel.pool[index]->sizeInFrames = sound.stream.buffer->sizeInFrames;
+    AUDIO.MultiChannel.pool[index]->data = sound.stream.buffer->data;
 
-    PlayAudioBuffer(audioBufferPool[index]);
+    PlayAudioBuffer(AUDIO.MultiChannel.pool[index]);
 }
 
 // Stop any sound played with PlaySoundMulti()
 void StopSoundMulti(void)
 {
-    for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) StopAudioBuffer(audioBufferPool[i]);
+    for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) StopAudioBuffer(AUDIO.MultiChannel.pool[i]);
 }
 
 // Get number of sounds playing in the multichannel buffer pool
@@ -1037,7 +819,7 @@ int GetSoundsPlaying(void)
 
     for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++)
     {
-        if (IsAudioBufferPlaying(audioBufferPool[i])) counter++;
+        if (IsAudioBufferPlaying(AUDIO.MultiChannel.pool[i])) counter++;
     }
 
     return counter;
@@ -1243,7 +1025,7 @@ Music LoadMusicStream(const char *fileName)
 
         int result = jar_xm_create_context_from_file(&ctxXm, 48000, fileName);
 
-        if (result == 0)    // XM context created successfully
+        if (result == 0)    // XM AUDIO.System.context created successfully
         {
             music.ctxType = MUSIC_MODULE_XM;
             jar_xm_set_max_loop_count(ctxXm, 0);    // Set infinite number of loops
@@ -1374,7 +1156,6 @@ void StopMusicStream(Music music)
 {
     StopAudioStream(music.stream);
 
-    // Restart music context
     switch (music.ctxType)
     {
 #if defined(SUPPORT_FILEFORMAT_OGG)
@@ -1401,7 +1182,7 @@ void UpdateMusicStream(Music music)
 {
     bool streamEnding = false;
 
-    unsigned int subBufferSizeInFrames = music.stream.buffer->bufferSizeInFrames/2;
+    unsigned int subBufferSizeInFrames = music.stream.buffer->sizeInFrames/2;
 
     // NOTE: Using dynamic allocation because it could require more than 16KB
     void *pcm = RL_CALLOC(subBufferSizeInFrames*music.stream.channels*music.stream.sampleSize/8, 1);
@@ -1559,7 +1340,7 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un
     ma_format formatIn = ((stream.sampleSize == 8)? ma_format_u8 : ((stream.sampleSize == 16)? ma_format_s16 : ma_format_f32));
 
     // The size of a streaming buffer must be at least double the size of a period
-    unsigned int periodSize = device.playback.internalBufferSizeInFrames/device.playback.internalPeriods;
+    unsigned int periodSize = AUDIO.System.device.playback.internalBufferSizeInFrames/AUDIO.System.device.playback.internalPeriods;
     unsigned int subBufferSize = AUDIO_BUFFER_SIZE;
 
     if (subBufferSize < periodSize) subBufferSize = periodSize;
@@ -1610,8 +1391,8 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
                 subBufferToUpdate = (audioBuffer->isSubBufferProcessed[0])? 0 : 1;
             }
 
-            ma_uint32 subBufferSizeInFrames = audioBuffer->bufferSizeInFrames/2;
-            unsigned char *subBuffer = audioBuffer->buffer + ((subBufferSizeInFrames*stream.channels*(stream.sampleSize/8))*subBufferToUpdate);
+            ma_uint32 subBufferSizeInFrames = audioBuffer->sizeInFrames/2;
+            unsigned char *subBuffer = audioBuffer->data + ((subBufferSizeInFrames*stream.channels*(stream.sampleSize/8))*subBufferToUpdate);
 
             // TODO: Get total frames processed on this buffer... DOES NOT WORK.
             audioBuffer->totalFramesProcessed += subBufferSizeInFrames;
@@ -1697,6 +1478,232 @@ void SetAudioStreamPitch(AudioStream stream, float pitch)
 // Module specific Functions Definition
 //----------------------------------------------------------------------------------
 
+// Log callback function
+static void OnLog(ma_context *pContext, ma_device *pDevice, ma_uint32 logLevel, const char *message)
+{
+    (void)pContext;
+    (void)pDevice;
+
+    TraceLog(LOG_ERROR, message);   // All log messages from miniaudio are errors
+}
+
+// Sending audio data to device callback function
+// NOTE: All the mixing takes place here
+static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const void *pFramesInput, ma_uint32 frameCount)
+{
+    (void)pDevice;
+
+    // Mixing is basically just an accumulation, we need to initialize the output buffer to 0
+    memset(pFramesOut, 0, frameCount*pDevice->playback.channels*ma_get_bytes_per_sample(pDevice->playback.format));
+
+    // Using a mutex here for thread-safety which makes things not real-time
+    // This is unlikely to be necessary for this project, but may want to consider how you might want to avoid this
+    ma_mutex_lock(&AUDIO.System.lock);
+    {
+        for (AudioBuffer *audioBuffer = AUDIO.Buffer.first; audioBuffer != NULL; audioBuffer = audioBuffer->next)
+        {
+            // Ignore stopped or paused sounds
+            if (!audioBuffer->playing || audioBuffer->paused) continue;
+
+            ma_uint32 framesRead = 0;
+
+            while (1)
+            {
+                if (framesRead > frameCount)
+                {
+                    TraceLog(LOG_DEBUG, "Mixed too many frames from audio buffer");
+                    break;
+                }
+
+                if (framesRead == frameCount) break;
+
+                // Just read as much data as we can from the stream
+                ma_uint32 framesToRead = (frameCount - framesRead);
+
+                while (framesToRead > 0)
+                {
+                    float tempBuffer[1024]; // 512 frames for stereo
+
+                    ma_uint32 framesToReadRightNow = framesToRead;
+                    if (framesToReadRightNow > sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS)
+                    {
+                        framesToReadRightNow = sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS;
+                    }
+
+                    ma_uint32 framesJustRead = (ma_uint32)ma_pcm_converter_read(&audioBuffer->dsp, tempBuffer, framesToReadRightNow);
+                    if (framesJustRead > 0)
+                    {
+                        float *framesOut = (float *)pFramesOut + (framesRead*AUDIO.System.device.playback.channels);
+                        float *framesIn  = tempBuffer;
+
+                        MixAudioFrames(framesOut, framesIn, framesJustRead, audioBuffer->volume);
+
+                        framesToRead -= framesJustRead;
+                        framesRead += framesJustRead;
+                    }
+                    
+                    if (!audioBuffer->playing)
+                    {
+                        framesRead = frameCount;
+                        break;
+                    }
+
+                    // If we weren't able to read all the frames we requested, break
+                    if (framesJustRead < framesToReadRightNow)
+                    {
+                        if (!audioBuffer->looping)
+                        {
+                            StopAudioBuffer(audioBuffer);
+                            break;
+                        }
+                        else
+                        {
+                            // Should never get here, but just for safety,
+                            // move the cursor position back to the start and continue the loop
+                            audioBuffer->frameCursorPos = 0;
+                            continue;
+                        }
+                    }
+                }
+
+                // If for some reason we weren't able to read every frame we'll need to break from the loop
+                // Not doing this could theoretically put us into an infinite loop
+                if (framesToRead > 0) break;
+            }
+        }
+    }
+
+    ma_mutex_unlock(&AUDIO.System.lock);
+}
+
+// DSP read from audio buffer callback function
+static ma_uint32 OnAudioBufferDSPRead(ma_pcm_converter *pDSP, void *pFramesOut, ma_uint32 frameCount, void *pUserData)
+{
+    AudioBuffer *audioBuffer = (AudioBuffer *)pUserData;
+
+    ma_uint32 subBufferSizeInFrames = (audioBuffer->sizeInFrames > 1)? audioBuffer->sizeInFrames/2 : audioBuffer->sizeInFrames;
+    ma_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos/subBufferSizeInFrames;
+
+    if (currentSubBufferIndex > 1)
+    {
+        TraceLog(LOG_DEBUG, "Frame cursor position moved too far forward in audio stream");
+        return 0;
+    }
+
+    // Another thread can update the processed state of buffers so
+    // we just take a copy here to try and avoid potential synchronization problems
+    bool isSubBufferProcessed[2];
+    isSubBufferProcessed[0] = audioBuffer->isSubBufferProcessed[0];
+    isSubBufferProcessed[1] = audioBuffer->isSubBufferProcessed[1];
+
+    ma_uint32 frameSizeInBytes = ma_get_bytes_per_sample(audioBuffer->dsp.formatConverterIn.config.formatIn)*audioBuffer->dsp.formatConverterIn.config.channels;
+
+    // Fill out every frame until we find a buffer that's marked as processed. Then fill the remainder with 0
+    ma_uint32 framesRead = 0;
+    while (1)
+    {
+        // We break from this loop differently depending on the buffer's usage
+        //  - For static buffers, we simply fill as much data as we can
+        //  - For streaming buffers we only fill the halves of the buffer that are processed
+        //    Unprocessed halves must keep their audio data in-tact
+        if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC)
+        {
+            if (framesRead >= frameCount) break;
+        }
+        else
+        {
+            if (isSubBufferProcessed[currentSubBufferIndex]) break;
+        }
+
+        ma_uint32 totalFramesRemaining = (frameCount - framesRead);
+        if (totalFramesRemaining == 0) break;
+
+        ma_uint32 framesRemainingInOutputBuffer;
+        if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC)
+        {
+            framesRemainingInOutputBuffer = audioBuffer->sizeInFrames - audioBuffer->frameCursorPos;
+        }
+        else
+        {
+            ma_uint32 firstFrameIndexOfThisSubBuffer = subBufferSizeInFrames*currentSubBufferIndex;
+            framesRemainingInOutputBuffer = subBufferSizeInFrames - (audioBuffer->frameCursorPos - firstFrameIndexOfThisSubBuffer);
+        }
+
+        ma_uint32 framesToRead = totalFramesRemaining;
+        if (framesToRead > framesRemainingInOutputBuffer) framesToRead = framesRemainingInOutputBuffer;
+
+        memcpy((unsigned char *)pFramesOut + (framesRead*frameSizeInBytes), audioBuffer->data + (audioBuffer->frameCursorPos*frameSizeInBytes), framesToRead*frameSizeInBytes);
+        audioBuffer->frameCursorPos = (audioBuffer->frameCursorPos + framesToRead)%audioBuffer->sizeInFrames;
+        framesRead += framesToRead;
+
+        // If we've read to the end of the buffer, mark it as processed
+        if (framesToRead == framesRemainingInOutputBuffer)
+        {
+            audioBuffer->isSubBufferProcessed[currentSubBufferIndex] = true;
+            isSubBufferProcessed[currentSubBufferIndex] = true;
+
+            currentSubBufferIndex = (currentSubBufferIndex + 1)%2;
+
+            // We need to break from this loop if we're not looping
+            if (!audioBuffer->looping)
+            {
+                StopAudioBuffer(audioBuffer);
+                break;
+            }
+        }
+    }
+
+    // Zero-fill excess
+    ma_uint32 totalFramesRemaining = (frameCount - framesRead);
+    if (totalFramesRemaining > 0)
+    {
+        memset((unsigned char *)pFramesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes);
+
+        // For static buffers we can fill the remaining frames with silence for safety, but we don't want
+        // to report those frames as "read". The reason for this is that the caller uses the return value
+        // to know whether or not a non-looping sound has finished playback.
+        if (audioBuffer->usage != AUDIO_BUFFER_USAGE_STATIC) framesRead += totalFramesRemaining;
+    }
+
+    return framesRead;
+}
+
+// This is the main mixing function. Mixing is pretty simple in this project - it's just an accumulation.
+// NOTE: framesOut is both an input and an output. It will be initially filled with zeros outside of this function.
+static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 frameCount, float localVolume)
+{
+    for (ma_uint32 iFrame = 0; iFrame < frameCount; ++iFrame)
+    {
+        for (ma_uint32 iChannel = 0; iChannel < AUDIO.System.device.playback.channels; ++iChannel)
+        {
+            float *frameOut = framesOut + (iFrame*AUDIO.System.device.playback.channels);
+            const float *frameIn  = framesIn  + (iFrame*AUDIO.System.device.playback.channels);
+
+            frameOut[iChannel] += (frameIn[iChannel]*AUDIO.System.masterVolume*localVolume);
+        }
+    }
+}
+
+// Initialise the multichannel buffer pool
+static void InitAudioBufferPool(void)
+{
+    // Dummy buffers
+    for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++)
+    {
+        AUDIO.MultiChannel.pool[i] = InitAudioBuffer(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, 0, AUDIO_BUFFER_USAGE_STATIC);
+    }
+}
+
+// Close the audio buffers pool
+static void CloseAudioBufferPool(void)
+{
+    for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) 
+    {
+        RL_FREE(AUDIO.MultiChannel.pool[i]->data);
+        RL_FREE(AUDIO.MultiChannel.pool[i]);
+    }
+}
+
 #if defined(SUPPORT_FILEFORMAT_WAV)
 // Load WAV file into Wave structure
 static Wave LoadWAV(const char *fileName)

+ 4 - 4
src/raylib.dll.rc

@@ -1,8 +1,8 @@
 GLFW_ICON ICON "raylib.ico"
 
 1 VERSIONINFO
-FILEVERSION     2,6,0,0
-PRODUCTVERSION  2,6,0,0
+FILEVERSION     3,0,0,0
+PRODUCTVERSION  3,0,0,0
 BEGIN
   BLOCK "StringFileInfo"
   BEGIN
@@ -11,12 +11,12 @@ BEGIN
     BEGIN
 	  //VALUE "CompanyName", "raylib technologies"
       VALUE "FileDescription", "raylib dynamic library (www.raylib.com)"
-      VALUE "FileVersion", "2.6.0"
+      VALUE "FileVersion", "3.0.0"
       VALUE "InternalName", "raylib_dll"
       VALUE "LegalCopyright", "(c) 2020 Ramon Santamaria (@raysan5)"
       //VALUE "OriginalFilename", "raylib.dll"
       VALUE "ProductName", "raylib"
-      VALUE "ProductVersion", "2.6.0"
+      VALUE "ProductVersion", "3.0.0"
     END
   END
   BLOCK "VarFileInfo"

+ 3 - 2
src/raylib.h

@@ -1081,8 +1081,6 @@ RLAPI void DrawTriangleStrip(Vector2 *points, int pointsCount, Color color);
 RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color);               // Draw a regular polygon (Vector version)
 RLAPI void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Color color);          // Draw a polygon outline of n sides
 
-RLAPI void SetShapesTexture(Texture2D texture, Rectangle source);                                        // Define default texture used to draw shapes
-
 // Basic shapes collision detection functions
 RLAPI bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2);                                           // Check collision between two rectangles
 RLAPI bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2);        // Check collision between two circles
@@ -1329,6 +1327,9 @@ RLAPI void UnloadShader(Shader shader);                                   // Unl
 
 RLAPI Shader GetShaderDefault(void);                                      // Get default shader
 RLAPI Texture2D GetTextureDefault(void);                                  // Get default texture
+RLAPI Texture2D GetShapesTexture(void);                                   // Get texture to draw shapes
+RLAPI Rectangle GetShapesTextureRec(void);                                // Get texture rectangle to draw shapes
+RLAPI void SetShapesTexture(Texture2D texture, Rectangle source);         // Define default texture used to draw shapes
 
 // Shader configuration functions
 RLAPI int GetShaderLocation(Shader shader, const char *uniformName);      // Get shader uniform location

+ 4 - 4
src/raylib.rc

@@ -1,8 +1,8 @@
 GLFW_ICON ICON "raylib.ico"
 
 1 VERSIONINFO
-FILEVERSION     2,6,0,0
-PRODUCTVERSION  2,6,0,0
+FILEVERSION     3,0,0,0
+PRODUCTVERSION  3,0,0,0
 BEGIN
   BLOCK "StringFileInfo"
   BEGIN
@@ -11,12 +11,12 @@ BEGIN
     BEGIN
 	  //VALUE "CompanyName", "raylib technologies"
       VALUE "FileDescription", "raylib application (www.raylib.com)"
-      VALUE "FileVersion", "2.6.0"
+      VALUE "FileVersion", "3.0.0"
       VALUE "InternalName", "raylib app"
       VALUE "LegalCopyright", "(c) 2020 Ramon Santamaria (@raysan5)"
       //VALUE "OriginalFilename", "raylib_app.exe"
       VALUE "ProductName", "raylib game"
-      VALUE "ProductVersion", "2.6.0"
+      VALUE "ProductVersion", "3.0.0"
     END
   END
   BLOCK "VarFileInfo"

File diff suppressed because it is too large
+ 261 - 244
src/rlgl.h


+ 85 - 113
src/shapes.c

@@ -58,14 +58,12 @@
 //----------------------------------------------------------------------------------
 // Global Variables Definition
 //----------------------------------------------------------------------------------
-static Texture2D texShapes = { 0 };         // Texture used on shapes drawing (usually a white)
-static Rectangle recTexShapes = { 0 };      // Texture source rectangle used on shapes drawing
+// ...
 
 //----------------------------------------------------------------------------------
 // Module specific Functions Declaration
 //----------------------------------------------------------------------------------
 static float EaseCubicInOut(float t, float b, float c, float d);    // Cubic easing
-static Texture2D GetShapesTexture(void);                            // Get texture to draw shapes
 
 //----------------------------------------------------------------------------------
 // Module Functions Definition
@@ -138,16 +136,16 @@ void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color)
             rlColor4ub(color.r, color.g, color.b, color.a);
             rlNormal3f(0.0f, 0.0f, 1.0f);
 
-            rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(0.0f, 0.0f);
 
-            rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(0.0f, thick);
 
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(d, thick);
 
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(d, 0.0f);
         rlEnd();
     rlPopMatrix();
@@ -241,16 +239,16 @@ void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle
         {
             rlColor4ub(color.r, color.g, color.b, color.a);
 
-            rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(center.x, center.y);
 
-            rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius);
 
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius);
 
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength*2))*radius, center.y + cosf(DEG2RAD*(angle + stepLength*2))*radius);
 
             angle += (stepLength*2);
@@ -261,16 +259,16 @@ void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle
         {
             rlColor4ub(color.r, color.g, color.b, color.a);
 
-            rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(center.x, center.y);
 
-            rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius);
 
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius);
 
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(center.x, center.y);
         }
     rlEnd();
@@ -489,16 +487,16 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, int startAng
         {
             rlColor4ub(color.r, color.g, color.b, color.a);
 
-            rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius);
 
-            rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius);
 
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius);
 
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius);
 
             angle += stepLength;
@@ -643,16 +641,16 @@ void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color
             rlNormal3f(0.0f, 0.0f, 1.0f);
             rlColor4ub(color.r, color.g, color.b, color.a);
 
-            rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(0.0f, 0.0f);
 
-            rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(0.0f, rec.height);
 
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(rec.width, rec.height);
 
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(rec.width, 0.0f);
         rlEnd();
     rlPopMatrix();
@@ -686,19 +684,19 @@ void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3,
 
             // NOTE: Default raylib font character 95 is a white square
             rlColor4ub(col1.r, col1.g, col1.b, col1.a);
-            rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(rec.x, rec.y);
 
             rlColor4ub(col2.r, col2.g, col2.b, col2.a);
-            rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(rec.x, rec.y + rec.height);
 
             rlColor4ub(col3.r, col3.g, col3.b, col3.a);
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(rec.x + rec.width, rec.y + rec.height);
 
             rlColor4ub(col4.r, col4.g, col4.b, col4.a);
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(rec.x + rec.width, rec.y);
         rlEnd();
     rlPopMatrix();
@@ -821,13 +819,13 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co
             for (int i = 0; i < segments/2; i++)
             {
                 rlColor4ub(color.r, color.g, color.b, color.a);
-                rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+                rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
                 rlVertex2f(center.x, center.y);
-                rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+                rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
                 rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius);
-                rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+                rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
                 rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius);
-                rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+                rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
                 rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength*2))*radius, center.y + cosf(DEG2RAD*(angle + stepLength*2))*radius);
                 angle += (stepLength*2);
             }
@@ -835,70 +833,70 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co
             if (segments%2)
             {
                 rlColor4ub(color.r, color.g, color.b, color.a);
-                rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+                rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
                 rlVertex2f(center.x, center.y);
-                rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+                rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
                 rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius);
-                rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+                rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
                 rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius);
-                rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+                rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
                 rlVertex2f(center.x, center.y);
             }
         }
 
         // [2] Upper Rectangle
         rlColor4ub(color.r, color.g, color.b, color.a);
-        rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+        rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
         rlVertex2f(point[0].x, point[0].y);
-        rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+        rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
         rlVertex2f(point[8].x, point[8].y);
-        rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+        rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
         rlVertex2f(point[9].x, point[9].y);
-        rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+        rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
         rlVertex2f(point[1].x, point[1].y);
 
         // [4] Right Rectangle
         rlColor4ub(color.r, color.g, color.b, color.a);
-        rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+        rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
         rlVertex2f(point[2].x, point[2].y);
-        rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+        rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
         rlVertex2f(point[9].x, point[9].y);
-        rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+        rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
         rlVertex2f(point[10].x, point[10].y);
-        rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+        rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
         rlVertex2f(point[3].x, point[3].y);
 
         // [6] Bottom Rectangle
         rlColor4ub(color.r, color.g, color.b, color.a);
-        rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+        rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
         rlVertex2f(point[11].x, point[11].y);
-        rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+        rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
         rlVertex2f(point[5].x, point[5].y);
-        rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+        rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
         rlVertex2f(point[4].x, point[4].y);
-        rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+        rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
         rlVertex2f(point[10].x, point[10].y);
 
         // [8] Left Rectangle
         rlColor4ub(color.r, color.g, color.b, color.a);
-        rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+        rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
         rlVertex2f(point[7].x, point[7].y);
-        rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+        rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
         rlVertex2f(point[6].x, point[6].y);
-        rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+        rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
         rlVertex2f(point[11].x, point[11].y);
-        rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+        rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
         rlVertex2f(point[8].x, point[8].y);
 
         // [9] Middle Rectangle
         rlColor4ub(color.r, color.g, color.b, color.a);
-        rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+        rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
         rlVertex2f(point[8].x, point[8].y);
-        rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+        rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
         rlVertex2f(point[11].x, point[11].y);
-        rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+        rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
         rlVertex2f(point[10].x, point[10].y);
-        rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+        rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
         rlVertex2f(point[9].x, point[9].y);
 
     rlEnd();
@@ -1053,13 +1051,13 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, int
                 for (int i = 0; i < segments; i++)
                 {
                     rlColor4ub(color.r, color.g, color.b, color.a);
-                    rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+                    rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
                     rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius);
-                    rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+                    rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
                     rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius);
-                    rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+                    rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
                     rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius);
-                    rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+                    rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
                     rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius);
 
                     angle += stepLength;
@@ -1068,46 +1066,46 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, int
 
             // Upper rectangle
             rlColor4ub(color.r, color.g, color.b, color.a);
-            rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(point[0].x, point[0].y);
-            rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(point[8].x, point[8].y);
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(point[9].x, point[9].y);
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(point[1].x, point[1].y);
 
             // Right rectangle
             rlColor4ub(color.r, color.g, color.b, color.a);
-            rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(point[2].x, point[2].y);
-            rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(point[10].x, point[10].y);
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(point[11].x, point[11].y);
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(point[3].x, point[3].y);
 
             // Lower rectangle
             rlColor4ub(color.r, color.g, color.b, color.a);
-            rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(point[13].x, point[13].y);
-            rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(point[5].x, point[5].y);
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(point[4].x, point[4].y);
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(point[12].x, point[12].y);
 
             // Left rectangle
             rlColor4ub(color.r, color.g, color.b, color.a);
-            rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(point[15].x, point[15].y);
-            rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(point[7].x, point[7].y);
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
             rlVertex2f(point[6].x, point[6].y);
-            rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+            rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
             rlVertex2f(point[14].x, point[14].y);
 
         rlEnd();
@@ -1221,16 +1219,16 @@ void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color)
     rlBegin(RL_QUADS);
         rlColor4ub(color.r, color.g, color.b, color.a);
 
-        rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+        rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
         rlVertex2f(v1.x, v1.y);
 
-        rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+        rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
         rlVertex2f(v2.x, v2.y);
 
-        rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+        rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
         rlVertex2f(v2.x, v2.y);
 
-        rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+        rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
         rlVertex2f(v3.x, v3.y);
     rlEnd();
 
@@ -1278,16 +1276,16 @@ void DrawTriangleFan(Vector2 *points, int pointsCount, Color color)
 
             for (int i = 1; i < pointsCount - 1; i++)
             {
-                rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+                rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
                 rlVertex2f(points[0].x, points[0].y);
 
-                rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+                rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
                 rlVertex2f(points[i].x, points[i].y);
 
-                rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+                rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
                 rlVertex2f(points[i + 1].x, points[i + 1].y);
 
-                rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+                rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
                 rlVertex2f(points[i + 1].x, points[i + 1].y);
             }
         rlEnd();
@@ -1345,17 +1343,17 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col
             {
                 rlColor4ub(color.r, color.g, color.b, color.a);
 
-                rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
+                rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
                 rlVertex2f(0, 0);
 
-                rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+                rlTexCoord2f(GetShapesTextureRec().x/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
                 rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius);
 
-                rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
+                rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, (GetShapesTextureRec().y + GetShapesTextureRec().height)/GetShapesTexture().height);
                 rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius);
 
                 centralAngle += 360.0f/(float)sides;
-                rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
+                rlTexCoord2f((GetShapesTextureRec().x + GetShapesTextureRec().width)/GetShapesTexture().width, GetShapesTextureRec().y/GetShapesTexture().height);
                 rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius);
             }
         rlEnd();
@@ -1402,13 +1400,6 @@ void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Colo
     rlPopMatrix();
 }
 
-// Define default texture used to draw shapes
-void SetShapesTexture(Texture2D texture, Rectangle source)
-{
-    texShapes = texture;
-    recTexShapes = source;
-}
-
 //----------------------------------------------------------------------------------
 // Module Functions Definition - Collision Detection functions
 //----------------------------------------------------------------------------------
@@ -1576,22 +1567,3 @@ static float EaseCubicInOut(float t, float b, float c, float d)
 
     return 0.5f*c*(t*t*t + 2.0f) + b;
 }
-
-// Get texture to draw shapes (RAII)
-static Texture2D GetShapesTexture(void)
-{
-    if (texShapes.id == 0)
-    {
-#if defined(SUPPORT_FONT_TEXTURE)
-        texShapes = GetFontDefault().texture;           // Use font texture white character
-        Rectangle rec = GetFontDefault().recs[95];
-        // NOTE: We setup a 1px padding on char rectangle to avoid texture bleeding on MSAA filtering
-        recTexShapes = (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 };
-#else
-        texShapes = GetTextureDefault();                // Use default white texture
-        recTexShapes = (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f };
-#endif
-    }
-
-    return texShapes;
-}

+ 12 - 0
src/utils.h

@@ -32,6 +32,18 @@
     #include <android/asset_manager.h>      // Required for: AAssetManager
 #endif
 
+#if defined(SUPPORT_TRACELOG)
+    #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__)
+    
+    #if defined(SUPPORT_TRACELOG_DEBUG)
+        #define TRACELOGD(...) TraceLog(LOG_DEBUG, __VA_ARGS__)
+    #else
+        #define TRACELOGD(...) void(0)
+    #endif
+#else
+    #define TRACELOG(level, ...) void(0)
+#endif
+
 //----------------------------------------------------------------------------------
 // Some basic Defines
 //----------------------------------------------------------------------------------

Some files were not shown because too many files changed in this diff