Browse Source

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 years ago
parent
commit
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();
+    //----------------------------------------------------------------------------------
+}

BIN
games/repair/resources/background.png


BIN
games/repair/resources/coin.wav


BIN
games/repair/resources/elevator_romance.ogg


BIN
games/repair/resources/eyes_models.png


BIN
games/repair/resources/font.png


BIN
games/repair/resources/hair_models.png


BIN
games/repair/resources/head_models.png


BIN
games/repair/resources/makeup.png


BIN
games/repair/resources/match.png


BIN
games/repair/resources/mouth_models.png


BIN
games/repair/resources/nose_models.png


BIN
games/repair/resources/npatch.png


BIN
games/repair/resources/qmark.png


BIN
games/repair/resources/raylib_logo.png


BIN
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
     MOVE_DOWN
 } CameraMove;
 } 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
 // 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
 // Module specific Functions Declaration
@@ -241,19 +253,19 @@ void SetCameraMode(Camera camera, int mode)
     float dy = v2.y - v1.y;
     float dy = v2.y - v1.y;
     float dz = v2.z - v1.z;
     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
     // 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
     // Lock cursor for first person and third person cameras
     if ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON)) DisableCursor();
     if ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON)) DisableCursor();
     else EnableCursor();
     else EnableCursor();
 
 
-    cameraMode = mode;
+    CAMERA.mode = mode;
 }
 }
 
 
 // Update camera depending on selected 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 int swingCounter = 0;    // Used for 1st person swinging movement
     static Vector2 previousMousePosition = { 0.0f, 0.0f };
     static Vector2 previousMousePosition = { 0.0f, 0.0f };
 
 
-    // TODO: Compute cameraTargetDistance and cameraAngle here
+    // TODO: Compute CAMERA.targetDistance and CAMERA.angle here
 
 
     // Mouse movement detection
     // Mouse movement detection
     Vector2 mousePositionDelta = { 0.0f, 0.0f };
     Vector2 mousePositionDelta = { 0.0f, 0.0f };
@@ -275,20 +287,20 @@ void UpdateCamera(Camera *camera)
     int mouseWheelMove = GetMouseWheelMove();
     int mouseWheelMove = GetMouseWheelMove();
 
 
     // Keys input detection
     // 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
     // TODO: Consider touch inputs for camera
 
 
-    if (cameraMode != CAMERA_CUSTOM)
+    if (CAMERA.mode != CAMERA_CUSTOM)
     {
     {
         mousePositionDelta.x = mousePosition.x - previousMousePosition.x;
         mousePositionDelta.x = mousePosition.x - previousMousePosition.x;
         mousePositionDelta.y = mousePosition.y - previousMousePosition.y;
         mousePositionDelta.y = mousePosition.y - previousMousePosition.y;
@@ -297,58 +309,58 @@ void UpdateCamera(Camera *camera)
     }
     }
 
 
     // Support for multiple automatic camera modes
     // Support for multiple automatic camera modes
-    switch (cameraMode)
+    switch (CAMERA.mode)
     {
     {
         case CAMERA_FREE:
         case CAMERA_FREE:
         {
         {
             // Camera zoom
             // 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
             // 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))
             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;
                 // if (camera->target.y < 0) camera->target.y = -0.001;
             }
             }
             else if ((camera->position.y > camera->target.y) && (camera->target.y < 0) && (mouseWheelMove > 0))
             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
             // 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))
             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;
                 // if (camera->target.y > 0) camera->target.y = 0.001;
             }
             }
             else if ((camera->position.y < camera->target.y) && (camera->target.y > 0) && (mouseWheelMove > 0))
             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
             // Input keys checks
@@ -359,78 +371,78 @@ void UpdateCamera(Camera *camera)
                     if (szoomKey)
                     if (szoomKey)
                     {
                     {
                         // Camera smooth zoom
                         // Camera smooth zoom
-                        cameraTargetDistance += (mousePositionDelta.y*CAMERA_FREE_SMOOTH_ZOOM_SENSITIVITY);
+                        CAMERA.targetDistance += (mousePositionDelta.y*CAMERA_FREE_SMOOTH_ZOOM_SENSITIVITY);
                     }
                     }
                     else
                     else
                     {
                     {
                         // Camera rotation
                         // 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
                         // 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
                 else
                 {
                 {
                     // Camera panning
                     // 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
             // 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;
         } break;
         case CAMERA_ORBITAL:
         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
             // 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
             // 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;
         } break;
         case CAMERA_FIRST_PERSON:
         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;
                                    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
             bool isMoving = false;  // Required for swinging
 
 
             for (int i = 0; i < 6; i++) if (direction[i]) { isMoving = true; break; }
             for (int i = 0; i < 6; i++) if (direction[i]) { isMoving = true; break; }
 
 
             // Camera orientation calculation
             // 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
             // 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
             // 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);
             Matrix transform = MatrixMultiply(translation, rotation);
             
             
             camera->target.x = camera->position.x - transform.m12;
             camera->target.x = camera->position.x - transform.m12;
@@ -441,7 +453,7 @@ void UpdateCamera(Camera *camera)
 
 
             // Camera position update
             // Camera position update
             // NOTE: On CAMERA_FIRST_PERSON player Y-movement is limited to player 'eyes position'
             // 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.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;
             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;
         } break;
         case CAMERA_THIRD_PERSON:
         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;
                                    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
             // 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
             // 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
             // Camera zoom
-            cameraTargetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY);
+            CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY);
 
 
             // Camera distance clamp
             // 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...
             // 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;
         } break;
         default: break;
         default: break;
@@ -490,23 +502,23 @@ void UpdateCamera(Camera *camera)
 }
 }
 
 
 // Set camera pan key to combine with mouse movement (free 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)
 // 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)
 // 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)
 // Set camera move controls (1st person and 3rd person cameras)
 void SetCameraMoveControls(int frontKey, int backKey, int rightKey, int leftKey, int upKey, int downKey)
 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
 #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
 // Edit to control what features Makefile'd raylib is compiled with
 #if defined(RAYLIB_CMAKE)
 #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
         #undef _POSIX_C_SOURCE
         #define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext.
         #define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext.
     #endif
     #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
 #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
 #endif
 
 
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 // Defines and Macros
 // 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
 // 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
 // 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
 // Module specific Functions Declaration
@@ -236,13 +237,13 @@ static double GetCurrentTime(void);
 // Enable only desired getures to be detected
 // Enable only desired getures to be detected
 void SetGesturesEnabled(unsigned int gestureFlags)
 void SetGesturesEnabled(unsigned int gestureFlags)
 {
 {
-    enabledGestures = gestureFlags;
+    GESTURES.enabledFlags = gestureFlags;
 }
 }
 
 
 // Check if a gesture have been detected
 // Check if a gesture have been detected
 bool IsGestureDetected(int gesture)
 bool IsGestureDetected(int gesture)
 {
 {
-    if ((enabledGestures & currentGesture) == gesture) return true;
+    if ((GESTURES.enabledFlags & GESTURES.current) == gesture) return true;
     else return false;
     else return false;
 }
 }
 
 
@@ -250,150 +251,150 @@ bool IsGestureDetected(int gesture)
 void ProcessGestureEvent(GestureEvent event)
 void ProcessGestureEvent(GestureEvent event)
 {
 {
     // Reset required variables
     // 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)
         if (event.touchAction == TOUCH_DOWN)
         {
         {
-            tapCounter++;    // Tap counter
+            GESTURES.Touch.tapCounter++;    // Tap counter
 
 
             // Detect GESTURE_DOUBLE_TAP
             // 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
             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)
         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
             // 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
                 // 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
             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)
         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
                 // 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
     else    // Two touch points
     {
     {
         if (event.touchAction == TOUCH_DOWN)
         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)
         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
             else
             {
             {
-                currentGesture = GESTURE_HOLD;
-                timeHold = GetCurrentTime();
+                GESTURES.current = GESTURE_HOLD;
+                GESTURES.Hold.timeDuration = GetCurrentTime();
             }
             }
 
 
             // NOTE: Angle should be inverted in Y
             // 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)
         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
     // NOTE: Gestures are processed through system callbacks on touch events
 
 
     // Detect GESTURE_HOLD
     // 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
     // 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
     // NOTE: point count is calculated when ProcessGestureEvent(GestureEvent event) is called
 
 
-    return pointCount;
+    return GESTURES.Touch.pointCount;
 }
 }
 
 
 // Get latest detected gesture
 // Get latest detected gesture
 int GetGestureDetected(void)
 int GetGestureDetected(void)
 {
 {
     // Get current gesture only if enabled
     // Get current gesture only if enabled
-    return (enabledGestures & currentGesture);
+    return (GESTURES.enabledFlags & GESTURES.current);
 }
 }
 
 
 // Hold time measured in ms
 // Hold time measured in ms
@@ -446,7 +447,7 @@ float GetGestureHoldDuration(void)
 
 
     double time = 0.0;
     double time = 0.0;
 
 
-    if (currentGesture == GESTURE_HOLD) time = GetCurrentTime() - timeHold;
+    if (GESTURES.current == GESTURE_HOLD) time = GetCurrentTime() - GESTURES.Hold.timeDuration;
 
 
     return (float)time;
     return (float)time;
 }
 }
@@ -456,7 +457,7 @@ Vector2 GetGestureDragVector(void)
 {
 {
     // NOTE: drag vector is calculated on one touch points TOUCH_MOVE
     // NOTE: drag vector is calculated on one touch points TOUCH_MOVE
 
 
-    return dragVector;
+    return GESTURES.Drag.vector;
 }
 }
 
 
 // Get drag angle
 // Get drag angle
@@ -465,16 +466,16 @@ float GetGestureDragAngle(void)
 {
 {
     // NOTE: drag angle is calculated on one touch points TOUCH_UP
     // NOTE: drag angle is calculated on one touch points TOUCH_UP
 
 
-    return dragAngle;
+    return GESTURES.Drag.angle;
 }
 }
 
 
 // Get distance between two pinch points
 // Get distance between two pinch points
 Vector2 GetGesturePinchVector(void)
 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
     // NOTE: pinch distance is calculated on two touch points TOUCH_MOVE
 
 
-    return pinchVector;
+    return GESTURES.Pinch.vector;
 }
 }
 
 
 // Get angle beween two pinch points
 // Get angle beween two pinch points
@@ -483,7 +484,7 @@ float GetGesturePinchAngle(void)
 {
 {
     // NOTE: pinch angle is calculated on two touch points TOUCH_MOVE
     // 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:
 *   FEATURES:
 *       - Manage audio device (init/close)
 *       - Manage audio device (init/close)
+*       - Manage raw audio context
+*       - Manage mixing channels
 *       - Load and unload audio files
 *       - Load and unload audio files
 *       - Format wave data (sample rate, size, channels)
 *       - Format wave data (sample rate, size, channels)
 *       - Play/Stop/Pause/Resume loaded audio
 *       - Play/Stop/Pause/Resume loaded audio
-*       - Manage mixing channels
-*       - Manage raw audio context
 *
 *
 *   CONFIGURATION:
 *   CONFIGURATION:
 *
 *
@@ -124,7 +124,15 @@
 // After some math, considering a sampleRate of 48000, a buffer refill rate of 1/60 seconds and a
 // 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
 // standard double-buffering system, a 4096 samples buffer has been chosen, it should be enough
 // In case of music-stalls, just increase this number
 // 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
 // Types and Structures Definition
@@ -155,14 +163,74 @@ typedef enum {
 } TraceLogType;
 } TraceLogType;
 #endif
 #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
 // Global Variables Definition
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
-// ...
+static AudioData AUDIO = { 0 };     // Global CORE context
 
 
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 // Module specific Functions Declaration
 // 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)
 #if defined(SUPPORT_FILEFORMAT_WAV)
 static Wave LoadWAV(const char *fileName);              // Load WAV file
 static Wave LoadWAV(const char *fileName);              // Load WAV file
 static int SaveWAV(Wave wave, const char *fileName);    // Save wave data as 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
 #endif
 
 
 #if defined(RAUDIO_STANDALONE)
 #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
 #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
 // AudioBuffer management functions declaration
 // NOTE: Those functions are not exposed by raylib... for the moment
 // 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);
 void CloseAudioBuffer(AudioBuffer *buffer);
 bool IsAudioBufferPlaying(AudioBuffer *buffer);
 bool IsAudioBufferPlaying(AudioBuffer *buffer);
 void PlayAudioBuffer(AudioBuffer *buffer);
 void PlayAudioBuffer(AudioBuffer *buffer);
@@ -256,248 +266,20 @@ void SetAudioBufferPitch(AudioBuffer *buffer, float pitch);
 void TrackAudioBuffer(AudioBuffer *buffer);
 void TrackAudioBuffer(AudioBuffer *buffer);
 void UntrackAudioBuffer(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
 // Module Functions Definition - Audio Device initialization and Closing
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 // Initialize audio device
 // Initialize audio device
 void InitAudioDevice(void)
 void InitAudioDevice(void)
 {
 {
+    // TODO: Load AUDIO context memory dynamically?
+    AUDIO.System.masterVolume = 1.0f;
+    
     // Init audio context
     // 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)
     if (result != MA_SUCCESS)
     {
     {
         TraceLog(LOG_ERROR, "Failed to initialize audio context");
         TraceLog(LOG_ERROR, "Failed to initialize audio context");
@@ -507,78 +289,78 @@ void InitAudioDevice(void)
     // Init audio device
     // Init audio device
     // NOTE: Using the default device. Format is floating point because it simplifies mixing.
     // 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);
     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.format    = DEVICE_FORMAT;
     config.playback.channels  = DEVICE_CHANNELS;
     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.format     = ma_format_s16;
     config.capture.channels   = 1;
     config.capture.channels   = 1;
     config.sampleRate         = DEVICE_SAMPLE_RATE;
     config.sampleRate         = DEVICE_SAMPLE_RATE;
     config.dataCallback       = OnSendAudioDataToDevice;
     config.dataCallback       = OnSendAudioDataToDevice;
     config.pUserData          = NULL;
     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)
     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;
         return;
     }
     }
 
 
     // Keep the device running the whole time. May want to consider doing something a bit smarter and only have the device running
     // 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.
     // 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)
     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;
         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
     // 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.
     // 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");
         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;
         return;
     }
     }
 
 
     TraceLog(LOG_INFO, "Audio device initialized successfully");
     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();
     InitAudioBufferPool();
     TraceLog(LOG_INFO, "Audio multichannel pool size: %i", MAX_AUDIO_BUFFER_POOL_CHANNELS);
     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
 // Close the audio device for all contexts
 void CloseAudioDevice(void)
 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();
         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
 // Check if device has been initialized successfully
 bool IsAudioDeviceReady(void)
 bool IsAudioDeviceReady(void)
 {
 {
-    return isAudioInitialized;
+    return AUDIO.System.isReady;
 }
 }
 
 
 // Set master volume (listener)
 // Set master volume (listener)
@@ -587,7 +369,7 @@ void SetMasterVolume(float volume)
     if (volume < 0.0f) volume = 0.0f;
     if (volume < 0.0f) volume = 0.0f;
     else if (volume > 1.0f) volume = 1.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)
 // 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));
     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;
         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
     // Audio data runs through a format converter
     ma_pcm_converter_config dspConfig;
     ma_pcm_converter_config dspConfig;
@@ -637,7 +419,7 @@ AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam
     audioBuffer->looping = false;
     audioBuffer->looping = false;
     audioBuffer->usage = usage;
     audioBuffer->usage = usage;
     audioBuffer->frameCursorPos = 0;
     audioBuffer->frameCursorPos = 0;
-    audioBuffer->bufferSizeInFrames = bufferSizeInFrames;
+    audioBuffer->sizeInFrames = sizeInFrames;
 
 
     // Buffers should be marked as processed by default so that a call to
     // Buffers should be marked as processed by default so that a call to
     // UpdateAudioStream() immediately after initialization works correctly
     // UpdateAudioStream() immediately after initialization works correctly
@@ -656,7 +438,7 @@ void CloseAudioBuffer(AudioBuffer *buffer)
     if (buffer != NULL)
     if (buffer != NULL)
     {
     {
         UntrackAudioBuffer(buffer);
         UntrackAudioBuffer(buffer);
-        RL_FREE(buffer->buffer);
+        RL_FREE(buffer->data);
         RL_FREE(buffer);
         RL_FREE(buffer);
     }
     }
     else TraceLog(LOG_ERROR, "CloseAudioBuffer() : No audio 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
 // Track audio buffer to linked list next position
 void TrackAudioBuffer(AudioBuffer *buffer)
 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
         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
 // Untrack audio buffer from linked list
 void UntrackAudioBuffer(AudioBuffer *buffer)
 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;
         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;
         else buffer->next->prev = buffer->prev;
 
 
         buffer->prev = NULL;
         buffer->prev = NULL;
         buffer->next = 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.
         // 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
         // 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).
         //   1) Convert the whole sound in one go at load time (here).
         //   2) Convert the audio data in chunks at mixing time.
         //   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);
         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");
         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");
         if (frameCount == 0) TraceLog(LOG_WARNING, "LoadSoundFromWave() : Format conversion failed");
 
 
         sound.sampleCount = frameCount*DEVICE_CHANNELS;
         sound.sampleCount = frameCount*DEVICE_CHANNELS;
@@ -884,7 +666,7 @@ void UpdateSound(Sound sound, const void *data, int samplesCount)
         StopAudioBuffer(audioBuffer);
         StopAudioBuffer(audioBuffer);
 
 
         // TODO: May want to lock/unlock this since this data buffer is read at mixing time
         // 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");
     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
     // find the first non playing pool entry
     for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++)
     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;
             oldIndex = i;
         }
         }
 
 
-        if (!IsAudioBufferPlaying(audioBufferPool[i]))
+        if (!IsAudioBufferPlaying(AUDIO.MultiChannel.pool[i]))
         {
         {
             index = i;
             index = i;
             break;
             break;
@@ -989,7 +771,7 @@ void PlaySoundMulti(Sound sound)
     // If no none playing pool members can be index choose the oldest
     // If no none playing pool members can be index choose the oldest
     if (index == -1)
     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)
         if (oldIndex == -1)
         {
         {
@@ -1002,32 +784,32 @@ void PlaySoundMulti(Sound sound)
         index = oldIndex;
         index = oldIndex;
 
 
         // Just in case...
         // Just in case...
-        StopAudioBuffer(audioBufferPool[index]);
+        StopAudioBuffer(AUDIO.MultiChannel.pool[index]);
     }
     }
 
 
     // Experimentally mutex lock doesn't seem to be needed this makes sense
     // 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...
     // 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()
 // Stop any sound played with PlaySoundMulti()
 void StopSoundMulti(void)
 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
 // 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++)
     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;
     return counter;
@@ -1243,7 +1025,7 @@ Music LoadMusicStream(const char *fileName)
 
 
         int result = jar_xm_create_context_from_file(&ctxXm, 48000, 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;
             music.ctxType = MUSIC_MODULE_XM;
             jar_xm_set_max_loop_count(ctxXm, 0);    // Set infinite number of loops
             jar_xm_set_max_loop_count(ctxXm, 0);    // Set infinite number of loops
@@ -1374,7 +1156,6 @@ void StopMusicStream(Music music)
 {
 {
     StopAudioStream(music.stream);
     StopAudioStream(music.stream);
 
 
-    // Restart music context
     switch (music.ctxType)
     switch (music.ctxType)
     {
     {
 #if defined(SUPPORT_FILEFORMAT_OGG)
 #if defined(SUPPORT_FILEFORMAT_OGG)
@@ -1401,7 +1182,7 @@ void UpdateMusicStream(Music music)
 {
 {
     bool streamEnding = false;
     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
     // NOTE: Using dynamic allocation because it could require more than 16KB
     void *pcm = RL_CALLOC(subBufferSizeInFrames*music.stream.channels*music.stream.sampleSize/8, 1);
     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));
     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
     // 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;
     unsigned int subBufferSize = AUDIO_BUFFER_SIZE;
 
 
     if (subBufferSize < periodSize) subBufferSize = periodSize;
     if (subBufferSize < periodSize) subBufferSize = periodSize;
@@ -1610,8 +1391,8 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
                 subBufferToUpdate = (audioBuffer->isSubBufferProcessed[0])? 0 : 1;
                 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.
             // TODO: Get total frames processed on this buffer... DOES NOT WORK.
             audioBuffer->totalFramesProcessed += subBufferSizeInFrames;
             audioBuffer->totalFramesProcessed += subBufferSizeInFrames;
@@ -1697,6 +1478,232 @@ void SetAudioStreamPitch(AudioStream stream, float pitch)
 // Module specific Functions Definition
 // 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)
 #if defined(SUPPORT_FILEFORMAT_WAV)
 // Load WAV file into Wave structure
 // Load WAV file into Wave structure
 static Wave LoadWAV(const char *fileName)
 static Wave LoadWAV(const char *fileName)

+ 4 - 4
src/raylib.dll.rc

@@ -1,8 +1,8 @@
 GLFW_ICON ICON "raylib.ico"
 GLFW_ICON ICON "raylib.ico"
 
 
 1 VERSIONINFO
 1 VERSIONINFO
-FILEVERSION     2,6,0,0
-PRODUCTVERSION  2,6,0,0
+FILEVERSION     3,0,0,0
+PRODUCTVERSION  3,0,0,0
 BEGIN
 BEGIN
   BLOCK "StringFileInfo"
   BLOCK "StringFileInfo"
   BEGIN
   BEGIN
@@ -11,12 +11,12 @@ BEGIN
     BEGIN
     BEGIN
 	  //VALUE "CompanyName", "raylib technologies"
 	  //VALUE "CompanyName", "raylib technologies"
       VALUE "FileDescription", "raylib dynamic library (www.raylib.com)"
       VALUE "FileDescription", "raylib dynamic library (www.raylib.com)"
-      VALUE "FileVersion", "2.6.0"
+      VALUE "FileVersion", "3.0.0"
       VALUE "InternalName", "raylib_dll"
       VALUE "InternalName", "raylib_dll"
       VALUE "LegalCopyright", "(c) 2020 Ramon Santamaria (@raysan5)"
       VALUE "LegalCopyright", "(c) 2020 Ramon Santamaria (@raysan5)"
       //VALUE "OriginalFilename", "raylib.dll"
       //VALUE "OriginalFilename", "raylib.dll"
       VALUE "ProductName", "raylib"
       VALUE "ProductName", "raylib"
-      VALUE "ProductVersion", "2.6.0"
+      VALUE "ProductVersion", "3.0.0"
     END
     END
   END
   END
   BLOCK "VarFileInfo"
   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 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 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
 // Basic shapes collision detection functions
 RLAPI bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2);                                           // Check collision between two rectangles
 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
 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 Shader GetShaderDefault(void);                                      // Get default shader
 RLAPI Texture2D GetTextureDefault(void);                                  // Get default texture
 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
 // Shader configuration functions
 RLAPI int GetShaderLocation(Shader shader, const char *uniformName);      // Get shader uniform location
 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"
 GLFW_ICON ICON "raylib.ico"
 
 
 1 VERSIONINFO
 1 VERSIONINFO
-FILEVERSION     2,6,0,0
-PRODUCTVERSION  2,6,0,0
+FILEVERSION     3,0,0,0
+PRODUCTVERSION  3,0,0,0
 BEGIN
 BEGIN
   BLOCK "StringFileInfo"
   BLOCK "StringFileInfo"
   BEGIN
   BEGIN
@@ -11,12 +11,12 @@ BEGIN
     BEGIN
     BEGIN
 	  //VALUE "CompanyName", "raylib technologies"
 	  //VALUE "CompanyName", "raylib technologies"
       VALUE "FileDescription", "raylib application (www.raylib.com)"
       VALUE "FileDescription", "raylib application (www.raylib.com)"
-      VALUE "FileVersion", "2.6.0"
+      VALUE "FileVersion", "3.0.0"
       VALUE "InternalName", "raylib app"
       VALUE "InternalName", "raylib app"
       VALUE "LegalCopyright", "(c) 2020 Ramon Santamaria (@raysan5)"
       VALUE "LegalCopyright", "(c) 2020 Ramon Santamaria (@raysan5)"
       //VALUE "OriginalFilename", "raylib_app.exe"
       //VALUE "OriginalFilename", "raylib_app.exe"
       VALUE "ProductName", "raylib game"
       VALUE "ProductName", "raylib game"
-      VALUE "ProductVersion", "2.6.0"
+      VALUE "ProductVersion", "3.0.0"
     END
     END
   END
   END
   BLOCK "VarFileInfo"
   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
 // 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
 // Module specific Functions Declaration
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 static float EaseCubicInOut(float t, float b, float c, float d);    // Cubic easing
 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
 // 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);
             rlColor4ub(color.r, color.g, color.b, color.a);
             rlNormal3f(0.0f, 0.0f, 1.0f);
             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);
             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);
             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);
             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);
             rlVertex2f(d, 0.0f);
         rlEnd();
         rlEnd();
     rlPopMatrix();
     rlPopMatrix();
@@ -241,16 +239,16 @@ void DrawCircleSector(Vector2 center, float radius, int startAngle, int endAngle
         {
         {
             rlColor4ub(color.r, color.g, color.b, color.a);
             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);
             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);
             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);
             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);
             rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength*2))*radius, center.y + cosf(DEG2RAD*(angle + stepLength*2))*radius);
 
 
             angle += (stepLength*2);
             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);
             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);
             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);
             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);
             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);
             rlVertex2f(center.x, center.y);
         }
         }
     rlEnd();
     rlEnd();
@@ -489,16 +487,16 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, int startAng
         {
         {
             rlColor4ub(color.r, color.g, color.b, color.a);
             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);
             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);
             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);
             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);
             rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius);
 
 
             angle += stepLength;
             angle += stepLength;
@@ -643,16 +641,16 @@ void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color
             rlNormal3f(0.0f, 0.0f, 1.0f);
             rlNormal3f(0.0f, 0.0f, 1.0f);
             rlColor4ub(color.r, color.g, color.b, color.a);
             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);
             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);
             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);
             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);
             rlVertex2f(rec.width, 0.0f);
         rlEnd();
         rlEnd();
     rlPopMatrix();
     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
             // NOTE: Default raylib font character 95 is a white square
             rlColor4ub(col1.r, col1.g, col1.b, col1.a);
             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);
             rlVertex2f(rec.x, rec.y);
 
 
             rlColor4ub(col2.r, col2.g, col2.b, col2.a);
             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);
             rlVertex2f(rec.x, rec.y + rec.height);
 
 
             rlColor4ub(col3.r, col3.g, col3.b, col3.a);
             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);
             rlVertex2f(rec.x + rec.width, rec.y + rec.height);
 
 
             rlColor4ub(col4.r, col4.g, col4.b, col4.a);
             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);
             rlVertex2f(rec.x + rec.width, rec.y);
         rlEnd();
         rlEnd();
     rlPopMatrix();
     rlPopMatrix();
@@ -821,13 +819,13 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co
             for (int i = 0; i < segments/2; i++)
             for (int i = 0; i < segments/2; i++)
             {
             {
                 rlColor4ub(color.r, color.g, color.b, color.a);
                 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);
                 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);
                 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);
                 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);
                 rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength*2))*radius, center.y + cosf(DEG2RAD*(angle + stepLength*2))*radius);
                 angle += (stepLength*2);
                 angle += (stepLength*2);
             }
             }
@@ -835,70 +833,70 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co
             if (segments%2)
             if (segments%2)
             {
             {
                 rlColor4ub(color.r, color.g, color.b, color.a);
                 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);
                 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);
                 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);
                 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);
                 rlVertex2f(center.x, center.y);
             }
             }
         }
         }
 
 
         // [2] Upper Rectangle
         // [2] Upper Rectangle
         rlColor4ub(color.r, color.g, color.b, color.a);
         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);
         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);
         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);
         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);
         rlVertex2f(point[1].x, point[1].y);
 
 
         // [4] Right Rectangle
         // [4] Right Rectangle
         rlColor4ub(color.r, color.g, color.b, color.a);
         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);
         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);
         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);
         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);
         rlVertex2f(point[3].x, point[3].y);
 
 
         // [6] Bottom Rectangle
         // [6] Bottom Rectangle
         rlColor4ub(color.r, color.g, color.b, color.a);
         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);
         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);
         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);
         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);
         rlVertex2f(point[10].x, point[10].y);
 
 
         // [8] Left Rectangle
         // [8] Left Rectangle
         rlColor4ub(color.r, color.g, color.b, color.a);
         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);
         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);
         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);
         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);
         rlVertex2f(point[8].x, point[8].y);
 
 
         // [9] Middle Rectangle
         // [9] Middle Rectangle
         rlColor4ub(color.r, color.g, color.b, color.a);
         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);
         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);
         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);
         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);
         rlVertex2f(point[9].x, point[9].y);
 
 
     rlEnd();
     rlEnd();
@@ -1053,13 +1051,13 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, int
                 for (int i = 0; i < segments; i++)
                 for (int i = 0; i < segments; i++)
                 {
                 {
                     rlColor4ub(color.r, color.g, color.b, color.a);
                     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);
                     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);
                     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);
                     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);
                     rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius);
 
 
                     angle += stepLength;
                     angle += stepLength;
@@ -1068,46 +1066,46 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, int
 
 
             // Upper rectangle
             // Upper rectangle
             rlColor4ub(color.r, color.g, color.b, color.a);
             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);
             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);
             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);
             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);
             rlVertex2f(point[1].x, point[1].y);
 
 
             // Right rectangle
             // Right rectangle
             rlColor4ub(color.r, color.g, color.b, color.a);
             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);
             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);
             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);
             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);
             rlVertex2f(point[3].x, point[3].y);
 
 
             // Lower rectangle
             // Lower rectangle
             rlColor4ub(color.r, color.g, color.b, color.a);
             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);
             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);
             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);
             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);
             rlVertex2f(point[12].x, point[12].y);
 
 
             // Left rectangle
             // Left rectangle
             rlColor4ub(color.r, color.g, color.b, color.a);
             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);
             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);
             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);
             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);
             rlVertex2f(point[14].x, point[14].y);
 
 
         rlEnd();
         rlEnd();
@@ -1221,16 +1219,16 @@ void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color)
     rlBegin(RL_QUADS);
     rlBegin(RL_QUADS);
         rlColor4ub(color.r, color.g, color.b, color.a);
         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);
         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);
         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);
         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);
         rlVertex2f(v3.x, v3.y);
     rlEnd();
     rlEnd();
 
 
@@ -1278,16 +1276,16 @@ void DrawTriangleFan(Vector2 *points, int pointsCount, Color color)
 
 
             for (int i = 1; i < pointsCount - 1; i++)
             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);
                 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);
                 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);
                 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);
                 rlVertex2f(points[i + 1].x, points[i + 1].y);
             }
             }
         rlEnd();
         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);
                 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);
                 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);
                 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);
                 rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius);
 
 
                 centralAngle += 360.0f/(float)sides;
                 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);
                 rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius);
             }
             }
         rlEnd();
         rlEnd();
@@ -1402,13 +1400,6 @@ void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Colo
     rlPopMatrix();
     rlPopMatrix();
 }
 }
 
 
-// Define default texture used to draw shapes
-void SetShapesTexture(Texture2D texture, Rectangle source)
-{
-    texShapes = texture;
-    recTexShapes = source;
-}
-
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 // Module Functions Definition - Collision Detection functions
 // 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;
     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
     #include <android/asset_manager.h>      // Required for: AAssetManager
 #endif
 #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 basic Defines
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------

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