diff --git a/games/cat_vs_roomba/LICENSE.txt b/games/cat_vs_roomba/LICENSE.txt
new file mode 100644
index 000000000..bd3a20cef
--- /dev/null
+++ b/games/cat_vs_roomba/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) 2013-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.
\ No newline at end of file
diff --git a/games/cat_vs_roomba/Makefile b/games/cat_vs_roomba/Makefile
new file mode 100644
index 000000000..6662a6be4
--- /dev/null
+++ b/games/cat_vs_roomba/Makefile
@@ -0,0 +1,331 @@
+#**************************************************************************************************
+#
+# raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5
+#
+# Copyright (c) 2013-2018 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
+# WARNING: To compile to HTML5, code must be redesigned to use emscripten.h and emscripten_set_main_loop()
+PLATFORM ?= PLATFORM_DESKTOP
+RAYLIB_PATH ?= C:/GitHub/raylib
+PROJECT_NAME ?= roomba
+
+# Default path for raylib on Raspberry Pi, if installed in different path, update it!
+ifeq ($(PLATFORM),PLATFORM_RPI)
+ RAYLIB_PATH ?= /home/pi/raylib
+endif
+
+# Library type used for raylib: STATIC (.a) or SHARED (.so/.dll)
+RAYLIB_LIBTYPE ?= STATIC
+
+# Use external GLFW library instead of rglfw module
+USE_EXTERNAL_GLFW ?= FALSE
+
+# Use Wayland display server protocol on Linux desktop
+# by default it uses X11 windowing system
+USE_WAYLAND_DISPLAY ?= FALSE
+
+# NOTE: On PLATFORM_WEB OpenAL Soft backend is used by default (check raylib/src/Makefile)
+
+
+# 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
+
+ifeq ($(PLATFORM),PLATFORM_WEB)
+ # Emscripten required variables
+ EMSDK_PATH = C:/emsdk
+ EMSCRIPTEN_VERSION = 1.38.20
+ CLANG_VERSION = e1.38.20_64bit
+ PYTHON_VERSION = 2.7.13.1_64bit\python-2.7.13.amd64
+ NODE_VERSION = 8.9.1_64bit
+ export PATH = $(EMSDK_PATH);$(EMSDK_PATH)\clang\$(CLANG_VERSION);$(EMSDK_PATH)\node\$(NODE_VERSION)\bin;$(EMSDK_PATH)\python\$(PYTHON_VERSION);$(EMSDK_PATH)\emscripten\$(EMSCRIPTEN_VERSION);C:\raylib\MinGW\bin:$$(PATH)
+ EMSCRIPTEN = $(EMSDK_PATH)\emscripten\$(EMSCRIPTEN_VERSION)
+endif
+
+RAYLIB_RELEASE_PATH ?= $(RAYLIB_PATH)/release/libs
+
+# Define raylib release directory for compiled library
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+ ifeq ($(PLATFORM_OS),WINDOWS)
+ RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)/release/libs/win32/mingw32
+ endif
+ ifeq ($(PLATFORM_OS),LINUX)
+ RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)/release/libs/linux
+ endif
+ ifeq ($(PLATFORM_OS),OSX)
+ RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)/release/libs/osx
+ endif
+ ifeq ($(PLATFORM_OS),BSD)
+ RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)/release/libs/bsd
+ endif
+endif
+ifeq ($(PLATFORM),PLATFORM_RPI)
+ RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)/release/libs/rpi
+endif
+ifeq ($(PLATFORM),PLATFORM_WEB)
+ RAYLIB_RELEASE_PATH = .
+endif
+
+# 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
+ 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 enable debugging
+# -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 += -O1 -s -Wall -std=c99 -D_DEFAULT_SOURCE -Wno-missing-braces
+
+# Additional flags for compiler (if desired)
+#CFLAGS += -Wextra -Wmissing-prototypes -Wstrict-prototypes
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+ ifeq ($(PLATFORM_OS),WINDOWS)
+ # resources file contains windows exe icon
+ # -Wl,--subsystem,windows hides the console window
+ CFLAGS += $(RAYLIB_PATH)/raylib.rc.data -Wl,--subsystem,windows
+ endif
+ ifeq ($(PLATFORM_OS),LINUX)
+ CFLAGS += -D_DEFAULT_SOURCE
+ endif
+endif
+ifeq ($(PLATFORM),PLATFORM_RPI)
+ CFLAGS += -std=gnu99
+endif
+ifeq ($(PLATFORM),PLATFORM_WEB)
+ # -O2 # if used, also set --memory-init-file 0
+ # --memory-init-file 0 # to avoid an external memory initialization code file (.mem)
+ # -s ALLOW_MEMORY_GROWTH=1 # to allow memory resizing
+ # -s TOTAL_MEMORY=16777216 # to specify heap memory size (default = 16MB)
+ # -s USE_PTHREADS=1 # multithreading support
+ # -s WASM=1 # support Web Assembly (https://github.com/kripken/emscripten/wiki/WebAssembly)
+ # --preload-file resources # specify a resources folder for data compilation
+ # -s ASSERTIONS=1 --profiling
+ CFLAGS += -s USE_GLFW=3 -s WASM=1 -s TOTAL_MEMORY=16777216 --preload-file resources
+
+ # Define a custom shell .html and output extension
+ CFLAGS += --shell-file $(RAYLIB_PATH)\templates\web_shell\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)/release/include -I$(RAYLIB_PATH)/src -I$(RAYLIB_PATH)/src/external
+
+# Define additional directories containing required header files
+ifeq ($(PLATFORM),PLATFORM_RPI)
+ # RPI requried 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
+
+# Define library paths containing required libs
+LDFLAGS = -L. -L$(RAYLIB_RELEASE_PATH) -L$(RAYLIB_PATH)/src
+
+ifeq ($(PLATFORM),PLATFORM_DESKTOP)
+ ifeq ($(PLATFORM_OS),BSD)
+ INCLUDE_PATHS += -I/usr/local/include
+ LDFLAGS += -L. -Lsrc -L/usr/local/lib
+ 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
+ LDLIBS = -lraylib -lopengl32 -lgdi32
+
+ # 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
+ 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 OpenAL -framework Cocoa
+ 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 ?= roomba.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' | rev | cut -d ':' -f 2- | rev | xargs rm -f
+ 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 -f *.o
+endif
+ifeq ($(PLATFORM),PLATFORM_WEB)
+ del *.o *.html *.js
+endif
+ @echo Cleaning done
+
diff --git a/games/cat_vs_roomba/Makefile.Android b/games/cat_vs_roomba/Makefile.Android
new file mode 100644
index 000000000..b6c68417f
--- /dev/null
+++ b/games/cat_vs_roomba/Makefile.Android
@@ -0,0 +1,298 @@
+#**************************************************************************************************
+#
+# raylib makefile for Android project (APK building)
+#
+# Copyright (c) 2017 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.
+#
+#**************************************************************************************************
+
+# Define required raylib variables
+PLATFORM ?= PLATFORM_ANDROID
+RAYLIB_PATH ?= ..\..
+
+# Define Android architecture (armeabi-v7a, arm64-v8a, x86, x86-64) and API version
+ANDROID_ARCH ?= ARM
+ANDROID_API_VERSION = 21
+ifeq ($(ANDROID_ARCH),ARM)
+ ANDROID_ARCH_NAME = armeabi-v7a
+endif
+ifeq ($(ANDROID_ARCH),ARM64)
+ ANDROID_ARCH_NAME = arm64-v8a
+endif
+
+# Required path variables
+# NOTE: JAVA_HOME must be set to JDK
+JAVA_HOME ?= C:/JavaJDK
+ANDROID_HOME = C:/android-sdk
+ANDROID_TOOLCHAIN = C:/android_toolchain_$(ANDROID_ARCH)_API$(ANDROID_API_VERSION)
+ANDROID_BUILD_TOOLS = $(ANDROID_HOME)/build-tools/28.0.1
+ANDROID_PLATFORM_TOOLS = $(ANDROID_HOME)/platform-tools
+
+# Android project configuration variables
+PROJECT_NAME ?= raylib_game
+PROJECT_LIBRARY_NAME ?= main
+PROJECT_BUILD_PATH ?= android.$(PROJECT_NAME)
+PROJECT_RESOURCES_PATH ?= resources
+PROJECT_SOURCE_FILES ?= raylib_game.c
+
+# Some source files are placed in directories, when compiling to some
+# output directory other than source, that directory must pre-exist.
+# Here we get a list of required folders that need to be created on
+# code output folder $(PROJECT_BUILD_PATH)\obj to avoid GCC errors.
+PROJECT_SOURCE_DIRS = $(sort $(dir $(PROJECT_SOURCE_FILES)))
+
+# Android app configuration variables
+APP_LABEL_NAME ?= rGame
+APP_COMPANY_NAME ?= raylib
+APP_PRODUCT_NAME ?= rgame
+APP_VERSION_CODE ?= 1
+APP_VERSION_NAME ?= 1.0
+APP_ICON_LDPI ?= $(RAYLIB_PATH)\logo\raylib_36x36.png
+APP_ICON_MDPI ?= $(RAYLIB_PATH)\logo\raylib_48x48.png
+APP_ICON_HDPI ?= $(RAYLIB_PATH)\logo\raylib_72x72.png
+APP_SCREEN_ORIENTATION ?= landscape
+APP_KEYSTORE_PASS ?= raylib
+
+# Library type used for raylib: STATIC (.a) or SHARED (.so/.dll)
+RAYLIB_LIBTYPE ?= STATIC
+RAYLIB_LIB_PATH = $(RAYLIB_PATH)\release\libs\android\$(ANDROID_ARCH_NAME)
+
+# Shared libs must be added to APK if required
+# NOTE: Generated NativeLoader.java automatically load those libraries
+ifeq ($(RAYLIB_LIBTYPE),SHARED)
+ PROJECT_SHARED_LIBS = lib/$(ANDROID_ARCH_NAME)/libraylib.so
+endif
+
+# Compiler and archiver
+# NOTE: GCC is being deprecated in Android NDK r16
+ifeq ($(ANDROID_ARCH),ARM)
+ CC = $(ANDROID_TOOLCHAIN)/bin/arm-linux-androideabi-clang
+ AR = $(ANDROID_TOOLCHAIN)/bin/arm-linux-androideabi-ar
+endif
+ifeq ($(ANDROID_ARCH),ARM64)
+ CC = $(ANDROID_TOOLCHAIN)/bin/aarch64-linux-android-clang
+ AR = $(ANDROID_TOOLCHAIN)/bin/aarch64-linux-android-ar
+endif
+
+# Compiler flags for arquitecture
+ifeq ($(ANDROID_ARCH),ARM)
+ CFLAGS = -std=c99 -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16
+endif
+ifeq ($(ANDROID_ARCH),ARM64)
+ CFLAGS = -std=c99 -target aarch64 -mfix-cortex-a53-835769
+endif
+# Compilation functions attributes options
+CFLAGS += -ffunction-sections -funwind-tables -fstack-protector-strong -fPIC
+# Compiler options for the linker
+CFLAGS += -Wall -Wa,--noexecstack -Wformat -Werror=format-security -no-canonical-prefixes
+# Preprocessor macro definitions
+CFLAGS += -DANDROID -DPLATFORM_ANDROID -D__ANDROID_API__=$(ANDROID_API_VERSION)
+
+# Paths containing required header files
+INCLUDE_PATHS = -I. -I$(RAYLIB_PATH)/release/include -I$(RAYLIB_PATH)/src/external/android/native_app_glue
+
+# Linker options
+LDFLAGS = -Wl,-soname,lib$(PROJECT_LIBRARY_NAME).so -Wl,--exclude-libs,libatomic.a
+LDFLAGS += -Wl,--build-id -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -Wl,--warn-shared-textrel -Wl,--fatal-warnings
+# Force linking of library module to define symbol
+LDFLAGS += -u ANativeActivity_onCreate
+# Library paths containing required libs
+LDFLAGS += -L. -L$(PROJECT_BUILD_PATH)/obj -L$(PROJECT_BUILD_PATH)/lib/$(ANDROID_ARCH_NAME) -L$(ANDROID_TOOLCHAIN)\sysroot\usr\lib
+
+# Define any libraries to link into executable
+# if you want to link libraries (libname.so or libname.a), use the -lname
+LDLIBS = -lm -lc -lraylib -llog -landroid -lEGL -lGLESv2 -lOpenSLES -ldl
+
+# Generate target objects list from PROJECT_SOURCE_FILES
+OBJS = $(patsubst %.c, $(PROJECT_BUILD_PATH)/obj/%.o, $(PROJECT_SOURCE_FILES))
+
+# Android APK building process... some steps required...
+# NOTE: typing 'make' will invoke the default target entry called 'all',
+all: create_temp_project_dirs \
+ copy_project_required_libs \
+ copy_project_resources \
+ generate_loader_script \
+ generate_android_manifest \
+ generate_apk_keystore \
+ config_project_package \
+ compile_project_code \
+ compile_project_class \
+ compile_project_class_dex \
+ create_project_apk_package \
+ sign_project_apk_package \
+ zipalign_project_apk_package
+
+# Create required temp directories for APK building
+create_temp_project_dirs:
+ if not exist $(PROJECT_BUILD_PATH) mkdir $(PROJECT_BUILD_PATH)
+ if not exist $(PROJECT_BUILD_PATH)\obj mkdir $(PROJECT_BUILD_PATH)\obj
+ if not exist $(PROJECT_BUILD_PATH)\src mkdir $(PROJECT_BUILD_PATH)\src
+ if not exist $(PROJECT_BUILD_PATH)\src\com mkdir $(PROJECT_BUILD_PATH)\src\com
+ if not exist $(PROJECT_BUILD_PATH)\src\com\$(APP_COMPANY_NAME) mkdir $(PROJECT_BUILD_PATH)\src\com\$(APP_COMPANY_NAME)
+ if not exist $(PROJECT_BUILD_PATH)\src\com\$(APP_COMPANY_NAME)\$(APP_PRODUCT_NAME) mkdir $(PROJECT_BUILD_PATH)\src\com\$(APP_COMPANY_NAME)\$(APP_PRODUCT_NAME)
+ if not exist $(PROJECT_BUILD_PATH)\lib mkdir $(PROJECT_BUILD_PATH)\lib
+ if not exist $(PROJECT_BUILD_PATH)\lib\$(ANDROID_ARCH_NAME) mkdir $(PROJECT_BUILD_PATH)\lib\$(ANDROID_ARCH_NAME)
+ if not exist $(PROJECT_BUILD_PATH)\bin mkdir $(PROJECT_BUILD_PATH)\bin
+ if not exist $(PROJECT_BUILD_PATH)\res mkdir $(PROJECT_BUILD_PATH)\res
+ if not exist $(PROJECT_BUILD_PATH)\res\drawable-ldpi mkdir $(PROJECT_BUILD_PATH)\res\drawable-ldpi
+ if not exist $(PROJECT_BUILD_PATH)\res\drawable-mdpi mkdir $(PROJECT_BUILD_PATH)\res\drawable-mdpi
+ if not exist $(PROJECT_BUILD_PATH)\res\drawable-hdpi mkdir $(PROJECT_BUILD_PATH)\res\drawable-hdpi
+ if not exist $(PROJECT_BUILD_PATH)\res\values mkdir $(PROJECT_BUILD_PATH)\res\values
+ if not exist $(PROJECT_BUILD_PATH)\assets mkdir $(PROJECT_BUILD_PATH)\assets
+ if not exist $(PROJECT_BUILD_PATH)\assets\$(PROJECT_RESOURCES_PATH) mkdir $(PROJECT_BUILD_PATH)\assets\$(PROJECT_RESOURCES_PATH)
+ if not exist $(PROJECT_BUILD_PATH)\obj\screens mkdir $(PROJECT_BUILD_PATH)\obj\screens
+ $(foreach dir, $(PROJECT_SOURCE_DIRS), $(call create_dir, $(dir)))
+
+define create_dir
+ if not exist $(PROJECT_BUILD_PATH)\obj\$(1) mkdir $(PROJECT_BUILD_PATH)\obj\$(1)
+endef
+
+# Copy required shared libs for integration into APK
+# NOTE: If using shared libs they are loaded by generated NativeLoader.java
+copy_project_required_libs:
+ifeq ($(RAYLIB_LIBTYPE),SHARED)
+ copy /Y $(RAYLIB_LIB_PATH)\libraylib.so $(PROJECT_BUILD_PATH)\lib\$(ANDROID_ARCH_NAME)\libraylib.so
+endif
+ifeq ($(RAYLIB_LIBTYPE),STATIC)
+ copy /Y $(RAYLIB_LIB_PATH)\libraylib.a $(PROJECT_BUILD_PATH)\lib\$(ANDROID_ARCH_NAME)\libraylib.a
+endif
+
+# Copy project required resources: strings.xml, icon.png, assets
+# NOTE: Required strings.xml is generated and game resources are copied to assets folder
+# TODO: Review xcopy usage, it can not be found in some systems!
+copy_project_resources:
+ copy $(APP_ICON_LDPI) $(PROJECT_BUILD_PATH)\res\drawable-ldpi\icon.png /Y
+ copy $(APP_ICON_MDPI) $(PROJECT_BUILD_PATH)\res\drawable-mdpi\icon.png /Y
+ copy $(APP_ICON_HDPI) $(PROJECT_BUILD_PATH)\res\drawable-hdpi\icon.png /Y
+ @echo ^ > $(PROJECT_BUILD_PATH)/res/values/strings.xml
+ @echo ^^$(APP_LABEL_NAME)^^ >> $(PROJECT_BUILD_PATH)/res/values/strings.xml
+ if exist $(PROJECT_RESOURCES_PATH) C:\Windows\System32\xcopy $(PROJECT_RESOURCES_PATH) $(PROJECT_BUILD_PATH)\assets\$(PROJECT_RESOURCES_PATH) /Y /E /F
+
+# Generate NativeLoader.java to load required shared libraries
+# NOTE: Probably not the bet way to generate this file... but it works.
+generate_loader_script:
+ @echo package com.$(APP_COMPANY_NAME).$(APP_PRODUCT_NAME); > $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java
+ @echo. >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java
+ @echo public class NativeLoader extends android.app.NativeActivity { >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java
+ @echo static { >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java
+ifeq ($(RAYLIB_LIBTYPE),SHARED)
+ @echo System.loadLibrary("raylib"); >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java
+endif
+ @echo System.loadLibrary("$(PROJECT_LIBRARY_NAME)"); >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java
+ @echo } >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java
+ @echo } >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java
+
+# Generate AndroidManifest.xml with all the required options
+# NOTE: Probably not the bet way to generate this file... but it works.
+generate_android_manifest:
+ @echo ^ > $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo package="com.$(APP_COMPANY_NAME).$(APP_PRODUCT_NAME)" >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo android:versionCode="$(APP_VERSION_CODE)" android:versionName="$(APP_VERSION_NAME)" ^> >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo android:configChanges="orientation|keyboardHidden|screenSize" >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo android:screenOrientation="$(APP_SCREEN_ORIENTATION)" android:launchMode="singleTask" >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo android:clearTaskOnLaunch="true"^> >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+ @echo ^ >> $(PROJECT_BUILD_PATH)/AndroidManifest.xml
+
+# Generate storekey for APK signing: $(PROJECT_NAME).keystore
+# NOTE: Configure here your Distinguished Names (-dname) if required!
+generate_apk_keystore:
+ if not exist $(PROJECT_BUILD_PATH)/$(PROJECT_NAME).keystore $(JAVA_HOME)/bin/keytool -genkeypair -validity 1000 -dname "CN=$(APP_COMPANY_NAME),O=Android,C=ES" -keystore $(PROJECT_BUILD_PATH)/$(PROJECT_NAME).keystore -storepass $(APP_KEYSTORE_PASS) -keypass $(APP_KEYSTORE_PASS) -alias $(PROJECT_NAME)Key -keyalg RSA
+
+# Config project package and resource using AndroidManifest.xml and res/values/strings.xml
+# NOTE: Generates resources file: src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/R.java
+config_project_package:
+ $(ANDROID_BUILD_TOOLS)/aapt package -f -m -S $(PROJECT_BUILD_PATH)/res -J $(PROJECT_BUILD_PATH)/src -M $(PROJECT_BUILD_PATH)/AndroidManifest.xml -I $(ANDROID_HOME)/platforms/android-$(ANDROID_API_VERSION)/android.jar
+
+# Compile native_app_glue code as static library: obj/libnative_app_glue.a
+compile_native_app_glue:
+ $(CC) -c $(RAYLIB_PATH)/src/external/android/native_app_glue/android_native_app_glue.c -o $(PROJECT_BUILD_PATH)/obj/native_app_glue.o $(CFLAGS)
+ $(AR) rcs $(PROJECT_BUILD_PATH)/obj/libnative_app_glue.a $(PROJECT_BUILD_PATH)/obj/native_app_glue.o
+
+# Compile project code into a shared library: lib/lib$(PROJECT_LIBRARY_NAME).so
+compile_project_code: $(OBJS)
+ $(CC) -o $(PROJECT_BUILD_PATH)/lib/$(ANDROID_ARCH_NAME)/lib$(PROJECT_LIBRARY_NAME).so $(OBJS) -shared $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS)
+
+# Compile all .c files required into object (.o) files
+# NOTE: Those files will be linked into a shared library
+$(PROJECT_BUILD_PATH)/obj/%.o:%.c
+ $(CC) -c $^ -o $@ $(INCLUDE_PATHS) $(CFLAGS) --sysroot=$(ANDROID_TOOLCHAIN)/sysroot
+
+# Compile project .java code into .class (Java bytecode)
+compile_project_class:
+ $(JAVA_HOME)/bin/javac -verbose -source 1.7 -target 1.7 -d $(PROJECT_BUILD_PATH)/obj -bootclasspath $(JAVA_HOME)/jre/lib/rt.jar -classpath $(ANDROID_HOME)/platforms/android-$(ANDROID_API_VERSION)/android.jar;$(PROJECT_BUILD_PATH)/obj -sourcepath $(PROJECT_BUILD_PATH)/src $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/R.java $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java
+
+# Compile .class files into Dalvik executable bytecode (.dex)
+# NOTE: Since Android 5.0, Dalvik interpreter (JIT) has been replaced by ART (AOT)
+compile_project_class_dex:
+ $(ANDROID_BUILD_TOOLS)/dx --verbose --dex --output=$(PROJECT_BUILD_PATH)/bin/classes.dex $(PROJECT_BUILD_PATH)/obj
+
+# Create Android APK package: bin/$(PROJECT_NAME).unsigned.apk
+# NOTE: Requires compiled classes.dex and lib$(PROJECT_LIBRARY_NAME).so
+# NOTE: Use -A resources to define additional directory in which to find raw asset files
+create_project_apk_package:
+ $(ANDROID_BUILD_TOOLS)/aapt package -f -M $(PROJECT_BUILD_PATH)/AndroidManifest.xml -S $(PROJECT_BUILD_PATH)/res -A $(PROJECT_BUILD_PATH)/assets -I $(ANDROID_HOME)/platforms/android-$(ANDROID_API_VERSION)/android.jar -F $(PROJECT_BUILD_PATH)/bin/$(PROJECT_NAME).unsigned.apk $(PROJECT_BUILD_PATH)/bin
+ cd $(PROJECT_BUILD_PATH) && $(ANDROID_BUILD_TOOLS)/aapt add bin/$(PROJECT_NAME).unsigned.apk lib/$(ANDROID_ARCH_NAME)/lib$(PROJECT_LIBRARY_NAME).so $(PROJECT_SHARED_LIBS)
+
+# Create signed APK package using generated Key: bin/$(PROJECT_NAME).signed.apk
+sign_project_apk_package:
+ $(JAVA_HOME)/bin/jarsigner -keystore $(PROJECT_BUILD_PATH)/$(PROJECT_NAME).keystore -storepass $(APP_KEYSTORE_PASS) -keypass $(APP_KEYSTORE_PASS) -signedjar $(PROJECT_BUILD_PATH)/bin/$(PROJECT_NAME).signed.apk $(PROJECT_BUILD_PATH)/bin/$(PROJECT_NAME).unsigned.apk $(PROJECT_NAME)Key
+
+# Create zip-aligned APK package: $(PROJECT_NAME).apk
+zipalign_project_apk_package:
+ $(ANDROID_BUILD_TOOLS)/zipalign -f 4 $(PROJECT_BUILD_PATH)/bin/$(PROJECT_NAME).signed.apk $(PROJECT_NAME).apk
+
+# Install $(PROJECT_NAME).apk to default emulator/device
+# NOTE: Use -e (emulator) or -d (device) parameters if required
+install:
+ $(ANDROID_PLATFORM_TOOLS)/adb install --abi $(ANDROID_ARCH_NAME) -rds $(PROJECT_NAME).apk
+
+# Check supported ABI for the device (armeabi-v7a, arm64-v8a, x86, x86_64)
+check_device_abi:
+ $(ANDROID_PLATFORM_TOOLS)/adb shell getprop ro.product.cpu.abi
+
+# Monitorize output log coming from device, only raylib tag
+logcat:
+ $(ANDROID_PLATFORM_TOOLS)/adb logcat -c
+ $(ANDROID_PLATFORM_TOOLS)/adb logcat raylib:V *:S
+
+# Install and monitorize $(PROJECT_NAME).apk to default emulator/device
+deploy:
+ $(ANDROID_PLATFORM_TOOLS)/adb install -r $(PROJECT_NAME).apk
+ $(ANDROID_PLATFORM_TOOLS)/adb logcat -c
+ $(ANDROID_PLATFORM_TOOLS)/adb logcat raylib:V *:S
+
+#$(ANDROID_PLATFORM_TOOLS)/adb logcat *:W
+
+# Clean everything
+clean:
+ del $(PROJECT_BUILD_PATH)\* /f /s /q
+ rmdir $(PROJECT_BUILD_PATH) /s /q
+ @echo Cleaning done
diff --git a/games/cat_vs_roomba/libraylib.bc b/games/cat_vs_roomba/libraylib.bc
new file mode 100644
index 000000000..4febf88dd
Binary files /dev/null and b/games/cat_vs_roomba/libraylib.bc differ
diff --git a/games/cat_vs_roomba/raymath.h b/games/cat_vs_roomba/raymath.h
new file mode 100644
index 000000000..42a45ad6c
--- /dev/null
+++ b/games/cat_vs_roomba/raymath.h
@@ -0,0 +1,1378 @@
+/**********************************************************************************************
+*
+* raymath v1.2 - Math functions to work with Vector3, Matrix and Quaternions
+*
+* CONFIGURATION:
+*
+* #define RAYMATH_IMPLEMENTATION
+* Generates the implementation of the library into the included file.
+* If not defined, the library is in header only mode and can be included in other headers
+* or source files without problems. But only ONE file should hold the implementation.
+*
+* #define RAYMATH_HEADER_ONLY
+* Define static inline functions code, so #include header suffices for use.
+* This may use up lots of memory.
+*
+* #define RAYMATH_STANDALONE
+* Avoid raylib.h header inclusion in this file.
+* Vector3 and Matrix data types are defined internally in raymath module.
+*
+*
+* LICENSE: zlib/libpng
+*
+* Copyright (c) 2015-2017 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 RAYMATH_H
+#define RAYMATH_H
+
+//#define RAYMATH_STANDALONE // NOTE: To use raymath as standalone lib, just uncomment this line
+//#define RAYMATH_HEADER_ONLY // NOTE: To compile functions as static inline, uncomment this line
+
+#ifndef RAYMATH_STANDALONE
+ #include "raylib.h" // Required for structs: Vector3, Matrix
+#endif
+
+#if defined(RAYMATH_IMPLEMENTATION) && defined(RAYMATH_HEADER_ONLY)
+ #error "Specifying both RAYMATH_IMPLEMENTATION and RAYMATH_HEADER_ONLY is contradictory"
+#endif
+
+#if defined(RAYMATH_IMPLEMENTATION)
+ #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED)
+ #define RMDEF __declspec(dllexport) extern inline // We are building raylib as a Win32 shared library (.dll).
+ #elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED)
+ #define RMDEF __declspec(dllimport) // We are using raylib as a Win32 shared library (.dll)
+ #else
+ #define RMDEF extern inline // Provide external definition
+ #endif
+#elif defined(RAYMATH_HEADER_ONLY)
+ #define RMDEF static inline // Functions may be inlined, no external out-of-line definition
+#else
+ #if defined(__TINYC__)
+ #define RMDEF static inline // plain inline not supported by tinycc (See issue #435)
+ #else
+ #define RMDEF inline // Functions may be inlined or external definition used
+ #endif
+#endif
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#ifndef PI
+ #define PI 3.14159265358979323846
+#endif
+
+#ifndef DEG2RAD
+ #define DEG2RAD (PI/180.0f)
+#endif
+
+#ifndef RAD2DEG
+ #define RAD2DEG (180.0f/PI)
+#endif
+
+// Return float vector for Matrix
+#ifndef MatrixToFloat
+ #define MatrixToFloat(mat) (MatrixToFloatV(mat).v)
+#endif
+
+// Return float vector for Vector3
+#ifndef Vector3ToFloat
+ #define Vector3ToFloat(vec) (Vector3ToFloatV(vec).v)
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+
+#if defined(RAYMATH_STANDALONE)
+ // Vector2 type
+ typedef struct Vector2 {
+ float x;
+ float y;
+ } Vector2;
+
+ // Vector3 type
+ typedef struct Vector3 {
+ float x;
+ float y;
+ float z;
+ } Vector3;
+
+ // Quaternion type
+ typedef struct Quaternion {
+ float x;
+ float y;
+ float z;
+ float w;
+ } Quaternion;
+
+ // Matrix type (OpenGL style 4x4 - right handed, column major)
+ typedef struct Matrix {
+ float m0, m4, m8, m12;
+ float m1, m5, m9, m13;
+ float m2, m6, m10, m14;
+ float m3, m7, m11, m15;
+ } Matrix;
+#endif
+
+// NOTE: Helper types to be used instead of array return types for *ToFloat functions
+typedef struct float3 { float v[3]; } float3;
+typedef struct float16 { float v[16]; } float16;
+
+#include // Required for: sinf(), cosf(), tan(), fabs()
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition - Utils math
+//----------------------------------------------------------------------------------
+
+// Clamp float value
+RMDEF float Clamp(float value, float min, float max)
+{
+ const float res = value < min ? min : value;
+ return res > max ? max : res;
+}
+
+// Calculate linear interpolation between two vectors
+RMDEF float Lerp(float start, float end, float amount)
+{
+ return start + amount*(end - start);
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition - Vector2 math
+//----------------------------------------------------------------------------------
+
+// Vector with components value 0.0f
+RMDEF Vector2 Vector2Zero(void)
+{
+ Vector2 result = { 0.0f, 0.0f };
+ return result;
+}
+
+// Vector with components value 1.0f
+RMDEF Vector2 Vector2One(void)
+{
+ Vector2 result = { 1.0f, 1.0f };
+ return result;
+}
+
+// Add two vectors (v1 + v2)
+RMDEF Vector2 Vector2Add(Vector2 v1, Vector2 v2)
+{
+ Vector2 result = { v1.x + v2.x, v1.y + v2.y };
+ return result;
+}
+
+// Subtract two vectors (v1 - v2)
+RMDEF Vector2 Vector2Subtract(Vector2 v1, Vector2 v2)
+{
+ Vector2 result = { v1.x - v2.x, v1.y - v2.y };
+ return result;
+}
+
+// Calculate vector length
+RMDEF float Vector2Length(Vector2 v)
+{
+ float result = sqrtf((v.x*v.x) + (v.y*v.y));
+ return result;
+}
+
+// Calculate two vectors dot product
+RMDEF float Vector2DotProduct(Vector2 v1, Vector2 v2)
+{
+ float result = (v1.x*v2.x + v1.y*v2.y);
+ return result;
+}
+
+// Calculate distance between two vectors
+RMDEF float Vector2Distance(Vector2 v1, Vector2 v2)
+{
+ float result = sqrtf((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y));
+ return result;
+}
+
+// Calculate angle from two vectors in X-axis
+RMDEF float Vector2Angle(Vector2 v1, Vector2 v2)
+{
+ float result = atan2f(v2.y - v1.y, v2.x - v1.x)*(180.0f/PI);
+ if (result < 0) result += 360.0f;
+ return result;
+}
+
+// Scale vector (multiply by value)
+RMDEF Vector2 Vector2Scale(Vector2 v, float scale)
+{
+ Vector2 result = { v.x*scale, v.y*scale };
+ return result;
+}
+
+// Multiply vector by vector
+RMDEF Vector2 Vector2MultiplyV(Vector2 v1, Vector2 v2)
+{
+ Vector2 result = { v1.x*v2.x, v1.y*v2.y };
+ return result;
+}
+
+// Negate vector
+RMDEF Vector2 Vector2Negate(Vector2 v)
+{
+ Vector2 result = { -v.x, -v.y };
+ return result;
+}
+
+// Divide vector by a float value
+RMDEF Vector2 Vector2Divide(Vector2 v, float div)
+{
+ Vector2 result = { v.x/div, v.y/div };
+ return result;
+}
+
+// Divide vector by vector
+RMDEF Vector2 Vector2DivideV(Vector2 v1, Vector2 v2)
+{
+ Vector2 result = { v1.x/v2.x, v1.y/v2.y };
+ return result;
+}
+
+// Normalize provided vector
+RMDEF Vector2 Vector2Normalize(Vector2 v)
+{
+ Vector2 result = Vector2Divide(v, Vector2Length(v));
+ return result;
+}
+
+// Calculate linear interpolation between two vectors
+RMDEF Vector2 Vector2Lerp(Vector2 v1, Vector2 v2, float amount)
+{
+ Vector2 result = { 0 };
+
+ result.x = v1.x + amount*(v2.x - v1.x);
+ result.y = v1.y + amount*(v2.y - v1.y);
+
+ return result;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition - Vector3 math
+//----------------------------------------------------------------------------------
+
+// Vector with components value 0.0f
+RMDEF Vector3 Vector3Zero(void)
+{
+ Vector3 result = { 0.0f, 0.0f, 0.0f };
+ return result;
+}
+
+// Vector with components value 1.0f
+RMDEF Vector3 Vector3One(void)
+{
+ Vector3 result = { 1.0f, 1.0f, 1.0f };
+ return result;
+}
+
+// Add two vectors
+RMDEF Vector3 Vector3Add(Vector3 v1, Vector3 v2)
+{
+ Vector3 result = { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z };
+ return result;
+}
+
+// Subtract two vectors
+RMDEF Vector3 Vector3Subtract(Vector3 v1, Vector3 v2)
+{
+ Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z };
+ return result;
+}
+
+// Multiply vector by scalar
+RMDEF Vector3 Vector3Multiply(Vector3 v, float scalar)
+{
+ Vector3 result = { v.x*scalar, v.y*scalar, v.z*scalar };
+ return result;
+}
+
+// Multiply vector by vector
+RMDEF Vector3 Vector3MultiplyV(Vector3 v1, Vector3 v2)
+{
+ Vector3 result = { v1.x*v2.x, v1.y*v2.y, v1.z*v2.z };
+ return result;
+}
+
+// Calculate two vectors cross product
+RMDEF Vector3 Vector3CrossProduct(Vector3 v1, Vector3 v2)
+{
+ Vector3 result = { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x };
+ return result;
+}
+
+// Calculate one vector perpendicular vector
+RMDEF Vector3 Vector3Perpendicular(Vector3 v)
+{
+ Vector3 result = { 0 };
+
+ float min = (float) fabs(v.x);
+ Vector3 cardinalAxis = {1.0f, 0.0f, 0.0f};
+
+ if (fabs(v.y) < min)
+ {
+ min = (float) fabs(v.y);
+ Vector3 tmp = {0.0f, 1.0f, 0.0f};
+ cardinalAxis = tmp;
+ }
+
+ if (fabs(v.z) < min)
+ {
+ Vector3 tmp = {0.0f, 0.0f, 1.0f};
+ cardinalAxis = tmp;
+ }
+
+ result = Vector3CrossProduct(v, cardinalAxis);
+
+ return result;
+}
+
+// Calculate vector length
+RMDEF float Vector3Length(const Vector3 v)
+{
+ float result = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);
+ return result;
+}
+
+// Calculate two vectors dot product
+RMDEF float Vector3DotProduct(Vector3 v1, Vector3 v2)
+{
+ float result = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);
+ return result;
+}
+
+// Calculate distance between two vectors
+RMDEF float Vector3Distance(Vector3 v1, Vector3 v2)
+{
+ float dx = v2.x - v1.x;
+ float dy = v2.y - v1.y;
+ float dz = v2.z - v1.z;
+ float result = sqrtf(dx*dx + dy*dy + dz*dz);
+ return result;
+}
+
+// Scale provided vector
+RMDEF Vector3 Vector3Scale(Vector3 v, float scale)
+{
+ Vector3 result = { v.x*scale, v.y*scale, v.z*scale };
+ return result;
+}
+
+// Negate provided vector (invert direction)
+RMDEF Vector3 Vector3Negate(Vector3 v)
+{
+ Vector3 result = { -v.x, -v.y, -v.z };
+ return result;
+}
+
+// Divide vector by a float value
+RMDEF Vector3 Vector3Divide(Vector3 v, float div)
+{
+ Vector3 result = { v.x / div, v.y / div, v.z / div };
+ return result;
+}
+
+// Divide vector by vector
+RMDEF Vector3 Vector3DivideV(Vector3 v1, Vector3 v2)
+{
+ Vector3 result = { v1.x/v2.x, v1.y/v2.y, v1.z/v2.z };
+ return result;
+}
+
+// Normalize provided vector
+RMDEF Vector3 Vector3Normalize(Vector3 v)
+{
+ Vector3 result = v;
+
+ float length, ilength;
+ length = Vector3Length(v);
+ if (length == 0.0f) length = 1.0f;
+ ilength = 1.0f/length;
+
+ result.x *= ilength;
+ result.y *= ilength;
+ result.z *= ilength;
+
+ return result;
+}
+
+// Orthonormalize provided vectors
+// Makes vectors normalized and orthogonal to each other
+// Gram-Schmidt function implementation
+RMDEF void Vector3OrthoNormalize(Vector3 *v1, Vector3 *v2)
+{
+ *v1 = Vector3Normalize(*v1);
+ Vector3 vn = Vector3CrossProduct(*v1, *v2);
+ vn = Vector3Normalize(vn);
+ *v2 = Vector3CrossProduct(vn, *v1);
+}
+
+// Transforms a Vector3 by a given Matrix
+RMDEF Vector3 Vector3Transform(Vector3 v, Matrix mat)
+{
+ Vector3 result = { 0 };
+ float x = v.x;
+ float y = v.y;
+ float z = v.z;
+
+ result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12;
+ result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13;
+ result.z = mat.m2*x + mat.m6*y + mat.m10*z + mat.m14;
+
+ return result;
+};
+
+// Transform a vector by quaternion rotation
+RMDEF Vector3 Vector3RotateByQuaternion(Vector3 v, Quaternion q)
+{
+ Vector3 result = { 0 };
+
+ result.x = v.x*(q.x*q.x + q.w*q.w - q.y*q.y - q.z*q.z) + v.y*(2*q.x*q.y - 2*q.w*q.z) + v.z*(2*q.x*q.z + 2*q.w*q.y);
+ result.y = v.x*(2*q.w*q.z + 2*q.x*q.y) + v.y*(q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z) + v.z*(-2*q.w*q.x + 2*q.y*q.z);
+ result.z = v.x*(-2*q.w*q.y + 2*q.x*q.z) + v.y*(2*q.w*q.x + 2*q.y*q.z)+ v.z*(q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z);
+
+ return result;
+}
+
+// Calculate linear interpolation between two vectors
+RMDEF Vector3 Vector3Lerp(Vector3 v1, Vector3 v2, float amount)
+{
+ Vector3 result = { 0 };
+
+ result.x = v1.x + amount*(v2.x - v1.x);
+ result.y = v1.y + amount*(v2.y - v1.y);
+ result.z = v1.z + amount*(v2.z - v1.z);
+
+ return result;
+}
+
+// Calculate reflected vector to normal
+RMDEF Vector3 Vector3Reflect(Vector3 v, Vector3 normal)
+{
+ // I is the original vector
+ // N is the normal of the incident plane
+ // R = I - (2*N*( DotProduct[ I,N] ))
+
+ Vector3 result = { 0 };
+
+ float dotProduct = Vector3DotProduct(v, normal);
+
+ result.x = v.x - (2.0f*normal.x)*dotProduct;
+ result.y = v.y - (2.0f*normal.y)*dotProduct;
+ result.z = v.z - (2.0f*normal.z)*dotProduct;
+
+ return result;
+}
+
+// Return min value for each pair of components
+RMDEF Vector3 Vector3Min(Vector3 v1, Vector3 v2)
+{
+ Vector3 result = { 0 };
+
+ result.x = fminf(v1.x, v2.x);
+ result.y = fminf(v1.y, v2.y);
+ result.z = fminf(v1.z, v2.z);
+
+ return result;
+}
+
+// Return max value for each pair of components
+RMDEF Vector3 Vector3Max(Vector3 v1, Vector3 v2)
+{
+ Vector3 result = { 0 };
+
+ result.x = fmaxf(v1.x, v2.x);
+ result.y = fmaxf(v1.y, v2.y);
+ result.z = fmaxf(v1.z, v2.z);
+
+ return result;
+}
+
+// Compute barycenter coordinates (u, v, w) for point p with respect to triangle (a, b, c)
+// NOTE: Assumes P is on the plane of the triangle
+RMDEF Vector3 Vector3Barycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c)
+{
+ //Vector v0 = b - a, v1 = c - a, v2 = p - a;
+
+ Vector3 v0 = Vector3Subtract(b, a);
+ Vector3 v1 = Vector3Subtract(c, a);
+ Vector3 v2 = Vector3Subtract(p, a);
+ float d00 = Vector3DotProduct(v0, v0);
+ float d01 = Vector3DotProduct(v0, v1);
+ float d11 = Vector3DotProduct(v1, v1);
+ float d20 = Vector3DotProduct(v2, v0);
+ float d21 = Vector3DotProduct(v2, v1);
+
+ float denom = d00*d11 - d01*d01;
+
+ Vector3 result = { 0 };
+
+ result.y = (d11*d20 - d01*d21)/denom;
+ result.z = (d00*d21 - d01*d20)/denom;
+ result.x = 1.0f - (result.z + result.y);
+
+ return result;
+}
+
+// Returns Vector3 as float array
+RMDEF float3 Vector3ToFloatV(Vector3 v)
+{
+ float3 buffer = { 0 };
+
+ buffer.v[0] = v.x;
+ buffer.v[1] = v.y;
+ buffer.v[2] = v.z;
+
+ return buffer;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition - Matrix math
+//----------------------------------------------------------------------------------
+
+// Compute matrix determinant
+RMDEF float MatrixDeterminant(Matrix mat)
+{
+ float result = { 0 };
+
+ // Cache the matrix values (speed optimization)
+ float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3;
+ float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7;
+ float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11;
+ float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15;
+
+ result = a30*a21*a12*a03 - a20*a31*a12*a03 - a30*a11*a22*a03 + a10*a31*a22*a03 +
+ a20*a11*a32*a03 - a10*a21*a32*a03 - a30*a21*a02*a13 + a20*a31*a02*a13 +
+ a30*a01*a22*a13 - a00*a31*a22*a13 - a20*a01*a32*a13 + a00*a21*a32*a13 +
+ a30*a11*a02*a23 - a10*a31*a02*a23 - a30*a01*a12*a23 + a00*a31*a12*a23 +
+ a10*a01*a32*a23 - a00*a11*a32*a23 - a20*a11*a02*a33 + a10*a21*a02*a33 +
+ a20*a01*a12*a33 - a00*a21*a12*a33 - a10*a01*a22*a33 + a00*a11*a22*a33;
+
+ return result;
+}
+
+// Returns the trace of the matrix (sum of the values along the diagonal)
+RMDEF float MatrixTrace(Matrix mat)
+{
+ float result = (mat.m0 + mat.m5 + mat.m10 + mat.m15);
+ return result;
+}
+
+// Transposes provided matrix
+RMDEF Matrix MatrixTranspose(Matrix mat)
+{
+ Matrix result = { 0 };
+
+ result.m0 = mat.m0;
+ result.m1 = mat.m4;
+ result.m2 = mat.m8;
+ result.m3 = mat.m12;
+ result.m4 = mat.m1;
+ result.m5 = mat.m5;
+ result.m6 = mat.m9;
+ result.m7 = mat.m13;
+ result.m8 = mat.m2;
+ result.m9 = mat.m6;
+ result.m10 = mat.m10;
+ result.m11 = mat.m14;
+ result.m12 = mat.m3;
+ result.m13 = mat.m7;
+ result.m14 = mat.m11;
+ result.m15 = mat.m15;
+
+ return result;
+}
+
+// Invert provided matrix
+RMDEF Matrix MatrixInvert(Matrix mat)
+{
+ Matrix result = { 0 };
+
+ // Cache the matrix values (speed optimization)
+ float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3;
+ float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7;
+ float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11;
+ float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15;
+
+ float b00 = a00*a11 - a01*a10;
+ float b01 = a00*a12 - a02*a10;
+ float b02 = a00*a13 - a03*a10;
+ float b03 = a01*a12 - a02*a11;
+ float b04 = a01*a13 - a03*a11;
+ float b05 = a02*a13 - a03*a12;
+ float b06 = a20*a31 - a21*a30;
+ float b07 = a20*a32 - a22*a30;
+ float b08 = a20*a33 - a23*a30;
+ float b09 = a21*a32 - a22*a31;
+ float b10 = a21*a33 - a23*a31;
+ float b11 = a22*a33 - a23*a32;
+
+ // Calculate the invert determinant (inlined to avoid double-caching)
+ float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06);
+
+ result.m0 = (a11*b11 - a12*b10 + a13*b09)*invDet;
+ result.m1 = (-a01*b11 + a02*b10 - a03*b09)*invDet;
+ result.m2 = (a31*b05 - a32*b04 + a33*b03)*invDet;
+ result.m3 = (-a21*b05 + a22*b04 - a23*b03)*invDet;
+ result.m4 = (-a10*b11 + a12*b08 - a13*b07)*invDet;
+ result.m5 = (a00*b11 - a02*b08 + a03*b07)*invDet;
+ result.m6 = (-a30*b05 + a32*b02 - a33*b01)*invDet;
+ result.m7 = (a20*b05 - a22*b02 + a23*b01)*invDet;
+ result.m8 = (a10*b10 - a11*b08 + a13*b06)*invDet;
+ result.m9 = (-a00*b10 + a01*b08 - a03*b06)*invDet;
+ result.m10 = (a30*b04 - a31*b02 + a33*b00)*invDet;
+ result.m11 = (-a20*b04 + a21*b02 - a23*b00)*invDet;
+ result.m12 = (-a10*b09 + a11*b07 - a12*b06)*invDet;
+ result.m13 = (a00*b09 - a01*b07 + a02*b06)*invDet;
+ result.m14 = (-a30*b03 + a31*b01 - a32*b00)*invDet;
+ result.m15 = (a20*b03 - a21*b01 + a22*b00)*invDet;
+
+ return result;
+}
+
+// Normalize provided matrix
+RMDEF Matrix MatrixNormalize(Matrix mat)
+{
+ Matrix result = { 0 };
+
+ float det = MatrixDeterminant(mat);
+
+ result.m0 = mat.m0/det;
+ result.m1 = mat.m1/det;
+ result.m2 = mat.m2/det;
+ result.m3 = mat.m3/det;
+ result.m4 = mat.m4/det;
+ result.m5 = mat.m5/det;
+ result.m6 = mat.m6/det;
+ result.m7 = mat.m7/det;
+ result.m8 = mat.m8/det;
+ result.m9 = mat.m9/det;
+ result.m10 = mat.m10/det;
+ result.m11 = mat.m11/det;
+ result.m12 = mat.m12/det;
+ result.m13 = mat.m13/det;
+ result.m14 = mat.m14/det;
+ result.m15 = mat.m15/det;
+
+ return result;
+}
+
+// Returns identity matrix
+RMDEF Matrix MatrixIdentity(void)
+{
+ Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f };
+
+ return result;
+}
+
+// Add two matrices
+RMDEF Matrix MatrixAdd(Matrix left, Matrix right)
+{
+ Matrix result = MatrixIdentity();
+
+ result.m0 = left.m0 + right.m0;
+ result.m1 = left.m1 + right.m1;
+ result.m2 = left.m2 + right.m2;
+ result.m3 = left.m3 + right.m3;
+ result.m4 = left.m4 + right.m4;
+ result.m5 = left.m5 + right.m5;
+ result.m6 = left.m6 + right.m6;
+ result.m7 = left.m7 + right.m7;
+ result.m8 = left.m8 + right.m8;
+ result.m9 = left.m9 + right.m9;
+ result.m10 = left.m10 + right.m10;
+ result.m11 = left.m11 + right.m11;
+ result.m12 = left.m12 + right.m12;
+ result.m13 = left.m13 + right.m13;
+ result.m14 = left.m14 + right.m14;
+ result.m15 = left.m15 + right.m15;
+
+ return result;
+}
+
+// Subtract two matrices (left - right)
+RMDEF Matrix MatrixSubtract(Matrix left, Matrix right)
+{
+ Matrix result = MatrixIdentity();
+
+ result.m0 = left.m0 - right.m0;
+ result.m1 = left.m1 - right.m1;
+ result.m2 = left.m2 - right.m2;
+ result.m3 = left.m3 - right.m3;
+ result.m4 = left.m4 - right.m4;
+ result.m5 = left.m5 - right.m5;
+ result.m6 = left.m6 - right.m6;
+ result.m7 = left.m7 - right.m7;
+ result.m8 = left.m8 - right.m8;
+ result.m9 = left.m9 - right.m9;
+ result.m10 = left.m10 - right.m10;
+ result.m11 = left.m11 - right.m11;
+ result.m12 = left.m12 - right.m12;
+ result.m13 = left.m13 - right.m13;
+ result.m14 = left.m14 - right.m14;
+ result.m15 = left.m15 - right.m15;
+
+ return result;
+}
+
+// Returns translation matrix
+RMDEF Matrix MatrixTranslate(float x, float y, float z)
+{
+ Matrix result = { 1.0f, 0.0f, 0.0f, x,
+ 0.0f, 1.0f, 0.0f, y,
+ 0.0f, 0.0f, 1.0f, z,
+ 0.0f, 0.0f, 0.0f, 1.0f };
+
+ return result;
+}
+
+// Create rotation matrix from axis and angle
+// NOTE: Angle should be provided in radians
+RMDEF Matrix MatrixRotate(Vector3 axis, float angle)
+{
+ Matrix result = { 0 };
+
+ float x = axis.x, y = axis.y, z = axis.z;
+
+ float length = sqrtf(x*x + y*y + z*z);
+
+ if ((length != 1.0f) && (length != 0.0f))
+ {
+ length = 1.0f/length;
+ x *= length;
+ y *= length;
+ z *= length;
+ }
+
+ float sinres = sinf(angle);
+ float cosres = cosf(angle);
+ float t = 1.0f - cosres;
+
+ result.m0 = x*x*t + cosres;
+ result.m1 = y*x*t + z*sinres;
+ result.m2 = z*x*t - y*sinres;
+ result.m3 = 0.0f;
+
+ result.m4 = x*y*t - z*sinres;
+ result.m5 = y*y*t + cosres;
+ result.m6 = z*y*t + x*sinres;
+ result.m7 = 0.0f;
+
+ result.m8 = x*z*t + y*sinres;
+ result.m9 = y*z*t - x*sinres;
+ result.m10 = z*z*t + cosres;
+ result.m11 = 0.0f;
+
+ result.m12 = 0.0f;
+ result.m13 = 0.0f;
+ result.m14 = 0.0f;
+ result.m15 = 1.0f;
+
+ return result;
+}
+
+// Returns x-rotation matrix (angle in radians)
+RMDEF Matrix MatrixRotateX(float angle)
+{
+ Matrix result = MatrixIdentity();
+
+ float cosres = cosf(angle);
+ float sinres = sinf(angle);
+
+ result.m5 = cosres;
+ result.m6 = -sinres;
+ result.m9 = sinres;
+ result.m10 = cosres;
+
+ return result;
+}
+
+// Returns y-rotation matrix (angle in radians)
+RMDEF Matrix MatrixRotateY(float angle)
+{
+ Matrix result = MatrixIdentity();
+
+ float cosres = cosf(angle);
+ float sinres = sinf(angle);
+
+ result.m0 = cosres;
+ result.m2 = sinres;
+ result.m8 = -sinres;
+ result.m10 = cosres;
+
+ return result;
+}
+
+// Returns z-rotation matrix (angle in radians)
+RMDEF Matrix MatrixRotateZ(float angle)
+{
+ Matrix result = MatrixIdentity();
+
+ float cosres = cosf(angle);
+ float sinres = sinf(angle);
+
+ result.m0 = cosres;
+ result.m1 = -sinres;
+ result.m4 = sinres;
+ result.m5 = cosres;
+
+ return result;
+}
+
+// Returns scaling matrix
+RMDEF Matrix MatrixScale(float x, float y, float z)
+{
+ Matrix result = { x, 0.0f, 0.0f, 0.0f,
+ 0.0f, y, 0.0f, 0.0f,
+ 0.0f, 0.0f, z, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f };
+
+ return result;
+}
+
+// Returns two matrix multiplication
+// NOTE: When multiplying matrices... the order matters!
+RMDEF Matrix MatrixMultiply(Matrix left, Matrix right)
+{
+ Matrix result = { 0 };
+
+ result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12;
+ result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13;
+ result.m2 = left.m0*right.m2 + left.m1*right.m6 + left.m2*right.m10 + left.m3*right.m14;
+ result.m3 = left.m0*right.m3 + left.m1*right.m7 + left.m2*right.m11 + left.m3*right.m15;
+ result.m4 = left.m4*right.m0 + left.m5*right.m4 + left.m6*right.m8 + left.m7*right.m12;
+ result.m5 = left.m4*right.m1 + left.m5*right.m5 + left.m6*right.m9 + left.m7*right.m13;
+ result.m6 = left.m4*right.m2 + left.m5*right.m6 + left.m6*right.m10 + left.m7*right.m14;
+ result.m7 = left.m4*right.m3 + left.m5*right.m7 + left.m6*right.m11 + left.m7*right.m15;
+ result.m8 = left.m8*right.m0 + left.m9*right.m4 + left.m10*right.m8 + left.m11*right.m12;
+ result.m9 = left.m8*right.m1 + left.m9*right.m5 + left.m10*right.m9 + left.m11*right.m13;
+ result.m10 = left.m8*right.m2 + left.m9*right.m6 + left.m10*right.m10 + left.m11*right.m14;
+ result.m11 = left.m8*right.m3 + left.m9*right.m7 + left.m10*right.m11 + left.m11*right.m15;
+ result.m12 = left.m12*right.m0 + left.m13*right.m4 + left.m14*right.m8 + left.m15*right.m12;
+ result.m13 = left.m12*right.m1 + left.m13*right.m5 + left.m14*right.m9 + left.m15*right.m13;
+ result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14;
+ result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15;
+
+ return result;
+}
+
+// Returns perspective projection matrix
+RMDEF Matrix MatrixFrustum(double left, double right, double bottom, double top, double near, double far)
+{
+ Matrix result = { 0 };
+
+ float rl = (float)(right - left);
+ float tb = (float)(top - bottom);
+ float fn = (float)(far - near);
+
+ result.m0 = ((float) near*2.0f)/rl;
+ result.m1 = 0.0f;
+ result.m2 = 0.0f;
+ result.m3 = 0.0f;
+
+ result.m4 = 0.0f;
+ result.m5 = ((float) near*2.0f)/tb;
+ result.m6 = 0.0f;
+ result.m7 = 0.0f;
+
+ result.m8 = ((float)right + (float)left)/rl;
+ result.m9 = ((float)top + (float)bottom)/tb;
+ result.m10 = -((float)far + (float)near)/fn;
+ result.m11 = -1.0f;
+
+ result.m12 = 0.0f;
+ result.m13 = 0.0f;
+ result.m14 = -((float)far*(float)near*2.0f)/fn;
+ result.m15 = 0.0f;
+
+ return result;
+}
+
+// Returns perspective projection matrix
+// NOTE: Angle should be provided in radians
+RMDEF Matrix MatrixPerspective(double fovy, double aspect, double near, double far)
+{
+ double top = near*tan(fovy*0.5);
+ double right = top*aspect;
+ Matrix result = MatrixFrustum(-right, right, -top, top, near, far);
+
+ return result;
+}
+
+// Returns orthographic projection matrix
+RMDEF Matrix MatrixOrtho(double left, double right, double bottom, double top, double near, double far)
+{
+ Matrix result = { 0 };
+
+ float rl = (float)(right - left);
+ float tb = (float)(top - bottom);
+ float fn = (float)(far - near);
+
+ result.m0 = 2.0f/rl;
+ result.m1 = 0.0f;
+ result.m2 = 0.0f;
+ result.m3 = 0.0f;
+ result.m4 = 0.0f;
+ result.m5 = 2.0f/tb;
+ result.m6 = 0.0f;
+ result.m7 = 0.0f;
+ result.m8 = 0.0f;
+ result.m9 = 0.0f;
+ result.m10 = -2.0f/fn;
+ result.m11 = 0.0f;
+ result.m12 = -((float)left + (float)right)/rl;
+ result.m13 = -((float)top + (float)bottom)/tb;
+ result.m14 = -((float)far + (float)near)/fn;
+ result.m15 = 1.0f;
+
+ return result;
+}
+
+// Returns camera look-at matrix (view matrix)
+RMDEF Matrix MatrixLookAt(Vector3 eye, Vector3 target, Vector3 up)
+{
+ Matrix result = { 0 };
+
+ Vector3 z = Vector3Subtract(eye, target);
+ z = Vector3Normalize(z);
+ Vector3 x = Vector3CrossProduct(up, z);
+ x = Vector3Normalize(x);
+ Vector3 y = Vector3CrossProduct(z, x);
+ y = Vector3Normalize(y);
+
+ result.m0 = x.x;
+ result.m1 = x.y;
+ result.m2 = x.z;
+ result.m3 = 0.0f;
+ result.m4 = y.x;
+ result.m5 = y.y;
+ result.m6 = y.z;
+ result.m7 = 0.0f;
+ result.m8 = z.x;
+ result.m9 = z.y;
+ result.m10 = z.z;
+ result.m11 = 0.0f;
+ result.m12 = eye.x;
+ result.m13 = eye.y;
+ result.m14 = eye.z;
+ result.m15 = 1.0f;
+
+ result = MatrixInvert(result);
+
+ return result;
+}
+
+// Returns float array of matrix data
+RMDEF float16 MatrixToFloatV(Matrix mat)
+{
+ float16 buffer = { 0 };
+
+ buffer.v[0] = mat.m0;
+ buffer.v[1] = mat.m1;
+ buffer.v[2] = mat.m2;
+ buffer.v[3] = mat.m3;
+ buffer.v[4] = mat.m4;
+ buffer.v[5] = mat.m5;
+ buffer.v[6] = mat.m6;
+ buffer.v[7] = mat.m7;
+ buffer.v[8] = mat.m8;
+ buffer.v[9] = mat.m9;
+ buffer.v[10] = mat.m10;
+ buffer.v[11] = mat.m11;
+ buffer.v[12] = mat.m12;
+ buffer.v[13] = mat.m13;
+ buffer.v[14] = mat.m14;
+ buffer.v[15] = mat.m15;
+
+ return buffer;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition - Quaternion math
+//----------------------------------------------------------------------------------
+
+// Returns identity quaternion
+RMDEF Quaternion QuaternionIdentity(void)
+{
+ Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f };
+ return result;
+}
+
+// Computes the length of a quaternion
+RMDEF float QuaternionLength(Quaternion q)
+{
+ float result = (float)sqrt(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w);
+ return result;
+}
+
+// Normalize provided quaternion
+RMDEF Quaternion QuaternionNormalize(Quaternion q)
+{
+ Quaternion result = { 0 };
+
+ float length, ilength;
+ length = QuaternionLength(q);
+ if (length == 0.0f) length = 1.0f;
+ ilength = 1.0f/length;
+
+ result.x = q.x*ilength;
+ result.y = q.y*ilength;
+ result.z = q.z*ilength;
+ result.w = q.w*ilength;
+
+ return result;
+}
+
+// Invert provided quaternion
+RMDEF Quaternion QuaternionInvert(Quaternion q)
+{
+ Quaternion result = q;
+ float length = QuaternionLength(q);
+ float lengthSq = length*length;
+
+ if (lengthSq != 0.0)
+ {
+ float i = 1.0f/lengthSq;
+
+ result.x *= -i;
+ result.y *= -i;
+ result.z *= -i;
+ result.w *= i;
+ }
+
+ return result;
+}
+
+// Calculate two quaternion multiplication
+RMDEF Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2)
+{
+ Quaternion result = { 0 };
+
+ float qax = q1.x, qay = q1.y, qaz = q1.z, qaw = q1.w;
+ float qbx = q2.x, qby = q2.y, qbz = q2.z, qbw = q2.w;
+
+ result.x = qax*qbw + qaw*qbx + qay*qbz - qaz*qby;
+ result.y = qay*qbw + qaw*qby + qaz*qbx - qax*qbz;
+ result.z = qaz*qbw + qaw*qbz + qax*qby - qay*qbx;
+ result.w = qaw*qbw - qax*qbx - qay*qby - qaz*qbz;
+
+ return result;
+}
+
+// Calculate linear interpolation between two quaternions
+RMDEF Quaternion QuaternionLerp(Quaternion q1, Quaternion q2, float amount)
+{
+ Quaternion result = { 0 };
+
+ result.x = q1.x + amount*(q2.x - q1.x);
+ result.y = q1.y + amount*(q2.y - q1.y);
+ result.z = q1.z + amount*(q2.z - q1.z);
+ result.w = q1.w + amount*(q2.w - q1.w);
+
+ return result;
+}
+
+// Calculate slerp-optimized interpolation between two quaternions
+RMDEF Quaternion QuaternionNlerp(Quaternion q1, Quaternion q2, float amount)
+{
+ Quaternion result = QuaternionLerp(q1, q2, amount);
+ result = QuaternionNormalize(result);
+
+ return result;
+}
+
+// Calculates spherical linear interpolation between two quaternions
+RMDEF Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount)
+{
+ Quaternion result = { 0 };
+
+ float cosHalfTheta = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w;
+
+ if (fabs(cosHalfTheta) >= 1.0f) result = q1;
+ else if (cosHalfTheta > 0.95f) result = QuaternionNlerp(q1, q2, amount);
+ else
+ {
+ float halfTheta = (float) acos(cosHalfTheta);
+ float sinHalfTheta = (float) sqrt(1.0f - cosHalfTheta*cosHalfTheta);
+
+ if (fabs(sinHalfTheta) < 0.001f)
+ {
+ result.x = (q1.x*0.5f + q2.x*0.5f);
+ result.y = (q1.y*0.5f + q2.y*0.5f);
+ result.z = (q1.z*0.5f + q2.z*0.5f);
+ result.w = (q1.w*0.5f + q2.w*0.5f);
+ }
+ else
+ {
+ float ratioA = sinf((1 - amount)*halfTheta)/sinHalfTheta;
+ float ratioB = sinf(amount*halfTheta)/sinHalfTheta;
+
+ result.x = (q1.x*ratioA + q2.x*ratioB);
+ result.y = (q1.y*ratioA + q2.y*ratioB);
+ result.z = (q1.z*ratioA + q2.z*ratioB);
+ result.w = (q1.w*ratioA + q2.w*ratioB);
+ }
+ }
+
+ return result;
+}
+
+// Calculate quaternion based on the rotation from one vector to another
+RMDEF Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to)
+{
+ Quaternion result = { 0 };
+
+ float cos2Theta = Vector3DotProduct(from, to);
+ Vector3 cross = Vector3CrossProduct(from, to);
+
+ result.x = cross.x;
+ result.y = cross.y;
+ result.z = cross.y;
+ result.w = 1.0f + cos2Theta; // NOTE: Added QuaternioIdentity()
+
+ // Normalize to essentially nlerp the original and identity to 0.5
+ result = QuaternionNormalize(result);
+
+ // Above lines are equivalent to:
+ //Quaternion result = QuaternionNlerp(q, QuaternionIdentity(), 0.5f);
+
+ return result;
+}
+
+// Returns a quaternion for a given rotation matrix
+RMDEF Quaternion QuaternionFromMatrix(Matrix mat)
+{
+ Quaternion result = { 0 };
+
+ float trace = MatrixTrace(mat);
+
+ if (trace > 0.0f)
+ {
+ float s = (float)sqrt(trace + 1)*2.0f;
+ float invS = 1.0f/s;
+
+ result.w = s*0.25f;
+ result.x = (mat.m6 - mat.m9)*invS;
+ result.y = (mat.m8 - mat.m2)*invS;
+ result.z = (mat.m1 - mat.m4)*invS;
+ }
+ else
+ {
+ float m00 = mat.m0, m11 = mat.m5, m22 = mat.m10;
+
+ if (m00 > m11 && m00 > m22)
+ {
+ float s = (float)sqrt(1.0f + m00 - m11 - m22)*2.0f;
+ float invS = 1.0f/s;
+
+ result.w = (mat.m6 - mat.m9)*invS;
+ result.x = s*0.25f;
+ result.y = (mat.m4 + mat.m1)*invS;
+ result.z = (mat.m8 + mat.m2)*invS;
+ }
+ else if (m11 > m22)
+ {
+ float s = (float)sqrt(1.0f + m11 - m00 - m22)*2.0f;
+ float invS = 1.0f/s;
+
+ result.w = (mat.m8 - mat.m2)*invS;
+ result.x = (mat.m4 + mat.m1)*invS;
+ result.y = s*0.25f;
+ result.z = (mat.m9 + mat.m6)*invS;
+ }
+ else
+ {
+ float s = (float)sqrt(1.0f + m22 - m00 - m11)*2.0f;
+ float invS = 1.0f/s;
+
+ result.w = (mat.m1 - mat.m4)*invS;
+ result.x = (mat.m8 + mat.m2)*invS;
+ result.y = (mat.m9 + mat.m6)*invS;
+ result.z = s*0.25f;
+ }
+ }
+
+ return result;
+}
+
+// Returns a matrix for a given quaternion
+RMDEF Matrix QuaternionToMatrix(Quaternion q)
+{
+ Matrix result = { 0 };
+
+ float x = q.x, y = q.y, z = q.z, w = q.w;
+
+ float x2 = x + x;
+ float y2 = y + y;
+ float z2 = z + z;
+
+ float length = QuaternionLength(q);
+ float lengthSquared = length*length;
+
+ float xx = x*x2/lengthSquared;
+ float xy = x*y2/lengthSquared;
+ float xz = x*z2/lengthSquared;
+
+ float yy = y*y2/lengthSquared;
+ float yz = y*z2/lengthSquared;
+ float zz = z*z2/lengthSquared;
+
+ float wx = w*x2/lengthSquared;
+ float wy = w*y2/lengthSquared;
+ float wz = w*z2/lengthSquared;
+
+ result.m0 = 1.0f - (yy + zz);
+ result.m1 = xy - wz;
+ result.m2 = xz + wy;
+ result.m3 = 0.0f;
+ result.m4 = xy + wz;
+ result.m5 = 1.0f - (xx + zz);
+ result.m6 = yz - wx;
+ result.m7 = 0.0f;
+ result.m8 = xz - wy;
+ result.m9 = yz + wx;
+ result.m10 = 1.0f - (xx + yy);
+ result.m11 = 0.0f;
+ result.m12 = 0.0f;
+ result.m13 = 0.0f;
+ result.m14 = 0.0f;
+ result.m15 = 1.0f;
+
+ return result;
+}
+
+// Returns rotation quaternion for an angle and axis
+// NOTE: angle must be provided in radians
+RMDEF Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle)
+{
+ Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f };
+
+ if (Vector3Length(axis) != 0.0f)
+
+ angle *= 0.5f;
+
+ axis = Vector3Normalize(axis);
+
+ float sinres = sinf(angle);
+ float cosres = cosf(angle);
+
+ result.x = axis.x*sinres;
+ result.y = axis.y*sinres;
+ result.z = axis.z*sinres;
+ result.w = cosres;
+
+ result = QuaternionNormalize(result);
+
+ return result;
+}
+
+// Returns the rotation angle and axis for a given quaternion
+RMDEF void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle)
+{
+ if (fabs(q.w) > 1.0f) q = QuaternionNormalize(q);
+
+ Vector3 resAxis = { 0.0f, 0.0f, 0.0f };
+ float resAngle = 0.0f;
+
+ resAngle = 2.0f*(float)acos(q.w);
+ float den = (float)sqrt(1.0f - q.w*q.w);
+
+ if (den > 0.0001f)
+ {
+ resAxis.x = q.x/den;
+ resAxis.y = q.y/den;
+ resAxis.z = q.z/den;
+ }
+ else
+ {
+ // This occurs when the angle is zero.
+ // Not a problem: just set an arbitrary normalized axis.
+ resAxis.x = 1.0f;
+ }
+
+ *outAxis = resAxis;
+ *outAngle = resAngle;
+}
+
+// Returns he quaternion equivalent to Euler angles
+RMDEF Quaternion QuaternionFromEuler(float roll, float pitch, float yaw)
+{
+ Quaternion q = { 0 };
+
+ float x0 = cosf(roll*0.5f);
+ float x1 = sinf(roll*0.5f);
+ float y0 = cosf(pitch*0.5f);
+ float y1 = sinf(pitch*0.5f);
+ float z0 = cosf(yaw*0.5f);
+ float z1 = sinf(yaw*0.5f);
+
+ q.x = x1*y0*z0 - x0*y1*z1;
+ q.y = x0*y1*z0 + x1*y0*z1;
+ q.z = x0*y0*z1 - x1*y1*z0;
+ q.w = x0*y0*z0 + x1*y1*z1;
+
+ return q;
+}
+
+// Return the Euler angles equivalent to quaternion (roll, pitch, yaw)
+// NOTE: Angles are returned in a Vector3 struct in degrees
+RMDEF Vector3 QuaternionToEuler(Quaternion q)
+{
+ Vector3 result = { 0 };
+
+ // roll (x-axis rotation)
+ float x0 = 2.0f*(q.w*q.x + q.y*q.z);
+ float x1 = 1.0f - 2.0f*(q.x*q.x + q.y*q.y);
+ result.x = atan2f(x0, x1)*RAD2DEG;
+
+ // pitch (y-axis rotation)
+ float y0 = 2.0f*(q.w*q.y - q.z*q.x);
+ y0 = y0 > 1.0f ? 1.0f : y0;
+ y0 = y0 < -1.0f ? -1.0f : y0;
+ result.y = asinf(y0)*RAD2DEG;
+
+ // yaw (z-axis rotation)
+ float z0 = 2.0f*(q.w*q.z + q.x*q.y);
+ float z1 = 1.0f - 2.0f*(q.y*q.y + q.z*q.z);
+ result.z = atan2f(z0, z1)*RAD2DEG;
+
+ return result;
+}
+
+// Transform a quaternion given a transformation matrix
+RMDEF Quaternion QuaternionTransform(Quaternion q, Matrix mat)
+{
+ Quaternion result = { 0 };
+
+ result.x = mat.m0*q.x + mat.m4*q.y + mat.m8*q.z + mat.m12*q.w;
+ result.y = mat.m1*q.x + mat.m5*q.y + mat.m9*q.z + mat.m13*q.w;
+ result.z = mat.m2*q.x + mat.m6*q.y + mat.m10*q.z + mat.m14*q.w;
+ result.w = mat.m3*q.x + mat.m7*q.y + mat.m11*q.z + mat.m15*q.w;
+
+ return result;
+}
+
+#endif // RAYMATH_H
diff --git a/games/cat_vs_roomba/resources/cat.png b/games/cat_vs_roomba/resources/cat.png
new file mode 100644
index 000000000..872d029a2
Binary files /dev/null and b/games/cat_vs_roomba/resources/cat.png differ
diff --git a/games/cat_vs_roomba/resources/cat_mouse.mod b/games/cat_vs_roomba/resources/cat_mouse.mod
new file mode 100644
index 000000000..ef7ce860f
Binary files /dev/null and b/games/cat_vs_roomba/resources/cat_mouse.mod differ
diff --git a/games/cat_vs_roomba/resources/catch22.mod b/games/cat_vs_roomba/resources/catch22.mod
new file mode 100644
index 000000000..1b793dc13
Binary files /dev/null and b/games/cat_vs_roomba/resources/catch22.mod differ
diff --git a/games/cat_vs_roomba/resources/coin.wav b/games/cat_vs_roomba/resources/coin.wav
new file mode 100644
index 000000000..6684ffc6a
Binary files /dev/null and b/games/cat_vs_roomba/resources/coin.wav differ
diff --git a/games/cat_vs_roomba/resources/collisions.txt b/games/cat_vs_roomba/resources/collisions.txt
new file mode 100644
index 000000000..839404cc2
--- /dev/null
+++ b/games/cat_vs_roomba/resources/collisions.txt
@@ -0,0 +1,16 @@
+1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,0,0,1,1,1,1,1,1,1,1,
+1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,0,0,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+0,0,1,1,1,1,2,2,2,1,1,1,0,0,1,1,1,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,
+0,0,1,1,1,1,2,2,2,1,1,1,0,0,1,1,1,2,2,2,1,1,1,1,0,0,1,1,1,1,0,0,
+0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,1,1,1,0,0,1,1,1,1,0,0,
+0,0,1,1,2,2,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,1,1,1,0,0,0,0,0,0,0,0,
+1,1,1,1,2,2,2,2,2,2,2,2,0,0,1,1,1,2,2,2,1,1,1,1,0,0,0,0,0,0,0,0,
+1,1,1,1,2,2,2,2,2,2,2,2,0,0,1,1,1,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,1,2,2,2,2,2,2,1,1,
+0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,2,2,2,2,2,2,1,1,
+0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,1,1,
+0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,1,1,
+0,0,0,0,0,0,0,0,0,0,2,2,2,0,0,0,0,1,1,0,0,1,2,2,2,2,2,2,2,2,0,0
\ No newline at end of file
diff --git a/games/cat_vs_roomba/resources/dirtiles.png b/games/cat_vs_roomba/resources/dirtiles.png
new file mode 100644
index 000000000..9c4c46573
Binary files /dev/null and b/games/cat_vs_roomba/resources/dirtiles.png differ
diff --git a/games/cat_vs_roomba/resources/dyson.png b/games/cat_vs_roomba/resources/dyson.png
new file mode 100644
index 000000000..7b15c768b
Binary files /dev/null and b/games/cat_vs_roomba/resources/dyson.png differ
diff --git a/games/cat_vs_roomba/resources/furmap.txt b/games/cat_vs_roomba/resources/furmap.txt
new file mode 100644
index 000000000..0b9a2ee25
--- /dev/null
+++ b/games/cat_vs_roomba/resources/furmap.txt
@@ -0,0 +1,34 @@
+.furniture
+.state: 0-Block, 1-Alpha, 2-Breakable
+.f setId cellX cellY state counter
+.puff and sits
+f 3 0 4 0 0
+f 3 13 0 0 0
+f 3 22 0 0 0
+f 3 21 11 0 0
+f 2 19 15 0 0
+f 2 30 5 0 0
+f 4 12 4 0 0
+.wood pieces
+f 26 3 0 0 0
+f 27 4 10 0 0
+f 27 24 7 0 0
+f 17 30 15 0 0
+.kitchen
+f 11 0 15 0 0
+f 13 0 12 0 0
+.sofa and bed (alpha)
+f 14 4 7 1 0
+f 10 24 11 1 0
+.glass and tables (alpha)
+f 9 17 4 1 0
+f 16 6 4 1 0
+f 16 10 15 1 0
+f 15 22 15 1 0
+.plants (breakable)
+f 6 0 6 2 1800
+f 6 12 10 2 1800
+f 7 17 0 2 1200
+f 7 15 15 2 1200
+f 8 13 15 2 2000
+f 8 24 5 2 2000
\ No newline at end of file
diff --git a/games/cat_vs_roomba/resources/furniture.png b/games/cat_vs_roomba/resources/furniture.png
new file mode 100644
index 000000000..7d040cfbb
Binary files /dev/null and b/games/cat_vs_roomba/resources/furniture.png differ
diff --git a/games/cat_vs_roomba/resources/furset.txt b/games/cat_vs_roomba/resources/furset.txt
new file mode 100644
index 000000000..306b24ca2
--- /dev/null
+++ b/games/cat_vs_roomba/resources/furset.txt
@@ -0,0 +1,37 @@
+.tileset
+.first line
+f 0 0 0 2 2
+f 1 2 0 2 2
+f 2 4 0 2 2
+f 3 6 0 2 2
+f 4 8 0 2 2
+f 5 10 0 2 2
+f 6 12 0 2 2
+f 7 14 0 2 2
+f 8 16 0 2 2
+f 9 18 0 5 6
+f 10 23 0 6 6
+.second line
+f 11 0 2 10 2
+f 12 10 2 7 3
+.third line
+f 13 0 4 2 3
+f 14 2 4 8 3
+f 15 10 5 2 2
+f 16 12 5 3 2
+f 17 15 5 2 2
+.cups
+f 18 17 6 1 1
+f 19 18 6 1 1
+f 20 19 6 1 1
+f 21 20 6 1 1
+f 22 21 6 1 1
+f 23 22 6 1 1
+f 24 23 6 1 1
+f 25 24 6 1 1
+.four line
+f 26 0 7 10 2
+f 27 10 7 8 2
+f 28 18 7 3 2
+f 29 21 7 3 2
+f 30 24 7 2 2
\ No newline at end of file
diff --git a/games/cat_vs_roomba/resources/fursetid.png b/games/cat_vs_roomba/resources/fursetid.png
new file mode 100644
index 000000000..ccb31465f
Binary files /dev/null and b/games/cat_vs_roomba/resources/fursetid.png differ
diff --git a/games/cat_vs_roomba/resources/fxcat01.wav b/games/cat_vs_roomba/resources/fxcat01.wav
new file mode 100644
index 000000000..085903fb4
Binary files /dev/null and b/games/cat_vs_roomba/resources/fxcat01.wav differ
diff --git a/games/cat_vs_roomba/resources/fxcat02.wav b/games/cat_vs_roomba/resources/fxcat02.wav
new file mode 100644
index 000000000..41957a886
Binary files /dev/null and b/games/cat_vs_roomba/resources/fxcat02.wav differ
diff --git a/games/cat_vs_roomba/resources/fxrobot01.wav b/games/cat_vs_roomba/resources/fxrobot01.wav
new file mode 100644
index 000000000..bd7c760fc
Binary files /dev/null and b/games/cat_vs_roomba/resources/fxrobot01.wav differ
diff --git a/games/cat_vs_roomba/resources/fxrobot02.wav b/games/cat_vs_roomba/resources/fxrobot02.wav
new file mode 100644
index 000000000..7aff7537f
Binary files /dev/null and b/games/cat_vs_roomba/resources/fxrobot02.wav differ
diff --git a/games/cat_vs_roomba/resources/fxrobot03.wav b/games/cat_vs_roomba/resources/fxrobot03.wav
new file mode 100644
index 000000000..b97a57df7
Binary files /dev/null and b/games/cat_vs_roomba/resources/fxrobot03.wav differ
diff --git a/games/cat_vs_roomba/resources/roomba.png b/games/cat_vs_roomba/resources/roomba.png
new file mode 100644
index 000000000..899ecd5ea
Binary files /dev/null and b/games/cat_vs_roomba/resources/roomba.png differ
diff --git a/games/cat_vs_roomba/resources/star.fnt b/games/cat_vs_roomba/resources/star.fnt
new file mode 100644
index 000000000..48f4c37dd
--- /dev/null
+++ b/games/cat_vs_roomba/resources/star.fnt
@@ -0,0 +1,139 @@
+info face="Starcatcher" size=-64 bold=0 italic=0 charset="ANSI" unicode=0 stretchH=100 smooth=1 aa=1 padding=4,4,4,4 spacing=1,1 outline=0
+common lineHeight=63 base=50 scaleW=512 scaleH=512 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4
+page id=0 file="star.png"
+chars count=135
+char id=32 x=416 y=360 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=27 page=0 chnl=15
+char id=33 x=347 y=360 width=17 height=71 xoffset=-4 yoffset=-4 xadvance=9 page=0 chnl=15
+char id=35 x=183 y=0 width=48 height=71 xoffset=-4 yoffset=-4 xadvance=40 page=0 chnl=15
+char id=36 x=328 y=144 width=34 height=71 xoffset=-4 yoffset=-4 xadvance=26 page=0 chnl=15
+char id=37 x=67 y=0 width=62 height=71 xoffset=-4 yoffset=-4 xadvance=55 page=0 chnl=15
+char id=38 x=488 y=360 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=39 x=266 y=360 width=21 height=71 xoffset=-4 yoffset=-4 xadvance=13 page=0 chnl=15
+char id=40 x=471 y=288 width=27 height=71 xoffset=-4 yoffset=-4 xadvance=19 page=0 chnl=15
+char id=41 x=387 y=288 width=27 height=71 xoffset=-4 yoffset=-4 xadvance=19 page=0 chnl=15
+char id=42 x=415 y=0 width=42 height=71 xoffset=-4 yoffset=-4 xadvance=34 page=0 chnl=15
+char id=43 x=403 y=216 width=31 height=71 xoffset=-4 yoffset=-4 xadvance=23 page=0 chnl=15
+char id=44 x=309 y=360 width=18 height=71 xoffset=-4 yoffset=-4 xadvance=10 page=0 chnl=15
+char id=45 x=82 y=360 width=26 height=71 xoffset=-4 yoffset=-4 xadvance=18 page=0 chnl=15
+char id=46 x=365 y=360 width=16 height=71 xoffset=-4 yoffset=-4 xadvance=7 page=0 chnl=15
+char id=47 x=155 y=288 width=29 height=71 xoffset=-4 yoffset=-4 xadvance=21 page=0 chnl=15
+char id=48 x=38 y=144 width=36 height=71 xoffset=-4 yoffset=-4 xadvance=27 page=0 chnl=15
+char id=49 x=188 y=360 width=25 height=71 xoffset=-4 yoffset=-4 xadvance=17 page=0 chnl=15
+char id=50 x=339 y=216 width=31 height=71 xoffset=-4 yoffset=-4 xadvance=23 page=0 chnl=15
+char id=51 x=0 y=288 width=30 height=71 xoffset=-4 yoffset=-4 xadvance=22 page=0 chnl=15
+char id=52 x=35 y=216 width=34 height=71 xoffset=-4 yoffset=-4 xadvance=26 page=0 chnl=15
+char id=53 x=105 y=216 width=33 height=71 xoffset=-4 yoffset=-4 xadvance=24 page=0 chnl=15
+char id=54 x=207 y=216 width=32 height=71 xoffset=-4 yoffset=-4 xadvance=24 page=0 chnl=15
+char id=55 x=433 y=144 width=34 height=71 xoffset=-4 yoffset=-4 xadvance=26 page=0 chnl=15
+char id=56 x=468 y=144 width=34 height=71 xoffset=-4 yoffset=-4 xadvance=26 page=0 chnl=15
+char id=57 x=0 y=216 width=34 height=71 xoffset=-4 yoffset=-4 xadvance=26 page=0 chnl=15
+char id=58 x=399 y=360 width=16 height=71 xoffset=-4 yoffset=-4 xadvance=7 page=0 chnl=15
+char id=59 x=328 y=360 width=18 height=71 xoffset=-4 yoffset=-4 xadvance=10 page=0 chnl=15
+char id=60 x=185 y=144 width=35 height=71 xoffset=-4 yoffset=-4 xadvance=27 page=0 chnl=15
+char id=61 x=371 y=216 width=31 height=71 xoffset=-4 yoffset=-4 xadvance=23 page=0 chnl=15
+char id=62 x=221 y=144 width=35 height=71 xoffset=-4 yoffset=-4 xadvance=27 page=0 chnl=15
+char id=63 x=398 y=144 width=34 height=71 xoffset=-4 yoffset=-4 xadvance=25 page=0 chnl=15
+char id=64 x=130 y=0 width=52 height=71 xoffset=-4 yoffset=-4 xadvance=44 page=0 chnl=15
+char id=65 x=445 y=72 width=37 height=71 xoffset=-4 yoffset=-4 xadvance=29 page=0 chnl=15
+char id=66 x=209 y=72 width=40 height=71 xoffset=-4 yoffset=-4 xadvance=32 page=0 chnl=15
+char id=67 x=173 y=216 width=33 height=71 xoffset=-4 yoffset=-4 xadvance=24 page=0 chnl=15
+char id=68 x=250 y=72 width=39 height=71 xoffset=-4 yoffset=-4 xadvance=31 page=0 chnl=15
+char id=69 x=306 y=216 width=32 height=71 xoffset=-4 yoffset=-4 xadvance=24 page=0 chnl=15
+char id=70 x=149 y=144 width=35 height=71 xoffset=-4 yoffset=-4 xadvance=27 page=0 chnl=15
+char id=71 x=112 y=144 width=36 height=71 xoffset=-4 yoffset=-4 xadvance=28 page=0 chnl=15
+char id=72 x=458 y=0 width=42 height=71 xoffset=-4 yoffset=-4 xadvance=34 page=0 chnl=15
+char id=73 x=288 y=360 width=20 height=71 xoffset=-4 yoffset=-4 xadvance=12 page=0 chnl=15
+char id=74 x=257 y=144 width=35 height=71 xoffset=-4 yoffset=-4 xadvance=27 page=0 chnl=15
+char id=75 x=0 y=144 width=37 height=71 xoffset=-4 yoffset=-4 xadvance=29 page=0 chnl=15
+char id=76 x=139 y=216 width=33 height=71 xoffset=-4 yoffset=-4 xadvance=25 page=0 chnl=15
+char id=77 x=85 y=72 width=41 height=71 xoffset=-4 yoffset=-4 xadvance=33 page=0 chnl=15
+char id=78 x=371 y=0 width=43 height=71 xoffset=-4 yoffset=-4 xadvance=35 page=0 chnl=15
+char id=79 x=168 y=72 width=40 height=71 xoffset=-4 yoffset=-4 xadvance=31 page=0 chnl=15
+char id=80 x=75 y=144 width=36 height=71 xoffset=-4 yoffset=-4 xadvance=28 page=0 chnl=15
+char id=81 x=280 y=0 width=45 height=71 xoffset=-4 yoffset=-4 xadvance=37 page=0 chnl=15
+char id=82 x=369 y=72 width=37 height=71 xoffset=-4 yoffset=-4 xadvance=29 page=0 chnl=15
+char id=83 x=70 y=216 width=34 height=71 xoffset=-4 yoffset=-4 xadvance=26 page=0 chnl=15
+char id=84 x=43 y=72 width=41 height=71 xoffset=-4 yoffset=-4 xadvance=33 page=0 chnl=15
+char id=85 x=0 y=72 width=42 height=71 xoffset=-4 yoffset=-4 xadvance=34 page=0 chnl=15
+char id=86 x=326 y=0 width=44 height=71 xoffset=-4 yoffset=-4 xadvance=36 page=0 chnl=15
+char id=87 x=232 y=0 width=47 height=71 xoffset=-4 yoffset=-4 xadvance=39 page=0 chnl=15
+char id=88 x=127 y=72 width=40 height=71 xoffset=-4 yoffset=-4 xadvance=33 page=0 chnl=15
+char id=89 x=290 y=72 width=39 height=71 xoffset=-4 yoffset=-4 xadvance=31 page=0 chnl=15
+char id=90 x=330 y=72 width=38 height=71 xoffset=-4 yoffset=-4 xadvance=30 page=0 chnl=15
+char id=91 x=499 y=288 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=92 x=109 y=360 width=26 height=71 xoffset=-4 yoffset=-4 xadvance=18 page=0 chnl=15
+char id=93 x=499 y=216 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=97 x=302 y=288 width=28 height=71 xoffset=-4 yoffset=-4 xadvance=20 page=0 chnl=15
+char id=98 x=415 y=288 width=27 height=71 xoffset=-4 yoffset=-4 xadvance=19 page=0 chnl=15
+char id=99 x=136 y=360 width=25 height=71 xoffset=-4 yoffset=-4 xadvance=17 page=0 chnl=15
+char id=100 x=124 y=288 width=30 height=71 xoffset=-4 yoffset=-4 xadvance=22 page=0 chnl=15
+char id=101 x=162 y=360 width=25 height=71 xoffset=-4 yoffset=-4 xadvance=17 page=0 chnl=15
+char id=102 x=443 y=288 width=27 height=71 xoffset=-4 yoffset=-4 xadvance=19 page=0 chnl=15
+char id=103 x=0 y=360 width=27 height=71 xoffset=-4 yoffset=-4 xadvance=19 page=0 chnl=15
+char id=104 x=240 y=216 width=32 height=71 xoffset=-4 yoffset=-4 xadvance=24 page=0 chnl=15
+char id=105 x=382 y=360 width=16 height=71 xoffset=-4 yoffset=-4 xadvance=8 page=0 chnl=15
+char id=106 x=359 y=288 width=27 height=71 xoffset=-4 yoffset=-4 xadvance=18 page=0 chnl=15
+char id=107 x=483 y=72 width=28 height=71 xoffset=-4 yoffset=-4 xadvance=20 page=0 chnl=15
+char id=108 x=55 y=360 width=26 height=71 xoffset=-4 yoffset=-4 xadvance=17 page=0 chnl=15
+char id=109 x=435 y=216 width=31 height=71 xoffset=-4 yoffset=-4 xadvance=23 page=0 chnl=15
+char id=110 x=273 y=216 width=32 height=71 xoffset=-4 yoffset=-4 xadvance=24 page=0 chnl=15
+char id=111 x=31 y=288 width=30 height=71 xoffset=-4 yoffset=-4 xadvance=22 page=0 chnl=15
+char id=112 x=331 y=288 width=27 height=71 xoffset=-4 yoffset=-4 xadvance=19 page=0 chnl=15
+char id=113 x=363 y=144 width=34 height=71 xoffset=-4 yoffset=-4 xadvance=26 page=0 chnl=15
+char id=114 x=273 y=288 width=28 height=71 xoffset=-4 yoffset=-4 xadvance=20 page=0 chnl=15
+char id=115 x=28 y=360 width=26 height=71 xoffset=-4 yoffset=-4 xadvance=18 page=0 chnl=15
+char id=116 x=62 y=288 width=30 height=71 xoffset=-4 yoffset=-4 xadvance=22 page=0 chnl=15
+char id=117 x=93 y=288 width=30 height=71 xoffset=-4 yoffset=-4 xadvance=22 page=0 chnl=15
+char id=118 x=467 y=216 width=31 height=71 xoffset=-4 yoffset=-4 xadvance=23 page=0 chnl=15
+char id=119 x=293 y=144 width=34 height=71 xoffset=-4 yoffset=-4 xadvance=26 page=0 chnl=15
+char id=120 x=185 y=288 width=29 height=71 xoffset=-4 yoffset=-4 xadvance=22 page=0 chnl=15
+char id=121 x=215 y=288 width=28 height=71 xoffset=-4 yoffset=-4 xadvance=20 page=0 chnl=15
+char id=122 x=244 y=288 width=28 height=71 xoffset=-4 yoffset=-4 xadvance=20 page=0 chnl=15
+char id=123 x=428 y=360 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=124 x=440 y=360 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=125 x=452 y=360 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=126 x=0 y=0 width=66 height=71 xoffset=-4 yoffset=-4 xadvance=58 page=0 chnl=15
+char id=162 x=464 y=360 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=163 x=407 y=72 width=37 height=71 xoffset=-4 yoffset=-4 xadvance=29 page=0 chnl=15
+char id=167 x=476 y=360 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=171 x=214 y=360 width=25 height=71 xoffset=-4 yoffset=-4 xadvance=17 page=0 chnl=15
+char id=187 x=240 y=360 width=25 height=71 xoffset=-4 yoffset=-4 xadvance=17 page=0 chnl=15
+char id=191 x=500 y=360 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=192 x=0 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=193 x=12 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=194 x=24 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=196 x=36 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=199 x=48 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=200 x=60 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=201 x=72 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=202 x=84 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=203 x=96 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=206 x=108 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=207 x=120 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=209 x=132 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=211 x=144 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=212 x=156 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=214 x=168 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=217 x=180 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=218 x=192 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=219 x=204 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=220 x=216 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=223 x=228 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=224 x=240 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=225 x=252 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=226 x=264 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=228 x=276 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=231 x=288 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=232 x=300 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=233 x=312 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=234 x=324 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=235 x=336 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=238 x=348 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=239 x=360 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=243 x=372 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=244 x=384 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=246 x=396 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=249 x=408 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=250 x=420 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=251 x=432 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=252 x=444 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
diff --git a/games/cat_vs_roomba/resources/star.png b/games/cat_vs_roomba/resources/star.png
new file mode 100644
index 000000000..682c5f0fd
Binary files /dev/null and b/games/cat_vs_roomba/resources/star.png differ
diff --git a/games/cat_vs_roomba/resources/star2.fnt b/games/cat_vs_roomba/resources/star2.fnt
new file mode 100644
index 000000000..3238321db
--- /dev/null
+++ b/games/cat_vs_roomba/resources/star2.fnt
@@ -0,0 +1,139 @@
+info face="Starcatcher" size=-64 bold=0 italic=0 charset="ANSI" unicode=0 stretchH=100 smooth=1 aa=1 padding=4,4,4,4 spacing=1,1 outline=0
+common lineHeight=63 base=50 scaleW=512 scaleH=512 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4
+page id=0 file="star2.png"
+chars count=135
+char id=32 x=416 y=360 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=27 page=0 chnl=15
+char id=33 x=347 y=360 width=17 height=71 xoffset=-4 yoffset=-4 xadvance=9 page=0 chnl=15
+char id=35 x=183 y=0 width=48 height=71 xoffset=-4 yoffset=-4 xadvance=40 page=0 chnl=15
+char id=36 x=328 y=144 width=34 height=71 xoffset=-4 yoffset=-4 xadvance=26 page=0 chnl=15
+char id=37 x=67 y=0 width=62 height=71 xoffset=-4 yoffset=-4 xadvance=55 page=0 chnl=15
+char id=38 x=488 y=360 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=39 x=266 y=360 width=21 height=71 xoffset=-4 yoffset=-4 xadvance=13 page=0 chnl=15
+char id=40 x=471 y=288 width=27 height=71 xoffset=-4 yoffset=-4 xadvance=19 page=0 chnl=15
+char id=41 x=387 y=288 width=27 height=71 xoffset=-4 yoffset=-4 xadvance=19 page=0 chnl=15
+char id=42 x=415 y=0 width=42 height=71 xoffset=-4 yoffset=-4 xadvance=34 page=0 chnl=15
+char id=43 x=403 y=216 width=31 height=71 xoffset=-4 yoffset=-4 xadvance=23 page=0 chnl=15
+char id=44 x=309 y=360 width=18 height=71 xoffset=-4 yoffset=-4 xadvance=10 page=0 chnl=15
+char id=45 x=82 y=360 width=26 height=71 xoffset=-4 yoffset=-4 xadvance=18 page=0 chnl=15
+char id=46 x=365 y=360 width=16 height=71 xoffset=-4 yoffset=-4 xadvance=7 page=0 chnl=15
+char id=47 x=155 y=288 width=29 height=71 xoffset=-4 yoffset=-4 xadvance=21 page=0 chnl=15
+char id=48 x=38 y=144 width=36 height=71 xoffset=-4 yoffset=-4 xadvance=27 page=0 chnl=15
+char id=49 x=188 y=360 width=25 height=71 xoffset=-4 yoffset=-4 xadvance=17 page=0 chnl=15
+char id=50 x=339 y=216 width=31 height=71 xoffset=-4 yoffset=-4 xadvance=23 page=0 chnl=15
+char id=51 x=0 y=288 width=30 height=71 xoffset=-4 yoffset=-4 xadvance=22 page=0 chnl=15
+char id=52 x=35 y=216 width=34 height=71 xoffset=-4 yoffset=-4 xadvance=26 page=0 chnl=15
+char id=53 x=105 y=216 width=33 height=71 xoffset=-4 yoffset=-4 xadvance=24 page=0 chnl=15
+char id=54 x=207 y=216 width=32 height=71 xoffset=-4 yoffset=-4 xadvance=24 page=0 chnl=15
+char id=55 x=433 y=144 width=34 height=71 xoffset=-4 yoffset=-4 xadvance=26 page=0 chnl=15
+char id=56 x=468 y=144 width=34 height=71 xoffset=-4 yoffset=-4 xadvance=26 page=0 chnl=15
+char id=57 x=0 y=216 width=34 height=71 xoffset=-4 yoffset=-4 xadvance=26 page=0 chnl=15
+char id=58 x=399 y=360 width=16 height=71 xoffset=-4 yoffset=-4 xadvance=7 page=0 chnl=15
+char id=59 x=328 y=360 width=18 height=71 xoffset=-4 yoffset=-4 xadvance=10 page=0 chnl=15
+char id=60 x=185 y=144 width=35 height=71 xoffset=-4 yoffset=-4 xadvance=27 page=0 chnl=15
+char id=61 x=371 y=216 width=31 height=71 xoffset=-4 yoffset=-4 xadvance=23 page=0 chnl=15
+char id=62 x=221 y=144 width=35 height=71 xoffset=-4 yoffset=-4 xadvance=27 page=0 chnl=15
+char id=63 x=398 y=144 width=34 height=71 xoffset=-4 yoffset=-4 xadvance=25 page=0 chnl=15
+char id=64 x=130 y=0 width=52 height=71 xoffset=-4 yoffset=-4 xadvance=44 page=0 chnl=15
+char id=65 x=445 y=72 width=37 height=71 xoffset=-4 yoffset=-4 xadvance=29 page=0 chnl=15
+char id=66 x=209 y=72 width=40 height=71 xoffset=-4 yoffset=-4 xadvance=32 page=0 chnl=15
+char id=67 x=173 y=216 width=33 height=71 xoffset=-4 yoffset=-4 xadvance=24 page=0 chnl=15
+char id=68 x=250 y=72 width=39 height=71 xoffset=-4 yoffset=-4 xadvance=31 page=0 chnl=15
+char id=69 x=306 y=216 width=32 height=71 xoffset=-4 yoffset=-4 xadvance=24 page=0 chnl=15
+char id=70 x=149 y=144 width=35 height=71 xoffset=-4 yoffset=-4 xadvance=27 page=0 chnl=15
+char id=71 x=112 y=144 width=36 height=71 xoffset=-4 yoffset=-4 xadvance=28 page=0 chnl=15
+char id=72 x=458 y=0 width=42 height=71 xoffset=-4 yoffset=-4 xadvance=34 page=0 chnl=15
+char id=73 x=288 y=360 width=20 height=71 xoffset=-4 yoffset=-4 xadvance=12 page=0 chnl=15
+char id=74 x=257 y=144 width=35 height=71 xoffset=-4 yoffset=-4 xadvance=27 page=0 chnl=15
+char id=75 x=0 y=144 width=37 height=71 xoffset=-4 yoffset=-4 xadvance=29 page=0 chnl=15
+char id=76 x=139 y=216 width=33 height=71 xoffset=-4 yoffset=-4 xadvance=25 page=0 chnl=15
+char id=77 x=85 y=72 width=41 height=71 xoffset=-4 yoffset=-4 xadvance=33 page=0 chnl=15
+char id=78 x=371 y=0 width=43 height=71 xoffset=-4 yoffset=-4 xadvance=35 page=0 chnl=15
+char id=79 x=168 y=72 width=40 height=71 xoffset=-4 yoffset=-4 xadvance=31 page=0 chnl=15
+char id=80 x=75 y=144 width=36 height=71 xoffset=-4 yoffset=-4 xadvance=28 page=0 chnl=15
+char id=81 x=280 y=0 width=45 height=71 xoffset=-4 yoffset=-4 xadvance=37 page=0 chnl=15
+char id=82 x=369 y=72 width=37 height=71 xoffset=-4 yoffset=-4 xadvance=29 page=0 chnl=15
+char id=83 x=70 y=216 width=34 height=71 xoffset=-4 yoffset=-4 xadvance=26 page=0 chnl=15
+char id=84 x=43 y=72 width=41 height=71 xoffset=-4 yoffset=-4 xadvance=33 page=0 chnl=15
+char id=85 x=0 y=72 width=42 height=71 xoffset=-4 yoffset=-4 xadvance=34 page=0 chnl=15
+char id=86 x=326 y=0 width=44 height=71 xoffset=-4 yoffset=-4 xadvance=36 page=0 chnl=15
+char id=87 x=232 y=0 width=47 height=71 xoffset=-4 yoffset=-4 xadvance=39 page=0 chnl=15
+char id=88 x=127 y=72 width=40 height=71 xoffset=-4 yoffset=-4 xadvance=33 page=0 chnl=15
+char id=89 x=290 y=72 width=39 height=71 xoffset=-4 yoffset=-4 xadvance=31 page=0 chnl=15
+char id=90 x=330 y=72 width=38 height=71 xoffset=-4 yoffset=-4 xadvance=30 page=0 chnl=15
+char id=91 x=499 y=288 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=92 x=109 y=360 width=26 height=71 xoffset=-4 yoffset=-4 xadvance=18 page=0 chnl=15
+char id=93 x=499 y=216 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=97 x=302 y=288 width=28 height=71 xoffset=-4 yoffset=-4 xadvance=20 page=0 chnl=15
+char id=98 x=415 y=288 width=27 height=71 xoffset=-4 yoffset=-4 xadvance=19 page=0 chnl=15
+char id=99 x=136 y=360 width=25 height=71 xoffset=-4 yoffset=-4 xadvance=17 page=0 chnl=15
+char id=100 x=124 y=288 width=30 height=71 xoffset=-4 yoffset=-4 xadvance=22 page=0 chnl=15
+char id=101 x=162 y=360 width=25 height=71 xoffset=-4 yoffset=-4 xadvance=17 page=0 chnl=15
+char id=102 x=443 y=288 width=27 height=71 xoffset=-4 yoffset=-4 xadvance=19 page=0 chnl=15
+char id=103 x=0 y=360 width=27 height=71 xoffset=-4 yoffset=-4 xadvance=19 page=0 chnl=15
+char id=104 x=240 y=216 width=32 height=71 xoffset=-4 yoffset=-4 xadvance=24 page=0 chnl=15
+char id=105 x=382 y=360 width=16 height=71 xoffset=-4 yoffset=-4 xadvance=8 page=0 chnl=15
+char id=106 x=359 y=288 width=27 height=71 xoffset=-4 yoffset=-4 xadvance=18 page=0 chnl=15
+char id=107 x=483 y=72 width=28 height=71 xoffset=-4 yoffset=-4 xadvance=20 page=0 chnl=15
+char id=108 x=55 y=360 width=26 height=71 xoffset=-4 yoffset=-4 xadvance=17 page=0 chnl=15
+char id=109 x=435 y=216 width=31 height=71 xoffset=-4 yoffset=-4 xadvance=23 page=0 chnl=15
+char id=110 x=273 y=216 width=32 height=71 xoffset=-4 yoffset=-4 xadvance=24 page=0 chnl=15
+char id=111 x=31 y=288 width=30 height=71 xoffset=-4 yoffset=-4 xadvance=22 page=0 chnl=15
+char id=112 x=331 y=288 width=27 height=71 xoffset=-4 yoffset=-4 xadvance=19 page=0 chnl=15
+char id=113 x=363 y=144 width=34 height=71 xoffset=-4 yoffset=-4 xadvance=26 page=0 chnl=15
+char id=114 x=273 y=288 width=28 height=71 xoffset=-4 yoffset=-4 xadvance=20 page=0 chnl=15
+char id=115 x=28 y=360 width=26 height=71 xoffset=-4 yoffset=-4 xadvance=18 page=0 chnl=15
+char id=116 x=62 y=288 width=30 height=71 xoffset=-4 yoffset=-4 xadvance=22 page=0 chnl=15
+char id=117 x=93 y=288 width=30 height=71 xoffset=-4 yoffset=-4 xadvance=22 page=0 chnl=15
+char id=118 x=467 y=216 width=31 height=71 xoffset=-4 yoffset=-4 xadvance=23 page=0 chnl=15
+char id=119 x=293 y=144 width=34 height=71 xoffset=-4 yoffset=-4 xadvance=26 page=0 chnl=15
+char id=120 x=185 y=288 width=29 height=71 xoffset=-4 yoffset=-4 xadvance=22 page=0 chnl=15
+char id=121 x=215 y=288 width=28 height=71 xoffset=-4 yoffset=-4 xadvance=20 page=0 chnl=15
+char id=122 x=244 y=288 width=28 height=71 xoffset=-4 yoffset=-4 xadvance=20 page=0 chnl=15
+char id=123 x=428 y=360 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=124 x=440 y=360 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=125 x=452 y=360 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=126 x=0 y=0 width=66 height=71 xoffset=-4 yoffset=-4 xadvance=58 page=0 chnl=15
+char id=162 x=464 y=360 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=163 x=407 y=72 width=37 height=71 xoffset=-4 yoffset=-4 xadvance=29 page=0 chnl=15
+char id=167 x=476 y=360 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=171 x=214 y=360 width=25 height=71 xoffset=-4 yoffset=-4 xadvance=17 page=0 chnl=15
+char id=187 x=240 y=360 width=25 height=71 xoffset=-4 yoffset=-4 xadvance=17 page=0 chnl=15
+char id=191 x=500 y=360 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=192 x=0 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=193 x=12 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=194 x=24 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=196 x=36 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=199 x=48 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=200 x=60 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=201 x=72 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=202 x=84 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=203 x=96 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=206 x=108 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=207 x=120 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=209 x=132 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=211 x=144 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=212 x=156 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=214 x=168 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=217 x=180 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=218 x=192 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=219 x=204 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=220 x=216 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=223 x=228 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=224 x=240 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=225 x=252 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=226 x=264 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=228 x=276 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=231 x=288 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=232 x=300 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=233 x=312 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=234 x=324 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=235 x=336 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=238 x=348 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=239 x=360 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=243 x=372 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=244 x=384 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=246 x=396 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=249 x=408 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=250 x=420 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=251 x=432 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
+char id=252 x=444 y=432 width=11 height=71 xoffset=-5 yoffset=-4 xadvance=1 page=0 chnl=15
diff --git a/games/cat_vs_roomba/resources/star2.png b/games/cat_vs_roomba/resources/star2.png
new file mode 100644
index 000000000..cd0074883
Binary files /dev/null and b/games/cat_vs_roomba/resources/star2.png differ
diff --git a/games/cat_vs_roomba/resources/title_cat.png b/games/cat_vs_roomba/resources/title_cat.png
new file mode 100644
index 000000000..01b88b0f8
Binary files /dev/null and b/games/cat_vs_roomba/resources/title_cat.png differ
diff --git a/games/cat_vs_roomba/resources/title_roomba.png b/games/cat_vs_roomba/resources/title_roomba.png
new file mode 100644
index 000000000..dedf0fe34
Binary files /dev/null and b/games/cat_vs_roomba/resources/title_roomba.png differ
diff --git a/games/cat_vs_roomba/resources/title_vs.png b/games/cat_vs_roomba/resources/title_vs.png
new file mode 100644
index 000000000..742bf92c8
Binary files /dev/null and b/games/cat_vs_roomba/resources/title_vs.png differ
diff --git a/games/cat_vs_roomba/resources/tracemap.png b/games/cat_vs_roomba/resources/tracemap.png
new file mode 100644
index 000000000..228221c04
Binary files /dev/null and b/games/cat_vs_roomba/resources/tracemap.png differ
diff --git a/games/cat_vs_roomba/roomba.c b/games/cat_vs_roomba/roomba.c
new file mode 100644
index 000000000..0d2367758
--- /dev/null
+++ b/games/cat_vs_roomba/roomba.c
@@ -0,0 +1,286 @@
+/*******************************************************************************************
+*
+* raylib - Advance Game template
+*
+*
+*
+*
+* 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-2018 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include "screens/screens.h" // NOTE: Defines global variable: currentScreen
+
+#if defined(PLATFORM_WEB)
+ #include
+#endif
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition (local to this module)
+//----------------------------------------------------------------------------------
+const int screenWidth = 1280;
+#if defined(TILE_VIEWER_MODE)
+const int screenHeight = 1080;
+#else
+const int screenHeight = 720;
+#endif
+
+// 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/star.fnt");
+ font2 = LoadFont("resources/star2.fnt");
+ music = LoadMusicStream("resources/cat_mouse.mod");
+ fxCoin = LoadSound("resources/coin.wav");
+
+ SetMusicVolume(music, 1.0f);
+ 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);
+ UnloadFont(font2);
+ UnloadMusicStream(music);
+ UnloadSound(fxCoin);
+
+ CloseAudioDevice(); // Close audio context
+
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
+
+//----------------------------------------------------------------------------------
+// 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.02f;
+
+ // 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);
+
+ } break;
+ case TITLE:
+ {
+ UpdateTitleScreen();
+
+ if (FinishTitleScreen() == 1) TransitionToScreen(OPTIONS);
+ 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();
+ //----------------------------------------------------------------------------------
+}
diff --git a/games/cat_vs_roomba/screens/screen_ending.c b/games/cat_vs_roomba/screens/screen_ending.c
new file mode 100644
index 000000000..ef2a5f749
--- /dev/null
+++ b/games/cat_vs_roomba/screens/screen_ending.c
@@ -0,0 +1,96 @@
+/**********************************************************************************************
+*
+* raylib - Advance Game template
+*
+* Ending Screen Functions Definitions (Init, Update, Draw, Unload)
+*
+* Copyright (c) 2014-2018 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)
+//----------------------------------------------------------------------------------
+
+// Ending screen global variables
+static int framesCounter;
+static int finishScreen;
+
+static int scrollPositionX;
+
+//----------------------------------------------------------------------------------
+// Ending Screen Functions Definition
+//----------------------------------------------------------------------------------
+
+// Ending Screen Initialization logic
+void InitEndingScreen(void)
+{
+ // TODO: Initialize ENDING screen variables here!
+ framesCounter = 0;
+ finishScreen = 0;
+
+ PlayMusicStream(music);
+}
+
+// Ending Screen Update logic
+void UpdateEndingScreen(void)
+{
+ framesCounter++;
+
+ scrollPositionX -= 5;
+ if (scrollPositionX < -GetScreenWidth()) scrollPositionX = 0;
+
+ // 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)
+{
+ for (int i = 0; i < 64*2*2; i++)
+ {
+ DrawRectangle(64*i + scrollPositionX, 0, 64, GetScreenHeight(), (i%2 == 0)? GetColor(0xf3726dff) : GetColor(0xffcf6bff));
+ }
+
+ if (result == 0) DrawTextEx(font2, "YOU LOOSE...", (Vector2){ 350, 200 }, font2.baseSize*2, 2, WHITE);
+ else if (result == 1) DrawTextEx(font, "YOU WIN!!!", (Vector2){ 380, 200 }, font.baseSize*2, 2, WHITE);
+
+ // Draw score
+ DrawTextEx(font, FormatText("FINAL SCORE: %i", score), (Vector2){ 400, 360 }, font2.baseSize, 2, WHITE);
+
+ if ((framesCounter/30)%2) DrawTextEx(font2, "PRESS ENTER to TITLE", (Vector2){ 340, 550 }, font2.baseSize, 2, WHITE);
+}
+
+// Ending Screen Unload logic
+void UnloadEndingScreen(void)
+{
+ // TODO: Unload ENDING screen variables here!
+}
+
+// Ending Screen should finish?
+int FinishEndingScreen(void)
+{
+ return finishScreen;
+}
\ No newline at end of file
diff --git a/games/cat_vs_roomba/screens/screen_gameplay.c b/games/cat_vs_roomba/screens/screen_gameplay.c
new file mode 100644
index 000000000..49a0bb6bf
--- /dev/null
+++ b/games/cat_vs_roomba/screens/screen_gameplay.c
@@ -0,0 +1,652 @@
+/**********************************************************************************************
+*
+* raylib - Advance Game template
+*
+* Gameplay Screen Functions Definitions (Init, Update, Draw, Unload)
+*
+* Copyright (c) 2014-2018 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"
+
+#include "raymath.h"
+
+#include
+
+#define TILE_REQUIRED_CLEAN_TIME 2 // Frames it takes to clean a dirt level
+#define TILE_SCORE_BY_CLEANED_LEVEL 100 // Score by cleanied dirt level
+#define TILE_REQUIRED_CLEAN_AREA 28*28 // Required are for actually cleaning tile
+
+#define TILE_SIZE 36 // Tile size, it should match texture
+#define MAX_TILES_X 32
+#define MAX_TILES_Y 17
+
+#define CAT_TARGET_RADIUS 3 // Target proximity radius
+#define CAT_DIRT_CELL_RADIUS 2 // Cells around cat for dirt spreading
+
+#define TIME_LIMIT_SECONDS 180 // Time to complete the level in seconds
+
+#define MAX_SCORE_POPUPS 60 // Maximum simultaneous score pop-ups!
+
+//----------------------------------------------------------------------------------
+// Module types
+//----------------------------------------------------------------------------------
+// One dirt tile type
+typedef struct {
+ Vector2 position; // Relative to top-left corner
+ int level; // Dirtiness: 0-Clean, 1-2-3-Dirt levels
+ int state; // Current dirtiness state
+ int counter; // Frames counter for cleaning
+ //int time; // Time it takes to make it clean --> Depends on level
+ //int score; // It depends on the dirt level
+ bool cleaned; // If it was cleaned (not clean by default)
+} Dirtile;
+
+// Score poping-up type
+typedef struct {
+ Vector2 position;
+ int value;
+ float alpha;
+ bool enabled;
+} ScorePopup;
+
+// Furniture tile set
+typedef struct {
+ int id; // Furniture tile id
+ int posX; // Position X on tileset
+ int posY; // Position Y on tileset
+ int width; // Furniture piece width
+ int height; // Furniture piece height
+} FurSet;
+
+// Furniture type
+typedef struct {
+ int furId; // Tileset id
+ int cellX; // Cell position X
+ int cellY; // Cell position Y
+ int state; // 0-Block, 1-Alpha, 2-Breakable
+ int counter; // Counter in case of break
+} Furniture;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition (local to this module)
+//----------------------------------------------------------------------------------
+
+// Gameplay screen global variables
+static int framesCounter;
+static int timeLevelSeconds;
+static bool levelFinished;
+static int finishScreen;
+
+const Vector2 roomOffset = { 70, 70 };
+
+static Texture2D roomba;
+static Texture2D cat;
+static Texture2D dirtiles;
+static Texture2D furniture;
+
+#if defined(TILE_VIEWER_MODE)
+static Texture2D tracemap;
+static Texture2D fursetid;
+#endif
+
+static Music catch;
+
+static Sound fxCat[2];
+static Sound fxRoomba[3];
+
+static Vector2 roombaPosition = { 100, 100 };
+static Vector2 roombaSpeed = { 4, 4 };
+static int roombaTilePosX = 0, roombaTilePosY = 0;
+
+static Vector2 catPosition = { 0, 0 };
+static Vector2 catTargetPosition = { 0, 0 };
+static Vector2 catSpeed = { 3, 3 };
+static int catTilePosX = 0, catTilePosY = 0;
+static bool catShouldMove = false;
+
+static Vector2 mousePosition = { 0, 0 };
+static int mouseTileX = -1, mouseTileY = -1;
+
+static Dirtile tiles[MAX_TILES_X*MAX_TILES_Y] = { 0 };
+
+static ScorePopup popup[MAX_SCORE_POPUPS] = { 0 };
+
+static FurSet furset[32] = { -1 };
+static Furniture furmap[40] = { -1 };
+static int furnitureCount = 0;
+
+// Furniture collisions map
+// 0-block, 1-normal, 2-alpha, 3-breakable
+static int furcolmap[MAX_TILES_X*MAX_TILES_Y] = {
+ 1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,3,3,1,1,1,0,0,1,1,1,1,1,1,1,1,
+ 1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,3,3,1,1,1,0,0,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 0,0,1,1,1,1,2,2,2,1,1,1,0,0,1,1,1,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,
+ 0,0,1,1,1,1,2,2,2,1,1,1,0,0,1,1,1,2,2,2,1,1,1,1,3,3,1,1,1,1,0,0,
+ 3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,1,1,1,3,3,1,1,1,1,0,0,
+ 3,3,1,1,2,2,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,1,1,1,0,0,0,0,0,0,0,0,
+ 1,1,1,1,2,2,2,2,2,2,2,2,1,1,1,1,1,2,2,2,1,1,1,1,0,0,0,0,0,0,0,0,
+ 1,1,1,1,2,2,2,2,2,2,2,2,1,1,1,1,1,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,0,0,0,0,0,0,0,0,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,0,0,0,0,0,0,0,0,3,3,1,1,1,1,1,1,1,0,0,1,2,2,2,2,2,2,1,1,
+ 0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,2,2,2,2,2,2,1,1,
+ 0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,1,1,
+ 0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,1,1,
+ 0,0,0,0,0,0,0,0,0,0,2,2,2,3,3,3,3,1,1,0,0,1,2,2,2,2,2,2,2,2,0,0,
+ 0,0,0,0,0,0,0,0,0,0,2,2,2,3,3,3,3,1,1,0,0,1,2,2,2,2,2,2,2,2,0,0 };
+
+static bool showObjective = false;
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+static float GetTileCleanPercent(void);
+
+//----------------------------------------------------------------------------------
+// Gameplay Screen Functions Definition
+//----------------------------------------------------------------------------------
+
+// Gameplay Screen Initialization logic
+void InitGameplayScreen(void)
+{
+ // Initialize GAMEPLAY screen variables here!
+ framesCounter = 0;
+ finishScreen = 0;
+ timeLevelSeconds = TIME_LIMIT_SECONDS;
+ levelFinished = false;
+
+ roomba = LoadTexture("resources/roomba.png");
+ cat = LoadTexture("resources/cat.png");
+ dirtiles = LoadTexture("resources/dirtiles.png");
+ furniture = LoadTexture("resources/furniture.png");
+
+#if defined(TILE_VIEWER_MODE)
+ tracemap = LoadTexture("resources/tracemap.png");
+ fursetid = LoadTexture("resources/fursetid.png");
+#endif
+
+ int furCount = 0;
+ FILE *fursetFile = fopen("resources/furset.txt", "rt");
+
+ if (fursetFile != NULL)
+ {
+ char buffer[512] = { 0 };
+
+ while (!feof(fursetFile))
+ {
+ fgets(buffer, 512, fursetFile);
+
+ switch (buffer[0])
+ {
+ case 'f':
+ {
+ sscanf(buffer, "f %i %i %i %i %i",
+ &furset[furCount].id,
+ &furset[furCount].posX,
+ &furset[furCount].posY,
+ &furset[furCount].width,
+ &furset[furCount].height);
+ furCount++;
+ } break;
+ case '.': // This is a comment
+ default: break;
+ }
+ }
+
+ fclose(fursetFile);
+ }
+
+ // Position and size come in cell form, not pixels
+ for (int i = 0; i < furCount; i++)
+ {
+ furset[i].posX *= TILE_SIZE;
+ furset[i].posY *= TILE_SIZE;
+ furset[i].width *= TILE_SIZE;
+ furset[i].height *= TILE_SIZE;
+ }
+
+ printf("Furniture SET elements read: %i\n", furCount);
+
+ // Init furniture elements
+ FILE *furnitureFile = fopen("resources/furmap.txt", "rt");
+
+ if (furnitureFile != NULL)
+ {
+ char buffer[512] = { 0 };
+
+ while (!feof(furnitureFile))
+ {
+ fgets(buffer, 512, furnitureFile);
+
+ switch (buffer[0])
+ {
+ case 'f':
+ {
+ sscanf(buffer, "f %i %i %i %i %i",
+ &furmap[furnitureCount].furId,
+ &furmap[furnitureCount].cellX,
+ &furmap[furnitureCount].cellY,
+ &furmap[furnitureCount].state,
+ &furmap[furnitureCount].counter);
+ furnitureCount++;
+ } break;
+ case '.': // This is a comment
+ default: break;
+ }
+ }
+
+ fclose(furnitureFile);
+ }
+
+ printf("Furniture MAP elements read: %i\n", furnitureCount);
+
+ // Init dirt tiles
+ for (int y = 0; y < MAX_TILES_Y; y++)
+ {
+ for (int x = 0; x < MAX_TILES_X; x++)
+ {
+ tiles[y*MAX_TILES_X + x].position = (Vector2){ roomOffset.x + TILE_SIZE*x, roomOffset.y + TILE_SIZE*y };
+
+ if ((furcolmap[y*MAX_TILES_X + x] != 0) &&
+ (furcolmap[y*MAX_TILES_X + x] != 3))
+ {
+ // TODO: Level of dirtiness depends on difficulty level
+ // Adjust probability of every tile dirt level
+ int dirt = GetRandomValue(0, 100);
+
+ if (dirt < 50) tiles[y*MAX_TILES_X + x].level = 0; // 50% probability
+ else if (dirt < 70) tiles[y*MAX_TILES_X + x].level = 1; // 20% probability
+ else if (dirt < 90) tiles[y*MAX_TILES_X + x].level = 2; // 10% probability
+ else if (dirt < 100) tiles[y*MAX_TILES_X + x].level = 3; // 10% probability
+ }
+ else tiles[y*MAX_TILES_X + x].level = 0;
+
+ tiles[y*MAX_TILES_X + x].state = tiles[y*MAX_TILES_X + x].level;
+ tiles[y*MAX_TILES_X + x].counter = (tiles[y*MAX_TILES_X + x].level == 0)? 0 : TILE_REQUIRED_CLEAN_TIME;
+ tiles[y*MAX_TILES_X + x].cleaned = (tiles[y*MAX_TILES_X + x].level == 0)? true : false;
+ }
+ }
+
+ // Init score popups
+ for (int i = 0; i < MAX_SCORE_POPUPS; i++)
+ {
+ popup[i].position = (Vector2){ 0, 0 };
+ popup[i].value = TILE_SCORE_BY_CLEANED_LEVEL;
+ popup[i].enabled = false;
+ popup[i].alpha = 1.0f;
+ }
+
+ // Init cat position
+ catPosition = (Vector2){ 30*TILE_SIZE + roomOffset.x, TILE_SIZE + roomOffset.y };
+ catTargetPosition = catPosition;
+
+ showObjective = true;
+
+ // Load music and sounds
+ fxCat[0] = LoadSound("resources/fxcat01.wav");
+ fxCat[1] = LoadSound("resources/fxcat02.wav");
+ fxRoomba[0] = LoadSound("resources/fxrobot01.wav");
+ fxRoomba[1] = LoadSound("resources/fxrobot02.wav");
+ fxRoomba[2] = LoadSound("resources/fxrobot03.wav");
+
+ catch = LoadMusicStream("resources/catch22.mod");
+
+ StopMusicStream(music);
+ SetMusicVolume(catch, 0.6f);
+ PlayMusicStream(catch);
+
+ result = 0; // Global variable: screens.h
+}
+
+// Gameplay Screen Update logic
+void UpdateGameplayScreen(void)
+{
+ UpdateMusicStream(catch);
+
+ if (showObjective)
+ {
+ if (IsKeyPressed(KEY_ENTER))
+ {
+ showObjective = false;
+ PlaySound(fxCoin);
+ }
+
+ return;
+ }
+
+ framesCounter++;
+
+ if (framesCounter == 60)
+ {
+ timeLevelSeconds--;
+
+ if (timeLevelSeconds == 0)
+ {
+ levelFinished = true;
+ finishScreen = 1;
+ PlaySound(fxCoin);
+
+ if (GetTileCleanPercent() >= 80) result = 1;
+ }
+
+ framesCounter = 0;
+ }
+
+ mousePosition = GetMousePosition();
+ mouseTileX = (int)floorf((mousePosition.x - roomOffset.x)/TILE_SIZE);
+ mouseTileY = (int)floorf((mousePosition.y - roomOffset.y)/TILE_SIZE);
+
+ // Roomba movement logic
+ Vector2 prevPosition = roombaPosition;
+
+ if (IsKeyDown(KEY_D)) roombaPosition.x += roombaSpeed.x;
+ else if (IsKeyDown(KEY_A)) roombaPosition.x -= roombaSpeed.x;
+ if (IsKeyDown(KEY_W)) roombaPosition.y -= roombaSpeed.y;
+ else if (IsKeyDown(KEY_S)) roombaPosition.y += roombaSpeed.y;
+
+ // Verify current player position is valid or reset
+ roombaTilePosX = (int)(floorf(roombaPosition.x - roomOffset.x)/TILE_SIZE);
+ roombaTilePosY = (int)(floorf(roombaPosition.y - roomOffset.y)/TILE_SIZE);
+ if ((roombaPosition.x - roomba.width/2 < roomOffset.x) ||
+ ((roombaPosition.x + roomba.width/2) >= (roomOffset.x + MAX_TILES_X*TILE_SIZE)) ||
+ (roombaPosition.y - roomba.height/2 < roomOffset.y) ||
+ ((roombaPosition.y + roomba.height/2) >= (roomOffset.y + MAX_TILES_Y*TILE_SIZE)) ||
+ (furcolmap[roombaTilePosY*MAX_TILES_X + roombaTilePosX] == 0) ||
+ (furcolmap[roombaTilePosY*MAX_TILES_X + roombaTilePosX] == 3)) roombaPosition = prevPosition;
+
+ // Dyson movement logic
+ // if (IsKeyDown(KEY_RIGHT)) dysonPosition.x += dysonSpeed.x;
+ // else if (IsKeyDown(KEY_LEFT)) dysonPosition.x -= dysonSpeed.x;
+ // if (IsKeyDown(KEY_UP)) dysonPosition.y -= dysonSpeed.y;
+ // else if (IsKeyDown(KEY_DOWN)) dysonPosition.y += dysonSpeed.y;
+
+ // Check collision area between Roomba and dirt tiles to verify it's beeing cleaned
+ // TODO: OPTIMIZATION: Check only Roomba surrounding tiles
+ for (int y = 0; y < MAX_TILES_Y; y++)
+ {
+ for (int x = 0; x < MAX_TILES_X; x++)
+ {
+ // Check if tile requires cleaning
+ if (tiles[y*MAX_TILES_X + x].state > 0)
+ {
+ // TODO: Get better collision area measure, considering round roomba
+ Rectangle cleanRec = GetCollisionRec((Rectangle){ tiles[y*MAX_TILES_X + x].position.x, tiles[y*MAX_TILES_X + x].position.y, 36, 36 },
+ (Rectangle){ roombaPosition.x - roomba.width/2, roombaPosition.y - roomba.height/2, roomba.width, roomba.height });
+
+ // Check Roomba is covering at least half of the tile
+ if ((cleanRec.width*cleanRec.height) > TILE_REQUIRED_CLEAN_AREA)
+ {
+ // Start cleaning tile
+ tiles[y*MAX_TILES_X + x].counter--;
+
+ if (tiles[y*MAX_TILES_X + x].counter < 0)
+ {
+ tiles[y*MAX_TILES_X + x].state--;
+
+ if (tiles[y*MAX_TILES_X + x].state == 0)
+ {
+ tiles[y*MAX_TILES_X + x].counter = 0;
+ score += tiles[y*MAX_TILES_X + x].level*TILE_SCORE_BY_CLEANED_LEVEL;
+
+ // Show scoring popup, enable first ready!
+ for (int i = 0; i < MAX_SCORE_POPUPS; i++)
+ {
+ if (!popup[i].enabled)
+ {
+ popup[i].position = tiles[y*MAX_TILES_X + x].position;
+ popup[i].value = TILE_SCORE_BY_CLEANED_LEVEL*tiles[y*MAX_TILES_X + x].level;
+ popup[i].enabled = true;
+ popup[i].alpha = 1.0f;
+ break;
+ }
+ }
+ }
+ else tiles[y*MAX_TILES_X + x].counter = TILE_REQUIRED_CLEAN_TIME;
+ }
+ }
+ }
+ }
+ }
+
+ // Update enabled popups!
+ for (int i = 0; i < MAX_SCORE_POPUPS; i++)
+ {
+ if (popup[i].enabled)
+ {
+ popup[i].position.y -= 2;
+ popup[i].alpha -= 0.015f;
+
+ if (popup[i].alpha < 0.0f) popup[i].enabled = false;
+ }
+ }
+
+ // Cat movement logic
+ if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+ {
+ // Check for a valid cell to move on
+ if ((mousePosition.x > roomOffset.x) && (mousePosition.x < (roomOffset.x + MAX_TILES_X*TILE_SIZE)) &&
+ (mousePosition.y > roomOffset.y) && (mousePosition.y < (roomOffset.y + MAX_TILES_Y*TILE_SIZE)) &&
+ furcolmap[mouseTileY*MAX_TILES_X + mouseTileX] != 0)
+ {
+ catTargetPosition = GetMousePosition();
+ catShouldMove = true;
+ }
+ }
+
+ if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) PlaySound(fxCat[GetRandomValue(0,1)]);
+ if (IsKeyPressed(KEY_SPACE)) PlaySound(fxRoomba[GetRandomValue(0,2)]);
+
+ // Check if cat should move
+ if (catShouldMove)
+ {
+ if (CheckCollisionPointCircle(catPosition, catTargetPosition, CAT_TARGET_RADIUS))
+ {
+ catShouldMove = false;
+
+ // Spread dirt all around selected cell!
+ // NOTE: We consider cat drawing offset
+ catTilePosX = (int)floorf((catPosition.x - cat.width/2 - roomOffset.x)/TILE_SIZE) + 1;
+ catTilePosY = (int)floorf((catPosition.y - cat.height/2 - 10 - roomOffset.y)/TILE_SIZE) + 1;
+
+ // Check if tile includes a dirt element
+ if (furcolmap[mouseTileY*MAX_TILES_X + mouseTileX] == 3)
+ {
+ for (int y = (catTilePosY - CAT_DIRT_CELL_RADIUS); y < (catTilePosY + CAT_DIRT_CELL_RADIUS + 1); y++)
+ {
+ for (int x = (catTilePosX - CAT_DIRT_CELL_RADIUS); x < (catTilePosX + CAT_DIRT_CELL_RADIUS + 1); x++)
+ {
+ if (((y >= 0) && (y < MAX_TILES_Y) && (x >= 0) && (x < MAX_TILES_X)) &&
+ (tiles[y*MAX_TILES_X + x].state == 0) &&
+ (furcolmap[y*MAX_TILES_X + x] != 0) &&
+ (furcolmap[y*MAX_TILES_X + x] != 3))
+ {
+ int dirt = GetRandomValue(0, 100);
+
+ if (dirt < 50) tiles[y*MAX_TILES_X + x].level = 0; // 50% probability
+ else if (dirt < 70) tiles[y*MAX_TILES_X + x].level = 1; // 20% probability
+ else if (dirt < 90) tiles[y*MAX_TILES_X + x].level = 2; // 10% probability
+ else if (dirt < 100) tiles[y*MAX_TILES_X + x].level = 3; // 10% probability
+
+ tiles[y*MAX_TILES_X + x].state = tiles[y*MAX_TILES_X + x].level;
+ tiles[y*MAX_TILES_X + x].counter = (tiles[y*MAX_TILES_X + x].level == 0)? 0 : TILE_REQUIRED_CLEAN_TIME;
+ tiles[y*MAX_TILES_X + x].cleaned = (tiles[y*MAX_TILES_X + x].level == 0)? true : false;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ Vector2 dir = Vector2Subtract(catTargetPosition, catPosition);
+ Vector2 dirnorm = Vector2Normalize(dir);
+
+ catPosition.x += catSpeed.x*dirnorm.x;
+ catPosition.y += catSpeed.y*dirnorm.y;
+ }
+ }
+
+ if (levelFinished)
+ {
+ // TODO: Check level finished
+ }
+}
+
+// Gameplay Screen Draw logic
+void DrawGameplayScreen(void)
+{
+ DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), GetColor(0x57374cff));
+
+ // Draw tiles
+ for (int y = 0; y < MAX_TILES_Y; y++)
+ {
+ for (int x = 0; x < MAX_TILES_X; x++)
+ {
+ // Draw dirty tiles
+ DrawTextureRec(dirtiles, (Rectangle){ tiles[y*MAX_TILES_X + x].state*TILE_SIZE, 0, TILE_SIZE, TILE_SIZE },
+ (Vector2){ roomOffset.x + TILE_SIZE*x, roomOffset.y + TILE_SIZE*y }, WHITE);
+
+ // TODO: Draw possible walls
+ }
+ }
+
+ // Draw starting point for roomba and cat
+ DrawTextureRec(furniture, (Rectangle){ furset[30].posX, furset[30].posY, furset[30].width, furset[30].height }, roomOffset, WHITE);
+ DrawTextureRec(furniture, (Rectangle){ furset[29].posX, furset[29].posY, furset[29].width, furset[29].height }, (Vector2){ roomOffset.x + 29*36, roomOffset.y }, WHITE);
+
+ DrawTexture(roomba, roombaPosition.x - roomba.width/2, roombaPosition.y - roomba.height/2, WHITE);
+ DrawTexture(cat, catPosition.x - cat.width/2, catPosition.y - cat.height/2 - 10, WHITE);
+
+ float furAlpha = 1.0f;
+
+ // Draw home objects
+ for (int i = 0; i < furnitureCount; i++)
+ {
+ if (CheckCollisionCircleRec((Vector2){ roombaPosition.x - roomba.width/2, roombaPosition.y - roomba.height/2 }, roomba.width,
+ (Rectangle){ roomOffset.x + furmap[i].cellX*TILE_SIZE, roomOffset.y + furmap[i].cellY*TILE_SIZE,
+ furset[furmap[i].furId].width, furset[furmap[i].furId].height}) && (furmap[i].state == 1))
+ {
+ DrawTextureRec(furniture, (Rectangle){ furset[furmap[i].furId].posX, furset[furmap[i].furId].posY, furset[furmap[i].furId].width, furset[furmap[i].furId].height },
+ (Vector2){ roomOffset.x + furmap[i].cellX*TILE_SIZE, roomOffset.y + furmap[i].cellY*TILE_SIZE }, Fade(WHITE, 0.5f));
+ }
+ else
+ {
+ DrawTextureRec(furniture, (Rectangle){ furset[furmap[i].furId].posX, furset[furmap[i].furId].posY, furset[furmap[i].furId].width, furset[furmap[i].furId].height },
+ (Vector2){ roomOffset.x + furmap[i].cellX*TILE_SIZE, roomOffset.y + furmap[i].cellY*TILE_SIZE }, Fade(WHITE, furAlpha));
+ }
+ }
+
+#if defined(TILE_VIEWER_MODE)
+ DrawTexture(tracemap, roomOffset.x, roomOffset.y, Fade(WHITE, 0.5f));
+ DrawTexture(fursetid, 0, 720, WHITE);
+#endif
+
+ // TODO: If an object has been used by cat, draw it in gray
+ // Maybe add a tempo bar for reusing?
+
+ // Draw UI
+ DrawTextEx(font2, "SCORE:", (Vector2){ 80, 10 }, font2.baseSize, 2, WHITE);
+ DrawTextEx(font, FormatText("%i", score), (Vector2){ 260, 10 }, font.baseSize, 2, WHITE);
+ DrawTextEx(font2, "CLEAN:", (Vector2){ 500, 10 }, font2.baseSize, 2, WHITE);
+ DrawTextEx(font, FormatText("%.2f%%", GetTileCleanPercent()), (Vector2){ 690, 10 }, font.baseSize, 2, WHITE);
+ DrawTextEx(font2, "TIME:", (Vector2){ 950, 10 }, font2.baseSize, 2, WHITE);
+ DrawTextEx(font, FormatText("%i:%02is", timeLevelSeconds/60, timeLevelSeconds%60), (Vector2){ 1100, 10 }, font.baseSize, 2, WHITE);
+
+ // Debug information
+ //DrawText(FormatText("CatTilePos: [ %i, %i ]", catTilePosX, catTilePosY), roomOffset.x, 690, 20, RAYWHITE);
+ //DrawText(FormatText("MousePos: [ %i, %i ]", mouseTileX, mouseTileY), 400, 690, 20, RED);
+ //DrawText(FormatText("RoombaPos: [ %i, %i ]", roombaTilePosX, roombaTilePosY), 600, 690, 20, GREEN);
+
+ if ((mouseTileY >= 0) && (mouseTileY < MAX_TILES_Y) && (mouseTileX >= 0) && (mouseTileX < MAX_TILES_X))
+ {
+ DrawRectangleLinesEx((Rectangle){ tiles[mouseTileY*MAX_TILES_X + mouseTileX].position.x,
+ tiles[mouseTileY*MAX_TILES_X + mouseTileX].position.y, TILE_SIZE, TILE_SIZE }, 2, RED);
+ }
+
+ // Draw enabled popups!
+ for (int i = 0; i < MAX_SCORE_POPUPS; i++)
+ {
+ if (popup[i].enabled) DrawText(FormatText("+%i", popup[i].value), popup[i].position.x, popup[i].position.y, 20, Fade(RED, popup[i].alpha));
+ }
+
+ // Show objective
+ if (showObjective)
+ {
+ DrawRectangle(0, 150, GetScreenWidth(), GetScreenHeight() - 300, Fade(DARKGRAY, 0.7f));
+ DrawTextEx(font2, "OBJECTIVE:", (Vector2){ 500, 240 }, font2.baseSize, 2, WHITE);
+ DrawTextEx(font, "CLEAN 80% OF THE ROOM", (Vector2){ 300, 320 }, font.baseSize, 2, WHITE);
+ }
+
+}
+
+// Gameplay Screen Unload logic
+void UnloadGameplayScreen(void)
+{
+ // Unload GAMEPLAY screen variables here!
+ UnloadTexture(roomba);
+ UnloadTexture(cat);
+ UnloadTexture(dirtiles);
+ UnloadTexture(furniture);
+
+ UnloadSound(fxCat[0]);
+ UnloadSound(fxCat[1]);
+ UnloadSound(fxRoomba[0]);
+ UnloadSound(fxRoomba[1]);
+ UnloadSound(fxRoomba[2]);
+
+ StopMusicStream(catch);
+ UnloadMusicStream(catch);
+}
+
+// Gameplay Screen should finish?
+int FinishGameplayScreen(void)
+{
+ return finishScreen;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+
+// Check how much cleaning we have done
+static float GetTileCleanPercent(void)
+{
+ float value = 0;
+
+ int tileLevelsToClean = 0;
+ int tileLevelsCleaned = 0;
+
+ for (int y = 0; y < MAX_TILES_Y; y++)
+ {
+ for (int x = 0; x < MAX_TILES_X; x++)
+ {
+ if (tiles[y*MAX_TILES_X + x].level > 0)
+ {
+ tileLevelsToClean += tiles[y*MAX_TILES_X + x].level;
+ tileLevelsCleaned += tiles[y*MAX_TILES_X + x].state;
+ }
+ }
+ }
+
+ value = ((float)(tileLevelsToClean - tileLevelsCleaned)/tileLevelsToClean)*100.0f;
+
+ return value;
+}
\ No newline at end of file
diff --git a/games/cat_vs_roomba/screens/screen_logo.c b/games/cat_vs_roomba/screens/screen_logo.c
new file mode 100644
index 000000000..9fc704c76
--- /dev/null
+++ b/games/cat_vs_roomba/screens/screen_logo.c
@@ -0,0 +1,211 @@
+/**********************************************************************************************
+*
+* raylib - Advance Game template
+*
+* Logo Screen Functions Definitions (Init, Update, Draw, Unload)
+*
+* Copyright (c) 2014-2018 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;
+static int finishScreen;
+
+static int logoPositionX;
+static int logoPositionY;
+
+static int lettersCount;
+
+static int topSideRecWidth;
+static int leftSideRecHeight;
+
+static int bottomSideRecWidth;
+static int rightSideRecHeight;
+
+static char raylib[8]; // raylib text array, max 8 letters
+static int state; // 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;
+}
\ No newline at end of file
diff --git a/games/cat_vs_roomba/screens/screen_title.c b/games/cat_vs_roomba/screens/screen_title.c
new file mode 100644
index 000000000..009fbd0a0
--- /dev/null
+++ b/games/cat_vs_roomba/screens/screen_title.c
@@ -0,0 +1,154 @@
+/**********************************************************************************************
+*
+* raylib - Advance Game template
+*
+* Title Screen Functions Definitions (Init, Update, Draw, Unload)
+*
+* Copyright (c) 2014-2018 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;
+static int finishScreen;
+static int state;
+
+static int scrollPositionX;
+
+static int catPosX;
+static int roombaPosX;
+
+static float vsAlpha;
+static float vsScale;
+
+static Texture2D cat;
+static Texture2D vs;
+static Texture2D roomba;
+
+//----------------------------------------------------------------------------------
+// Title Screen Functions Definition
+//----------------------------------------------------------------------------------
+
+// Title Screen Initialization logic
+void InitTitleScreen(void)
+{
+ // TODO: Initialize TITLE screen variables here!
+ framesCounter = 0;
+ finishScreen = 0;
+
+ cat = LoadTexture("resources/title_cat.png");
+ vs = LoadTexture("resources/title_vs.png");
+ roomba = LoadTexture("resources/title_roomba.png");
+
+ state = 0;
+ catPosX = 1760;
+ roombaPosX = -700;
+ scrollPositionX = 0;
+
+ vsAlpha = 0.0f;
+ vsScale = 10.0f;
+
+ PlayMusicStream(music);
+}
+
+// Title Screen Update logic
+void UpdateTitleScreen(void)
+{
+ scrollPositionX -= 5;
+ if (scrollPositionX < -GetScreenWidth()) scrollPositionX = 0;
+
+ if (state == 0)
+ {
+ catPosX -= 4;
+ roombaPosX += 3;
+
+ if (catPosX < (GetScreenWidth()/2 - cat.width/2)) catPosX = (GetScreenWidth()/2 - cat.width/2);
+ if (roombaPosX > (GetScreenWidth()/2 - roomba.width/2)) roombaPosX = (GetScreenWidth()/2 - roomba.width/2);
+
+ if ((catPosX == (GetScreenWidth()/2 - cat.width/2)) && (roombaPosX == (GetScreenWidth()/2 - roomba.width/2)))
+ {
+ state = 1;
+ framesCounter = 0;
+ }
+ }
+ else if (state == 1)
+ {
+ framesCounter++;
+
+ vsScale -= 0.1f;
+ vsAlpha += 0.01f;
+
+ if (vsScale < 1.0f) vsScale = 1.0f;
+ if (vsAlpha > 1.0f) vsAlpha = 1.0f;
+
+ if (framesCounter > 160)
+ {
+ state = 2;
+ framesCounter = 0;
+ }
+ }
+ else if (state == 2) framesCounter++;
+
+ // Press enter or tap to change to GAMEPLAY screen
+ if (IsKeyPressed(KEY_ENTER) || IsGestureDetected(GESTURE_TAP))
+ {
+ //finishScreen = 1; // OPTIONS
+ finishScreen = 2; // GAMEPLAY
+ PlaySound(fxCoin);
+ }
+}
+
+// Title Screen Draw logic
+void DrawTitleScreen(void)
+{
+ for (int i = 0; i < 64*2*2; i++)
+ {
+ DrawRectangle(64*i + scrollPositionX, 0, 64, GetScreenHeight(), (i%2 == 0)? GetColor(0xf3726dff) : GetColor(0xffcf6bff));
+ }
+
+ DrawTexture(cat, catPosX, 80, WHITE);
+ DrawTexture(roomba, roombaPosX, 320, WHITE);
+
+ if (state > 0)
+ {
+ DrawTexturePro(vs, (Rectangle){ 0, 0, vs.width, vs.height }, (Rectangle){ GetScreenWidth()/2, 300, vs.width*vsScale, vs.height*vsScale }, (Vector2){ vs.width/2*vsScale, vs.height/2*vsScale }, 0.0f, Fade(WHITE, vsAlpha));
+ }
+
+ if ((state == 2) && ((framesCounter/30)%2)) DrawTextEx(font2, "PRESS ENTER to START", (Vector2){ 340, 550 }, font2.baseSize, 2, WHITE);
+}
+
+// Title Screen Unload logic
+void UnloadTitleScreen(void)
+{
+ UnloadTexture(cat);
+ UnloadTexture(vs);
+ UnloadTexture(roomba);
+}
+
+// Title Screen should finish?
+int FinishTitleScreen(void)
+{
+ return finishScreen;
+}
\ No newline at end of file
diff --git a/games/cat_vs_roomba/screens/screens.h b/games/cat_vs_roomba/screens/screens.h
new file mode 100644
index 000000000..9cc07eab6
--- /dev/null
+++ b/games/cat_vs_roomba/screens/screens.h
@@ -0,0 +1,92 @@
+/**********************************************************************************************
+*
+* raylib - Advance Game template
+*
+* Screens Functions Declarations (Init, Update, Draw, Unload)
+*
+* Copyright (c) 2014-2018 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 TILE_VIEWER_MODE
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef enum GameScreen { LOGO = 0, TITLE, OPTIONS, GAMEPLAY, ENDING } GameScreen;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+GameScreen currentScreen;
+Font font;
+Font font2;
+Music music;
+Sound fxCoin;
+
+int score;
+int result; // 0-Loose, 1-Win
+
+#ifdef __cplusplus
+extern "C" { // Prevents name mangling of functions
+#endif
+
+//----------------------------------------------------------------------------------
+// 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);
+
+//----------------------------------------------------------------------------------
+// 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
\ No newline at end of file