diff --git a/.gitignore b/.gitignore index 8083a27..32d3ba1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,13 @@ +<<<<<<< HEAD +======= +# ---> KDevelop4 +#*.kdev4 +#.kdev4/ + +# ---> build artifacts +build/* + +>>>>>>> master # ---> C++ # Prerequisites *.d @@ -44,7 +54,10 @@ # Linker output *.ilk +<<<<<<< HEAD *.map +======= +>>>>>>> master *.exp # Precompiled Headers @@ -86,6 +99,7 @@ Module.symvers Mkfile.old dkms.conf +<<<<<<< HEAD # ---> CMake CMakeLists.txt.user CMakeCache.txt @@ -98,4 +112,8 @@ install_manifest.txt compile_commands.json CTestTestfile.cmake _deps +======= +# CMake artifacts +CMakeCache.txt +>>>>>>> master diff --git a/.kdev4/BeagleRescue.git.kdev4 b/.kdev4/BeagleRescue.git.kdev4 new file mode 100644 index 0000000..220a2ee --- /dev/null +++ b/.kdev4/BeagleRescue.git.kdev4 @@ -0,0 +1,94 @@ +<<<<<<< HEAD +[Buildset] +BuildItems=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x01\x00\x00\x00 \x00B\x00e\x00a\x00g\x00l\x00e\x00R\x00e\x00s\x00c\x00u\x00e\x00.\x00g\x00i\x00t) + +[CMake] +Build Directory Count=1 +Current Build Directory Index-Host System=0 + +[CMake][CMake Build Directory 0] +Build Directory Path=/home/ayoungblood/BeagleRescue.git/build +Build Type=Release +CMake Binary=/usr/bin/cmake +CMake Executable=/usr/bin/cmake +Environment Profile= +Extra Arguments= +Install Directory= +Runtime=Host System + +[CustomDefinesAndIncludes][ProjectPath0] +Path=. +parseAmbiguousAsCPP=true +parserArguments=-ferror-limit=100 -fspell-checking -Wdocumentation -Wunused-parameter -Wunreachable-code -Wall -std=c++17 +parserArgumentsC=-ferror-limit=100 -fspell-checking -Wdocumentation -Wunused-parameter -Wunreachable-code -Wall -std=c99 +parserArgumentsCuda=-ferror-limit=100 -fspell-checking -Wdocumentation -Wunused-parameter -Wunreachable-code -Wall -std=c++11 +parserArgumentsOpenCL=-ferror-limit=100 -fspell-checking -Wdocumentation -Wunused-parameter -Wunreachable-code -Wall -cl-std=CL1.1 + +[CustomDefinesAndIncludes][ProjectPath0][Compiler] +Name=GCC + +[Launch] +Launch Configurations=Launch Configuration 0 + +[Launch][Launch Configuration 0] +Configured Launch Modes=execute,debug +Configured Launchers=nativeAppLauncher,lldb +Name=BeagleRescue +Type=Native Application + +[Launch][Launch Configuration 0][Data] +Arguments= +Break on Start=false +Dependencies=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x00) +Dependency Action=Nothing +EnvironmentGroup= +Executable=file:///home/ayoungblood/BeagleRescue.git/build/BeagleRescue +External Terminal=konsole --noclose --workdir %workdir -e %exe +Kill Before Executing Again=4194304 +LLDB Arguments= +LLDB Config Script= +LLDB Environment= +LLDB Executable= +LLDB Inherit System Env=true +LLDB Remote Debugging=false +LLDB Remote Path= +LLDB Remote Server= +Project Target=BeagleRescue,BeagleRescue +Start With=ApplicationOutput +Use External Terminal=false +Working Directory=file:///home/ayoungblood/BeagleRescue.git +isExecutable=true + +[Project] +VersionControlSupport=kdevgit +||||||| 8aa7974 +======= +[Buildset] +BuildItems=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x01\x00\x00\x00 \x00B\x00e\x00a\x00g\x00l\x00e\x00R\x00e\x00s\x00c\x00u\x00e\x00.\x00g\x00i\x00t) + +[CMake] +Build Directory Count=1 +Current Build Directory Index-Host System=0 + +[CMake][CMake Build Directory 0] +Build Directory Path=/home/ayoungblood/projects/BeagleRescue.git/build +Build Type=Release +CMake Binary=/usr/bin/cmake +CMake Executable=/usr/bin/cmake +Environment Profile= +Extra Arguments= +Install Directory= +Runtime=Host System + +[Launch] +Launch Configurations=Launch Configuration 0 + +[Launch][Launch Configuration 0] +Configured Launch Modes=execute +Configured Launchers=nativeAppLauncher +Name=New Compiled Binary Launcher +Type=Native Application + +[Project] +VersionControlSupport=kdevgit +>>>>>>> a3d13f32a5f0e4e11c618a4c3d524fd52fc0ed08 diff --git a/.kdev4/BeagleRescue.kdev4 b/.kdev4/BeagleRescue.kdev4 new file mode 100644 index 0000000..a162aaf --- /dev/null +++ b/.kdev4/BeagleRescue.kdev4 @@ -0,0 +1,51 @@ +[Buildset] +BuildItems=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x01\x00\x00\x00\x18\x00B\x00e\x00a\x00g\x00l\x00e\x00R\x00e\x00s\x00c\x00u\x00e) + +[CMake] +Build Directory Count=2 +Current Build Directory Index-Host System=0 +Temporary Build Directory Index=1 + +[CMake][CMake Build Directory 0] +Build Directory Path=/home/ayoungblood/projects/BeagleRescue/build +Build Type=Release +CMake Binary=/usr/bin/cmake +CMake Executable=/usr/bin/cmake +Environment Profile= +Extra Arguments= +Install Directory=/usr/local +Runtime=Host System + +[CMake][CMake Build Directory 1] +Build Directory Path=/home/ayoungblood/Projects/BeagleRescue/build +Build Type=Release +CMake Binary=/usr/bin/cmake +CMake Executable=/usr/bin/cmake +Environment Profile= +Extra Arguments= +Install Directory= + +[Launch] +Launch Configurations=Launch Configuration 0 + +[Launch][Launch Configuration 0] +Configured Launch Modes=execute +Configured Launchers=nativeAppLauncher +Name=BeagleRescue +Type=Native Application + +[Launch][Launch Configuration 0][Data] +Arguments= +Dependencies=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x00) +Dependency Action=Nothing +EnvironmentGroup= +Executable=file:///home/ayoungblood/Projects/BeagleRescue +External Terminal=konsole --noclose --workdir %workdir -e %exe +Kill Before Executing Again=4194304 +Project Target=BeagleRescue,BeagleRescue +Use External Terminal=false +Working Directory=file:///home/ayoungblood/projects/BeagleRescue +isExecutable=true + +[Project] +VersionControlSupport=kdevgit diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..3955b63 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.25.1) +project(KaijuSaveEarth VERSION 1.0.0 ) + +#set(CMAKE_CXX_STANDARD 11) # old requirements +set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard to be used") # new requirements for tileson +set(CMAKE_CXX_REQUIRED ON) + +include(FetchContent) +include(FindPkgConfig) + +PKG_SEARCH_MODULE(SDL2 REQUIRED sdl2) +PKG_SEARCH_MODULE(SDL2IMAGE REQUIRED SDL2_image>=2.0.0) +PKG_SEARCH_MODULE(SDL2MIXER REQUIRED SDL2_mixer>=2.0.0) + +# Point to our own cmake modules +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/sdl2) + +# Add global definitions +add_definitions("-Wall") +include_directories(${PROJECT_NAME} ${SDL2_INCLUDE_DIRS} ${SDL2IMAGE_INCLUDE_DIRS} ${SDL2MIXER_INCLUDE_DIRS}) +#add_custom_target(${TMXPARSER}) + +file(GLOB_RECURSE CSOURCES src/cjson/*.c) +file(GLOB_RECURSE CPPSOURCES src/*.cpp) + +add_executable(${PROJECT_NAME} ${CSOURCES} ${CPPSOURCES}) +install(TARGETS ${PROJECT_NAME} DESTINATION bin) +target_link_libraries(${PROJECT_NAME} PRIVATE ${SDL2_LIBRARIES} ${SDL2IMAGE_LIBRARIES} ${SDL2MIXER_LIBRARIES}) diff --git a/KaijuSaveEarth.kdev4 b/KaijuSaveEarth.kdev4 new file mode 100644 index 0000000..b4fb847 --- /dev/null +++ b/KaijuSaveEarth.kdev4 @@ -0,0 +1,5 @@ +[Project] +CreatedFrom=CMakeLists.txt +Manager=KDevCMakeManager +Name=KaijuSaveEarth +VersionControl=%{VERSIONCONTROLPLUGIN} diff --git a/LICENSE b/LICENSE index 2071b23..99741a3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,4 @@ +<<<<<<< HEAD MIT License Copyright (c) @@ -7,3 +8,24 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +======= +MIT License Copyright (c) 2023 Alan Youngblood + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +>>>>>>> master diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5995941 --- /dev/null +++ b/Makefile @@ -0,0 +1,608 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.25 + +# Default target executed when no arguments are given to make. +default_target: all +.PHONY : default_target + +# Allow only one "make -f Makefile2" at a time, but pass parallelism. +.NOTPARALLEL: + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + +# Disable VCS-based implicit rules. +% : %,v + +# Disable VCS-based implicit rules. +% : RCS/% + +# Disable VCS-based implicit rules. +% : RCS/%,v + +# Disable VCS-based implicit rules. +% : SCCS/s.% + +# Disable VCS-based implicit rules. +% : s.% + +.SUFFIXES: .hpux_make_needs_suffix_list + +# Command-line flag to silence nested $(MAKE). +$(VERBOSE)MAKESILENT = -s + +#Suppress display of executed commands. +$(VERBOSE).SILENT: + +# A target that is always out of date. +cmake_force: +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E rm -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /home/ayoungblood/Projects/BeagleRescue + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /home/ayoungblood/Projects/BeagleRescue + +#============================================================================= +# Targets provided globally by CMake. + +# Special rule for the target edit_cache +edit_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..." + /usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available. +.PHONY : edit_cache + +# Special rule for the target edit_cache +edit_cache/fast: edit_cache +.PHONY : edit_cache/fast + +# Special rule for the target rebuild_cache +rebuild_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." + /usr/bin/cmake --regenerate-during-build -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : rebuild_cache + +# Special rule for the target rebuild_cache +rebuild_cache/fast: rebuild_cache +.PHONY : rebuild_cache/fast + +# Special rule for the target list_install_components +list_install_components: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Available install components are: \"Unspecified\"" +.PHONY : list_install_components + +# Special rule for the target list_install_components +list_install_components/fast: list_install_components +.PHONY : list_install_components/fast + +# Special rule for the target install +install: preinstall + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..." + /usr/bin/cmake -P cmake_install.cmake +.PHONY : install + +# Special rule for the target install +install/fast: preinstall/fast + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..." + /usr/bin/cmake -P cmake_install.cmake +.PHONY : install/fast + +# Special rule for the target install/local +install/local: preinstall + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing only the local directory..." + /usr/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake +.PHONY : install/local + +# Special rule for the target install/local +install/local/fast: preinstall/fast + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing only the local directory..." + /usr/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake +.PHONY : install/local/fast + +# Special rule for the target install/strip +install/strip: preinstall + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing the project stripped..." + /usr/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake +.PHONY : install/strip + +# Special rule for the target install/strip +install/strip/fast: preinstall/fast + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing the project stripped..." + /usr/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake +.PHONY : install/strip/fast + +# The main all target +all: cmake_check_build_system + $(CMAKE_COMMAND) -E cmake_progress_start /home/ayoungblood/Projects/BeagleRescue/CMakeFiles /home/ayoungblood/Projects/BeagleRescue//CMakeFiles/progress.marks + $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 all + $(CMAKE_COMMAND) -E cmake_progress_start /home/ayoungblood/Projects/BeagleRescue/CMakeFiles 0 +.PHONY : all + +# The main clean target +clean: + $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 clean +.PHONY : clean + +# The main clean target +clean/fast: clean +.PHONY : clean/fast + +# Prepare targets for installation. +preinstall: all + $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 preinstall +.PHONY : preinstall + +# Prepare targets for installation. +preinstall/fast: + $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 preinstall +.PHONY : preinstall/fast + +# clear depends +depend: + $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 +.PHONY : depend + +#============================================================================= +# Target rules for targets named BeagleRescue + +# Build rule for target. +BeagleRescue: cmake_check_build_system + $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 BeagleRescue +.PHONY : BeagleRescue + +# fast build rule for target. +BeagleRescue/fast: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/build +.PHONY : BeagleRescue/fast + +src/assetmgr/AssetManager.o: src/assetmgr/AssetManager.cpp.o +.PHONY : src/assetmgr/AssetManager.o + +# target to build an object file +src/assetmgr/AssetManager.cpp.o: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/assetmgr/AssetManager.cpp.o +.PHONY : src/assetmgr/AssetManager.cpp.o + +src/assetmgr/AssetManager.i: src/assetmgr/AssetManager.cpp.i +.PHONY : src/assetmgr/AssetManager.i + +# target to preprocess a source file +src/assetmgr/AssetManager.cpp.i: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/assetmgr/AssetManager.cpp.i +.PHONY : src/assetmgr/AssetManager.cpp.i + +src/assetmgr/AssetManager.s: src/assetmgr/AssetManager.cpp.s +.PHONY : src/assetmgr/AssetManager.s + +# target to generate assembly for a file +src/assetmgr/AssetManager.cpp.s: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/assetmgr/AssetManager.cpp.s +.PHONY : src/assetmgr/AssetManager.cpp.s + +src/assetmgr/GameObject.o: src/assetmgr/GameObject.cpp.o +.PHONY : src/assetmgr/GameObject.o + +# target to build an object file +src/assetmgr/GameObject.cpp.o: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/assetmgr/GameObject.cpp.o +.PHONY : src/assetmgr/GameObject.cpp.o + +src/assetmgr/GameObject.i: src/assetmgr/GameObject.cpp.i +.PHONY : src/assetmgr/GameObject.i + +# target to preprocess a source file +src/assetmgr/GameObject.cpp.i: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/assetmgr/GameObject.cpp.i +.PHONY : src/assetmgr/GameObject.cpp.i + +src/assetmgr/GameObject.s: src/assetmgr/GameObject.cpp.s +.PHONY : src/assetmgr/GameObject.s + +# target to generate assembly for a file +src/assetmgr/GameObject.cpp.s: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/assetmgr/GameObject.cpp.s +.PHONY : src/assetmgr/GameObject.cpp.s + +src/assetmgr/Map.o: src/assetmgr/Map.cpp.o +.PHONY : src/assetmgr/Map.o + +# target to build an object file +src/assetmgr/Map.cpp.o: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/assetmgr/Map.cpp.o +.PHONY : src/assetmgr/Map.cpp.o + +src/assetmgr/Map.i: src/assetmgr/Map.cpp.i +.PHONY : src/assetmgr/Map.i + +# target to preprocess a source file +src/assetmgr/Map.cpp.i: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/assetmgr/Map.cpp.i +.PHONY : src/assetmgr/Map.cpp.i + +src/assetmgr/Map.s: src/assetmgr/Map.cpp.s +.PHONY : src/assetmgr/Map.s + +# target to generate assembly for a file +src/assetmgr/Map.cpp.s: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/assetmgr/Map.cpp.s +.PHONY : src/assetmgr/Map.cpp.s + +src/assetmgr/MusicManager.o: src/assetmgr/MusicManager.cpp.o +.PHONY : src/assetmgr/MusicManager.o + +# target to build an object file +src/assetmgr/MusicManager.cpp.o: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/assetmgr/MusicManager.cpp.o +.PHONY : src/assetmgr/MusicManager.cpp.o + +src/assetmgr/MusicManager.i: src/assetmgr/MusicManager.cpp.i +.PHONY : src/assetmgr/MusicManager.i + +# target to preprocess a source file +src/assetmgr/MusicManager.cpp.i: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/assetmgr/MusicManager.cpp.i +.PHONY : src/assetmgr/MusicManager.cpp.i + +src/assetmgr/MusicManager.s: src/assetmgr/MusicManager.cpp.s +.PHONY : src/assetmgr/MusicManager.s + +# target to generate assembly for a file +src/assetmgr/MusicManager.cpp.s: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/assetmgr/MusicManager.cpp.s +.PHONY : src/assetmgr/MusicManager.cpp.s + +src/assetmgr/TextureManager.o: src/assetmgr/TextureManager.cpp.o +.PHONY : src/assetmgr/TextureManager.o + +# target to build an object file +src/assetmgr/TextureManager.cpp.o: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/assetmgr/TextureManager.cpp.o +.PHONY : src/assetmgr/TextureManager.cpp.o + +src/assetmgr/TextureManager.i: src/assetmgr/TextureManager.cpp.i +.PHONY : src/assetmgr/TextureManager.i + +# target to preprocess a source file +src/assetmgr/TextureManager.cpp.i: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/assetmgr/TextureManager.cpp.i +.PHONY : src/assetmgr/TextureManager.cpp.i + +src/assetmgr/TextureManager.s: src/assetmgr/TextureManager.cpp.s +.PHONY : src/assetmgr/TextureManager.s + +# target to generate assembly for a file +src/assetmgr/TextureManager.cpp.s: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/assetmgr/TextureManager.cpp.s +.PHONY : src/assetmgr/TextureManager.cpp.s + +src/cjson/cJSON.o: src/cjson/cJSON.c.o +.PHONY : src/cjson/cJSON.o + +# target to build an object file +src/cjson/cJSON.c.o: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/cjson/cJSON.c.o +.PHONY : src/cjson/cJSON.c.o + +src/cjson/cJSON.i: src/cjson/cJSON.c.i +.PHONY : src/cjson/cJSON.i + +# target to preprocess a source file +src/cjson/cJSON.c.i: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/cjson/cJSON.c.i +.PHONY : src/cjson/cJSON.c.i + +src/cjson/cJSON.s: src/cjson/cJSON.c.s +.PHONY : src/cjson/cJSON.s + +# target to generate assembly for a file +src/cjson/cJSON.c.s: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/cjson/cJSON.c.s +.PHONY : src/cjson/cJSON.c.s + +src/ecs/ECS.o: src/ecs/ECS.cpp.o +.PHONY : src/ecs/ECS.o + +# target to build an object file +src/ecs/ECS.cpp.o: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/ecs/ECS.cpp.o +.PHONY : src/ecs/ECS.cpp.o + +src/ecs/ECS.i: src/ecs/ECS.cpp.i +.PHONY : src/ecs/ECS.i + +# target to preprocess a source file +src/ecs/ECS.cpp.i: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/ecs/ECS.cpp.i +.PHONY : src/ecs/ECS.cpp.i + +src/ecs/ECS.s: src/ecs/ECS.cpp.s +.PHONY : src/ecs/ECS.s + +# target to generate assembly for a file +src/ecs/ECS.cpp.s: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/ecs/ECS.cpp.s +.PHONY : src/ecs/ECS.cpp.s + +src/game/Collision.o: src/game/Collision.cpp.o +.PHONY : src/game/Collision.o + +# target to build an object file +src/game/Collision.cpp.o: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/game/Collision.cpp.o +.PHONY : src/game/Collision.cpp.o + +src/game/Collision.i: src/game/Collision.cpp.i +.PHONY : src/game/Collision.i + +# target to preprocess a source file +src/game/Collision.cpp.i: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/game/Collision.cpp.i +.PHONY : src/game/Collision.cpp.i + +src/game/Collision.s: src/game/Collision.cpp.s +.PHONY : src/game/Collision.s + +# target to generate assembly for a file +src/game/Collision.cpp.s: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/game/Collision.cpp.s +.PHONY : src/game/Collision.cpp.s + +src/game/Game.o: src/game/Game.cpp.o +.PHONY : src/game/Game.o + +# target to build an object file +src/game/Game.cpp.o: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/game/Game.cpp.o +.PHONY : src/game/Game.cpp.o + +src/game/Game.i: src/game/Game.cpp.i +.PHONY : src/game/Game.i + +# target to preprocess a source file +src/game/Game.cpp.i: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/game/Game.cpp.i +.PHONY : src/game/Game.cpp.i + +src/game/Game.s: src/game/Game.cpp.s +.PHONY : src/game/Game.s + +# target to generate assembly for a file +src/game/Game.cpp.s: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/game/Game.cpp.s +.PHONY : src/game/Game.cpp.s + +src/game/GameStateManager.o: src/game/GameStateManager.cpp.o +.PHONY : src/game/GameStateManager.o + +# target to build an object file +src/game/GameStateManager.cpp.o: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/game/GameStateManager.cpp.o +.PHONY : src/game/GameStateManager.cpp.o + +src/game/GameStateManager.i: src/game/GameStateManager.cpp.i +.PHONY : src/game/GameStateManager.i + +# target to preprocess a source file +src/game/GameStateManager.cpp.i: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/game/GameStateManager.cpp.i +.PHONY : src/game/GameStateManager.cpp.i + +src/game/GameStateManager.s: src/game/GameStateManager.cpp.s +.PHONY : src/game/GameStateManager.s + +# target to generate assembly for a file +src/game/GameStateManager.cpp.s: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/game/GameStateManager.cpp.s +.PHONY : src/game/GameStateManager.cpp.s + +src/game/Main.o: src/game/Main.cpp.o +.PHONY : src/game/Main.o + +# target to build an object file +src/game/Main.cpp.o: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/game/Main.cpp.o +.PHONY : src/game/Main.cpp.o + +src/game/Main.i: src/game/Main.cpp.i +.PHONY : src/game/Main.i + +# target to preprocess a source file +src/game/Main.cpp.i: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/game/Main.cpp.i +.PHONY : src/game/Main.cpp.i + +src/game/Main.s: src/game/Main.cpp.s +.PHONY : src/game/Main.s + +# target to generate assembly for a file +src/game/Main.cpp.s: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/game/Main.cpp.s +.PHONY : src/game/Main.cpp.s + +src/game/StateMachine.o: src/game/StateMachine.cpp.o +.PHONY : src/game/StateMachine.o + +# target to build an object file +src/game/StateMachine.cpp.o: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/game/StateMachine.cpp.o +.PHONY : src/game/StateMachine.cpp.o + +src/game/StateMachine.i: src/game/StateMachine.cpp.i +.PHONY : src/game/StateMachine.i + +# target to preprocess a source file +src/game/StateMachine.cpp.i: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/game/StateMachine.cpp.i +.PHONY : src/game/StateMachine.cpp.i + +src/game/StateMachine.s: src/game/StateMachine.cpp.s +.PHONY : src/game/StateMachine.s + +# target to generate assembly for a file +src/game/StateMachine.cpp.s: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/game/StateMachine.cpp.s +.PHONY : src/game/StateMachine.cpp.s + +src/game/Vector2D.o: src/game/Vector2D.cpp.o +.PHONY : src/game/Vector2D.o + +# target to build an object file +src/game/Vector2D.cpp.o: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/game/Vector2D.cpp.o +.PHONY : src/game/Vector2D.cpp.o + +src/game/Vector2D.i: src/game/Vector2D.cpp.i +.PHONY : src/game/Vector2D.i + +# target to preprocess a source file +src/game/Vector2D.cpp.i: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/game/Vector2D.cpp.i +.PHONY : src/game/Vector2D.cpp.i + +src/game/Vector2D.s: src/game/Vector2D.cpp.s +.PHONY : src/game/Vector2D.s + +# target to generate assembly for a file +src/game/Vector2D.cpp.s: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/game/Vector2D.cpp.s +.PHONY : src/game/Vector2D.cpp.s + +src/ui/UINineSlice.o: src/ui/UINineSlice.cpp.o +.PHONY : src/ui/UINineSlice.o + +# target to build an object file +src/ui/UINineSlice.cpp.o: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/ui/UINineSlice.cpp.o +.PHONY : src/ui/UINineSlice.cpp.o + +src/ui/UINineSlice.i: src/ui/UINineSlice.cpp.i +.PHONY : src/ui/UINineSlice.i + +# target to preprocess a source file +src/ui/UINineSlice.cpp.i: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/ui/UINineSlice.cpp.i +.PHONY : src/ui/UINineSlice.cpp.i + +src/ui/UINineSlice.s: src/ui/UINineSlice.cpp.s +.PHONY : src/ui/UINineSlice.s + +# target to generate assembly for a file +src/ui/UINineSlice.cpp.s: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/ui/UINineSlice.cpp.s +.PHONY : src/ui/UINineSlice.cpp.s + +src/ui/UIText.o: src/ui/UIText.cpp.o +.PHONY : src/ui/UIText.o + +# target to build an object file +src/ui/UIText.cpp.o: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/ui/UIText.cpp.o +.PHONY : src/ui/UIText.cpp.o + +src/ui/UIText.i: src/ui/UIText.cpp.i +.PHONY : src/ui/UIText.i + +# target to preprocess a source file +src/ui/UIText.cpp.i: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/ui/UIText.cpp.i +.PHONY : src/ui/UIText.cpp.i + +src/ui/UIText.s: src/ui/UIText.cpp.s +.PHONY : src/ui/UIText.s + +# target to generate assembly for a file +src/ui/UIText.cpp.s: + $(MAKE) $(MAKESILENT) -f CMakeFiles/BeagleRescue.dir/build.make CMakeFiles/BeagleRescue.dir/src/ui/UIText.cpp.s +.PHONY : src/ui/UIText.cpp.s + +# Help Target +help: + @echo "The following are some of the valid targets for this Makefile:" + @echo "... all (the default if no target is provided)" + @echo "... clean" + @echo "... depend" + @echo "... edit_cache" + @echo "... install" + @echo "... install/local" + @echo "... install/strip" + @echo "... list_install_components" + @echo "... rebuild_cache" + @echo "... BeagleRescue" + @echo "... src/assetmgr/AssetManager.o" + @echo "... src/assetmgr/AssetManager.i" + @echo "... src/assetmgr/AssetManager.s" + @echo "... src/assetmgr/GameObject.o" + @echo "... src/assetmgr/GameObject.i" + @echo "... src/assetmgr/GameObject.s" + @echo "... src/assetmgr/Map.o" + @echo "... src/assetmgr/Map.i" + @echo "... src/assetmgr/Map.s" + @echo "... src/assetmgr/MusicManager.o" + @echo "... src/assetmgr/MusicManager.i" + @echo "... src/assetmgr/MusicManager.s" + @echo "... src/assetmgr/TextureManager.o" + @echo "... src/assetmgr/TextureManager.i" + @echo "... src/assetmgr/TextureManager.s" + @echo "... src/cjson/cJSON.o" + @echo "... src/cjson/cJSON.i" + @echo "... src/cjson/cJSON.s" + @echo "... src/ecs/ECS.o" + @echo "... src/ecs/ECS.i" + @echo "... src/ecs/ECS.s" + @echo "... src/game/Collision.o" + @echo "... src/game/Collision.i" + @echo "... src/game/Collision.s" + @echo "... src/game/Game.o" + @echo "... src/game/Game.i" + @echo "... src/game/Game.s" + @echo "... src/game/GameStateManager.o" + @echo "... src/game/GameStateManager.i" + @echo "... src/game/GameStateManager.s" + @echo "... src/game/Main.o" + @echo "... src/game/Main.i" + @echo "... src/game/Main.s" + @echo "... src/game/StateMachine.o" + @echo "... src/game/StateMachine.i" + @echo "... src/game/StateMachine.s" + @echo "... src/game/Vector2D.o" + @echo "... src/game/Vector2D.i" + @echo "... src/game/Vector2D.s" + @echo "... src/ui/UINineSlice.o" + @echo "... src/ui/UINineSlice.i" + @echo "... src/ui/UINineSlice.s" + @echo "... src/ui/UIText.o" + @echo "... src/ui/UIText.i" + @echo "... src/ui/UIText.s" +.PHONY : help + + + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system + diff --git a/README.md b/README.md index 305095e..1714a36 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,16 @@ +<<<<<<< HEAD # KaijuSaveEarth LowRezJam 2023 Kaiju Save Earth -A game where you play as a giant monster trying to save as many humans as possible before they destroy themselves and the world! \ No newline at end of file +A game where you play as a giant monster trying to save as many humans as possible before they destroy themselves and the world! +======= +# Kaiju Save Earth +--- +Low Rez Jam 2023 + +A game where you play as a giant monster (Kaiju) who must save all the humans from destroying themselves and the world by catching them and safely stowing them on your back. + +By: +Alan Youngblood +>>>>>>> master diff --git a/assets/ColTex.png b/assets/ColTex.png new file mode 100644 index 0000000..6e1708b Binary files /dev/null and b/assets/ColTex.png differ diff --git a/assets/audio/music/sillypuppy.ogg b/assets/audio/music/sillypuppy.ogg new file mode 100644 index 0000000..c2622a2 Binary files /dev/null and b/assets/audio/music/sillypuppy.ogg differ diff --git a/assets/audio/music/victory.ogg b/assets/audio/music/victory.ogg new file mode 100644 index 0000000..597bf39 Binary files /dev/null and b/assets/audio/music/victory.ogg differ diff --git a/assets/audio/sfx/Bark1.wav b/assets/audio/sfx/Bark1.wav new file mode 100644 index 0000000..a83f12b Binary files /dev/null and b/assets/audio/sfx/Bark1.wav differ diff --git a/assets/audio/sfx/Bark2.wav b/assets/audio/sfx/Bark2.wav new file mode 100644 index 0000000..3121d15 Binary files /dev/null and b/assets/audio/sfx/Bark2.wav differ diff --git a/assets/audio/sfx/Bark3.wav b/assets/audio/sfx/Bark3.wav new file mode 100644 index 0000000..8fb9f26 Binary files /dev/null and b/assets/audio/sfx/Bark3.wav differ diff --git a/assets/audio/sfx/Bark4.wav b/assets/audio/sfx/Bark4.wav new file mode 100644 index 0000000..f4e3baa Binary files /dev/null and b/assets/audio/sfx/Bark4.wav differ diff --git a/assets/audio/sfx/Bark5.wav b/assets/audio/sfx/Bark5.wav new file mode 100644 index 0000000..f59e8df Binary files /dev/null and b/assets/audio/sfx/Bark5.wav differ diff --git a/assets/audio/sfx/Fire.wav b/assets/audio/sfx/Fire.wav new file mode 100644 index 0000000..87b3a13 Binary files /dev/null and b/assets/audio/sfx/Fire.wav differ diff --git a/assets/audio/sfx/Voice_Oooh_1.wav b/assets/audio/sfx/Voice_Oooh_1.wav new file mode 100644 index 0000000..11871c9 Binary files /dev/null and b/assets/audio/sfx/Voice_Oooh_1.wav differ diff --git a/assets/audio/sfx/Voice_Ouch_1.wav b/assets/audio/sfx/Voice_Ouch_1.wav new file mode 100644 index 0000000..5932e11 Binary files /dev/null and b/assets/audio/sfx/Voice_Ouch_1.wav differ diff --git a/assets/audio/sfx/Voice_Ouch_2.wav b/assets/audio/sfx/Voice_Ouch_2.wav new file mode 100644 index 0000000..11a8f8f Binary files /dev/null and b/assets/audio/sfx/Voice_Ouch_2.wav differ diff --git a/assets/audio/sfx/Voice_Ouch_3.wav b/assets/audio/sfx/Voice_Ouch_3.wav new file mode 100644 index 0000000..99e99e4 Binary files /dev/null and b/assets/audio/sfx/Voice_Ouch_3.wav differ diff --git a/assets/audio/sfx/Whimper1.wav b/assets/audio/sfx/Whimper1.wav new file mode 100644 index 0000000..3f6e800 Binary files /dev/null and b/assets/audio/sfx/Whimper1.wav differ diff --git a/assets/audio/sfx/Whimper2.wav b/assets/audio/sfx/Whimper2.wav new file mode 100644 index 0000000..9061e81 Binary files /dev/null and b/assets/audio/sfx/Whimper2.wav differ diff --git a/assets/audio/sfx/bwoop.wav b/assets/audio/sfx/bwoop.wav new file mode 100644 index 0000000..4d7e121 Binary files /dev/null and b/assets/audio/sfx/bwoop.wav differ diff --git a/assets/maps/br-map-color.txt b/assets/maps/br-map-color.txt new file mode 100644 index 0000000..72c6455 --- /dev/null +++ b/assets/maps/br-map-color.txt @@ -0,0 +1,91 @@ +10,11,12,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,12,10,11,11,11,11,11,11,11,11,11,11,12,13,15,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,13,14,15 +13,14,15,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,13,15,39,39,39,39,39,39,39,39,39,39,39,39,13,15,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,13,14,15 +13,14,15,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,13,15,39,39,39,39,39,39,39,39,39,39,39,39,13,15,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,13,14,15 +13,14,15,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,13,15,39,39,39,39,39,39,39,39,39,39,39,39,13,15,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,13,14,15 +13,14,15,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,19,21,39,39,39,39,39,39,39,39,39,39,39,39,19,21,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,13,14,15 +13,14,15,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,22,25,39,39,39,39,39,39,39,39,39,39,39,39,22,25,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,13,14,15 +13,14,15,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,23,26,39,39,39,39,39,39,39,39,39,39,39,39,23,26,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,13,14,15 +13,14,15,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,24,27,39,39,39,39,39,39,39,39,39,39,39,39,24,27,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,13,14,15 +13,14,15,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,45,10,12,43,44,45,38,38,38,38,38,43,44,44,45,13,15,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,45,13,14,15 +13,14,15,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,13,15,38,38,38,38,38,38,38,38,38,38,38,38,13,15,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,13,14,15 +13,14,15,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,13,15,38,38,38,38,38,38,38,38,38,38,38,38,13,15,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,13,14,15 +13,14,15,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,19,21,38,38,38,38,38,38,38,38,38,38,38,38,19,21,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,13,14,15 +13,14,15,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,22,25,38,38,38,38,43,45,38,38,38,38,38,38,22,25,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,13,14,15 +13,14,15,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,23,26,38,38,38,38,38,38,38,38,38,38,38,38,23,26,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,13,14,15 +13,14,15,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,24,27,38,38,38,38,38,38,38,38,38,38,38,38,24,27,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,13,14,15 +13,14,15,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,45,11,12,43,44,45,36,36,36,36,36,36,43,44,45,11,11,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,45,13,14,15 +13,14,15,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,13,15,36,36,36,36,36,36,36,36,36,36,36,36,13,15,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,13,14,15 +13,14,15,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,13,15,36,36,36,36,36,36,36,36,36,36,36,36,13,15,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,13,14,15 +13,14,15,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,19,21,36,36,36,36,36,36,36,36,36,36,36,36,19,21,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,13,14,15 +13,14,15,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,22,25,36,36,36,36,36,36,43,45,36,36,36,36,22,25,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,13,14,15 +13,14,15,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,23,26,36,36,36,36,36,36,36,36,36,36,36,36,23,26,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,13,14,15 +13,14,15,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,24,27,36,36,36,36,36,36,36,36,36,36,36,36,24,27,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,13,14,15 +13,14,15,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,45,10,12,43,44,45,33,33,33,33,33,33,43,44,45,10,12,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,45,13,14,15 +13,14,15,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,13,15,33,33,33,33,33,33,33,33,33,33,33,33,13,15,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,13,14,15 +13,14,15,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,13,15,33,33,33,33,33,33,33,33,33,33,33,33,13,15,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,13,14,15 +13,14,15,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,19,21,33,33,33,33,33,33,33,33,33,33,33,33,19,21,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,13,14,15 +13,14,15,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,22,25,33,33,33,33,43,45,33,33,33,33,33,33,22,25,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,13,14,15 +13,14,15,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,23,26,33,33,33,33,33,33,33,33,33,33,33,33,23,26,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,13,14,15 +13,14,15,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,24,27,33,33,33,33,33,33,33,33,33,33,33,33,24,27,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,13,14,15 +13,14,15,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,45,10,12,43,44,45,34,34,34,34,34,34,43,44,45,10,12,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,45,13,14,15 +13,14,15,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,13,15,34,34,34,34,34,34,34,34,34,34,34,34,13,15,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,13,14,15 +13,14,15,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,13,15,34,34,34,34,34,34,34,34,34,34,34,34,13,15,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,13,14,15 +13,14,15,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,19,21,34,34,34,34,34,34,34,34,34,34,34,34,19,21,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,13,14,15 +13,14,15,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,22,25,34,34,34,34,34,43,45,34,34,34,34,34,22,25,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,13,14,15 +13,14,15,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,23,26,34,34,34,34,34,34,34,34,34,34,34,34,23,26,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,13,14,15 +13,14,15,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,24,27,34,34,34,34,34,34,34,34,34,34,34,34,24,27,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,19,20,21 +13,14,15,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,45,10,12,43,44,45,37,37,37,37,37,37,43,44,45,10,12,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,45,13,14,15 +13,14,15,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,13,15,37,37,37,37,37,37,37,37,37,37,37,37,13,15,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,13,14,15 +13,14,15,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,13,15,37,37,37,37,37,37,37,37,37,37,37,37,13,15,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,13,14,15 +13,14,15,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,19,21,37,37,37,37,43,43,45,37,37,37,37,37,19,21,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,13,14,15 +13,14,15,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,22,25,37,37,37,37,37,37,37,37,37,37,37,37,22,25,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,13,14,15 +13,14,15,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,23,26,37,37,37,37,37,37,37,37,37,37,37,37,23,26,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,13,14,15 +13,14,15,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,24,27,37,37,37,37,37,37,37,37,37,37,37,37,24,27,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,13,14,15 +13,14,15,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,13,14,15 +13,14,15,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,13,14,15 + +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,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,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,1,1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,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,1,1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,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,1,1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,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,1,1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,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,1,1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,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,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 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 diff --git a/assets/maps/br-map.txt b/assets/maps/br-map.txt new file mode 100644 index 0000000..18b6ab8 --- /dev/null +++ b/assets/maps/br-map.txt @@ -0,0 +1,92 @@ +10,11,12,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,12,10,11,11,11,11,11,11,11,11,11,11,12,13,15,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,15,17,17,17,17,17,17,17,17,17,17,17,17,13,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,15,17,17,17,17,17,17,17,17,17,17,17,17,13,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,15,17,17,17,17,17,17,17,17,17,17,17,17,13,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,19,21,17,17,17,17,17,17,17,17,17,17,17,17,19,21,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,12,11,11,17,17,17,17,17,17,17,17,11,11,13,15,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,15,17,17,17,17,17,17,17,17,17,17,17,17,13,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,15,17,17,17,17,17,17,17,17,17,17,17,17,13,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,19,21,17,17,17,17,17,17,17,17,17,17,17,17,19,21,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,11,11,11,17,17,17,17,17,17,17,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,15,17,17,17,17,17,17,17,17,17,17,17,17,13,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,15,17,17,17,17,17,17,17,17,17,17,17,17,13,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,19,21,17,17,17,17,17,17,17,17,17,17,17,17,19,21,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,12,11,11,11,16,17,17,17,17,17,29,28,28,10,12,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,13,14,15 +13,14,15,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,18,13,15,16,17,17,17,17,17,17,17,29,30,17,18,13,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,18,13,15,16,17,17,17,17,17,17,29,30,17,17,18,13,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,18,19,21,16,17,17,17,17,17,29,30,17,17,17,18,19,21,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,18,22,25,16,17,17,17,17,29,30,17,17,17,17,18,22,25,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,18,23,26,16,17,17,17,29,30,17,17,17,17,17,18,23,26,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,18,24,27,16,17,17,17,17,17,17,17,17,17,17,18,24,27,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,12,11,11,12,31,17,17,17,17,17,11,11,11,10,12,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,15,17,17,17,32,31,17,17,17,17,17,17,17,13,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,15,17,17,17,17,32,31,17,17,17,17,17,17,13,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,19,21,17,17,17,17,17,32,31,17,17,17,17,17,19,21,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,22,25,17,17,17,17,17,17,32,31,17,17,17,17,22,25,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,23,26,17,17,17,17,17,17,17,32,31,17,17,17,23,26,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,24,27,17,17,17,17,17,17,17,17,17,17,17,17,24,27,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,19,20,21 +13,14,15,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,12,11,11,17,17,17,17,17,17,17,29,11,11,10,12,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,15,17,17,17,17,17,17,17,17,29,30,17,17,13,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,15,17,17,17,17,17,17,17,29,30,17,17,17,13,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,19,21,17,17,17,17,17,17,29,30,17,17,17,17,19,21,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,22,25,17,17,17,17,17,29,30,17,17,17,17,17,22,25,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,23,26,17,17,17,17,29,30,17,17,17,17,17,17,23,26,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,24,27,17,17,17,17,17,17,17,17,17,17,17,17,24,27,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,13,14,15 +13,14,15,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,13,14,15 +13,14,15,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,13,14,15 + +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,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,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,1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,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,1,1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,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,1,1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,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,1,1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,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,1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,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,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 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + diff --git a/assets/maps/br-tiles.png b/assets/maps/br-tiles.png new file mode 100644 index 0000000..8e80265 Binary files /dev/null and b/assets/maps/br-tiles.png differ diff --git a/assets/maps/br-tiles.tsj b/assets/maps/br-tiles.tsj new file mode 100644 index 0000000..09035a3 --- /dev/null +++ b/assets/maps/br-tiles.tsj @@ -0,0 +1,338 @@ +{ "columns":10, + "image":"..\/..\/..\/Projects\/BeagleRescue\/assets\/maps\/br-tiles.png", + "imageheight":80, + "imagewidth":160, + "margin":0, + "name":"br-tiles", + "spacing":0, + "tilecount":50, + "tiledversion":"1.8.2", + "tileheight":16, + "tiles":[ + { + "id":10, + "objectgroup": + { + "draworder":"index", + "id":2, + "name":"", + "objects":[ + { + "height":16, + "id":1, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":0, + "y":0 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + } + }, + { + "id":11, + "objectgroup": + { + "draworder":"index", + "id":2, + "name":"", + "objects":[ + { + "height":16, + "id":1, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":0, + "y":0 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + } + }, + { + "id":12, + "objectgroup": + { + "draworder":"index", + "id":2, + "name":"", + "objects":[ + { + "height":16, + "id":1, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":0, + "y":0 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + } + }, + { + "id":13, + "objectgroup": + { + "draworder":"index", + "id":2, + "name":"", + "objects":[ + { + "height":16, + "id":1, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":0, + "y":0 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + } + }, + { + "id":15, + "objectgroup": + { + "draworder":"index", + "id":2, + "name":"", + "objects":[ + { + "height":16, + "id":1, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":0, + "y":0 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + } + }, + { + "id":19, + "objectgroup": + { + "draworder":"index", + "id":2, + "name":"", + "objects":[ + { + "height":16, + "id":1, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":0, + "y":0 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + } + }, + { + "id":20, + "objectgroup": + { + "draworder":"index", + "id":2, + "name":"", + "objects":[ + { + "height":16, + "id":1, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":0, + "y":0 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + } + }, + { + "id":21, + "objectgroup": + { + "draworder":"index", + "id":2, + "name":"", + "objects":[ + { + "height":16, + "id":1, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":0, + "y":0 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + } + }, + { + "id":28, + "objectgroup": + { + "draworder":"index", + "id":2, + "name":"", + "objects":[ + { + "height":16, + "id":1, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":0, + "y":0 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + } + }, + { + "id":43, + "objectgroup": + { + "draworder":"index", + "id":2, + "name":"", + "objects":[ + { + "height":16, + "id":2, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":0, + "y":0 + }, + { + "height":14.2813, + "id":3, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":14.4375, + "x":0.96875, + "y":0.875 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + } + }, + { + "id":44, + "objectgroup": + { + "draworder":"index", + "id":2, + "name":"", + "objects":[ + { + "height":16, + "id":1, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":0, + "y":0 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + } + }, + { + "id":45, + "objectgroup": + { + "draworder":"index", + "id":2, + "name":"", + "objects":[ + { + "height":16, + "id":1, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":0, + "y":0 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + } + }], + "tilewidth":16, + "type":"tileset", + "version":"1.8" +} \ No newline at end of file diff --git a/assets/maps/br-tiles.tsx b/assets/maps/br-tiles.tsx new file mode 100644 index 0000000..389b54b --- /dev/null +++ b/assets/maps/br-tiles.tsx @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/maps/testmap b/assets/maps/testmap new file mode 100644 index 0000000..8052737 --- /dev/null +++ b/assets/maps/testmap @@ -0,0 +1,44 @@ +{ "compressionlevel":-1, + "height":42, + "infinite":false, + "layers":[ + { + "data":[11, 12, 13, 35, 35, 35, 36, 36, 36, 35, 36, 35, 35, 9, 35, 35, 35, 35, 35, 35, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 35, 35, 35, 35, 36, 36, 35, 35, 35, 35, 10, 35, 35, 35, 35, 35, 35, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 35, 35, 37, 35, 35, 35, 35, 41, 42, 43, 35, 35, 35, 35, 35, 35, 35, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 35, 35, 35, 35, 35, 35, 38, 38, 35, 35, 35, 35, 35, 35, 35, 35, 35, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 35, 35, 35, 35, 35, 38, 38, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 35, 35, 35, 35, 38, 38, 35, 35, 37, 35, 35, 35, 35, 35, 35, 35, 35, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 35, 35, 35, 38, 38, 35, 35, 35, 37, 37, 35, 35, 35, 35, 35, 35, 35, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 35, 35, 38, 38, 35, 35, 35, 37, 37, 37, 35, 35, 35, 35, 35, 35, 35, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 35, 38, 38, 35, 35, 35, 35, 35, 35, 37, 35, 35, 35, 35, 35, 35, 35, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 34, 45, 46, 34, 34, 34, 45, 46, 44, 45, 46, 44, 45, 46, 44, 45, 46, 44, 45, 46, 44, 45, 44, 45, 44, 45, 46, 45, 44, 44, 44, 44, 44, 44, 44, 44, 14, 15, 16, 14, 15, 16, 34, 34, 34, 34, 34, 34, 34, 34, 34, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 34, 34, 34, 41, 42, 43, 34, 34, 34, 34, 34, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 44, 45, 46, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 44, 45, 46, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 11, 12, 13, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 11, 12, 13, 40, 40, 40, 40, 40, 14, 15, 16, 40, 40, 40, 40, 40, 40, 14, 15, 16, 14, 15, 16, 17, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 17, 18, 19, 40, 40, 40, 40, 40, 14, 15, 16, 40, 40, 40, 40, 40, 40, 14, 15, 16, 20, 21, 22, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 11, 12, 13], + "height":42, + "id":1, + "name":"TileMap", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":42, + "x":0, + "y":0 + }, + { + "data":[2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + "height":42, + "id":3, + "name":"Collision", + "opacity":1, + "type":"tilelayer", + "visible":false, + "width":42, + "x":0, + "y":0 + }], + "nextlayerid":4, + "nextobjectid":1, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"1.8.2", + "tileheight":16, + "tilesets":[ + { + "firstgid":1, + "source":"br-tiles.tsx" + }], + "tilewidth":16, + "type":"map", + "version":"1.8", + "width":42 +} \ No newline at end of file diff --git a/assets/maps/testmap.json b/assets/maps/testmap.json new file mode 100644 index 0000000..2f04078 --- /dev/null +++ b/assets/maps/testmap.json @@ -0,0 +1,48 @@ +{ "compressionlevel":-1, + "height":42, + "infinite":false, + "layers":[ + { + "compression":"", + "data":"KAAAACgAAAANAAAALAAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAACwAAACgAAAAoAAAAKAAAAA8AAAAQAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAADgAAAA8AAAAoAAAADgAAAA8AAAAQAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAALAAAAC0AAAAuAAAAIwAAACMAAAAjAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACwAAAAtAAAALgAAACMAAAAjAAAAIwAAACMAAAAjAAAALAAAAC0AAAAuAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAALAAAAC0AAAAuAAAADgAAAA8AAAAQAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACYAAAAmAAAAJgAAACwAAAAsAAAALAAAACwAAAAtAAAALgAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAtAAAALgAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAAC0AAAAuAAAALAAAAC0AAAAuAAAALAAAACwAAAAsAAAADgAAAA8AAAAQAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAALAAAACwAAAAsAAAALAAAACwAAAAtAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACwAAAAtAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAALAAAAC0AAAAuAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAALAAAAC0AAAAuAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACwAAAAtAAAALgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAALAAAACwAAAAsAAAALAAAACwAAAAtAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAALAAAAC0AAAAuAAAALgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAAC0AAAAuAAAAJgAAACYAAAAsAAAADgAAAA8AAAAQAAAALAAAACwAAAAsAAAALAAAACwAAAAtAAAALgAAAC4AAAAuAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAtAAAALAAAACwAAAAiAAAAIgAAACIAAAAiAAAALQAAAC4AAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAtAAAALgAAAC4AAAAsAAAALAAAACwAAAAsAAAALAAAAC0AAAAuAAAADgAAAA8AAAAQAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAsAAAALQAAAC4AAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACwAAAAsAAAALQAAAC4AAAAiAAAAIgAAACIAAAAiAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIgAAACIAAAAsAAAALAAAACwAAAAsAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAtAAAALgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAADgAAAA8AAAAQAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAAC0AAAAuAAAALAAAACwAAAAsAAAALAAAACwAAAAtAAAALgAAACIAAAAiAAAAIgAAACIAAAAiAAAADgAAAA8AAAAQAAAALAAAAC0AAAAuAAAALgAAAC4AAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALQAAAC4AAAAiAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAALAAAAC0AAAAuAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAsAAAALQAAAC4AAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAALAAAAC0AAAAuAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACwAAAAtAAAALgAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACwAAAAtAAAALgAAAC4AAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAQAAAAKAAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAoAAAAKAAAACgAAAAWAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALQAAAC4AAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAAC0AAAAuAAAAFAAAACgAAAAoAAAA", + "encoding":"base64", + "height":42, + "id":1, + "name":"Tile Layer 1", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":42, + "x":0, + "y":0 + }, + { + "compression":"", + "data":"AgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAA", + "encoding":"base64", + "height":42, + "id":2, + "name":"Collision", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":42, + "x":0, + "y":0 + }], + "nextlayerid":3, + "nextobjectid":1, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"1.8.2", + "tileheight":16, + "tilesets":[ + { + "firstgid":1, + "source":"br-tiles.tsj" + }], + "tilewidth":16, + "type":"map", + "version":"1.8", + "width":42 +} \ No newline at end of file diff --git a/assets/maps/testmap.tmx b/assets/maps/testmap.tmx new file mode 100644 index 0000000..59d9046 --- /dev/null +++ b/assets/maps/testmap.tmx @@ -0,0 +1,96 @@ + + + + + +11,12,13,35,35,35,36,36,36,35,36,35,35,9,35,35,35,35,35,35,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,35,35,35,35,36,36,35,35,35,35,10,35,35,35,35,35,35,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,35,35,37,35,35,35,35,41,42,43,35,35,35,35,35,35,35,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,35,35,35,35,35,35,38,38,35,35,35,35,35,35,35,35,35,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,35,35,35,35,35,38,38,35,35,35,35,35,35,35,35,35,35,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,35,35,35,35,38,38,35,35,37,35,35,35,35,35,35,35,35,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,35,35,35,38,38,35,35,35,37,37,35,35,35,35,35,35,35,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,35,35,38,38,35,35,35,37,37,37,35,35,35,35,35,35,35,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,35,38,38,35,35,35,35,35,35,37,35,35,35,35,35,35,35,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,34,45,46,34,34,34,45,46,44,45,46,44,45,46,44,45,46,44,45,46,44,45,44,45,44,45,46,45,44,44,44,44,44,44,44,44,14,15,16, +14,15,16,34,34,34,34,34,34,34,34,34,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,34,34,34,34,34,34,34,34,34,34,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,34,34,34,41,42,43,34,34,34,34,34,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,34,34,34,34,34,34,34,34,34,34,34,34,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,34,34,34,34,34,34,34,34,34,34,34,34,34,34,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,34,34,34,34,34,34,34,34,34,34,34,34,34,34,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,34,34,34,34,34,34,34,34,34,34,34,34,34,34,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,44,45,46,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,44,45,46,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,11,12,13,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,11,12,13,40,40,40,40,40,14,15,16,40,40,40,40,40,40,14,15,16, +14,15,16,17,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,17,18,19,40,40,40,40,40,14,15,16,40,40,40,40,40,40,14,15,16, +20,21,22,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,11,12,13 + + + + +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,0,0,0,0,0,2,2,2,0,0,0,0,0,0,2,2,2, +2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,0,0,0,0,0,0,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 + + + diff --git a/assets/maps/testmapb64.tmx b/assets/maps/testmapb64.tmx new file mode 100644 index 0000000..393661e --- /dev/null +++ b/assets/maps/testmapb64.tmx @@ -0,0 +1,14 @@ + + + + + + KAAAACgAAAANAAAALAAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAACwAAACgAAAAoAAAAKAAAAA8AAAAQAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAADgAAAA8AAAAoAAAADgAAAA8AAAAQAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAALAAAAC0AAAAuAAAAIwAAACMAAAAjAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACwAAAAtAAAALgAAACMAAAAjAAAAIwAAACMAAAAjAAAALAAAAC0AAAAuAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAAIwAAACMAAAAjAAAALAAAAC0AAAAuAAAADgAAAA8AAAAQAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACYAAAAmAAAAJgAAACwAAAAsAAAALAAAACwAAAAtAAAALgAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAtAAAALgAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAAC0AAAAuAAAALAAAAC0AAAAuAAAALAAAACwAAAAsAAAADgAAAA8AAAAQAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAALAAAACwAAAAsAAAALAAAACwAAAAtAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACwAAAAtAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAALAAAAC0AAAAuAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAALAAAAC0AAAAuAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACwAAAAtAAAALgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAALAAAACwAAAAsAAAALAAAACwAAAAtAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAALAAAAC0AAAAuAAAALgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAAC0AAAAuAAAAJgAAACYAAAAsAAAADgAAAA8AAAAQAAAALAAAACwAAAAsAAAALAAAACwAAAAtAAAALgAAAC4AAAAuAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAtAAAALAAAACwAAAAiAAAAIgAAACIAAAAiAAAALQAAAC4AAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAtAAAALgAAAC4AAAAsAAAALAAAACwAAAAsAAAALAAAAC0AAAAuAAAADgAAAA8AAAAQAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAsAAAALQAAAC4AAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACwAAAAsAAAALQAAAC4AAAAiAAAAIgAAACIAAAAiAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAIgAAACIAAAAsAAAALAAAACwAAAAsAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAtAAAALgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAADgAAAA8AAAAQAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAAC0AAAAuAAAALAAAACwAAAAsAAAALAAAACwAAAAtAAAALgAAACIAAAAiAAAAIgAAACIAAAAiAAAADgAAAA8AAAAQAAAALAAAAC0AAAAuAAAALgAAAC4AAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALQAAAC4AAAAiAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAALAAAAC0AAAAuAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAsAAAALQAAAC4AAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAALAAAAC0AAAAuAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACwAAAAtAAAALgAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACwAAAAtAAAALgAAAC4AAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAQAAAADgAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAQAAAAKAAAAA8AAAAQAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAAJQAAACUAAAAlAAAADgAAAA8AAAAoAAAAKAAAACgAAAAWAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALQAAAC4AAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAAC0AAAAuAAAAFAAAACgAAAAoAAAA + + + + + AgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAA + + + diff --git a/assets/maps/testmapb64decoded.txt b/assets/maps/testmapb64decoded.txt new file mode 100644 index 0000000..e69de29 diff --git a/assets/screenshots/BeagleRescue-Screen1.png b/assets/screenshots/BeagleRescue-Screen1.png new file mode 100644 index 0000000..87186bc Binary files /dev/null and b/assets/screenshots/BeagleRescue-Screen1.png differ diff --git a/assets/screenshots/BeagleRescue-Screen2.png b/assets/screenshots/BeagleRescue-Screen2.png new file mode 100644 index 0000000..f40b577 Binary files /dev/null and b/assets/screenshots/BeagleRescue-Screen2.png differ diff --git a/assets/screenshots/UI-preview.ase b/assets/screenshots/UI-preview.ase new file mode 100644 index 0000000..1230a43 Binary files /dev/null and b/assets/screenshots/UI-preview.ase differ diff --git a/assets/textures/actors/beagle.aseprite b/assets/textures/actors/beagle.aseprite new file mode 100644 index 0000000..3cc2a3c Binary files /dev/null and b/assets/textures/actors/beagle.aseprite differ diff --git a/assets/textures/actors/beagle.png b/assets/textures/actors/beagle.png new file mode 100644 index 0000000..7d82b42 Binary files /dev/null and b/assets/textures/actors/beagle.png differ diff --git a/assets/textures/actors/beaglepuppy-bark.gif b/assets/textures/actors/beaglepuppy-bark.gif new file mode 100644 index 0000000..d237308 Binary files /dev/null and b/assets/textures/actors/beaglepuppy-bark.gif differ diff --git a/assets/textures/actors/beaglepuppy-idle.gif b/assets/textures/actors/beaglepuppy-idle.gif new file mode 100644 index 0000000..fb179cc Binary files /dev/null and b/assets/textures/actors/beaglepuppy-idle.gif differ diff --git a/assets/textures/actors/beaglepuppy.png b/assets/textures/actors/beaglepuppy.png new file mode 100644 index 0000000..409e005 Binary files /dev/null and b/assets/textures/actors/beaglepuppy.png differ diff --git a/assets/textures/actors/firefighter-idle.gif b/assets/textures/actors/firefighter-idle.gif new file mode 100644 index 0000000..e8f1b32 Binary files /dev/null and b/assets/textures/actors/firefighter-idle.gif differ diff --git a/assets/textures/actors/firefighter-walk.gif b/assets/textures/actors/firefighter-walk.gif new file mode 100644 index 0000000..9bb5dcb Binary files /dev/null and b/assets/textures/actors/firefighter-walk.gif differ diff --git a/assets/textures/actors/firefighter.aseprite b/assets/textures/actors/firefighter.aseprite new file mode 100644 index 0000000..9a59f76 Binary files /dev/null and b/assets/textures/actors/firefighter.aseprite differ diff --git a/assets/textures/actors/firefighter.json b/assets/textures/actors/firefighter.json new file mode 100644 index 0000000..b63edcd --- /dev/null +++ b/assets/textures/actors/firefighter.json @@ -0,0 +1,98 @@ +{ "frames": [ + { + "filename": "0", + "frame": { "x": 0, "y": 0, "w": 22, "h": 42 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 22, "h": 42 }, + "sourceSize": { "w": 22, "h": 42 }, + "duration": 100 + }, + { + "filename": "1", + "frame": { "x": 22, "y": 0, "w": 22, "h": 42 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 22, "h": 42 }, + "sourceSize": { "w": 22, "h": 42 }, + "duration": 100 + }, + { + "filename": "2", + "frame": { "x": 44, "y": 0, "w": 22, "h": 42 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 22, "h": 42 }, + "sourceSize": { "w": 22, "h": 42 }, + "duration": 100 + }, + { + "filename": "3", + "frame": { "x": 0, "y": 42, "w": 22, "h": 42 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 22, "h": 42 }, + "sourceSize": { "w": 22, "h": 42 }, + "duration": 100 + }, + { + "filename": "4", + "frame": { "x": 22, "y": 42, "w": 22, "h": 42 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 22, "h": 42 }, + "sourceSize": { "w": 22, "h": 42 }, + "duration": 100 + }, + { + "filename": "5", + "frame": { "x": 44, "y": 42, "w": 22, "h": 42 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 22, "h": 42 }, + "sourceSize": { "w": 22, "h": 42 }, + "duration": 100 + }, + { + "filename": "6", + "frame": { "x": 0, "y": 84, "w": 22, "h": 42 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 22, "h": 42 }, + "sourceSize": { "w": 22, "h": 42 }, + "duration": 100 + }, + { + "filename": "7", + "frame": { "x": 22, "y": 84, "w": 22, "h": 42 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 22, "h": 42 }, + "sourceSize": { "w": 22, "h": 42 }, + "duration": 100 + }, + { + "filename": "8", + "frame": { "x": 44, "y": 84, "w": 22, "h": 42 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 22, "h": 42 }, + "sourceSize": { "w": 22, "h": 42 }, + "duration": 100 + } + ], + "meta": { + "app": "http://www.aseprite.org/", + "version": "1.2.25-x64", + "image": "firefighter.png", + "format": "RGBA8888", + "size": { "w": 66, "h": 126 }, + "scale": "1", + "frameTags": [ + { "name": "idle", "from": 0, "to": 2, "direction": "forward" }, + { "name": "walk", "from": 3, "to": 5, "direction": "forward" }, + { "name": "jump", "from": 6, "to": 6, "direction": "forward" }, + { "name": "fall", "from": 7, "to": 7, "direction": "forward" } + ] + } +} diff --git a/assets/textures/actors/firefighter.png b/assets/textures/actors/firefighter.png new file mode 100644 index 0000000..6a35cdb Binary files /dev/null and b/assets/textures/actors/firefighter.png differ diff --git a/assets/textures/arne32-palette.png b/assets/textures/arne32-palette.png new file mode 100644 index 0000000..b63f9f2 Binary files /dev/null and b/assets/textures/arne32-palette.png differ diff --git a/assets/textures/particles/fire.aseprite b/assets/textures/particles/fire.aseprite new file mode 100644 index 0000000..cf68f65 Binary files /dev/null and b/assets/textures/particles/fire.aseprite differ diff --git a/assets/textures/particles/fire2.aseprite b/assets/textures/particles/fire2.aseprite new file mode 100644 index 0000000..0c70b52 Binary files /dev/null and b/assets/textures/particles/fire2.aseprite differ diff --git a/assets/textures/particles/fire2.gif b/assets/textures/particles/fire2.gif new file mode 100644 index 0000000..d03181c Binary files /dev/null and b/assets/textures/particles/fire2.gif differ diff --git a/assets/textures/tiles/background-sky.aseprite b/assets/textures/tiles/background-sky.aseprite new file mode 100644 index 0000000..358cc5e Binary files /dev/null and b/assets/textures/tiles/background-sky.aseprite differ diff --git a/assets/textures/tiles/background-sky.png b/assets/textures/tiles/background-sky.png new file mode 100644 index 0000000..f3ae0f2 Binary files /dev/null and b/assets/textures/tiles/background-sky.png differ diff --git a/assets/textures/tiles/br-tiles.png b/assets/textures/tiles/br-tiles.png new file mode 100644 index 0000000..8e80265 Binary files /dev/null and b/assets/textures/tiles/br-tiles.png differ diff --git a/assets/textures/tiles/br-tiles.pyxel b/assets/textures/tiles/br-tiles.pyxel new file mode 100644 index 0000000..d11540e Binary files /dev/null and b/assets/textures/tiles/br-tiles.pyxel differ diff --git a/assets/textures/tiles/br-tiles.tsx b/assets/textures/tiles/br-tiles.tsx new file mode 100644 index 0000000..561ba45 --- /dev/null +++ b/assets/textures/tiles/br-tiles.tsx @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/textures/ui/font.ase b/assets/textures/ui/font.ase new file mode 100644 index 0000000..bec458f Binary files /dev/null and b/assets/textures/ui/font.ase differ diff --git a/assets/textures/ui/ui-element-cloud.aseprite b/assets/textures/ui/ui-element-cloud.aseprite new file mode 100644 index 0000000..759199a Binary files /dev/null and b/assets/textures/ui/ui-element-cloud.aseprite differ diff --git a/assets/textures/ui/ui-element-cloud.png b/assets/textures/ui/ui-element-cloud.png new file mode 100644 index 0000000..fa678c1 Binary files /dev/null and b/assets/textures/ui/ui-element-cloud.png differ diff --git a/assets/textures/ui/ui-font-cloud-sans.png b/assets/textures/ui/ui-font-cloud-sans.png new file mode 100644 index 0000000..e675f2f Binary files /dev/null and b/assets/textures/ui/ui-font-cloud-sans.png differ diff --git a/build/.cmake/api/v1/query/client-kdevelop/query.json b/build/.cmake/api/v1/query/client-kdevelop/query.json new file mode 100644 index 0000000..41b3430 --- /dev/null +++ b/build/.cmake/api/v1/query/client-kdevelop/query.json @@ -0,0 +1 @@ +{"requests": [{"kind": "codemodel", "version": 2}, {"kind": "cmakeFiles", "version": 1}]} \ No newline at end of file diff --git a/build/.ninja_deps b/build/.ninja_deps new file mode 100644 index 0000000..041a704 Binary files /dev/null and b/build/.ninja_deps differ diff --git a/build/.ninja_log b/build/.ninja_log new file mode 100644 index 0000000..93276eb --- /dev/null +++ b/build/.ninja_log @@ -0,0 +1,66 @@ +# ninja log v5 +5 1257 1680995986388821260 CMakeFiles/BeagleRescue.dir/src/ecs/ECS.cpp.o 2b68ee632af548aa +4 4614 1680995989752187549 CMakeFiles/BeagleRescue.dir/src/assetmgr/AssetManager.cpp.o eb4c2e382e340f10 +4 68 1666035599307923948 libtmx-parser/liblibtmxparser.a 4a03bfde239caf5d +1829 5506 1680995990645529632 CMakeFiles/BeagleRescue.dir/src/ui/UIText.cpp.o 2e1a08cbe2ad039c +5 803 1665858094321634764 CMakeFiles/BeagleRescue.dir/libtmx-parser/src/base64.cpp.o 4bbee0e045ced8b2 +8176 8273 1680995073021895354 libtmx-parser/liblibtmxparser.so 9df69ecfe8aa882a +3771 6588 1680995071338547771 libtmx-parser/CMakeFiles/libtmxparser.dir/libs/tinyxml2/tinyxml2.cpp.o 96043e32043d331c +13 8224 1680995993362222899 CMakeFiles/BeagleRescue.dir/src/game/Game.cpp.o 4ba02c1268a57ad0 +5 3446 1665858096961659219 CMakeFiles/BeagleRescue.dir/libtmx-parser/src/tmxparser.cpp.o 15f574a728100366 +4 1896 1680995987032160899 CMakeFiles/BeagleRescue.dir/src/cjson/cJSON.c.o 77f7fe5671dba050 +5 4075 1680995989215515625 CMakeFiles/BeagleRescue.dir/src/assetmgr/TextureManager.cpp.o 811f88fdfc09263c +6990 7109 1680995071858552173 libtmx-parser/libs/tinyxml2/xmltest c7f1f4ebf26757c2 +4036 6875 1680995071625216864 libtmx-parser/libs/tinyxml2/CMakeFiles/tinyxml2.dir/tinyxml2.cpp.o 660491a0c7495e62 +1258 5876 1680995991015533256 CMakeFiles/BeagleRescue.dir/src/ui/UINineSlice.cpp.o 203c7ee19411f630 +2059 8176 1680995072928561230 libtmx-parser/CMakeFiles/libtmxparser.dir/src/tmxparser.cpp.o ead5f95e0e72b1e0 +4 794 1665858094311634671 CMakeFiles/BeagleRescue.dir/libtmx-parser/libs/tinyxml2/xmltest.cpp.o ee2e170a3bac274c +6978 6990 1680995071728551073 libtmx-parser/libs/tinyxml2/libtinyxml2.so d0fad9500a04c852 +4 1088 1665858094604970721 CMakeFiles/BeagleRescue.dir/libtmx-parser/main.cpp.o 86331d80cf701d8d +13 638 1680995985768815183 CMakeFiles/BeagleRescue.dir/src/game/GameStateManager.cpp.o e18efa52ba3b4829 +638 874 1680995986008817536 CMakeFiles/BeagleRescue.dir/src/game/StateMachine.cpp.o c995c41f2f407d79 +5 3575 1680995988715510727 CMakeFiles/BeagleRescue.dir/src/assetmgr/MusicManager.cpp.o 743e525b8bc7e022 +874 1829 1680995986968826945 CMakeFiles/BeagleRescue.dir/src/game/Vector2D.cpp.o d6bbbe0fad885aeb +6978 6990 1680995071728551073 libtmx-parser/libs/tinyxml2/libtinyxml2.so.2 d0fad9500a04c852 +5127 6527 1680995071278547263 libtmx-parser/libs/tinyxml2/CMakeFiles/xmltest.dir/xmltest.cpp.o 88e42020838dcd8e +2957 4036 1680995068778526099 libtmx-parser/CMakeFiles/libtmxparser.dir/src/base64.cpp.o e84a5ef1b712902d +9777 9902 1680995074651909151 BeagleRescue e5cb768adc438473 +4 4132 1680995989272182847 CMakeFiles/BeagleRescue.dir/src/assetmgr/GameObject.cpp.o ced4d92dc065c0bf +2 730 1681062043486327943 build.ninja bd959f8df49bfbdd +6 3453 1680995988588842819 CMakeFiles/BeagleRescue.dir/src/game/Collision.cpp.o d59bce856486c23e +6875 6978 1680995071728551073 libtmx-parser/libs/tinyxml2/libtinyxml2.so.2.2.0 8aed3caeabaa38d5 +20 4401 1680995989538852126 CMakeFiles/BeagleRescue.dir/src/game/Main.cpp.o c1352eaae73a833f +4 2192 1665858095708314276 CMakeFiles/BeagleRescue.dir/libtmx-parser/libs/tinyxml2/tinyxml2.cpp.o 60acd5a4117a4833 +4 2488 1681062059289774035 CMakeFiles/BeagleRescue.dir/src/assetmgr/MusicManager.cpp.o 743e525b8bc7e022 +4 2608 1681062059413108248 CMakeFiles/BeagleRescue.dir/src/assetmgr/TextureManager.cpp.o 811f88fdfc09263c +4 2703 1681062059509775604 CMakeFiles/BeagleRescue.dir/src/game/Collision.cpp.o d59bce856486c23e +3 2706 1681062059513108961 CMakeFiles/BeagleRescue.dir/src/assetmgr/GameObject.cpp.o ced4d92dc065c0bf +5 2815 1681062059623109746 CMakeFiles/BeagleRescue.dir/src/game/Main.cpp.o c1352eaae73a833f +5 2962 1681062059769777459 CMakeFiles/BeagleRescue.dir/src/ui/UIText.cpp.o 2e1a08cbe2ad039c +5 3207 1681062060013112528 CMakeFiles/BeagleRescue.dir/src/ui/UINineSlice.cpp.o 203c7ee19411f630 +3 3347 1681062060153113527 CMakeFiles/BeagleRescue.dir/src/assetmgr/AssetManager.cpp.o eb4c2e382e340f10 +5 4194 1681062060999786234 CMakeFiles/BeagleRescue.dir/src/game/Game.cpp.o 4ba02c1268a57ad0 +4194 4286 1681062061093120232 BeagleRescue e58e98489844e561 +6 591 1681062457555937468 CMakeFiles/BeagleRescue.dir/src/game/GameStateManager.cpp.o e18efa52ba3b4829 +591 742 1681062457719271959 CMakeFiles/BeagleRescue.dir/src/game/StateMachine.cpp.o c995c41f2f407d79 +5 1761 1681062458725945758 CMakeFiles/BeagleRescue.dir/src/ecs/ECS.cpp.o 2b68ee632af548aa +3 1777 1681062458739279185 CMakeFiles/BeagleRescue.dir/src/cjson/cJSON.c.o 77f7fe5671dba050 +742 1911 1681062458882613534 CMakeFiles/BeagleRescue.dir/src/game/Vector2D.cpp.o d6bbbe0fad885aeb +4 2480 1681062459459284286 CMakeFiles/BeagleRescue.dir/src/assetmgr/TextureManager.cpp.o 811f88fdfc09263c +4 2628 1681062459609285349 CMakeFiles/BeagleRescue.dir/src/assetmgr/MusicManager.cpp.o 743e525b8bc7e022 +4 2658 1681062459635952204 CMakeFiles/BeagleRescue.dir/src/assetmgr/GameObject.cpp.o ced4d92dc065c0bf +5 2882 1681062459862620477 CMakeFiles/BeagleRescue.dir/src/game/Collision.cpp.o d59bce856486c23e +13 3065 1681062460045955109 CMakeFiles/BeagleRescue.dir/src/game/Main.cpp.o c1352eaae73a833f +3 3323 1681062460302623594 CMakeFiles/BeagleRescue.dir/src/assetmgr/AssetManager.cpp.o eb4c2e382e340f10 +1777 4185 1681062461165963044 CMakeFiles/BeagleRescue.dir/src/ui/UIText.cpp.o 2e1a08cbe2ad039c +1761 4194 1681062461172629758 CMakeFiles/BeagleRescue.dir/src/ui/UINineSlice.cpp.o 203c7ee19411f630 +5 4476 1681062461455965099 CMakeFiles/BeagleRescue.dir/src/game/Game.cpp.o 4ba02c1268a57ad0 +4476 4568 1681062461545965736 BeagleRescue e58e98489844e561 +6 3541 1689099824178728855 CMakeFiles/BeagleRescue.dir/src/assetmgr/GameObject.cpp.o ced4d92dc065c0bf +7 3541 1689099824178728855 CMakeFiles/BeagleRescue.dir/src/assetmgr/TextureManager.cpp.o 811f88fdfc09263c +7 3604 1689099824242062276 CMakeFiles/BeagleRescue.dir/src/assetmgr/MusicManager.cpp.o 743e525b8bc7e022 +8 3817 1689099824455395905 CMakeFiles/BeagleRescue.dir/src/game/Main.cpp.o c1352eaae73a833f +7 3968 1689099824605396113 CMakeFiles/BeagleRescue.dir/src/game/Collision.cpp.o d59bce856486c23e +14 4213 1689099824852063121 CMakeFiles/BeagleRescue.dir/src/ui/UIText.cpp.o 2e1a08cbe2ad039c +6 4495 1689099825135396846 CMakeFiles/BeagleRescue.dir/src/assetmgr/AssetManager.cpp.o eb4c2e382e340f10 +9 4558 1689099825195396929 CMakeFiles/BeagleRescue.dir/src/ui/UINineSlice.cpp.o 203c7ee19411f630 diff --git a/design/collision-PseudoCode.txt b/design/collision-PseudoCode.txt new file mode 100644 index 0000000..b3a7954 --- /dev/null +++ b/design/collision-PseudoCode.txt @@ -0,0 +1,27 @@ + +checkCollision(movingRect, moveVector, solidRect) +{ + // Need to make additional similar checks for each side + if (moveVector.x > 0) + { + if (movingRect.xMax < solidRect.xMin && + movingRect.xMax + moveVector.x > solidRect.xMin) + { + xHitRatio = (solidRect.xMin - moveingRect.xMax) / moveVector.x + } + } + // Also find hit ratio of y movement and see which one will hit first if either does. + if (xHitRatio < yHitRatio) + { + yHitPosMin = movingRect.yMin + moveVector.y * xHitRatio + yHitPosMax = movingRect.yMax + moveVector.y * xHitRatio + if (yHitPosMin < solidRect.yMax && + yHitPosMax > solidRect.yMin) + { + // We know here now where we should collide, however if we stop here the collision will feel 'sticky' + // rather than just applying the ratio to the movement vector, we want to instead slide along the collision + output.x = movingRect.xOrigin + moveVector.x * xHitRatio + output.y = movingRect.yOrigin + moveVector.y // Here is where we should split off a new movement vector to check collision again really, since it is a new direction to check, so return the hit position with a new smaller vector to 'finish' the movement. + } + } +} diff --git a/design/debug.txt b/design/debug.txt new file mode 100644 index 0000000..d40e46d --- /dev/null +++ b/design/debug.txt @@ -0,0 +1,7 @@ +Collision Stats: +Player Center +x: 871 +y: 690 +ObjectCollider Center +x: 872 +y: 696 diff --git a/src/assetmgr/AssetManager.cpp b/src/assetmgr/AssetManager.cpp new file mode 100644 index 0000000..2eae23a --- /dev/null +++ b/src/assetmgr/AssetManager.cpp @@ -0,0 +1,70 @@ +/* + * AssetManager.cpp + * + * Created on: Apr 4, 2020 + * Author: ayoungblood + */ + +#include "AssetManager.h" +#include "../ecs/Components.h" +#include "../ecs/ECS.h" + + +class Manager; + +AssetManager::AssetManager(Manager* man) : manager(man) +{} + +AssetManager::~AssetManager() +{} + +// void AssetManager::CreateProjectile(Vector2D pos, Vector2D vel, int range, int speed, std::string id) +// { +// auto& projectile(manager->addEntity()); +// projectile.addComponent(pos.x, pos.y, 32, 32, 1); +// projectile.addComponent(id); +// projectile.addComponent(range,speed,vel); +// projectile.addComponent("projectile"); +// projectile.addGroup(Game::groupProjectiles); +// ;} + +//void AssetManager::CreateText(Vector2D pos, std::string textToDisplay, std::string id) +//{ +// auto& text(manager->addEntity()); +// text.addComponent(pos.x,pos.y,32,32,1); +// +// text.addGroup(Game::groupUI); +//} + + +void AssetManager::AddTexture(std::string id, const char* path) +{ + textures.emplace(id, TextureManager::LoadTexture(path)); +} + +SDL_Texture* AssetManager::GetTexture(std::string id) +{ + return textures[id]; +} + +//Sound Mixer + +//Sound Clips +void AssetManager::AddSoundClip(std::string id, const char* path) +{ + soundClips.emplace(id, MusicManager::LoadSound(path)); +} +Mix_Chunk* AssetManager::GetSoundClip(std::string id) +{ + return soundClips[id]; +} + +//Music +void AssetManager::AddMusicTrack(std::string id, const char* path) +{ + musicTracks.emplace(id, MusicManager::LoadMusic(path)); +} +Mix_Music* AssetManager::GetMusicTrack(std::string id) +{ + return musicTracks[id]; +} diff --git a/src/assetmgr/AssetManager.h b/src/assetmgr/AssetManager.h new file mode 100644 index 0000000..515548b --- /dev/null +++ b/src/assetmgr/AssetManager.h @@ -0,0 +1,54 @@ +/* + * AssetManager.h + * + * Created on: Apr 4, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_ASSETMANAGER_H_ +#define SRC_ASSETMANAGER_H_ + +#include +#include +#include "TextureManager.h" +#include "../game/Vector2D.h" +#include "../ecs/ECS.h" +#include "../game/Game.hpp" +#include "MusicManager.h" + +class AssetManager +{ +public: + AssetManager(Manager* man); + ~AssetManager(); + + + //gameobjects +// void CreateProjectile(Vector2D pos, Vector2D vel, int range, int speed, std::string id); + void CreateText(Vector2D pos, std::string textToDisplay, std::string id); + + //texture management + void AddTexture(std::string id, const char* path); + SDL_Texture* GetTexture(std::string id); + + //Sound Mixer + //Sound Clips + void AddSoundClip(std::string id, const char* path); + Mix_Chunk* GetSoundClip(std::string id); + + //Music + void AddMusicTrack(std::string id, const char* path); + Mix_Music* GetMusicTrack(std::string id); + + + +private: + Manager* manager; + std::map textures; + std::map soundClips; + std::map musicTracks; +}; + + + +#endif /* SRC_ASSETMANAGER_H_ */ diff --git a/src/assetmgr/GameObject.cpp b/src/assetmgr/GameObject.cpp new file mode 100644 index 0000000..2d9780b --- /dev/null +++ b/src/assetmgr/GameObject.cpp @@ -0,0 +1,41 @@ +/* + * GameObject.cpp + * + * Created on: Feb 11, 2020 + * Author: ayoungblood + */ + +#include "GameObject.h" +#include "TextureManager.h" + +GameObject::GameObject(const char* texturesheet, int x, int y) +{ + + objTexture = TextureManager::LoadTexture(texturesheet); + + xpos = x; + ypos = y; + +} + + +void GameObject::Update() +{ + xpos++; +// ypos = 0; + + srcRect.h = 40; + srcRect.w = 30; + srcRect.x = 0; + srcRect.y = 0; + + destRect.x = xpos; + destRect.y = ypos; + destRect.w = srcRect.w * 2; + destRect.h = srcRect.h * 2; +} + +void GameObject::Render() +{ + SDL_RenderCopy(Game::renderer, objTexture, &srcRect, &destRect); +} diff --git a/src/assetmgr/GameObject.h b/src/assetmgr/GameObject.h new file mode 100644 index 0000000..e5251a6 --- /dev/null +++ b/src/assetmgr/GameObject.h @@ -0,0 +1,34 @@ +/* + * GameObject.h + * + * Created on: Feb 11, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_GAMEOBJECT_H_ +#define SRC_GAMEOBJECT_H_ + +#include "../game/Game.hpp" + +class GameObject +{ + +public: + GameObject(const char* texturesheet, int x, int y); + ~GameObject(); + + void Update(); + void Render(); + +private: + int xpos; + int ypos; + + SDL_Texture* objTexture; + SDL_Rect srcRect, destRect; + + +}; + + +#endif /* SRC_GAMEOBJECT_H_ */ diff --git a/src/assetmgr/Map.cpp b/src/assetmgr/Map.cpp new file mode 100644 index 0000000..a3e1c44 --- /dev/null +++ b/src/assetmgr/Map.cpp @@ -0,0 +1,76 @@ +/* + * Map.cpp + * + * Created on: Feb 13, 2020 + * Author: ayoungblood + */ + +#include "Map.h" +#include "../game/Game.hpp" +#include +#include "../ecs/ECS.h" +#include "../ecs/Components.h" +#include +#include + +extern Manager manager; + +Map::Map(std::string tID, int ms, int ts) : texID(tID), mapScale(ms), tileSize(ts) +{ + scaledSize = ms* ts; + width = 0; + height = 0; + tSize = ts; +} + +Map::~Map() +{ +} + +void Map::LoadMap(std::string path, int sizeX, int sizeY, int scale) +{ + char c; + std::fstream mapFile; + mapFile.open(path); + int srcX, srcY; + + width = tSize*scale*sizeX; + height = tSize*scale*sizeY; + + for (int y = 0; y < sizeY; y++) + { + for (int x = 0; x < sizeX; x++) + { + mapFile.get(c); + srcY = atoi(&c) * tileSize; + mapFile.get(c); + srcX = atoi(&c) * tileSize; + mapFile.ignore(2,','); + } + } + + mapFile.ignore(); +// colliders + for (int y =0; y < sizeY; y++) + { + for (int x = 0; x < sizeX; x++) + { + mapFile.get(c); + if (c == '1') + { + auto& tcol(manager.addEntity()); + tcol.addComponent("terrain",x*scaledSize,y*scaledSize,tileSize,scale,texID); + tcol.addGroup(Game::groupColliders); + } + mapFile.ignore(2,','); + } + } + mapFile.close(); +} + +void Map::AddTile(int srcX, int srcY, int xpos, int ypos) +{ + auto& tile(manager.addEntity()); + tile.addComponent(srcX,srcY,xpos,ypos,tileSize, mapScale, texID); + tile.addGroup(Game::groupMap); +} diff --git a/src/assetmgr/Map.h b/src/assetmgr/Map.h new file mode 100644 index 0000000..7498e22 --- /dev/null +++ b/src/assetmgr/Map.h @@ -0,0 +1,33 @@ +/* + * Map.h + * + * Created on: Feb 13, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_MAP_H_ +#define SRC_MAP_H_ + +#include + +class Map +{ +public: + Map(std::string tID, int ms, int ts); + ~Map(); + + void LoadMap(std::string path, int sizeX, int sizeY, int scale); + void AddTile(int srcX, int srcY, int xpos, int ypos); + + int width; + int height; + int tSize; + +private: + std::string texID; + int mapScale; + int tileSize; + int scaledSize; +}; + +#endif /* SRC_MAP_H_ */ diff --git a/src/assetmgr/MusicManager.cpp b/src/assetmgr/MusicManager.cpp new file mode 100644 index 0000000..e35231f --- /dev/null +++ b/src/assetmgr/MusicManager.cpp @@ -0,0 +1,26 @@ +/* + * MusicManager.cpp + * + * Created on: Apr 9, 2020 + * Author: ayoungblood + */ + +#include "MusicManager.h" +#include + + Mix_Music* MusicManager::LoadMusic(const char* path){ + Mix_Music* music = Mix_LoadMUS(path); + return music; + } + + Mix_Chunk* MusicManager::LoadSound(const char* path){ + Mix_Chunk* sfx = Mix_LoadWAV(path); + return sfx; + } + + void Play(Mix_Music* music, int replay) + { + + } + void Pause(Mix_Music* music){} + void Stop(Mix_Music* music){} diff --git a/src/assetmgr/MusicManager.h b/src/assetmgr/MusicManager.h new file mode 100644 index 0000000..e3e3d61 --- /dev/null +++ b/src/assetmgr/MusicManager.h @@ -0,0 +1,25 @@ +/* + * MusicManager.h + * + * Created on: Apr 9, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_MUSICMANAGER_H_ +#define SRC_MUSICMANAGER_H_ + +#include "../game/Game.hpp" +#include + +class MusicManager +{ + public: + static Mix_Music* LoadMusic(const char* path); + static void Play(Mix_Music* music); + static void Pause(Mix_Music* music); + static void Stop(Mix_Music* music); + static Mix_Chunk* LoadSound(const char* path); +}; + + +#endif /* SRC_MUSICMANAGER_H_ */ diff --git a/src/assetmgr/TextureManager.cpp b/src/assetmgr/TextureManager.cpp new file mode 100644 index 0000000..594a349 --- /dev/null +++ b/src/assetmgr/TextureManager.cpp @@ -0,0 +1,34 @@ +/* + * TextureManager.cpp + * + * Created on: Feb 11, 2020 + * Author: ayoungblood + */ + +#include "TextureManager.h" + +SDL_Texture* TextureManager::LoadTexture(const char* texture) +{ + SDL_Surface* tempSurface = IMG_Load(texture); + SDL_Texture* tex = SDL_CreateTextureFromSurface(Game::renderer, tempSurface); + SDL_FreeSurface(tempSurface); + return tex; +} + +void TextureManager::Draw(SDL_Texture* tex, SDL_Rect src, SDL_Rect dest, SDL_RendererFlip flip) +{ + SDL_RenderCopyEx(Game::renderer, tex, &src, &dest, NULL, NULL, flip); +} + +void TextureManager::DrawCollider(SDL_Rect colliderRect) +{ + Vector2D pt1, pt2, pt3, pt4; + pt1 = Vector2D(colliderRect.x,colliderRect.y); + pt2 = Vector2D(colliderRect.w-2,colliderRect.y); + pt3 = Vector2D(colliderRect.x,colliderRect.h-2); + pt4 = Vector2D(colliderRect.w-2,colliderRect.h-2); + Game::drawLine(pt1,pt2,255,0,255); + Game::drawLine(pt1,pt3,255,255,0); + Game::drawLine(pt2,pt4,0,0,255); + Game::drawLine(pt3,pt4,0,255,0); +} diff --git a/src/assetmgr/TextureManager.h b/src/assetmgr/TextureManager.h new file mode 100644 index 0000000..e45bc73 --- /dev/null +++ b/src/assetmgr/TextureManager.h @@ -0,0 +1,22 @@ +/* + * TextureManager.h + * + * Created on: Feb 11, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_TEXTUREMANAGER_H_ +#define SRC_TEXTUREMANAGER_H_ + +// #include +#include "../game/Game.hpp" + +class TextureManager +{ + public: + static SDL_Texture* LoadTexture(const char* fileName); + static void Draw(SDL_Texture* tex, SDL_Rect src, SDL_Rect dest, SDL_RendererFlip flip); + static void DrawCollider(SDL_Rect colliderRect); +}; + +#endif /* SRC_TEXTUREMANAGER_H_ */ diff --git a/src/cjson/cJSON.c b/src/cjson/cJSON.c new file mode 100644 index 0000000..2a860c8 --- /dev/null +++ b/src/cjson/cJSON.c @@ -0,0 +1,3096 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#ifdef true +#undef true +#endif +#define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif +#define false ((cJSON_bool)0) + +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + +#ifndef NAN +#define NAN 0.0/0.0 +#endif + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) +{ + if (!cJSON_IsString(item)) + { + return NULL; + } + + return item->valuestring; +} + +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) +{ + if (!cJSON_IsNumber(item)) + { + return (double) NAN; + } + + return item->valuedouble; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 14) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(CJSON_CDECL *allocate)(size_t size); + void (CJSON_CDECL *deallocate)(void *pointer); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void * CJSON_CDECL internal_malloc(size_t size) +{ + return malloc(size); +} +static void CJSON_CDECL internal_free(void *pointer) +{ + free(pointer); +} +static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) +{ + char *copy = NULL; + /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ + if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) + { + return NULL; + } + if (strlen(valuestring) <= strlen(object->valuestring)) + { + strcpy(object->valuestring, valuestring); + return object->valuestring; + } + copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); + if (copy == NULL) + { + return NULL; + } + if (object->valuestring != NULL) + { + cJSON_free(object->valuestring); + } + object->valuestring = copy; + + return copy; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + if (newbuffer) + { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) +{ + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test = 0.0; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if (isnan(d) || isinf(d)) + { + length = sprintf((char*)number_buffer, "null"); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occurred */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + if (cannot_access_at_index(buffer, 0)) + { + return buffer; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + size_t buffer_length; + + if (NULL == value) + { + return NULL; + } + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL || 0 == buffer_length) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = buffer_length; + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) +{ + return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +} + +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((length < 0) || (buffer == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buffer; + p.length = (size_t)length; + p.offset = 0; + p.noalloc = true; + p.format = format; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + if ((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL) || (array == item)) + { + return false; + } + + child = array->child; + /* + * To find the last item in array quickly, we use prev in array + */ + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + item->prev = item; + item->next = NULL; + } + else + { + /* append to the end */ + if (child->prev) + { + suffix_object(child->prev, item); + array->child->prev = item; + } + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + return add_item_to_array(array, item); +} + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) +{ + return (void*)string; +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +{ + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) + { + return false; + } + + if (constant_key) + { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return false; + } + + return add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return false; + } + + return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) + { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) +{ + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item != parent->child) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + else if (item->next == NULL) + { + /* last element */ + parent->child->prev = item->prev; + } + + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return false; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + return add_item_to_array(array, newitem); + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (parent->child == item) + { + if (parent->child->prev == parent->child) + { + replacement->prev = replacement; + } + parent->child = replacement; + } + else + { /* + * To find the last item in array quickly, we use prev in array. + * We can't modify the last item's next pointer where this item was the parent's child + */ + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (replacement->next == NULL) + { + parent->child->prev = replacement; + } + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return false; + } + + return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = boolean ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + a->child->prev = n; + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + a->child->prev = n; + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0;a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + a->child->prev = n; + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + a->child->prev = n; + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + if (newitem && newitem->child) + { + newitem->child->prev = newchild; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +static void skip_oneline_comment(char **input) +{ + *input += static_strlen("//"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if ((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; + } + } +} + +static void skip_multiline_comment(char **input) +{ + *input += static_strlen("/*"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if (((*input)[0] == '*') && ((*input)[1] == '/')) + { + *input += static_strlen("*/"); + return; + } + } +} + +static void minify_string(char **input, char **output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + + for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if ((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); + } + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + char *into = json; + + if (json == NULL) + { + return; + } + + while (json[0] != '\0') + { + switch (json[0]) + { + case ' ': + case '\t': + case '\r': + case '\n': + json++; + break; + + case '/': + if (json[1] == '/') + { + skip_oneline_comment(&json); + } + else if (json[1] == '*') + { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (compare_double(a->valuedouble, b->valuedouble)) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); +} + diff --git a/src/cjson/cJSON.h b/src/cjson/cJSON.h new file mode 100644 index 0000000..e633019 --- /dev/null +++ b/src/cjson/cJSON.h @@ -0,0 +1,293 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 14 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void *(CJSON_CDECL *malloc_fn)(size_t sz); + void (CJSON_CDECL *free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check item type and return its value */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/array that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. + * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detach items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will + * need to be released. With recurse!=0, it will duplicate any children connected to the item. + * The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. + * The input pointer json cannot point to a read-only address area, such as a string constant, + * but should point to a readable and writable adress area. */ +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) +/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/config/config.json b/src/config/config.json new file mode 100644 index 0000000..5c4ae94 --- /dev/null +++ b/src/config/config.json @@ -0,0 +1,7 @@ +{ +"GameName":"Beagle Rescue", +"WindowName":"Beagle Rescue", +"WindowSize":{"w":427,"h":240}, +"WindowFullScreen": 0, +"GlobalScale": 2 +} diff --git a/src/config/credits.json b/src/config/credits.json new file mode 100644 index 0000000..85f1aa9 --- /dev/null +++ b/src/config/credits.json @@ -0,0 +1,6 @@ +{ +"GameName":"Beagle Rescue", +"Developers": "Alan Youngblood, Simon Zaleski, Daniel Rinaldi", +"LibraryDevelopers": "Sam Lantinga, Dave Gamble, Carl Birch, Job Vranish, David Lafreniere, Bayle Jonathan, Robin Berg Pettersen", +"SpecialThanks":"Nic Allen, Brian Lhota, Rodrigo Monteiro" +} diff --git a/src/ecs/Animation.h b/src/ecs/Animation.h new file mode 100644 index 0000000..35370e9 --- /dev/null +++ b/src/ecs/Animation.h @@ -0,0 +1,28 @@ +/* + * Animation.h + * + * Created on: Mar 27, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_ECS_ANIMATION_H_ +#define SRC_ECS_ANIMATION_H_ + +struct Animation +{ + int index; + int frames; + int speed; + + Animation() {} + Animation(int i, int f, int s) + { + index = i; + frames = f; + speed = s; + } +}; + + + +#endif /* SRC_ECS_ANIMATION_H_ */ diff --git a/src/ecs/ColliderComponent.h b/src/ecs/ColliderComponent.h new file mode 100644 index 0000000..c927b5e --- /dev/null +++ b/src/ecs/ColliderComponent.h @@ -0,0 +1,125 @@ +/* + * ColliderComponent.h + * + * Created on: Mar 8, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_ECS_COLLIDERCOMPONENT_H_ +#define SRC_ECS_COLLIDERCOMPONENT_H_ + +#include +#include +#include "Components.h" +#include "ECS.h" +#include "../assetmgr/TextureManager.h" +#include +#include "../game/Vector2D.h" + +class ColliderComponent : public Component +{ + +public: + + SDL_Rect collider; + std::string tag; + + SDL_Texture* tex; + SDL_Rect srcR, destR; + + int offsetX = 0; + int offsetY = 0; + + bool hidden = true; + + Vector2D center; + + TransformComponent* transform; + + ColliderComponent() + { + center.Zero(); + } + + ColliderComponent(std::string t) + { + tag = t; + } + + ColliderComponent(std::string t, int xpos, int ypos, int size, int scale, std::string texture) + { + tag = t; + collider.x = xpos; + collider.y = ypos; + collider.w = collider.h = size*scale; + setTex(texture); + center.x = collider.x+collider.w/2; + center.y = collider.y+collider.h/2; + } + + ColliderComponent(std::string t, int width, int height, bool hasOffset, int oX, int oY, std::string texture) + { + tag = t; + collider.w = width; + collider.h = height; + offsetX = oX; + offsetY = oY; + setTex(texture); + center.x = collider.x+collider.w/2; + center.y = collider.y+collider.h/2; + } + + void init() override + { + if (!entity->hasComponent()) + { + entity->addComponent(); + } + transform = &entity->getComponent(); + collider.x = collider.x + offsetX; + collider.y = collider.y + offsetX; + tex = TextureManager::LoadTexture("assets/ColTex.png"); + srcR = { 0, 0, 16, 16}; + destR = { collider.x, collider.y, collider.w, collider.h }; + } + + void update() override + { + if(tag != "terrain") + { + collider.x = static_cast(transform->position.x+offsetX); + collider.y = static_cast(transform->position.y+offsetY); + } + destR.x = collider.x - Game::camera.x; + destR.y = collider.y - Game::camera.y; + center.x = collider.x+collider.w/2; + center.y = collider.y+collider.h/2; +// collider.x = transform->position.x; +// collider.y = transform->position.y; + } + + void draw() override + { +// if(tag == "terrain") +// { +// TextureManager::Draw(tex, srcR, destR, SDL_FLIP_NONE); +// } + if(hidden == false) + { +// TextureManager::Draw(tex, srcR,destR,SDL_FLIP_NONE); + SDL_SetRenderDrawColor(Game::renderer,255,0,255,134); + SDL_RenderDrawRect(Game::renderer, &destR); +// SDL_RenderDrawPoint(Game::renderer, center.x, center.y); + } + } + + void setTex(std::string id) + { + tex = Game::assets->GetTexture(id); + } + + +}; + + +#endif /* SRC_ECS_COLLIDERCOMPONENT_H_ */ diff --git a/src/ecs/Components.h b/src/ecs/Components.h new file mode 100644 index 0000000..5f27a78 --- /dev/null +++ b/src/ecs/Components.h @@ -0,0 +1,22 @@ +/* + * Components.h + * + * Created on: Feb 21, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_COMPONENTS_H_ +#define SRC_COMPONENTS_H_ + +#include "ECS.h" +#include "TransformComponent.h" +#include "SpriteComponent.h" +#include "KeyboardController.h" +#include "ColliderComponent.h" +#include "PlayerController.h" +#include "ProjectileComponent.h" +#include "TileComponent.h" +#include "TileMapComponent.h" +#include "UITextComponent.h" + +#endif /* SRC_COMPONENTS_H_ */ diff --git a/src/ecs/ECS.cpp b/src/ecs/ECS.cpp new file mode 100644 index 0000000..a8095b9 --- /dev/null +++ b/src/ecs/ECS.cpp @@ -0,0 +1,19 @@ +/* + * ECS.cpp + * + * Created on: Mar 24, 2020 + * Author: ayoungblood + */ + +#include "ECS.h" + +void Entity::addGroup(Group mGroup) +{ + groupBitset[mGroup] = true; + manager.AddToGroup(this, mGroup); +} +void Entity::setTag(std::string t) +{ + this->tag = t; +} + diff --git a/src/ecs/ECS.h b/src/ecs/ECS.h new file mode 100644 index 0000000..cdd7647 --- /dev/null +++ b/src/ecs/ECS.h @@ -0,0 +1,200 @@ +/* + * ECS.h + * + * Created on: Feb 21, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_ECS_H_ +#define SRC_ECS_H_ + +#include +#include +#include +#include +#include +#include + +class Component; +class Entity; +class Manager; + +using ComponentID = std::size_t; +using Group = std::size_t; + +inline ComponentID getNewComponentTypeID() +{ + static ComponentID lastID = 0u; + return lastID++; +} + + +template inline ComponentID getComponentTypeID() noexcept +{ + static_assert (std::is_base_of::value, ""); + static ComponentID typeID = getNewComponentTypeID(); + return typeID; +} + +constexpr std::size_t maxComponents = 32; +constexpr std::size_t maxGroups = 32; +constexpr std::size_t maxEntities = 32; + +using ComponentBitSet = std::bitset; +using GroupBitset = std::bitset; + +using ComponentArray = std::array; + +class Component +{ +public: + Entity* entity; + + virtual void init() {} + virtual void update() {} + virtual void draw() {} + + virtual ~Component() {} + +}; + +class Entity +{ +private: + Manager& manager; + bool active = true; + std::vector> components; + ComponentArray componentArray; + ComponentBitSet componentBitSet; + GroupBitset groupBitset; + +public: + Entity(Manager& mManager) : manager(mManager) {} + + void update() + { + for(auto& c : components) c->update(); + } + void draw() + { + for(auto& c : components) c->draw(); + } + bool isActive() const { return active; } + void destroy() { active = false; } + bool hasGroup(Group mGroup) + { + return groupBitset[mGroup]; + } + + void addGroup(Group mGroup); + void delGroup(Group mGroup) + { + groupBitset[mGroup] = false; + } + + std::string tag; + void setTag(std::string t); + + Entity* getEntity() + { + return this; + } + + template bool hasComponent() const + { + return componentBitSet[getComponentTypeID()]; + } + + template + T& addComponent(TArgs&&... mArgs) + { + T* c(new T(std::forward(mArgs)...)); + c->entity = this; + std::unique_ptr uPtr{ c }; + components.emplace_back(std::move(uPtr)); + + componentArray[getComponentTypeID()] = c; + componentBitSet[getComponentTypeID()] = true; + + c->init(); + return *c; + } + + template T& getComponent() const + { + auto ptr(componentArray[getComponentTypeID()]); + return *static_cast(ptr); + } + +}; + +class Manager +{ +private: + std::vector> entities; + std::array, maxGroups> groupedEntities; + std::vector> taggedEntities; +public: + void update() + { + for (auto& e : entities) e->update(); + } + void draw() + { + for (auto& e : entities) e->draw(); + } + void refresh() + { + for (auto i(0u); i < maxGroups; i++) + { + auto& v(groupedEntities[i]); + v.erase( + std::remove_if(std::begin(v), std::end(v), + [i](Entity* mEntity) + { + return !mEntity->isActive() || !mEntity->hasGroup(i); + }), + std::end(v)); + } + + entities.erase(std::remove_if(std::begin(entities), std::end(entities), + [](const std::unique_ptr &mEntity) + { + return !mEntity->isActive(); + }), + std::end(entities)); + } + + void AddToGroup(Entity* mEntity, Group mGroup) + { + groupedEntities[mGroup].emplace_back(mEntity); + } + + std::vector getEntitiesByTag(std::string t) + { + std::vector taggedEntities; + for (int e = 0; e<=entities.size(); e++) + { + if (entities[e]->tag == t) + { + taggedEntities.emplace_back(entities[e]->getEntity()); + } + } + return taggedEntities; + } + + std::vector& getGroup(Group mGroup) + { + return groupedEntities[mGroup]; + } + + Entity& addEntity() + { + Entity* e = new Entity(*this); + std::unique_ptr uPtr{ e }; + entities.emplace_back(std::move(uPtr)); + return *e; + } +}; + +#endif /* SRC_ECS_H_ */ diff --git a/src/ecs/KeyboardController.h b/src/ecs/KeyboardController.h new file mode 100644 index 0000000..e82a6e6 --- /dev/null +++ b/src/ecs/KeyboardController.h @@ -0,0 +1,281 @@ +/* + * KeyboardController.h + * + * Created on: Mar 1, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_ECS_KEYBOARDCONTROLLER_H_ +#define SRC_ECS_KEYBOARDCONTROLLER_H_ + +#include "../game/Game.hpp" +#include "ECS.h" +#include "Components.h" +#include "PlayerController.h" +#include "../assetmgr/AssetManager.h" +#include "ColliderComponent.h" +#include +// #include "../game/Vector2D.h" + +class KeyboardController : public Component +{ +public: + + TransformComponent *transform; + SpriteComponent *sprite; + PlayerController *playerCtrl; + ColliderComponent *collider; + + Game *game; + + void init() override + { + transform = &entity->getComponent(); + sprite = &entity->getComponent(); +// collider = &entity->getComponent(); +// playerCtrl = &entity->getComponent(); + } + + void update() override + { + if (Game::event.type == SDL_KEYDOWN) + { + switch (Game::event.key.keysym.sym) + { + case SDLK_UP: + if(Game::gsm->currentState == GameStateManager::ST_COREGAME){ +// if(transform->position.y>0){ +// ====== Get Player.yTile and scan column up ScreenTilesHeight/2 on Player.xTile-1, Player.xTile, and Player.xTile+1 return first hit that is > 0 for each row. +// transform->tilePos.x + transform->velocity.y = -1; +// if(Game::playerIsGrounded){ + sprite->Play("Walk"); +// } + sprite->spriteFlip = SDL_FLIP_NONE; +// } + } + break; + case SDLK_DOWN: + if(Game::gsm->currentState == GameStateManager::ST_COREGAME){ + if(transform->position.yvelocity.y = 1; +// if(Game::playerIsGrounded){ + sprite->Play("Walk"); +// } + int *borders; + borders = Game::predictCollisions(); + sprite->spriteFlip = SDL_FLIP_NONE; + } + } + break; + case SDLK_LEFT: + if(Game::gsm->currentState == GameStateManager::ST_COREGAME){ + if(transform->position.x>Game::levelMap.x){ + transform->velocity.x = -1; +// if(Game::playerIsGrounded){ + sprite->Play("Walk"); +// } + sprite->spriteFlip = SDL_FLIP_NONE; + } +// if (playerCtrl == NULL){ +// printf("No player controller found\n"); +// } else { +// printf("playerctrl.lastsafepos .x: %g .y: %g \n",playerCtrl->lastSafePosition.x,playerCtrl->lastSafePosition.y); +// playerCtrl->lastSafePosition.x = transform->position.x; +// } +// transform->lastSafePos = Vector2D(transform->position.x,transform->position.y); +// printf("lastSafePos .x: %g .y: %g \n",transform->lastSafePos.x,transform->lastSafePos.y); +// Game::pTileX +// if(transform->position.x>0){ + + /*int *borders; + borders = Game::predictCollisions(); + float max = borders[0]; + for (int b=0;b<3;b++){ + if (borders[b]>max){ + max = borders[b]; + } + } + float desiredMovement = -1*transform->speed*0.016; // 0.016 is the FrameTime from the Main.cpp core game loop. later, we will pass in the variable + float difference = std::abs(max-transform->position.x); + if(max*transform->scaleposition.x){ + Game::playerIsGrounded = false; + transform->velocity.x = -1; + sprite->Play("Walk"); + sprite->spriteFlip = SDL_FLIP_NONE; + }else{ +// transform->velocity.x = -difference/desiredMovement; + Game::playerIsGrounded = true; + transform->velocity.x = 0; + sprite->Play("Idle"); + sprite->spriteFlip = SDL_FLIP_NONE; + }*/ + } + break; + case SDLK_RIGHT: + if(Game::gsm->currentState == GameStateManager::ST_COREGAME){ +// &entity->getComponent().setLastSafePos(Vector2D(transform->position.x,transform->position.y)); +// playerCtrl->lastSafePosition.x = transform->position.x; +// transform->lastSafePos = Vector2D(transform->position.x,transform->position.y); +// ========= +// Do a pre-check for tile-based smooth algorithm +// ========= +// int intersectionTileX = collider->center.x; + if(transform->position.xvelocity.x = 1; +// if(Game::playerIsGrounded){ + sprite->Play("Walk"); +// } + sprite->spriteFlip = SDL_FLIP_HORIZONTAL; + } + } + break; +// case SDLK_a: +// if(Game::gsm->currentState == GameStateManager::ST_COREGAME){ +// transform->velocity.x = -1; +// if(Game::playerIsGrounded){ +// sprite->Play("Walk"); +// } +// sprite->spriteFlip = SDL_FLIP_NONE; +// } +// break; +// case SDLK_d: +// if(Game::gsm->currentState == GameStateManager::ST_COREGAME){ +// transform->velocity.x = 1; +// if(Game::playerIsGrounded){ +// sprite->Play("Walk"); +// } +// sprite->spriteFlip = SDL_FLIP_HORIZONTAL; +// } +// break; + case SDLK_k: + if (!Game::debugCollisionBoxes) + { Game::debugCollisionBoxes = true; } + else + {Game::debugCollisionBoxes = false; } + if (!Game::debugMenu) + { Game::debugMenu = true; } + else + { Game::debugMenu = false; } + break; + case SDLK_j: + if(Game::playerIsGrounded){ + if(Game::gsm->currentState == GameStateManager::ST_COREGAME){ + if (Mix_PlayChannel(-1, Game::assets->GetSoundClip("bwoop"),0) == 0) + { + Mix_PlayChannel(-1, Game::assets->GetSoundClip("bwoop"),0); + } +// transform->lastSafePos = Vector2D(transform->position.x,transform->position.y); + Game::gravityOnPlayer = true; + sprite->Play("Jump"); + transform->velocity.y = -2; + } + } + break; + case SDLK_u: + Game::gsm->AdvanceState(); + break; + case SDLK_i: + Game::gsm->GameOver(); + break; + default: + break; + } + } +// =============================================== +// ON KEY UP +// =============================================== + else if (Game::event.type == SDL_KEYUP) + { + switch (Game::event.key.keysym.sym) + { + case SDLK_LEFT: + if(Game::gsm->currentState == GameStateManager::ST_COREGAME){ + transform->velocity.x = 0; + sprite->Play("Idle"); + Game::playerIsGrounded = false; +// if (!Game::gravityOnPlayer){ +// Game::gravityOnPlayer = true; +// // sprite->Play("Fall"); +// } + } + break; + case SDLK_RIGHT: + if(Game::gsm->currentState == GameStateManager::ST_COREGAME){ + transform->velocity.x = 0; + sprite->Play("Idle"); +// if (!Game::gravityOnPlayer){ +// Game::gravityOnPlayer = true; +// } + } + break; + case SDLK_UP: + if(Game::gsm->currentState == GameStateManager::ST_COREGAME){ + transform->velocity.y = 0; + sprite->Play("Idle"); +// if (!Game::gravityOnPlayer){ +// Game::gravityOnPlayer = true; +// // sprite->Play("Fall"); +// } + } + break; + case SDLK_DOWN: + if(Game::gsm->currentState == GameStateManager::ST_COREGAME){ + transform->velocity.y = 0; + sprite->Play("Idle"); +// if (!Game::gravityOnPlayer){ +// Game::gravityOnPlayer = true; +// } + } + break; +// case SDLK_a: +// if(Game::gsm->currentState == GameStateManager::ST_COREGAME){ +// transform->velocity.x = 0; +// sprite->Play("Idle"); +// if (!Game::gravityOnPlayer){ +// Game::gravityOnPlayer = true; +// // sprite->Play("Fall"); +// } +// } +// break; +// case SDLK_d: +// if(Game::gsm->currentState == GameStateManager::ST_COREGAME){ +// transform->velocity.x = 0; +// sprite->Play("Idle"); +// if (!Game::gravityOnPlayer){ +// Game::gravityOnPlayer = true; +// } +// } +// break; + case SDLK_k: + break; + case SDLK_j: + if(Game::gsm->currentState == GameStateManager::ST_COREGAME){ + transform->velocity.y = 0; + sprite->Play("Fall"); + Game::gravityOnPlayer = true; + } + break; + case SDLK_u: + break; + case SDLK_i: + break; +// case SDLK_d: +// if (Game::debugMenu == false){ +// Game::debugMenu = true;} +// else { +// Game::debugMenu = false; +// } +// break; + case SDLK_ESCAPE: // exit the game when Escape pressed + Game::isRunning = false; + break; + default: + break; + } + } + } +}; + + +#endif /* SRC_ECS_KEYBOARDCONTROLLER_H_ */ diff --git a/src/ecs/PlayerController.h b/src/ecs/PlayerController.h new file mode 100644 index 0000000..1ea0a0c --- /dev/null +++ b/src/ecs/PlayerController.h @@ -0,0 +1,77 @@ +/* + * PlayerController.h + * + * Created on: Aug 27, 2021 + * Author: ayoungblood + */ + +#ifndef SRC_ECS_PlayerController_H_ +#define SRC_ECS_PlayerController_H_ + +#include "Components.h" +#include "../game/Vector2D.h" + +class PlayerController : public Component +{ + +public: + + Vector2D lastSafePosition; + float jumpTime; + float jumpForce; + + bool isJumping; + bool isGrounded; + + PlayerController() + { + jumpTime = 0.0f; + jumpForce = 0.0f; + + bool isJumping = false; + bool isGrounded = false; + + lastSafePosition = Vector2D(); + } + + PlayerController(float jumpT, float jumpF, bool jumping, bool grounded, Vector2D lastPos) + { + jumpTime = jumpT; + jumpForce = jumpF; + + bool isJumping = jumping; + bool isGrounded = grounded; + + lastSafePosition = lastPos; + } + + ~PlayerController() + { + + } + + void init() override + { + lastSafePosition.Zero(); + } + + void update() override + { + + } + + void setLastSafePos(Vector2D prevSafePos) + { + lastSafePosition = prevSafePos; + } + +// void init() override +// { +// +// } + +}; + + +#endif /* SRC_ECS_PlayerController_H_ */ + diff --git a/src/ecs/ProjectileComponent.h b/src/ecs/ProjectileComponent.h new file mode 100644 index 0000000..585ae06 --- /dev/null +++ b/src/ecs/ProjectileComponent.h @@ -0,0 +1,57 @@ +/* + * Projectile.h + * + * Created on: Apr 4, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_ECS_PROJECTILECOMPONENT_H_ +#define SRC_ECS_PROJECTILECOMPONENT_H_ + +#include "ECS.h" +#include "Components.h" +#include "../game/Vector2D.h" + +class ProjectileComponent : public Component +{ +public: + ProjectileComponent(int rng, int sp, Vector2D vel) : range(rng), speed(sp), velocity(vel) + {} + ~ProjectileComponent() + {} + + void init() override + { + transform = &entity->getComponent(); + transform->velocity = velocity; + } + + void update() override + { + distance += speed; + if (distance > range) + { + entity->destroy(); + } + else if (transform->position.x > Game::camera.x + Game::camera.w || + transform->position.x < Game::camera.x || + transform->position.y > Game::camera.y +Game::camera.h || + transform->position.y < Game::camera.y) + { + entity->destroy(); + } + + } + +private: + + TransformComponent* transform; + + int range; + int speed; + int distance; + Vector2D velocity; + +}; + +#endif /* SRC_ECS_PROJECTILECOMPONENT_H_ */ diff --git a/src/ecs/SpriteComponent.h b/src/ecs/SpriteComponent.h new file mode 100644 index 0000000..d1a3713 --- /dev/null +++ b/src/ecs/SpriteComponent.h @@ -0,0 +1,195 @@ +/* + * SpriteComponent.h + * + * Created on: Feb 22, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_ECS_SPRITECOMPONENT_H_ +#define SRC_ECS_SPRITECOMPONENT_H_ + +#include "Components.h" +#include +#include "../assetmgr/TextureManager.h" +#include "Animation.h" +#include +#include "../assetmgr/AssetManager.h" +#include "../cjson/cJSON.h" +#include +#include +#include +#include +#include + +class SpriteComponent : public Component +{ +private: + TransformComponent *transform; + SDL_Texture *texture; + SDL_Rect srcRect, destRect; + char letter; + int frames = 0; + int speed = 100; + int letterWidth, letterHeight; + int scale = 1; + +public: + enum SpriteType : std::size_t + { + spriteUIL0, + spriteUIL1, + spriteText, + spriteObject, + spriteTileMap, + spriteBackground, + spriteActor, + spriteAnimation, + spriteAtlas + }; + SpriteType spriteType; + int animIndex = 0; + bool ui = false; + std::map animations; + + SDL_RendererFlip spriteFlip = SDL_FLIP_NONE; + +// SpriteComponent() = default; + SpriteComponent(std::string id, SpriteType sType) + { + spriteType = sType; + setTex(id); + } + +// One initializer to rule them all + SpriteComponent(std::string id, SpriteType sType, std::string json) + { + spriteType = sType; + if(sType == spriteAnimation) + { + std::ifstream fin(json); + + if(fin.is_open()){ + } else { + std::cout<<"json file is NOT open"<valueint; + int toFrame = cJSON_GetObjectItem(animItem, "to")->valueint; + Animation anim = Animation(fromFrame,toFrame,100); + animations.emplace(name, anim); + Play(name); + } + + Animation idle = Animation(0,3,100); + animations.emplace("Idle", idle); + Animation walk = Animation(1,3,100); + animations.emplace("Walk", walk); + Animation jump = Animation(2,1,100); + animations.emplace("Jump", jump); + Animation fall = Animation(3,1,100); + animations.emplace("Fall",fall); + Play("Idle"); + } + setTex(id); + } + + SpriteComponent(std::string id, SpriteType sType, SDL_Rect srcR, SDL_Rect destR) + { + spriteType = sType; + setTex(id); + srcRect = srcR; + destRect = destR; + } + + ~SpriteComponent() + { + SDL_DestroyTexture(texture); + } + + void setTex(std::string id) + { + texture = Game::assets->GetTexture(id); + } + + void init() override + { + transform = &entity->getComponent(); + + switch(spriteType) + { + case spriteUIL0: case spriteUIL1: case spriteBackground: + break; + case spriteActor: case spriteAnimation: case spriteAtlas: case spriteObject: case spriteTileMap: + srcRect.x = srcRect.y = 0; + srcRect.w = transform->width; + srcRect.h = transform->height; + break; + default: + break; + } + } + + void update() override + { + switch(spriteType) + { + case spriteUIL0: case spriteUIL1: case spriteText: case spriteBackground: + destRect.x = static_cast(transform->position.x); + destRect.y = static_cast(transform->position.y); + break; + case spriteActor: case spriteObject: case spriteAtlas: + srcRect.y = animIndex * transform->height; + destRect.x = static_cast(transform->position.x) - Game::camera.x; + destRect.y = static_cast(transform->position.y) - Game::camera.y; + destRect.w = transform->width * transform->scale; + destRect.h = transform->height * transform->scale; + break; + case spriteTileMap: + break; + case spriteAnimation: + srcRect.x = srcRect.w * static_cast((SDL_GetTicks()/speed) % frames); + srcRect.y = animIndex * transform->height; + destRect.x = static_cast(transform->position.x) - Game::camera.x; + destRect.y = static_cast(transform->position.y) - Game::camera.y; + destRect.w = transform->width * transform->scale; + destRect.h = transform->height * transform->scale; + break; + default: + break; + } + } + + void draw() override + { + TextureManager::Draw(texture, srcRect, destRect, spriteFlip); + } + + void Play(const char* animName) + { + frames = animations[animName].frames; + animIndex = animations[animName].index; + speed = animations[animName].speed; + } + +}; + +#endif /* SRC_ECS_SPRITECOMPONENT_H_ */ diff --git a/src/ecs/TileComponent.h b/src/ecs/TileComponent.h new file mode 100644 index 0000000..9cb6dd7 --- /dev/null +++ b/src/ecs/TileComponent.h @@ -0,0 +1,58 @@ +/* + * TileComponent.h + * + * Created on: Mar 21, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_ECS_TILECOMPONENT_H_ +#define SRC_ECS_TILECOMPONENT_H_ + +#include "ECS.h" +#include +#include "../assetmgr/AssetManager.h" + +class TileComponent : public Component +{ +public: + + SDL_Texture* texture; + SDL_Rect srcRect, destRect; + Vector2D position; + + TileComponent() = default; + + ~TileComponent() + { + SDL_DestroyTexture(texture); + } + + + TileComponent(int srcX, int srcY, int xpos, int ypos, int tsize, int tscale, std::string id) + { + texture = Game::assets->GetTexture(id); + position.x = xpos; + position.y = ypos; + + srcRect.x = srcX; + srcRect.y = srcY; + srcRect.w = srcRect.h = tsize; + + destRect.x = xpos; + destRect.y = ypos; + destRect.w = destRect.h = tsize * tscale; + } + + void update() override + { + destRect.x = position.x - Game::camera.x; + destRect.y = position.y - Game::camera.y; + } + + void draw() override + { + TextureManager::Draw(texture, srcRect, destRect, SDL_FLIP_NONE); + } +}; + +#endif /* SRC_ECS_TILECOMPONENT_H_ */ diff --git a/src/ecs/TileMapComponent.h b/src/ecs/TileMapComponent.h new file mode 100644 index 0000000..ff5c478 --- /dev/null +++ b/src/ecs/TileMapComponent.h @@ -0,0 +1,163 @@ +/* + * + * TileMapComponent.h + * + * Created on: Mar 21, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_ECS_TILEMAPCOMPONENT_H_ +#define SRC_ECS_TILEMAPCOMPONENT_H_ + +#include "ECS.h" +#include +#include "../assetmgr/AssetManager.h" +#include +#include +#include +#include +#include "../tileson/tileson.hpp" + +class TileMapComponent : public Component +{ +public: + SDL_Texture* texture; + SDL_Rect srcRect, destRect; + tson::Tileson t; + std::unique_ptr map; + int globalScale; + std::vector tileSet; + std::vector destRects; + std::vector> initialPositions; + int tileSetTotal; + std::vector> colliders; + int tilesWide; + int tilesHigh; + int tileWidth; + + TileMapComponent() = default; + + ~TileMapComponent() + { + SDL_DestroyTexture(texture); + } + + TileMapComponent(std::string mapPath, int gScale, int offsetX, int offsetY) + { +// TILESON ~~~~~~~~~~~ + + const std::filesystem::path jsonPath = std::filesystem::u8path(mapPath); + map = t.parse(jsonPath); + + if(map->getStatus() == tson::ParseStatus::OK) + { + tson::Tileset *tileset = map->getTileset("br-tiles"); + std::string fullPath = tileset->getImage(); + + size_t charPos = fullPath.find("assets"); + fullPath.erase(0,charPos); + tson::Layer *tileLayer = map->getLayer("Tile Layer 1"); //This is a Layer + std::string texName = tileLayer->getName(); + Game::assets->AddTexture(texName, fullPath.c_str()); + setTex(texName); + globalScale = gScale; + + tson::Layer *collisionLayer = map->getLayer("Collision"); + + tilesWide = map->getSize().x; + tilesHigh = map->getSize().y; + tileWidth = map->getTileSize().x; + + Game::levelMap.w = tilesWide*tileWidth*globalScale; + Game::levelMap.h = tilesHigh*tileWidth*globalScale; + + + // =========== Setup Tile Set =========== + + tileSetTotal = tileset->getTileCount(); + tileSet.resize(tileSetTotal); + int tileSetCols = tileset->getColumns(); + int tileSetRows = tileSetTotal/tileSetCols; + + for (int r=0;rgetType() == tson::LayerType::TileLayer) + { + destRects.resize(tilesWide*tilesHigh); + initialPositions.resize(tilesWide*tilesHigh); + for (int r=0;r ogPos = std::make_tuple(thisRect.x,thisRect.y); + initialPositions[elem] = ogPos; + thisRect.x = thisRect.x-offsetX*globalScale; + thisRect.y = thisRect.y=offsetY*globalScale; + destRects[elem] = thisRect; + } + } + destRect.w = destRect.h = tileWidth * gScale; + } + + } else { + printf("Failed to load Tileson map\n"); + std::cout << map->getStatusMessage(); + } + } + + void update() override + { +// if (Game::gsm->currentState == GameStateManager::ST_COREGAME){ + + for (int i=0;i(initialPositions[i]) - Game::camera.x; + destRects[i].y = std::get<1>(initialPositions[i]) - Game::camera.y; + } +// } + } + + void draw() override + { + //iterate through rows and columns of the map to draw the tiles + // First cycle through rows + tson::Layer *myLayer = map->getLayer("Tile Layer 1"); + tson::Layer *collisionLayer = map->getLayer("Collision"); + for (int r = 0;rgetSize().y;r++){ + // Next cycle through each column or tile in that row: + for (int c = 0;cgetSize().x;c++){ + int i = r*map->getSize().x+c; + int elem = c+r*map->getSize().x; + tson::Tile *myTile = myLayer->getTileData(c,r); + int tid = myTile->getId()-1; + int tileToDraw = tid; + TextureManager::Draw(texture, tileSet[tileToDraw], destRects[elem], SDL_FLIP_NONE); + if (Game::debugMenu){ + if (collisionLayer->getTileData(c,r)) { + SDL_SetRenderDrawColor(Game::renderer,255,0,255,134); + SDL_RenderDrawRect(Game::renderer, &destRects[elem]); + } + } + } + } + + } + + void setTex(std::string id) + { + texture = Game::assets->GetTexture(id); + } +}; + +#endif /* SRC_ECS_TILEMAPCOMPONENT_H_ */ + diff --git a/src/ecs/TransformComponent.h b/src/ecs/TransformComponent.h new file mode 100644 index 0000000..b44e7f4 --- /dev/null +++ b/src/ecs/TransformComponent.h @@ -0,0 +1,86 @@ +/* + * TransformComponent.h + * + * Created on: Feb 22, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_ECS_TransformComponent_H_ +#define SRC_ECS_TransformComponent_H_ + +#include "Components.h" +#include "../game/Vector2D.h" + +class TransformComponent : public Component +{ + +public: + + Vector2D position; + Vector2D velocity; +// Vector2D lastSafePos; + Vector2D tilePos; + + int height = 40; + int width = 30; + int scale = 1; + int speed = 1; + + TransformComponent() + { + position.Zero(); + } + + TransformComponent(int sc) + { + position.x = 64*sc; + position.y = 80*sc; + scale = sc; + speed = speed*sc; + } + +// TransformComponent(float x, float y) +// { +// position.Zero(); +// } + + TransformComponent(int x, int y, int w, int h, int sc) + { + position.x = x; + position.y = y; + width = w; + height = h; + scale = sc; + speed = speed*sc; + } + + TransformComponent(int x, int y, int w, int h, int sc, int spd) + { + position.x = x; + position.y = y; + width = w; + height = h; + scale = sc; + speed = spd*sc; + } + + void init() override + { + velocity.Zero(); + tilePos.Zero(); +// lastSafePos.Zero(); + } + void update() override + { + position.x += velocity.x * speed; + position.y += velocity.y * speed; + } + + void updateTilePosition(int x,int y){ + tilePos.x = x; + tilePos.y = y; + } +}; + + +#endif /* SRC_ECS_TransformComponent_H_ */ diff --git a/src/ecs/UITextComponent.h b/src/ecs/UITextComponent.h new file mode 100644 index 0000000..4b2ac6d --- /dev/null +++ b/src/ecs/UITextComponent.h @@ -0,0 +1,101 @@ +/* + * UITextComponent.h + * + * Created on: Feb 22, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_ECS_UITEXTCOMPONENT_H_ +#define SRC_ECS_UITEXTCOMPONENT_H_ + +#define ASCII_START_IDX 32 +#define ASCII_COUNT 96 +#define ASCII_ROW_COUNT 16 + +#include "Components.h" +#include +#include "../assetmgr/TextureManager.h" +#include "../assetmgr/AssetManager.h" +#include +#include +#include +#include +#include + +class UITextComponent : public Component +{ +private: + TransformComponent *transform; + SDL_Texture *texture; + SDL_Rect srcRect, destRect; + std::string text; + int letterWidth, letterHeight; + int scale = 1; + SDL_RendererFlip spriteFlip = SDL_FLIP_NONE; + std::tuple letter; + +public: + UITextComponent(std::string id, std::string textToPrint, int letterW, int letterH, int letterScale) + { + setTex(id); + text = textToPrint; + letterWidth = letterW; + letterHeight = letterH; + scale = letterScale; + } + + ~UITextComponent() + { + SDL_DestroyTexture(texture); + } + + void setTex(std::string id) + { + texture = Game::assets->GetTexture(id); + } + + void init() override + { + transform = &entity->getComponent(); + } + + void update() override + { + destRect.x = static_cast(transform->position.x); + destRect.y = static_cast(transform->position.y); + } + + void draw() override + { + for (int l = 0; l < text.length(); l++) + { + std::tuple lttr = getLetterTexture(text[l],l); + TextureManager::Draw(texture, std::get<0>(lttr), std::get<1>(lttr), spriteFlip); + } + } + + std::tuple getLetterTexture(char currentLetter, int i) + { + std::tuple letterTuple; + int charsPerLine = std::floor(transform->width/(letterWidth*scale)); + srcRect.x = ((currentLetter-ASCII_START_IDX) % ASCII_ROW_COUNT)*letterWidth; + srcRect.y = ((currentLetter-ASCII_START_IDX)/ASCII_ROW_COUNT)*letterHeight; + srcRect.w = letterWidth; + srcRect.h = letterHeight; + destRect.x = static_cast(transform->position.x)*scale+(i%charsPerLine)*letterWidth*scale; + destRect.y = static_cast(transform->position.y)*scale+std::floor(i/charsPerLine)*letterHeight*scale*1.1; + destRect.w = letterWidth*scale; + destRect.h = letterHeight*scale; + letterTuple = std::make_tuple(srcRect,destRect); + return letterTuple; + } + + void updateString(std::string newString) + { + text = newString; + } + +}; + +#endif /* SRC_ECS_UITEXTCOMPONENT_H_ */ + diff --git a/src/game/Collision.cpp b/src/game/Collision.cpp new file mode 100644 index 0000000..78430d9 --- /dev/null +++ b/src/game/Collision.cpp @@ -0,0 +1,35 @@ +/* + * Collision.cpp + * + * Created on: Mar 8, 2020 + * Author: ayoungblood + */ + +#include "Collision.h" +#include "../ecs/ColliderComponent.h" + +bool Collision::AABB(const SDL_Rect& recA, const SDL_Rect& recB) +{ + if( + recA.x + recA.w > recB.x && + recB.x + recB.w > recA.x && + recA.y + recA.h > recB.y && + recB.y + recB.h > recA.y + ) + { + return true; + } + return false; +} + +bool Collision::AABB(const ColliderComponent& colA, const ColliderComponent& colB) +{ + if(AABB(colA.collider, colB.collider)) + { + return true; + } + else + { + return false; + } +} diff --git a/src/game/Collision.h b/src/game/Collision.h new file mode 100644 index 0000000..b3c055f --- /dev/null +++ b/src/game/Collision.h @@ -0,0 +1,24 @@ +/* + * Collision.h + * + * Created on: Mar 8, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_COLLISION_H_ +#define SRC_COLLISION_H_ + +#include +// #include "Vector2D.h" + +class ColliderComponent; + +class Collision +{ +public: + static bool AABB(const SDL_Rect& recA, const SDL_Rect& recB); + static bool AABB(const ColliderComponent& colA, const ColliderComponent& colB); +}; + + +#endif /* SRC_COLLISION_H_ */ diff --git a/src/game/Game.cpp b/src/game/Game.cpp new file mode 100644 index 0000000..bb97f86 --- /dev/null +++ b/src/game/Game.cpp @@ -0,0 +1,554 @@ +/* + * Game.cpp + * + * Created on: Feb 9, 2020 + * Author: ayoungblood + * Special Thanks to: Carl Birch of Let's Make Games + * Nic Allen + * Brian Lhota + */ + +#include "Game.hpp" +#include "../assetmgr/TextureManager.h" +#include "../assetmgr/Map.h" +#include "../ecs/Components.h" +#include "Collision.h" +#include +#include +#include +#include +#include "../assetmgr/AssetManager.h" +#include "../ui/UIText.h" +#include "../ui/UINineSlice.h" +#include "../cjson/cJSON.h" +#include "../tileson/tileson.hpp" +#include +#include + +Manager manager; +UINineSlice* my9Slice; +UINineSlice* scoreboard9Slice; +UINineSlice* debugBox; + +GameStateManager* Game::gsm = new GameStateManager(); + +SDL_Renderer* Game::renderer = nullptr; +SDL_Event Game::event; + +SDL_Rect Game::camera; +SDL_Rect Game::levelMap; + +AssetManager* Game::assets = new AssetManager(&manager); + +bool Game::isRunning = false; +bool Game::debugMenu = false; + +auto& player(manager.addEntity()); +Vector2D Game::playerPosition; +Vector2D Game::pVel; + +// auto& enemy(manager.addEntity()); +auto& puppy(manager.addEntity()); + +// auto& scoreboard(manager.addEntity()); +auto& uiInfo(manager.addEntity()); +auto& uiJumpInfo(manager.addEntity()); +auto& uiCamXInfo(manager.addEntity()); +auto& uiCamYInfo(manager.addEntity()); +auto& uiPlayerXInfo(manager.addEntity()); +auto& uiPlayerYInfo(manager.addEntity()); +auto& uiBoundary1Info(manager.addEntity()); +auto& uiBoundary2Info(manager.addEntity()); +auto& uiBoundary3Info(manager.addEntity()); + +auto& uiTextInstructions(manager.addEntity()); + +auto& gameScene(manager.addEntity()); + +bool Game::debugCollisionBoxes = false; +bool Game::gravityOnPlayer = true; +bool Game::playerIsGrounded = false; +bool Game::playerIsJumping = false; + +int gScale = 0; +int last_time; +int current_time; +int diff_time; + +tson::Tileson t; + +int Game::pTileX = 0; +int Game::pTileY = 0; + +std::string Game::BoolToString(bool b) { + std::string myString; + if (b) { + myString = "true"; + } else { + myString = "false"; + } + return myString; +} + +Game::Game() { + // TODO Auto-generated constructor stub + +} + +Game::~Game() { + // TODO Auto-generated destructor stub +} + +void Game::init(const char *title, int width, int height, bool fullscreen, int globalScale) +{ + camera = { 0, 0, width, height }; + int flags = 0; + gScale = globalScale; + if(fullscreen) + { + flags = SDL_WINDOW_FULLSCREEN; + } + + if(SDL_Init(SDL_INIT_EVERYTHING) == 0) + { + window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, flags); + SDL_SetWindowBordered(window,SDL_FALSE); + if(!window) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Couldn't create window: %s", SDL_GetError()); + } + renderer = SDL_CreateRenderer(window, -1, 0); + if(!renderer) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s", SDL_GetError()); + isRunning = false; + } + else + { + SDL_SetRenderDrawColor(renderer, 255,255,255,255); + SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1"); + isRunning = true; + } + //Initialize SDL_mixer + if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048)<0) + { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Couldn't initialize SDL audio mixer!, Error: %s", SDL_GetError()); + } + + assets->AddTexture("player", "assets/textures/actors/firefighter.png"); + assets->AddTexture("font", "assets/textures/ui/ui-font-cloud-sans.png"); + assets->AddTexture("textBox", "assets/textures/ui/ui-element-cloud.png"); + assets->AddTexture("puppy","assets/textures/actors/beaglepuppy.png"); + assets->AddTexture("collider","assets/textures/ColTex.png"); + + assets->AddMusicTrack("simonZ","assets/audio/music/sillypuppy.ogg"); + assets->AddMusicTrack("simonZ","assets/audio/music/victory.ogg"); + + assets->AddSoundClip("bwoop","assets/audio/sfx/bwoop.wav"); + assets->AddSoundClip("bark1","assets/audio/sfx/Bark1.wav"); + assets->AddSoundClip("bark2","assets/audio/sfx/Bark2.wav"); + +// map = new Map("terrain",globalScale,16); +// std::string myText = "Find lost puppies!\nThey need your help!"; + std::string myText = "Press U to Start"; + + uiTextInstructions.addComponent(18,22,138*gScale,20*gScale,gScale); + uiTextInstructions.addComponent("font",myText,8,12,gScale); + uiTextInstructions.addGroup(groupUI_Layer1); + + SDL_Rect myDestRect = SDL_Rect(); + myDestRect.x = 12; + myDestRect.y = 8; + myDestRect.w = 160; + myDestRect.h = 40; + my9Slice = new UINineSlice("textBox"); + my9Slice->MakeSlices("textBox",32,32,14,16,14,16,myDestRect,globalScale,Game::groupUI_Layer0); + +// std::cout << "camera.w " << camera.w << std::endl; + + uiInfo.addComponent(camera.w/gScale-94,10,72*gScale,96*gScale,gScale); +// uiInfo.addComponent("font", "CollisionHori: Vert: Jump: P.y : P.dy: YVec: ", 8, 12, 1); +// uiInfo.addComponent("font", "Player PTiX: PTiY: P.x: P.y : coll: Px2: tson: ", 8, 12, gScale); + uiInfo.addComponent("font", "Player PTiX: PTiY: P.x: P.y : coll: ", 8, 12, gScale); + uiInfo.addGroup(groupUI_Layer3); + + uiCamXInfo.addComponent(camera.w/gScale-48,23,40*gScale,12*gScale,gScale); + uiCamXInfo.addComponent("font", "nan", 8, 12, gScale); + uiCamXInfo.addGroup(groupUI_Layer3); + + uiCamYInfo.addComponent(camera.w/gScale-48,36,40*gScale,12*gScale,gScale); + uiCamYInfo.addComponent("font", "nan", 8, 12, gScale); + uiCamYInfo.addGroup(groupUI_Layer3); + + uiPlayerXInfo.addComponent(camera.w/gScale-49,49,40*gScale,12*gScale,gScale); + uiPlayerXInfo.addComponent("font", "nan", 8, 12, gScale); + uiPlayerXInfo.addGroup(groupUI_Layer3); + + uiPlayerYInfo.addComponent(camera.w/gScale-48,62,40*gScale,12*gScale,gScale); + uiPlayerYInfo.addComponent("font", "nan", 8, 12, gScale); + uiPlayerYInfo.addGroup(groupUI_Layer3); + + uiBoundary1Info.addComponent(camera.w/gScale-48,75,128*gScale,12*gScale,gScale); + uiBoundary1Info.addComponent("font", "nan", 8, 12, gScale); + uiBoundary1Info.addGroup(groupUI_Layer3); + +// uiBoundary2Info.addComponent(camera.w/gScale-64,88,128*gScale,12*gScale,gScale); +// uiBoundary2Info.addComponent("font", "nan", 8, 12, gScale); +// uiBoundary2Info.addGroup(groupUI_Layer3); +// +// uiBoundary3Info.addComponent(camera.w/gScale-64,101,128*gScale,12*gScale,gScale); +// uiBoundary3Info.addComponent("font", "nan", 8, 12, gScale); +// uiBoundary3Info.addGroup(groupUI_Layer3); + + // debug UI box + SDL_Rect debugBoxRect = SDL_Rect(); + debugBoxRect.x = camera.w-(100*gScale); + debugBoxRect.y = 4*gScale; + debugBoxRect.w = 98*gScale; + debugBoxRect.h = 112*gScale; + debugBox = new UINineSlice("textBox"); + debugBox->MakeSlices("textBox",32,32,14,16,14,16,debugBoxRect,1,Game::groupUI_Layer2); + + //ecs implementation + +// player.addComponent(860*globalScale,640*globalScale,22,42,globalScale); + player.addComponent(150*gScale,100*gScale,22,42,globalScale,3); // 180,120 + player.addComponent("player", SpriteComponent::spriteAnimation, "assets/textures/actors/firefighter.json"); + +// player.addComponent(0.0,0.0,false,false,Vector2D().Zero()); + player.addComponent("player",16*globalScale,32*globalScale, true, 2*globalScale,10*globalScale, "collider"); + player.addComponent(); + player.addGroup(groupPlayers); + + levelMap.x = 0; + levelMap.y = 0; + + gameScene.addComponent("assets/maps/testmap.json",gScale,player.getComponent().position.x+player.getComponent().width/2,player.getComponent().position.y+player.getComponent().height/2); //150,100 + gameScene.addGroup(groupMap); + + playerPosition = Vector2D().Zero(); + pVel = Vector2D().Zero(); + + puppy.addComponent(1024*globalScale,210*globalScale,36,30,globalScale); + puppy.addComponent("puppy", SpriteComponent::spriteObject); + puppy.addGroup(groupObjects); + + } else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't Initialize SDL: %s", SDL_GetError()); + isRunning = false; + } +} + +// ====== Setup groups +auto& tiles(manager.getGroup(Game::groupMap)); +auto& players(manager.getGroup(Game::groupPlayers)); +auto& colliders(manager.getGroup(Game::groupColliders)); +auto& objects(manager.getGroup(Game::groupObjects)); +// auto& enemies(manager.getGroup(Game::groupEnemies)); +// auto& projectiles(manager.getGroup(Game::groupProjectiles)); +auto& gui(manager.getGroup(Game::groupUI_Layer0)); +auto& uiText(manager.getGroup(Game::groupUI_Layer1)); +auto& debugBG(manager.getGroup(Game::groupUI_Layer2)); +auto& debugText(manager.getGroup(Game::groupUI_Layer3)); + +void Game::handleEvents() +{ + SDL_PollEvent(&event); + switch (event.type) + { + case SDL_QUIT: + isRunning = false; + break; + default: + break; + } + +} + +void Game::update() +{ + SDL_Rect playerCol = player.getComponent().collider; + Vector2D playerPos = player.getComponent().position; + +// if (gsm->currentState == GameStateManager::ST_INIT) +// { +// const char* initText = "Loading..."; +// text->ParseString(initText,12,22,gScale,"init"); +// const char* titleText = "Beagle Rescue"; +// const char* gameOverText = "Game Over"; +// } + + if (Mix_PlayingMusic() == 0 && gsm->currentState == GameStateManager::ST_COREGAME) + { +// std::cout << "Play Music Now" << std::endl; +// Mix_PlayMusic(assets->GetMusicTrack("simonZ"), -1); + } + if (Mix_PlayingMusic() != 0 && gsm->currentState != GameStateManager::ST_COREGAME) + { + Mix_HaltMusic(); + } +// if (Mix_PlayChannel(-1, Game::assets->GetSoundClip("bark1"),0) == 0) +// { +// Mix_PlayChannel(-1, Game::assets->GetSoundClip("bark1"),0); +// } + + manager.refresh(); + manager.update(); + + for (auto& c : colliders) + { + SDL_Rect cCol = c->getComponent().collider; + if(Collision::AABB(cCol, playerCol)) + { +// if(!playerIsGrounded){ +// player.getComponent().Play("Idle"); +// } +// playerIsGrounded = true; +// player.getComponent().position.y = player.getComponent().lastSafePos.y; +// gravityOnPlayer = false; + } + + } + + + +// Gravity +// if (gravityOnPlayer){ +// player.getComponent().position.y += 3*gScale; +// uiJumpInfo.getComponent().updateString("true"); +// } else { +// uiJumpInfo.getComponent().updateString("false"); +// } +// for(auto& p: projectiles) +// { +// if(Collision::AABB(player.getComponent().collider, p->getComponent().collider)) +// { +// std::cout << "Projectile hit player" << std::endl; +// p->destroy(); +// } +// } + + auto& tileMap = gameScene.getComponent(); + + camera.x = player.getComponent().position.x - camera.w/2 + player.getComponent().width/2; + camera.y = player.getComponent().position.y - camera.h/2 + player.getComponent().height/2; + + pTileX = (player.getComponent().position.x+player.getComponent().width/2)/gScale/tileMap.tileWidth; + pTileY = (player.getComponent().position.y+player.getComponent().height/2)/gScale/tileMap.tileWidth; + + player.getComponent().updateTilePosition(pTileX,pTileY); + + uiCamXInfo.getComponent().updateString(std::to_string((int)player.getComponent().tilePos.x)); + uiCamYInfo.getComponent().updateString(std::to_string((int)player.getComponent().tilePos.y)); + int playerX = player.getComponent().position.x; + int playerY = player.getComponent().position.y; + + uiPlayerXInfo.getComponent().updateString(std::to_string(playerX)); + uiPlayerYInfo.getComponent().updateString(std::to_string(playerY)); + + // int * foundBoundaries = predictCollisions(); + // int max = foundBoundaries[0]; + // for (int b=0;b<3;b++){ + // if (foundBoundaries[b]>max){ + // max = foundBoundaries[b]; + // } + // } + float desiredMovementX = -1*player.getComponent().speed*0.016; +// int desiredMovementY = player.getComponent().velocity.y*player.getComponent().speed*player.getComponent().scale; +// uiBoundary1Info.getComponent().updateString(std::to_string((int)foundBoundaries[0])); +// uiBoundary1Info.getComponent().updateString(std::to_string((float)desiredMovementX)); + if (playerIsGrounded) { + uiBoundary1Info.getComponent().updateString("yes"); + } else { + uiBoundary1Info.getComponent().updateString("no"); + } +// uiBoundary2Info.getComponent().updateString(std::to_string((int)foundBoundaries[1])); + float difference = player.getComponent().position.x+desiredMovementX; + //uiBoundary2Info.getComponent().updateString(std::to_string((float)difference)); + //uiBoundary3Info.getComponent().updateString(std::to_string((int)(max*gScale))); + +// uiBoundary3Info.getComponent().updateString(std::to_string((int)foundBoundaries[2])); + + playerPosition.x = playerX; + playerPosition.y = playerY; + + pVel.x = player.getComponent().velocity.x; + pVel.y = player.getComponent().velocity.y; + + if(camera.x < 0) + camera.x = 0; + if (camera.y < 0) + camera.y = 0; + if (camera.x > levelMap.w-camera.w) + camera.x = levelMap.w-camera.w; + if (camera.y > levelMap.h-camera.h) + camera.y = levelMap.h-camera.h; + if (Game::debugCollisionBoxes) + { + for (auto& c: colliders) + { + c->getComponent().hidden = false; + } + for (auto& p: players) + { + p->getComponent().hidden = false; + } + } else { + for (auto& c: colliders) + { + c->getComponent().hidden = true; + } + for (auto& p: players) + { + p->getComponent().hidden = true; + } + } +} + +void Game::render() +{ + SDL_RenderClear(renderer); +// if (gsm->currentState==GameStateManager::ST_COREGAME) +// { +// printf("Core Game state\n"); +// } + for (auto& t : tiles) + { + t->draw(); + } + + for (auto& c : colliders) + { + c->draw(); + } + + for (auto& o : objects) + { + o->draw(); + } +// +// for (auto& e : enemies) +// { +// e->draw(); +// } + + for (auto& p : players) + { + p->draw(); + } + if (gsm->currentState==GameStateManager::ST_TITLESCREEN||gsm->currentState==GameStateManager::ST_INIT||gsm->currentState==GameStateManager::ST_GAMEOVER){ + for (auto& guiElement : gui) + { + guiElement->draw(); + } + for (auto& text : uiText) + { + text->draw(); + } + } + if (debugMenu) + { + for (auto& guiElement : debugBG) + { + guiElement->draw(); + } + for (auto& text : debugText) + { + text->draw(); + } + } + + SDL_RenderPresent(renderer); +} + +void Game::clean() +{ + SDL_DestroyWindow(window); + SDL_DestroyRenderer(renderer); + IMG_Quit(); + Mix_Quit(); + SDL_Quit(); + printf("Game Cleaned\n"); +} + +void Game::drawLine(Vector2D srcpt, Vector2D destpt, int red, int green, int blue) +{ + SDL_SetRenderDrawColor(renderer, red, green, blue, 200); + SDL_RenderDrawLine(renderer, srcpt.x, srcpt.y, destpt.x, destpt.y); +} + +int * Game::predictCollisions(){ + static int boundaries[3]; +// ===== LEFT ===== + /*if (player.getComponent().velocity.x<0){ +// ====== For Each Row ==== + int i = 0; + for (int r=Game::pTileY-1;r<=Game::pTileY+1;r++){ +// ====== For Each Tile (Column) ===== + if(r<0){r=0;} + for (int c=Game::pTileX;c>Game::pTileX-Game::camera.w/gameScene.getComponent().tileWidth;c--){ + if(c<0){c=0;} + if(gameScene.getComponent().colliders[r][c]>0){ + boundaries[i] = c*gameScene.getComponent().tileWidth+gameScene.getComponent().tileWidth; + i++; + break; + } + } + } + }*/ + /* +// ====== RIGHT ==== + if (player.getComponent().velocity.x>0){ +// ====== For Each Row ==== + int i = 0; + for (int r=Game::pTileY-1;r<=Game::pTileY+1;r++){ + if(r<0){r=0;} +// ====== For Each Tile (Column) ===== + for (int c=Game::pTileX;c().tileWidth;c++){ + if(c<0){c=0;} + if(gameScene.getComponent().colliders[r][c]>0){ + boundaries[i] = c*gameScene.getComponent().tileWidth; + i++; + break; + } + } + } + } +// ===== UP ==== + if (player.getComponent().velocity.y<0){ +// ====== For Each Column ==== + int i = 0; + for (int c=Game::pTileX-1;c<=Game::pTileX+1;c++){ + if(c<0){c=0;} +// ====== For Each Tile (Row) ===== + for (int r=Game::pTileY;r>Game::pTileY-Game::camera.h/gameScene.getComponent().tileWidth;r--){ + if(r<0){r=0;} + if(gameScene.getComponent().colliders[r][c]>0){ + boundaries[i] = r*gameScene.getComponent().tileWidth+gameScene.getComponent().tileWidth; + i++; + break; + } + } + } + + } +// ===== DOWN ==== + if (player.getComponent().velocity.y>0){ +// ====== For Each Column ==== + int i = 0; + for (int c=Game::pTileX-1;c<=Game::pTileX+1;c++){ + if(c<0){c=0;} +// ====== For Each Tile (Row) ===== + for (int r=Game::pTileY;r().tileWidth;r++){ + if(r<0){r=0;} + if(gameScene.getComponent().colliders[r][c]>0){ + boundaries[i] = r*gameScene.getComponent().tileWidth+gameScene.getComponent().tileWidth; + i++; + break; + } + } + } + } + */ + return boundaries; +} diff --git a/src/game/Game.hpp b/src/game/Game.hpp new file mode 100644 index 0000000..a1ee8eb --- /dev/null +++ b/src/game/Game.hpp @@ -0,0 +1,81 @@ +/* + * Game.hpp + * + * Created on: Feb 9, 2020 + * Author: ayoungblood + */ + +#ifndef GAME_HPP_ +#define GAME_HPP_ +#include +#include +#include +#include +#include +#include +#include "Vector2D.h" +#include "../assetmgr/AssetManager.h" +#include "GameStateManager.h" +//#include "../../libtmx-parser/src/tmxparser.h" +//#include "libtmx-parser/src/tmxparser.h" +//#include + +class ColliderComponent; +class AssetManager; + +class Game { +public: + Game(); + virtual ~Game(); + void init(const char* title, int width, int height, bool fullscreen, int globalScale); + void handleEvents(); + void update(); + void render(); + void clean(); +// void printDebug(std::string debugInfo); + static void drawLine(Vector2D srcpt, Vector2D destpt, int red, int green, int blue); + static int * predictCollisions(); + bool running() { return isRunning; } +// static void AddTile(int srcX, int srcY, int xpos, int ypos); + static SDL_Renderer *renderer; + static SDL_Event event; +// static std::vector colliders; +// static tmxparser::TmxMap map; +// gameScene; + + static bool debugMenu; + static bool isRunning; + static bool debugCollisionBoxes; + static bool gravityOnPlayer; + static bool playerIsGrounded; + static bool playerIsJumping; + static Vector2D playerPosition; + static Vector2D pVel; + static SDL_Rect camera; + static SDL_Rect levelMap; + static AssetManager* assets; + static GameStateManager* gsm; + static int pTileX; + static int pTileY; + std::string BoolToString(bool b); + enum groupLabels : std::size_t + { + groupMap, + groupPlayers, + groupEnemies, + groupColliders, + groupProjectiles, + groupObjects, + groupBackground, + groupUI_Layer0, + groupUI_Layer1, + groupUI_Layer2, + groupUI_Layer3 + }; +private: + int counter = 0; +// bool isRunning = false; + SDL_Window *window; +// SDL_Renderer *renderer; +}; +#endif /* GAME_HPP_ */ diff --git a/src/game/GameStateManager.cpp b/src/game/GameStateManager.cpp new file mode 100644 index 0000000..ac95ed1 --- /dev/null +++ b/src/game/GameStateManager.cpp @@ -0,0 +1,95 @@ +#include +#include "GameStateManager.h" +#include + +using namespace std; + +void GameStateManager::AdvanceState() +{ + //given the AdvanceState event transition to a new state based on current state and gameData + BEGIN_TRANSITION_MAP // (Next State) // - Current State - + TRANSITION_MAP_ENTRY (ST_TITLESCREEN) // ST_Init + TRANSITION_MAP_ENTRY (ST_COREGAME) // ST_TitleScreen + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_Instructions + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_HiScoreList + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_Credits + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_CoreGame + TRANSITION_MAP_ENTRY (ST_COREGAME) // ST_PauseScreen + TRANSITION_MAP_ENTRY (ST_INIT) // ST_GameOver + END_TRANSITION_MAP(NULL) +} + +void GameStateManager::PauseGame(GameData* gameData) +{ +// given the PauseGame event transition to a new state based on current state and gameData +// printf("gameData->isPaused: %d\n",gameData->isPaused); + BEGIN_TRANSITION_MAP // - Current State - + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_Init + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_TitleScreen + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_Instructions + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_HiScoreList + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_Credits + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_CoreGame + TRANSITION_MAP_ENTRY (ST_INSTRUCTIONS) // ST_PauseScreen + TRANSITION_MAP_ENTRY (CANNOT_HAPPEN) // ST_GameOver + END_TRANSITION_MAP(gameData) + + } +void GameStateManager::GameOver() +{ + //given the PauseGame event transition to a new state based on current state and gameData +// printf("GameOver triggered\n"); + BEGIN_TRANSITION_MAP // - Current State - + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_Init + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_TitleScreen + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_Instructions + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_HiScoreList + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_Credits + TRANSITION_MAP_ENTRY (ST_GAMEOVER) // ST_CoreGame + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_PauseScreen + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_GameOver + END_TRANSITION_MAP(NULL) +} + +void GameStateManager::ST_Init(EventData*) +{ + printf("GameStateManager::ST_Init\n"); +// InternalEvent(ST_TITLESCREEN); +} + +void GameStateManager::ST_TitleScreen(EventData*) +{ + printf("GameStateManager::ST_TitleScreen\n"); +// InternalEvent(ST_TITLESCREEN); +} + +void GameStateManager::ST_Instructions(GameData*) +{ + printf("GameStateManager::ST_Instructions\n"); +} + +void GameStateManager::ST_HiScoreList(GameData*) +{ + printf("GameStateManager::ST_HiScoreList\n"); +} + +void GameStateManager::ST_Credits(GameData*) +{ + printf("GameStateManager::ST_Credits\n"); +} + +void GameStateManager::ST_CoreGame(EventData*) +{ + printf("GameStateManager::ST_CoreGame\n"); +} + +void GameStateManager::ST_PauseScreen(EventData*) +{ + printf("GameStateManager::ST_PauseScreen\n"); +} + +void GameStateManager::ST_GameOver(GameData*) +{ + printf("GameStateManager::ST_GameOver\n"); +} + diff --git a/src/game/GameStateManager.h b/src/game/GameStateManager.h new file mode 100644 index 0000000..d2f8168 --- /dev/null +++ b/src/game/GameStateManager.h @@ -0,0 +1,57 @@ +#pragma once +#include "StateMachine.h" + +// Structure to hold event data passed into state StateMachine +struct GameData : public EventData +{ + char* menuId; + bool win; +}; + +// The Game State Machine Class +class GameStateManager : public StateMachine +{ +public: + GameStateManager() : StateMachine(ST_MAX_STATES) {} + + // external events taken by this state machine + void AdvanceState(); + void GameOver(); + void PauseGame(GameData* gameData); +private: + //state machine state functions + void ST_Init(EventData*); + void ST_TitleScreen(EventData*); + void ST_Instructions(GameData*); + void ST_HiScoreList(GameData*); + void ST_Credits(GameData*); + void ST_CoreGame(EventData*); + void ST_PauseScreen(EventData*); + void ST_GameOver(GameData*); + + //state map to define state function order + BEGIN_STATE_MAP + STATE_MAP_ENTRY(&GameStateManager::ST_Init) + STATE_MAP_ENTRY(&GameStateManager::ST_TitleScreen) + STATE_MAP_ENTRY(&GameStateManager::ST_Instructions) + STATE_MAP_ENTRY(&GameStateManager::ST_HiScoreList) + STATE_MAP_ENTRY(&GameStateManager::ST_Credits) + STATE_MAP_ENTRY(&GameStateManager::ST_CoreGame) + STATE_MAP_ENTRY(&GameStateManager::ST_PauseScreen) + STATE_MAP_ENTRY(&GameStateManager::ST_GameOver) + END_STATE_MAP + + //state enumeration order must match order of state method entries on state map + enum E_States { + ST_INIT = 0, + ST_TITLESCREEN, + ST_INSTRUCTIONS, + ST_HISCORELIST, + ST_CREDITS, + ST_COREGAME, + ST_PAUSESCREEN, + ST_GAMEOVER, + ST_MAX_STATES + }; +}; + diff --git a/src/game/Main.cpp b/src/game/Main.cpp new file mode 100644 index 0000000..791169b --- /dev/null +++ b/src/game/Main.cpp @@ -0,0 +1,85 @@ +/* + * main.cpp + * + * Created on: Feb 9, 2020 + * Author: ayoungblood + */ + +#include "Game.hpp" +#include "../cjson/cJSON.h" +#include +#include +#include +#include + +Game *game = nullptr; + +int main(int argc, const char * argv[]) +{ + const int FPS = 60; + const int frameDelay = 1000 / FPS; + + Uint64 frameStart; + int frameTime; + +// ============================= +// Load cJSON config.json file +// ============================= +// Starting with Error Checking + std::string configPath = "src/config/config.json"; + std::ifstream fin(configPath); + + if(fin.is_open()){ + std::ifstream jsonText("src/config/config.json"); + std::ostringstream tmp; + tmp << jsonText.rdbuf(); + std::string json = tmp.str(); + cJSON * myJSON = cJSON_Parse(json.c_str()); + cJSON * windowName = cJSON_GetObjectItemCaseSensitive(myJSON, "WindowName"); + cJSON * windowSize = cJSON_GetObjectItem(myJSON, "WindowSize"); + int windowWidth = cJSON_GetObjectItem(windowSize, "w")->valueint; + int windowHeight = cJSON_GetObjectItem(windowSize, "h")->valueint; + int windowFS = cJSON_GetObjectItem(myJSON, "WindowFullScreen")->valueint; + int globalScale = cJSON_GetObjectItem(myJSON, "GlobalScale")->valueint; + bool isWindowFS; + if (windowFS==0) + { + isWindowFS = false; + } else + { + isWindowFS = true; + } + windowWidth = windowWidth*globalScale; + windowHeight = windowHeight*globalScale; + game = new Game(); + game->init(windowName->valuestring, windowWidth, windowHeight, isWindowFS, globalScale); +// cJSON memory management + cJSON_Delete(myJSON); + + while (game->running()) + { + frameStart = SDL_GetTicks64(); + + game->handleEvents(); + game->update(); + game->render(); + + frameTime = SDL_GetTicks64() - frameStart; + + if(frameDelay > frameTime) + { + SDL_Delay(frameDelay - frameTime); + } + + } + game->clean(); + } else { + std::cout<<"config.json not found or opened"< +#include "StateMachine.h" +// ============================================================================= +// Do NOT modify this file, instead make custom changes in the GameStateManager +// or create your own StateMachine including and implementing this header +// ============================================================================= + +StateMachine::StateMachine(unsigned char maxStates) : + currentState(0), + _maxStates(maxStates), + _eventGenerated(false), + _pEventData(NULL) +{ +} + +// generates an external event. called once per external event +// to start the state machine executing +void StateMachine::ExternalEvent(unsigned char newState, + EventData* pData) +{ + // if we are supposed to ignore this event + if (newState == EVENT_IGNORED) { + // just delete the event data, if any + if (pData) + delete pData; + } + else { + // TODO - capture software lock here for thread-safety if necessary + + // generate the event and execute the state engine + InternalEvent(newState, pData); + StateEngine(); + + // TODO - release software lock here + } +} + +// generates an internal event. called from within a state +// function to transition to a new state +void StateMachine::InternalEvent(unsigned char newState, + EventData* pData) +{ + if (pData == NULL) + pData = new EventData(); + + _pEventData = pData; + _eventGenerated = true; + currentState = newState; +} + +// the state engine executes the state machine states +void StateMachine::StateEngine(void) +{ + EventData* pDataTemp = NULL; + + // while events are being generated keep executing states + while (_eventGenerated) { + pDataTemp = _pEventData; // copy of event data pointer + _pEventData = NULL; // event data used up, reset ptr + _eventGenerated = false; // event used up, reset flag + + assert(currentState < _maxStates); + + // get state map + const StateStruct* pStateMap = GetStateMap(); + + // execute the state passing in event data, if any + (this->*pStateMap[currentState].pStateFunc)(pDataTemp); + + // if event data was used, then delete it + if (pDataTemp) { + delete pDataTemp; + pDataTemp = NULL; + } + } +} diff --git a/src/game/StateMachine.h b/src/game/StateMachine.h new file mode 100644 index 0000000..bda2119 --- /dev/null +++ b/src/game/StateMachine.h @@ -0,0 +1,64 @@ +#ifndef _STATE_MACHINE_H +#define _STATE_MACHINE_H +#include +// ============================================================================= +// Do NOT modify this file, instead make custom changes in the GameStateManager +// or create your own StateMachine including and implementing this header +// ============================================================================= +class EventData +{ +public: + virtual ~EventData() {}; +}; + +struct StateStruct; + +// base class for state machines +class StateMachine +{ +public: + StateMachine(unsigned char maxStates); + virtual ~StateMachine() {} +// unsigned char getCurrentState(); + unsigned char currentState; +protected: + enum { EVENT_IGNORED = 0xFE, CANNOT_HAPPEN }; + void ExternalEvent(unsigned char, EventData* = NULL); + void InternalEvent(unsigned char, EventData* = NULL); + virtual const StateStruct* GetStateMap() = 0; +private: + const unsigned char _maxStates; + bool _eventGenerated; + EventData* _pEventData; + void StateEngine(void); +}; + +typedef void (StateMachine::*StateFunc)(EventData *); +struct StateStruct +{ + StateFunc pStateFunc; +}; + +#define BEGIN_STATE_MAP \ +public:\ +const StateStruct* GetStateMap() {\ + static const StateStruct StateMap[] = { + +#define STATE_MAP_ENTRY(stateFunc)\ + { reinterpret_cast(stateFunc) }, + +#define END_STATE_MAP \ + }; \ + return &StateMap[0]; } + +#define BEGIN_TRANSITION_MAP \ + static const unsigned char TRANSITIONS[] = {\ + +#define TRANSITION_MAP_ENTRY(entry)\ + entry, + +#define END_TRANSITION_MAP(data) \ + 0 };\ + ExternalEvent(TRANSITIONS[currentState], data); + +#endif //STATE_MACHINE_H diff --git a/src/game/Vector2D.cpp b/src/game/Vector2D.cpp new file mode 100644 index 0000000..1f89af4 --- /dev/null +++ b/src/game/Vector2D.cpp @@ -0,0 +1,122 @@ +/* + * Vector2D.cpp + * + * Created on: Mar 1, 2020 + * Author: ayoungblood + */ + +#include "Vector2D.h" + +Vector2D::Vector2D() +{ + x = 0.0f; + y = 0.0f; + r = 0.0f; + t = 0.0f; +} + +Vector2D::Vector2D(float x, float y) +{ + this->x = x; + this->y = y; + this->r = sqrt(pow(x,2.0)+pow(y,2.0)); + this->t = atan(y/x); +} + +Vector2D::Vector2D(float r, float t, bool isPolar) +{ + this->x = r*(cos(t)); + this->y = r*(sin(t)); + this->r = r; + this->t = t; +} + +Vector2D& Vector2D::Add(const Vector2D& vec) +{ + this->x += vec.x; + this->y += vec.y; + return *this; +} + +Vector2D& Vector2D::Subtract(const Vector2D& vec) +{ + this->x -= vec.x; + this->y -= vec.y; + return *this; +} + +Vector2D& Vector2D::Multiply(const Vector2D& vec) +{ + this->x *= vec.x; + this->y *= vec.y; + return *this; +} + +Vector2D& Vector2D::Divide(const Vector2D& vec) +{ + this->x /= vec.x; + this->y /= vec.y; + return *this; +} + +Vector2D& operator+(Vector2D& v1, const Vector2D& v2) +{ + return v1.Add(v2); +} + +Vector2D& operator-(Vector2D& v1, const Vector2D& v2) +{ + return v1.Subtract(v2); +} + +Vector2D& operator*(Vector2D& v1, const Vector2D& v2) +{ + return v1.Multiply(v2); +} + +Vector2D& operator/(Vector2D& v1, const Vector2D& v2) +{ + return v1.Divide(v2); +} + +Vector2D& Vector2D::operator +=(const Vector2D& vec) +{ + return this->Add(vec); +} + +Vector2D& Vector2D::operator -=(const Vector2D& vec) +{ + return this->Subtract(vec); +} + +Vector2D& Vector2D::operator *=(const Vector2D& vec) +{ + return this->Multiply(vec); +} + +Vector2D& Vector2D::operator /=(const Vector2D& vec) +{ + return this->Divide(vec); +} + +Vector2D& Vector2D::operator *(const int& i) +{ + this->x *= i; + this->y *= i; + + return *this; +} + +Vector2D& Vector2D::Zero() +{ + this->x = 0; + this->y = 0; + return *this; +} + + +std::ostream& operator<<(std::ostream& stream, const Vector2D& vec) +{ + stream << "(" << vec.x << "," << vec.y << ")"; + return stream; +} diff --git a/src/game/Vector2D.h b/src/game/Vector2D.h new file mode 100644 index 0000000..9902205 --- /dev/null +++ b/src/game/Vector2D.h @@ -0,0 +1,50 @@ +/* + * Vector2d.h + * + * Created on: Feb 22, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_ECS_VECTOR2D_H_ +#define SRC_ECS_VECTOR2D_H_ + +#include +#include + +class Vector2D +{ +public: + float x; + float y; + float r; + float t; + + Vector2D(); + Vector2D(float x, float y); + Vector2D(float r, float t, bool isPolar); + + Vector2D& Add(const Vector2D& vec); + Vector2D& Subtract(const Vector2D& vec); + Vector2D& Multiply(const Vector2D& vec); + Vector2D& Divide(const Vector2D& vec); + + friend Vector2D& operator+(Vector2D& v1, const Vector2D& v2); + friend Vector2D& operator-(Vector2D& v1, const Vector2D& v2); + friend Vector2D& operator*(Vector2D& v1, const Vector2D& v2); + friend Vector2D& operator/(Vector2D& v1, const Vector2D& v2); + + Vector2D& operator+=(const Vector2D& vec); + Vector2D& operator-=(const Vector2D& vec); + Vector2D& operator*=(const Vector2D& vec); + Vector2D& operator/=(const Vector2D& vec); + + Vector2D& operator*(const int& i); + Vector2D& Zero(); + + friend std::ostream& operator<<(std::ostream& stream, const Vector2D& vec); + +}; + + + +#endif /* SRC_ECS_VECTOR2D_H_ */ diff --git a/src/tileson/tileson.hpp b/src/tileson/tileson.hpp new file mode 100644 index 0000000..9e26d2a --- /dev/null +++ b/src/tileson/tileson.hpp @@ -0,0 +1,8992 @@ +/// +/// T I L E S O N V E R S I O N 1 . 4 . 0 +/// ------------------------------------------------ +/// BSD 2-Clause License +/// +/// Copyright (c) 2022, Robin Berg Pettersen +/// All rights reserved. +/// +/// Redistribution and use in source and binary forms, with or without +/// modification, are permitted provided that the following conditions are met: +/// +/// 1. Redistributions of source code must retain the above copyright notice, this +/// list of conditions and the following disclaimer. +/// +/// 2. Redistributions in binary form must reproduce the above copyright notice, +/// this list of conditions and the following disclaimer in the documentation +/// and/or other materials provided with the distribution. +/// +/// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +/// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +/// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +/// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +/// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +/// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +/// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +/// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +/// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +/// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef TILESON_TILESON_H +#define TILESON_TILESON_H + + +/*** Start of inlined file: json11.hpp ***/ +/*** Start of inlined file: json11.cpp ***/ + +/*** Start of inlined file: json11.hpp ***/ +/* Copyright (c) 2013 Dropbox, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#define JSON11_IS_DEFINED + +#ifdef _MSC_VER +#if _MSC_VER <= 1800 // VS 2013 + #ifndef noexcept + #define noexcept throw() + #endif + + #ifndef snprintf + #define snprintf _snprintf_s + #endif + #endif +#endif + +namespace json11 { + + enum JsonParse { + STANDARD, COMMENTS + }; + + class JsonValue; + + class Json final { + public: + // Types + enum Type { + NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT + }; + + // Array and object typedefs + typedef std::vector array; + typedef std::map object; + + // Constructors for the various types of JSON value. + inline Json() noexcept; // NUL + inline Json(std::nullptr_t) noexcept; // NUL + inline Json(double value); // NUMBER + inline Json(int value); // NUMBER + inline Json(bool value); // BOOL + inline Json(const std::string &value); // STRING + inline Json(std::string &&value); // STRING + inline Json(const char * value); // STRING + inline Json(const array &values); // ARRAY + inline Json(array &&values); // ARRAY + inline Json(const object &values); // OBJECT + inline Json(object &&values); // OBJECT + + // Implicit constructor: anything with a to_json() function. + template + inline Json(const T & t) : Json(t.to_json()) {} + + // Implicit constructor: map-like objects (std::map, std::unordered_map, etc) + template ().begin()->first)>::value + && std::is_constructible().begin()->second)>::value, + int>::type = 0> + inline Json(const M & m) : Json(object(m.begin(), m.end())) {} + + // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc) + template ().begin())>::value, + int>::type = 0> + inline Json(const V & v) : Json(array(v.begin(), v.end())) {} + + // This prevents Json(some_pointer) from accidentally producing a bool. Use + // Json(bool(some_pointer)) if that behavior is desired. + Json(void *) = delete; + + // Accessors + inline Type type() const; + + inline bool is_null() const { return type() == NUL; } + inline bool is_number() const { return type() == NUMBER; } + inline bool is_bool() const { return type() == BOOL; } + inline bool is_string() const { return type() == STRING; } + inline bool is_array() const { return type() == ARRAY; } + inline bool is_object() const { return type() == OBJECT; } + + // Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not + // distinguish between integer and non-integer numbers - number_value() and int_value() + // can both be applied to a NUMBER-typed object. + inline double number_value() const; + inline int int_value() const; + + // Return the enclosed value if this is a boolean, false otherwise. + inline bool bool_value() const; + // Return the enclosed string if this is a string, "" otherwise. + inline const std::string &string_value() const; + // Return the enclosed std::vector if this is an array, or an empty vector otherwise. + inline const array &array_items() const; + // Return the enclosed std::map if this is an object, or an empty map otherwise. + inline const object &object_items() const; + + // Return a reference to arr[i] if this is an array, Json() otherwise. + inline const Json & operator[](size_t i) const; + // Return a reference to obj[key] if this is an object, Json() otherwise. + inline const Json & operator[](const std::string &key) const; + + // Serialize. + inline void dump(std::string &out) const; + inline std::string dump() const { + std::string out; + dump(out); + return out; + } + + // Parse. If parse fails, return Json() and assign an error message to err. + static inline Json parse(const std::string & in, + std::string & err, + JsonParse strategy = JsonParse::STANDARD); + static inline Json parse(const char * in, + std::string & err, + JsonParse strategy = JsonParse::STANDARD) { + if (in) { + return parse(std::string(in), err, strategy); + } else { + err = "null input"; + return nullptr; + } + } + // Parse multiple objects, concatenated or separated by whitespace + static inline std::vector parse_multi( + const std::string & in, + std::string::size_type & parser_stop_pos, + std::string & err, + JsonParse strategy = JsonParse::STANDARD); + + static inline std::vector parse_multi( + const std::string & in, + std::string & err, + JsonParse strategy = JsonParse::STANDARD) { + std::string::size_type parser_stop_pos; + return parse_multi(in, parser_stop_pos, err, strategy); + } + + inline bool operator== (const Json &rhs) const; + inline bool operator< (const Json &rhs) const; + inline bool operator!= (const Json &rhs) const { return !(*this == rhs); } + inline bool operator<= (const Json &rhs) const { return !(rhs < *this); } + inline bool operator> (const Json &rhs) const { return (rhs < *this); } + inline bool operator>= (const Json &rhs) const { return !(*this < rhs); } + + /* has_shape(types, err) + * + * Return true if this is a JSON object and, for each item in types, has a field of + * the given type. If not, return false and set err to a descriptive message. + */ + typedef std::initializer_list> shape; + inline bool has_shape(const shape & types, std::string & err) const; + + private: + std::shared_ptr m_ptr; + }; + +// Internal class hierarchy - JsonValue objects are not exposed to users of this API. + class JsonValue { + protected: + friend class Json; + friend class JsonInt; + friend class JsonDouble; + virtual Json::Type type() const = 0; + virtual bool equals(const JsonValue * other) const = 0; + virtual bool less(const JsonValue * other) const = 0; + virtual void dump(std::string &out) const = 0; + virtual double number_value() const; + virtual int int_value() const; + virtual bool bool_value() const; + virtual const std::string &string_value() const; + virtual const Json::array &array_items() const; + virtual const Json &operator[](size_t i) const; + virtual const Json::object &object_items() const; + virtual const Json &operator[](const std::string &key) const; + virtual ~JsonValue() {} + }; + +} // namespace json11 + +/*** End of inlined file: json11.hpp ***/ + +#include +#include +#include +#include +#include + +namespace json11 { + + static const int max_depth = 200; + + using std::string; + using std::vector; + using std::map; + using std::make_shared; + using std::initializer_list; + using std::move; + +/* Helper for representing null - just a do-nothing struct, plus comparison + * operators so the helpers in JsonValue work. We can't use nullptr_t because + * it may not be orderable. + */ + struct NullStruct { + bool operator==(NullStruct) const { return true; } + bool operator<(NullStruct) const { return false; } + }; + +/* * * * * * * * * * * * * * * * * * * * + * Serialization + */ + + static void dump(NullStruct, string &out) { + out += "null"; + } + + static void dump(double value, string &out) { + if (std::isfinite(value)) { + char buf[32]; + snprintf(buf, sizeof buf, "%.17g", value); + out += buf; + } else { + out += "null"; + } + } + + static void dump(int value, string &out) { + char buf[32]; + snprintf(buf, sizeof buf, "%d", value); + out += buf; + } + + static void dump(bool value, string &out) { + out += value ? "true" : "false"; + } + + static void dump(const string &value, string &out) { + out += '"'; + for (size_t i = 0; i < value.length(); i++) { + const char ch = value[i]; + if (ch == '\\') { + out += "\\\\"; + } else if (ch == '"') { + out += "\\\""; + } else if (ch == '\b') { + out += "\\b"; + } else if (ch == '\f') { + out += "\\f"; + } else if (ch == '\n') { + out += "\\n"; + } else if (ch == '\r') { + out += "\\r"; + } else if (ch == '\t') { + out += "\\t"; + } else if (static_cast(ch) <= 0x1f) { + char buf[8]; + snprintf(buf, sizeof buf, "\\u%04x", ch); + out += buf; + } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 + && static_cast(value[i+2]) == 0xa8) { + out += "\\u2028"; + i += 2; + } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 + && static_cast(value[i+2]) == 0xa9) { + out += "\\u2029"; + i += 2; + } else { + out += ch; + } + } + out += '"'; + } + + static void dump(const Json::array &values, string &out) { + bool first = true; + out += "["; + for (const auto &value : values) { + if (!first) + out += ", "; + value.dump(out); + first = false; + } + out += "]"; + } + + static void dump(const Json::object &values, string &out) { + bool first = true; + out += "{"; + for (const auto &kv : values) { + if (!first) + out += ", "; + dump(kv.first, out); + out += ": "; + kv.second.dump(out); + first = false; + } + out += "}"; + } + + void Json::dump(string &out) const { + m_ptr->dump(out); + } + +/* * * * * * * * * * * * * * * * * * * * + * Value wrappers + */ + + template + class Value : public JsonValue { + protected: + + // Constructors + explicit Value(const T &value) : m_value(value) {} + explicit Value(T &&value) : m_value(move(value)) {} + + // Get type tag + Json::Type type() const override { + return tag; + } + + // Comparisons + bool equals(const JsonValue * other) const override { + return m_value == static_cast *>(other)->m_value; + } + bool less(const JsonValue * other) const override { + return m_value < static_cast *>(other)->m_value; + } + + const T m_value; + void dump(string &out) const override { json11::dump(m_value, out); } + }; + + class JsonDouble final : public Value { + double number_value() const override { return m_value; } + int int_value() const override { return static_cast(m_value); } + bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } + bool less(const JsonValue * other) const override { return m_value < other->number_value(); } + public: + explicit JsonDouble(double value) : Value(value) {} + }; + + class JsonInt final : public Value { + double number_value() const override { return m_value; } + int int_value() const override { return m_value; } + bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } + bool less(const JsonValue * other) const override { return m_value < other->number_value(); } + public: + explicit JsonInt(int value) : Value(value) {} + }; + + class JsonBoolean final : public Value { + bool bool_value() const override { return m_value; } + public: + explicit JsonBoolean(bool value) : Value(value) {} + }; + + class JsonString final : public Value { + const string &string_value() const override { return m_value; } + public: + explicit JsonString(const string &value) : Value(value) {} + explicit JsonString(string &&value) : Value(move(value)) {} + }; + + class JsonArray final : public Value { + const Json::array &array_items() const override { return m_value; } + const Json & operator[](size_t i) const override; + public: + explicit JsonArray(const Json::array &value) : Value(value) {} + explicit JsonArray(Json::array &&value) : Value(move(value)) {} + }; + + class JsonObject final : public Value { + const Json::object &object_items() const override { return m_value; } + const Json & operator[](const string &key) const override; + public: + explicit JsonObject(const Json::object &value) : Value(value) {} + explicit JsonObject(Json::object &&value) : Value(move(value)) {} + }; + + class JsonNull final : public Value { + public: + JsonNull() : Value({}) {} + }; + +/* * * * * * * * * * * * * * * * * * * * + * Static globals - static-init-safe + */ + struct Statics { + const std::shared_ptr null = make_shared(); + const std::shared_ptr t = make_shared(true); + const std::shared_ptr f = make_shared(false); + const string empty_string; + const vector empty_vector; + const map empty_map; + Statics() {} + }; + + static const Statics & statics() { + static const Statics s {}; + return s; + } + + static const Json & static_null() { + // This has to be separate, not in Statics, because Json() accesses statics().null. + static const Json json_null; + return json_null; + } + +/* * * * * * * * * * * * * * * * * * * * + * Constructors + */ + + Json::Json() noexcept : m_ptr(statics().null) {} + Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {} + Json::Json(double value) : m_ptr(make_shared(value)) {} + Json::Json(int value) : m_ptr(make_shared(value)) {} + Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {} + Json::Json(const string &value) : m_ptr(make_shared(value)) {} + Json::Json(string &&value) : m_ptr(make_shared(move(value))) {} + Json::Json(const char * value) : m_ptr(make_shared(value)) {} + Json::Json(const Json::array &values) : m_ptr(make_shared(values)) {} + Json::Json(Json::array &&values) : m_ptr(make_shared(move(values))) {} + Json::Json(const Json::object &values) : m_ptr(make_shared(values)) {} + Json::Json(Json::object &&values) : m_ptr(make_shared(move(values))) {} + +/* * * * * * * * * * * * * * * * * * * * + * Accessors + */ + + inline Json::Type Json::type() const { return m_ptr->type(); } + inline double Json::number_value() const { return m_ptr->number_value(); } + inline int Json::int_value() const { return m_ptr->int_value(); } + inline bool Json::bool_value() const { return m_ptr->bool_value(); } + inline const string & Json::string_value() const { return m_ptr->string_value(); } + inline const vector & Json::array_items() const { return m_ptr->array_items(); } + inline const map & Json::object_items() const { return m_ptr->object_items(); } + inline const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; } + inline const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; } + + inline double JsonValue::number_value() const { return 0; } + inline int JsonValue::int_value() const { return 0; } + inline bool JsonValue::bool_value() const { return false; } + inline const string & JsonValue::string_value() const { return statics().empty_string; } + inline const vector & JsonValue::array_items() const { return statics().empty_vector; } + inline const map & JsonValue::object_items() const { return statics().empty_map; } + inline const Json & JsonValue::operator[] (size_t) const { return static_null(); } + inline const Json & JsonValue::operator[] (const string &) const { return static_null(); } + + inline const Json & JsonObject::operator[] (const string &key) const { + auto iter = m_value.find(key); + return (iter == m_value.end()) ? static_null() : iter->second; + } + inline const Json & JsonArray::operator[] (size_t i) const { + if (i >= m_value.size()) return static_null(); + else return m_value[i]; + } + +/* * * * * * * * * * * * * * * * * * * * + * Comparison + */ + + bool Json::operator== (const Json &other) const { + if (m_ptr == other.m_ptr) + return true; + if (m_ptr->type() != other.m_ptr->type()) + return false; + + return m_ptr->equals(other.m_ptr.get()); + } + + bool Json::operator< (const Json &other) const { + if (m_ptr == other.m_ptr) + return false; + if (m_ptr->type() != other.m_ptr->type()) + return m_ptr->type() < other.m_ptr->type(); + + return m_ptr->less(other.m_ptr.get()); + } + +/* * * * * * * * * * * * * * * * * * * * + * Parsing + */ + +/* esc(c) + * + * Format char c suitable for printing in an error message. + */ + static inline string esc(char c) { + char buf[12]; + if (static_cast(c) >= 0x20 && static_cast(c) <= 0x7f) { + snprintf(buf, sizeof buf, "'%c' (%d)", c, c); + } else { + snprintf(buf, sizeof buf, "(%d)", c); + } + return string(buf); + } + + static inline bool in_range(long x, long lower, long upper) { + return (x >= lower && x <= upper); + } + + namespace { +/* JsonParser + * + * Object that tracks all state of an in-progress parse. + */ + struct JsonParser final { + + /* State + */ + const string &str; + size_t i; + string &err; + bool failed; + const JsonParse strategy; + + /* fail(msg, err_ret = Json()) + * + * Mark this parse as failed. + */ + Json fail(string &&msg) { + return fail(move(msg), Json()); + } + + template + T fail(string &&msg, const T err_ret) { + if (!failed) + err = std::move(msg); + failed = true; + return err_ret; + } + + /* consume_whitespace() + * + * Advance until the current character is non-whitespace. + */ + void consume_whitespace() { + while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t') + i++; + } + + /* consume_comment() + * + * Advance comments (c-style inline and multiline). + */ + bool consume_comment() { + bool comment_found = false; + if (str[i] == '/') { + i++; + if (i == str.size()) + return fail("unexpected end of input after start of comment", false); + if (str[i] == '/') { // inline comment + i++; + // advance until next line, or end of input + while (i < str.size() && str[i] != '\n') { + i++; + } + comment_found = true; + } + else if (str[i] == '*') { // multiline comment + i++; + if (i > str.size()-2) + return fail("unexpected end of input inside multi-line comment", false); + // advance until closing tokens + while (!(str[i] == '*' && str[i+1] == '/')) { + i++; + if (i > str.size()-2) + return fail( + "unexpected end of input inside multi-line comment", false); + } + i += 2; + comment_found = true; + } + else + return fail("malformed comment", false); + } + return comment_found; + } + + /* consume_garbage() + * + * Advance until the current character is non-whitespace and non-comment. + */ + void consume_garbage() { + consume_whitespace(); + if(strategy == JsonParse::COMMENTS) { + bool comment_found = false; + do { + comment_found = consume_comment(); + if (failed) return; + consume_whitespace(); + } + while(comment_found); + } + } + + /* get_next_token() + * + * Return the next non-whitespace character. If the end of the input is reached, + * flag an error and return 0. + */ + char get_next_token() { + consume_garbage(); + if (failed) return static_cast(0); + if (i == str.size()) + return fail("unexpected end of input", static_cast(0)); + + return str[i++]; + } + + /* encode_utf8(pt, out) + * + * Encode pt as UTF-8 and add it to out. + */ + void encode_utf8(long pt, string & out) { + if (pt < 0) + return; + + if (pt < 0x80) { + out += static_cast(pt); + } else if (pt < 0x800) { + out += static_cast((pt >> 6) | 0xC0); + out += static_cast((pt & 0x3F) | 0x80); + } else if (pt < 0x10000) { + out += static_cast((pt >> 12) | 0xE0); + out += static_cast(((pt >> 6) & 0x3F) | 0x80); + out += static_cast((pt & 0x3F) | 0x80); + } else { + out += static_cast((pt >> 18) | 0xF0); + out += static_cast(((pt >> 12) & 0x3F) | 0x80); + out += static_cast(((pt >> 6) & 0x3F) | 0x80); + out += static_cast((pt & 0x3F) | 0x80); + } + } + + /* parse_string() + * + * Parse a string, starting at the current position. + */ + string parse_string() { + string out; + long last_escaped_codepoint = -1; + while (true) { + if (i == str.size()) + return fail("unexpected end of input in string", ""); + + char ch = str[i++]; + + if (ch == '"') { + encode_utf8(last_escaped_codepoint, out); + return out; + } + + if (in_range(ch, 0, 0x1f)) + return fail("unescaped " + esc(ch) + " in string", ""); + + // The usual case: non-escaped characters + if (ch != '\\') { + encode_utf8(last_escaped_codepoint, out); + last_escaped_codepoint = -1; + out += ch; + continue; + } + + // Handle escapes + if (i == str.size()) + return fail("unexpected end of input in string", ""); + + ch = str[i++]; + + if (ch == 'u') { + // Extract 4-byte escape sequence + string esc = str.substr(i, 4); + // Explicitly check length of the substring. The following loop + // relies on std::string returning the terminating NUL when + // accessing str[length]. Checking here reduces brittleness. + if (esc.length() < 4) { + return fail("bad \\u escape: " + esc, ""); + } + for (size_t j = 0; j < 4; j++) { + if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F') + && !in_range(esc[j], '0', '9')) + return fail("bad \\u escape: " + esc, ""); + } + + long codepoint = strtol(esc.data(), nullptr, 16); + + // JSON specifies that characters outside the BMP shall be encoded as a pair + // of 4-hex-digit \u escapes encoding their surrogate pair components. Check + // whether we're in the middle of such a beast: the previous codepoint was an + // escaped lead (high) surrogate, and this is a trail (low) surrogate. + if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF) + && in_range(codepoint, 0xDC00, 0xDFFF)) { + // Reassemble the two surrogate pairs into one astral-plane character, per + // the UTF-16 algorithm. + encode_utf8((((last_escaped_codepoint - 0xD800) << 10) + | (codepoint - 0xDC00)) + 0x10000, out); + last_escaped_codepoint = -1; + } else { + encode_utf8(last_escaped_codepoint, out); + last_escaped_codepoint = codepoint; + } + + i += 4; + continue; + } + + encode_utf8(last_escaped_codepoint, out); + last_escaped_codepoint = -1; + + if (ch == 'b') { + out += '\b'; + } else if (ch == 'f') { + out += '\f'; + } else if (ch == 'n') { + out += '\n'; + } else if (ch == 'r') { + out += '\r'; + } else if (ch == 't') { + out += '\t'; + } else if (ch == '"' || ch == '\\' || ch == '/') { + out += ch; + } else { + return fail("invalid escape character " + esc(ch), ""); + } + } + } + + /* parse_number() + * + * Parse a double. + */ + Json parse_number() { + size_t start_pos = i; + + if (str[i] == '-') + i++; + + // Integer part + if (str[i] == '0') { + i++; + if (in_range(str[i], '0', '9')) + return fail("leading 0s not permitted in numbers"); + } else if (in_range(str[i], '1', '9')) { + i++; + while (in_range(str[i], '0', '9')) + i++; + } else { + return fail("invalid " + esc(str[i]) + " in number"); + } + + if (str[i] != '.' && str[i] != 'e' && str[i] != 'E' + && (i - start_pos) <= static_cast(std::numeric_limits::digits10)) { + return std::atoi(str.c_str() + start_pos); + } + + // Decimal part + if (str[i] == '.') { + i++; + if (!in_range(str[i], '0', '9')) + return fail("at least one digit required in fractional part"); + + while (in_range(str[i], '0', '9')) + i++; + } + + // Exponent part + if (str[i] == 'e' || str[i] == 'E') { + i++; + + if (str[i] == '+' || str[i] == '-') + i++; + + if (!in_range(str[i], '0', '9')) + return fail("at least one digit required in exponent"); + + while (in_range(str[i], '0', '9')) + i++; + } + + return std::strtod(str.c_str() + start_pos, nullptr); + } + + /* expect(str, res) + * + * Expect that 'str' starts at the character that was just read. If it does, advance + * the input and return res. If not, flag an error. + */ + Json expect(const string &expected, Json res) { + assert(i != 0); + i--; + if (str.compare(i, expected.length(), expected) == 0) { + i += expected.length(); + return res; + } else { + return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length())); + } + } + + /* parse_json() + * + * Parse a JSON object. + */ + Json parse_json(int depth) { + if (depth > max_depth) { + return fail("exceeded maximum nesting depth"); + } + + char ch = get_next_token(); + if (failed) + return Json(); + + if (ch == '-' || (ch >= '0' && ch <= '9')) { + i--; + return parse_number(); + } + + if (ch == 't') + return expect("true", true); + + if (ch == 'f') + return expect("false", false); + + if (ch == 'n') + return expect("null", Json()); + + if (ch == '"') + return parse_string(); + + if (ch == '{') { + map data; + ch = get_next_token(); + if (ch == '}') + return data; + + while (1) { + if (ch != '"') + return fail("expected '\"' in object, got " + esc(ch)); + + string key = parse_string(); + if (failed) + return Json(); + + ch = get_next_token(); + if (ch != ':') + return fail("expected ':' in object, got " + esc(ch)); + + data[std::move(key)] = parse_json(depth + 1); + if (failed) + return Json(); + + ch = get_next_token(); + if (ch == '}') + break; + if (ch != ',') + return fail("expected ',' in object, got " + esc(ch)); + + ch = get_next_token(); + } + return data; + } + + if (ch == '[') { + vector data; + ch = get_next_token(); + if (ch == ']') + return data; + + while (1) { + i--; + data.push_back(parse_json(depth + 1)); + if (failed) + return Json(); + + ch = get_next_token(); + if (ch == ']') + break; + if (ch != ',') + return fail("expected ',' in list, got " + esc(ch)); + + ch = get_next_token(); + (void)ch; + } + return data; + } + + return fail("expected value, got " + esc(ch)); + } + }; + }//namespace { + + Json Json::parse(const string &in, string &err, JsonParse strategy) { + JsonParser parser { in, 0, err, false, strategy }; + Json result = parser.parse_json(0); + + // Check for any trailing garbage + parser.consume_garbage(); + if (parser.failed) + return Json(); + if (parser.i != in.size() && + ((parser.i + 1) != in.size() && in[parser.i] != 0)) //RBP: If there is only 1 character diff, it is probably just a terminating zero from a memory read. + { + return parser.fail("unexpected trailing " + esc(in[parser.i])); + } + return result; + } + +// Documented in json11.hpp + vector Json::parse_multi(const string &in, + std::string::size_type &parser_stop_pos, + string &err, + JsonParse strategy) { + JsonParser parser { in, 0, err, false, strategy }; + parser_stop_pos = 0; + vector json_vec; + while (parser.i != in.size() && !parser.failed) { + json_vec.push_back(parser.parse_json(0)); + if (parser.failed) + break; + + // Check for another object + parser.consume_garbage(); + if (parser.failed) + break; + parser_stop_pos = parser.i; + } + return json_vec; + } + +/* * * * * * * * * * * * * * * * * * * * + * Shape-checking + */ + + bool Json::has_shape(const shape & types, string & err) const { + if (!is_object()) { + err = "expected JSON object, got " + dump(); + return false; + } + + const auto& obj_items = object_items(); + for (auto & item : types) { + const auto it = obj_items.find(item.first); + if (it == obj_items.cend() || it->second.type() != item.second) { + err = "bad type for " + item.first + " in " + dump(); + return false; + } + } + + return true; + } + +} // namespace json11 + +/*** End of inlined file: json11.cpp ***/ + +/*** End of inlined file: json11.hpp ***/ + + +/*** Start of inlined file: tileson_parser.hpp ***/ +// +// Created by robin on 22.03.2020. +// + +#ifndef TILESON_TILESON_PARSER_HPP +#define TILESON_TILESON_PARSER_HPP + +//RBP: FS-namespace is defined in tileson_parser now! +#if _MSC_VER && !__INTEL_COMPILER + #include + namespace fs = std::filesystem; +#elif __MINGW64__ + #if __MINGW64_VERSION_MAJOR > 6 + #include + namespace fs = std::filesystem; + #else + #include + namespace fs = std::experimental::filesystem; + #endif +#elif __clang__ + #if __clang_major__ < 8 + #include + namespace fs = std::experimental::filesystem; + #else + #include + namespace fs = std::filesystem; + #endif +#else //Linux + #if __GNUC__ < 8 //GCC major version less than 8 + #include + namespace fs = std::experimental::filesystem; + #else + #include + namespace fs = std::filesystem; + #endif +#endif + +#include +#include +#include + + +/*** Start of inlined file: Tools.hpp ***/ +// +// Created by robin on 31.07.2020. +// + +#ifndef TILESON_TOOLS_HPP +#define TILESON_TOOLS_HPP + +#include +#include +#include +namespace tson +{ + class Tools + { + + public: + Tools() = delete; + ~Tools() = delete; + inline static std::vector Base64DecodedStringToBytes(std::string_view str); + inline static std::vector BytesToUnsignedInts(const std::vector &bytes); + inline static std::vector SplitString(const std::string &s, char delim); + inline static bool Equal(float a, float b, float precision = 8192.f); + + private: + template + static void split(const std::string &s, char delim, Out result) + { + std::stringstream ss; + ss.str(s); + std::string item; + + while (std::getline(ss, item, delim)) + { + *(result++) = item; + } + } + }; + + /*! + * When you have decoded a Base64 string, you'll get a string representing bytes. This function turns them into actual bytes. + * @param str + * @return + */ + std::vector Tools::Base64DecodedStringToBytes(std::string_view str) + { + std::vector bytes; + for(size_t i = 0; i < str.size(); ++i) + { + uint8_t u8 = static_cast(str[i]); + bytes.push_back(u8); + } + return bytes; + } + + /*! + * Converts bytes into unsigned int values. The bytes are converted in the Little Endian byte order to fit Tiled's specs. + * @param bytes A vector of bytes. + * @return Bytes converted to unsigned ints + */ + std::vector Tools::BytesToUnsignedInts(const std::vector &bytes) + { + std::vector uints; + std::vector toConvert; + //uint32_t size8 = (compressed[55] << 24) | (compressed[56] << 16) | (compressed[57] << 8) | compressed[58]; //Should be 66000 + + for(size_t i = 0; i < bytes.size(); ++i) + { + toConvert.push_back(bytes[i]); + if(toConvert.size() == 4) + { + uint32_t u32 = (toConvert[3] << 24) | (toConvert[2] << 16) | (toConvert[1] << 8) | toConvert[0]; + uints.push_back(u32); + toConvert.clear(); + } + } + + return uints; + } + + std::vector Tools::SplitString(const std::string &s, char delim) + { + std::vector elems; + split(s, delim, std::back_inserter(elems)); + return elems; + } + + /*! + * Uses a threshold for comparing floats, as they are not precise in many cases. + * @param a + * @param b + * @return true if equal based on the currently defined precision + */ + bool Tools::Equal(float a, float b, float precision) + { + float threshold = 1.f / precision; + float diff = fabsf(a - b); + return diff <= threshold; + } +} + +#endif //TILESON_TOOLS_HPP + +/*** End of inlined file: Tools.hpp ***/ + + +/*** Start of inlined file: Base64Decompressor.hpp ***/ +// +// Created by robin on 29.07.2020. +// The Base64 decoding logic is heavily based on: https://github.com/ReneNyffenegger/cpp-base64 +// + +#ifndef TILESON_BASE64DECOMPRESSOR_HPP +#define TILESON_BASE64DECOMPRESSOR_HPP + + +/*** Start of inlined file: IDecompressor.hpp ***/ +// +// Created by robin on 29.07.2020. +// + +#ifndef TILESON_IDECOMPRESSOR_HPP +#define TILESON_IDECOMPRESSOR_HPP + +#include + +namespace tson +{ + template + class IDecompressor + { + public: + /*! + * If the name matches with 'compression' or 'encoding' the decompress() function will + * be called automatically for the actual Layer. Encoding-related matching is handled first! + * + * Known values: + * + * compression: zlib, gzip, zstd (since Tiled 1.3) or empty (default) (tilelayer only). + * encoding: csv (default) or base64 (tilelayer only). + * + * @return + */ + [[nodiscard]] virtual const std::string &name() const = 0; + + /*! + * Used primarily for Tiled related decompression. + * @param input Input data + * @return Decompressed data + */ + virtual TOut decompress(const TIn &input) = 0; + + /*! + * Used for whole file decompression. Not related to Tiled + * @param path + * @return + */ + virtual TOut decompressFile(const fs::path &path) = 0; + + /*! + * Used for whole file decompression. Not related to Tiled + * @param path + * @return + */ + virtual TOut decompress(const void *data, size_t size) = 0; + + /*! + * Pure virtual class needs virtual destructor so derived classes can call their own destructors + */ + virtual ~IDecompressor() = default; + }; +} + +#endif //TILESON_IDECOMPRESSOR_HPP + +/*** End of inlined file: IDecompressor.hpp ***/ + +#include + +namespace tson +{ + class Base64Decompressor : public IDecompressor + { + public: + [[nodiscard]] inline const std::string &name() const override; + + inline std::string decompress(const std::string_view &s) override; + + inline std::string decompressFile(const fs::path &path) override; + inline std::string decompress(const void *data, size_t size) override; + + private: + inline unsigned int pos_of_char(const unsigned char chr); + inline static const std::string NAME = "base64"; + }; + + const std::string &Base64Decompressor::name() const + { + return NAME; + } + + std::string Base64Decompressor::decompress(const std::string_view &s) + { + + size_t length_of_string = s.length(); + if (!length_of_string) return std::string(""); + + size_t in_len = length_of_string; + size_t pos = 0; + + // + // The approximate length (bytes) of the decoded string might be one ore + // two bytes smaller, depending on the amount of trailing equal signs + // in the encoded string. This approximation is needed to reserve + // enough space in the string to be returned. + // + size_t approx_length_of_decoded_string = length_of_string / 4 * 3; + std::string ret; + ret.reserve(approx_length_of_decoded_string); + + while (pos < in_len) { + + unsigned int pos_of_char_1 = pos_of_char(s[pos+1] ); + + ret.push_back(static_cast( ( (pos_of_char(s[pos+0]) ) << 2 ) + ( (pos_of_char_1 & 0x30 ) >> 4))); + + if (s[pos+2] != '=' && s[pos+2] != '.') { // accept URL-safe base 64 strings, too, so check for '.' also. + + unsigned int pos_of_char_2 = pos_of_char(s[pos+2] ); + ret.push_back(static_cast( (( pos_of_char_1 & 0x0f) << 4) + (( pos_of_char_2 & 0x3c) >> 2))); + + if (s[pos+3] != '=' && s[pos+3] != '.') { + ret.push_back(static_cast( ( (pos_of_char_2 & 0x03 ) << 6 ) + pos_of_char(s[pos+3]) )); + } + } + + pos += 4; + } + + return ret; + } + + unsigned int Base64Decompressor::pos_of_char(const unsigned char chr) + { + // + // Return the position of chr within base64_encode() + // + + if (chr >= 'A' && chr <= 'Z') return chr - 'A'; + else if (chr >= 'a' && chr <= 'z') return chr - 'a' + ('Z' - 'A') + 1; + else if (chr >= '0' && chr <= '9') return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2; + else if (chr == '+' || chr == '-') return 62; // Be liberal with input and accept both url ('-') and non-url ('+') base 64 characters ( + else if (chr == '/' || chr == '_') return 63; // Ditto for '/' and '_' + + throw "If input is correct, this line should never be reached."; + } + + /*! + * UNUSED! Does nothing + * @param path + * @return + */ + std::string Base64Decompressor::decompressFile(const fs::path &) + { + return std::string(); + } + + /*! + * UNUSED! Does nothing + * @param path + * @return + */ + std::string Base64Decompressor::decompress(const void *, size_t) + { + return std::string(); + } +} + +#endif //TILESON_BASE64DECOMPRESSOR_HPP + +/*** End of inlined file: Base64Decompressor.hpp ***/ + + +/*** Start of inlined file: Lzma.hpp ***/ +// +// Created by robin on 16.01.2021. +// +//#include "../../extras/pocketlzma.hpp" +#ifdef POCKETLZMA_POCKETLZMA_H + +#ifndef TILESON_LZMA_HPP +#define TILESON_LZMA_HPP + +namespace tson +{ + class Lzma : public IDecompressor, std::vector> + { + public: + inline const std::string &name() const override + { + return NAME; + } + + inline std::vector decompress(const std::vector &input) override + { + std::vector out; + + plz::PocketLzma p; + plz::StatusCode status = p.decompress(input, out); + + if(status != plz::StatusCode::Ok) + return std::vector(); + + return out; + } + + inline std::vector decompressFile(const fs::path &path) override + { + std::vector in; + std::vector out; + + plz::PocketLzma p; + plz::FileStatus fileStatus = plz::File::FromFile(path.generic_string(), in); + if(fileStatus.status() != plz::FileStatus::Code::Ok) + return std::vector(); + + plz::StatusCode status = p.decompress(in, out); + + if(status != plz::StatusCode::Ok) + return std::vector(); + + return out; + } + + inline std::vector decompress(const void *data, size_t size) override + { + std::vector out; + + plz::PocketLzma p; + plz::StatusCode status = p.decompress((uint8_t*) data, size, out); + + if(status != plz::StatusCode::Ok) + return std::vector(); + + return out; + } + + private: + inline static const std::string NAME {"lzma"}; + }; +} + +#endif //TILESON_LZMA_HPP + +#endif +/*** End of inlined file: Lzma.hpp ***/ + + +/*** Start of inlined file: DecompressorContainer.hpp ***/ +// +// Created by robin on 30.07.2020. +// + +#ifndef TILESON_DECOMPRESSORCONTAINER_HPP +#define TILESON_DECOMPRESSORCONTAINER_HPP + +#include +#include +#include +#include +namespace tson +{ + class DecompressorContainer + { + public: + inline DecompressorContainer() = default; + template + inline void add(Args &&... args); + inline void remove(std::string_view name); + inline bool contains(std::string_view name) const; + inline bool empty() const; + inline size_t size() const; + inline void clear(); + + inline IDecompressor *get(std::string_view name); + private: + //Key: name, + std::vector>> m_decompressors; + }; + + template + void DecompressorContainer::add(Args &&... args) + { + m_decompressors.emplace_back(new T(args...)); + } + + /*! + * + * @param name The name of the decompressor to check whether exists. + * @return Whether a decompressor with the given name exists or not. + */ + bool DecompressorContainer::contains(std::string_view name) const + { + auto iter = std::find_if(m_decompressors.begin(), m_decompressors.end(), [&](const auto &item) + { + return item->name() == name; + }); + + return iter != m_decompressors.end(); + } + + /*! + * Removed an element with the given name. + * @param name The name of the decompressor + */ + void DecompressorContainer::remove(std::string_view name) + { + auto iter = std::remove_if(m_decompressors.begin(), m_decompressors.end(), [&](const auto &item) + { + return item->name() == name; + }); + m_decompressors.erase(iter); + } + + size_t DecompressorContainer::size() const + { + return m_decompressors.size(); + } + + /*! + * + * @param name The name of the container + * @return An ICompressor pointer if it exists. nullptr otherwise. + */ + IDecompressor *DecompressorContainer::get(std::string_view name) + { + auto iter = std::find_if(m_decompressors.begin(), m_decompressors.end(), [&](const auto &item) + { + return item->name() == name; + }); + + return (iter != m_decompressors.end()) ? iter->get() : nullptr; + } + + /*! + * Check if container is empty + * @return Whether or not the container is empty + */ + bool DecompressorContainer::empty() const + { + return m_decompressors.empty(); + } + + /*! + * Clears all IDecompressor elements in the container + */ + void DecompressorContainer::clear() + { + m_decompressors.clear(); + } +} +#endif //TILESON_DECOMPRESSORCONTAINER_HPP + +/*** End of inlined file: DecompressorContainer.hpp ***/ + + +/*** Start of inlined file: MemoryStream.hpp ***/ +// +// Created by robin on 22.03.2020. +// + +#ifndef TILESON_MEMORYSTREAM_HPP +#define TILESON_MEMORYSTREAM_HPP + + +/*** Start of inlined file: MemoryBuffer.hpp ***/ +// +// Created by robin on 22.03.2020. +// + +#ifndef TILESON_MEMORYBUFFER_HPP +#define TILESON_MEMORYBUFFER_HPP + +#include + +namespace tson +{ + class MemoryBuffer : public std::basic_streambuf { + public: + MemoryBuffer(const uint8_t *p, size_t l) { + setg((char*)p, (char*)p, (char*)p + l); + } + }; +} + +#endif //TILESON_MEMORYBUFFER_HPP + +/*** End of inlined file: MemoryBuffer.hpp ***/ + +namespace tson +{ + class MemoryStream : public std::istream { + public: + MemoryStream(const uint8_t *p, size_t l) : + std::istream(&m_buffer), + m_buffer(p, l) { + rdbuf(&m_buffer); + } + + private: + MemoryBuffer m_buffer; + }; +} + +#endif //TILESON_MEMORYSTREAM_HPP + +/*** End of inlined file: MemoryStream.hpp ***/ + + +/*** Start of inlined file: Map.hpp ***/ +// +// Created by robin on 22.03.2020. +// + +#ifndef TILESON_MAP_HPP +#define TILESON_MAP_HPP + + +/*** Start of inlined file: Color.hpp ***/ +// +// Created by robin on 09.08.2019. +// + +#ifndef TILESON_COLOR_HPP +#define TILESON_COLOR_HPP + +#include +#include +#include + +namespace tson +{ + + template + class Color + { + + public: + /*! + * Parses color from Tiled's own color format, which is #aarrggbb in hex format or optionally #rrggbb. + * @param color Color in "#rrggbbaa" hex format. + * @example "#ffaa07ff" and "#aa07ff". In cases where alpha is not a value, it is set to 255. + */ + inline explicit Color(const std::string &color) + { + parseHexString(color); + } + inline Color(T red, T green, T blue, T alpha); + inline Color() { r = g = b = 0; a = 255; } + + inline bool operator==(const Color &rhs) const; + inline bool operator==(const std::string &rhs) const; + inline bool operator!=(const Color &rhs) const; + + inline Color asFloat(); + inline Color asInt(); + + /*! Red */ + T r; + /*! Green */ + T g; + /*! Blue */ + T b; + /*! Alpha */ + T a; + + private: + void parseHexString(const std::string &color) + { + if constexpr (std::is_same::value) + { + if (color.size() == 9) + { + a = (float) std::stoi(color.substr(1, 2), nullptr, 16) / 255; + r = (float) std::stoi(color.substr(3, 2), nullptr, 16) / 255; + g = (float) std::stoi(color.substr(5, 2), nullptr, 16) / 255; + b = (float) std::stoi(color.substr(7, 2), nullptr, 16) / 255; + } + else if (color.size() == 7) + { + r = (float) std::stoi(color.substr(1, 2), nullptr, 16) / 255; + g = (float) std::stoi(color.substr(3, 2), nullptr, 16) / 255; + b = (float) std::stoi(color.substr(5, 2), nullptr, 16) / 255; + a = 1.f; + } + } + else if constexpr (std::is_same::value) + { + if (color.size() == 9) + { + a = static_cast(std::stoi(color.substr(1, 2), nullptr, 16)); + r = static_cast(std::stoi(color.substr(3, 2), nullptr, 16)); + g = static_cast(std::stoi(color.substr(5, 2), nullptr, 16)); + b = static_cast(std::stoi(color.substr(7, 2), nullptr, 16)); + } + else if (color.size() == 7) + { + r = static_cast(std::stoi(color.substr(1, 2), nullptr, 16)); + g = static_cast(std::stoi(color.substr(3, 2), nullptr, 16)); + b = static_cast(std::stoi(color.substr(5, 2), nullptr, 16)); + a = 255; + } + } + } + + }; + + typedef Color Colori; + typedef Color Colorf; + + /*! + * Gets the Color as a float. Only useful if the template related to the current color is NOT float + * @tparam T The template type + * @return If the T type is float, the value will be returned as a copy of itself. Else: All values will be divided by 255 + * before returning. + */ + template + tson::Colorf Color::asFloat() + { + if constexpr (std::is_same::value) + *this; + else + return tson::Colorf((float) r / 255, (float) g / 255, (float) b / 255, (float) a / 255); + } + + /*! + * Gets the Color as an 32-bit variable, where each channel is 8-bit. + * Only useful if the template related to the current color is NOT already 8-bit int + * @tparam T The template type + * @return If the T type is float, the value of each channel will be multiplied by 255. Else: The value will be returned as a copy of itself. + */ + template + tson::Colori Color::asInt() + { + if constexpr (std::is_same::value) + return tson::Colori(static_cast((float) r * 255), + static_cast((float) g * 255), + static_cast((float) b * 255), + static_cast((float) a * 255)); + else + *this; + } + + /*! + * Create a new color in rgba (red, green, blue, alpha) format + * @tparam T the template type for each channel. Usually uint8_t (8-bit int) or float. + * @param red Red channel + * @param green Green channel + * @param blue Blue channel + * @param alpha Alpha channel + */ + template + Color::Color(T red, T green, T blue, T alpha) + { + r = red; + g = green; + b = blue; + a = alpha; + } + + template + bool Color::operator==(const std::string &rhs) const { + Color other {rhs}; + return *this == other; + } + + template + bool Color::operator==(const Color &rhs) const + { + return r == rhs.r && + g == rhs.g && + b == rhs.b && + a == rhs.a; + } + + template + bool Color::operator!=(const Color &rhs) const + { + return !(rhs == *this); + } + +} + +#endif //TILESON_COLOR_HPP + +/*** End of inlined file: Color.hpp ***/ + + +/*** Start of inlined file: Vector2.hpp ***/ +// +// Created by robin on 31.07.2019. +// + +#ifndef TILESON_VECTOR2_HPP +#define TILESON_VECTOR2_HPP + +namespace tson +{ + template + class Vector2 + { + + public: + inline Vector2(T xPos, T yPos); + inline Vector2() { x = y = 0; } + + inline bool operator==(const Vector2 &rhs) const; + inline bool operator!=(const Vector2 &rhs) const; + + T x; + T y; + }; + + /*! + * + * @tparam T template type + * @param xPos x-position + * @param yPos y-position + */ + template + Vector2::Vector2(T xPos, T yPos) + { + x = xPos; + y = yPos; + } + + template + bool Vector2::operator==(const Vector2 &rhs) const + { + return x == rhs.x && + y == rhs.y; + } + + template + bool Vector2::operator!=(const Vector2 &rhs) const + { + return !(rhs == *this); + } + + typedef Vector2 Vector2i; + typedef Vector2 Vector2f; +} + +#endif //TILESON_VECTOR2_HPP + +/*** End of inlined file: Vector2.hpp ***/ + +//#include "../external/json.hpp" + +/*** Start of inlined file: IJson.hpp ***/ +// +// Created by robin on 06.01.2021. +// + +#ifndef TILESON_IJSON_HPP +#define TILESON_IJSON_HPP + +namespace tson +{ + class IJson + { + public: + + virtual IJson& operator[](std::string_view key) = 0; + virtual IJson &at(std::string_view key) = 0; + virtual IJson &at(size_t pos) = 0; + /*! + * If current json object is an array, this will get all elements of it! + * @return An array + */ + [[nodiscard]] virtual std::vector> array() = 0; + [[nodiscard]] virtual std::vector> &array(std::string_view key) = 0; + /*! + * Get the size of an object. This will be equal to the number of + * variables an object contains. + * @return + */ + [[nodiscard]] virtual size_t size() const = 0; + [[nodiscard]] virtual bool parse(const fs::path &path) = 0; + [[nodiscard]] virtual bool parse(const void *data, size_t size) = 0; + + template + [[nodiscard]] T get(std::string_view key); + template + [[nodiscard]] T get(); + [[nodiscard]] virtual size_t count(std::string_view key) const = 0; + [[nodiscard]] virtual bool any(std::string_view key) const = 0; + [[nodiscard]] virtual bool isArray() const = 0; + [[nodiscard]] virtual bool isObject() const = 0; + [[nodiscard]] virtual bool isNull() const = 0; + + /*! + * Get the directory where the json was loaded. + * Only assigned if json is parsed by file. + * @return + */ + [[nodiscard]] virtual fs::path directory() const = 0; + virtual void directory(const fs::path &directory) = 0; + + /*! + * Pure virtual class needs virtual destructor so derived classes can call their own destructors + */ + virtual ~IJson() = default; + + protected: + [[nodiscard]] virtual int32_t getInt32(std::string_view key) = 0; + [[nodiscard]] virtual uint32_t getUInt32(std::string_view key) = 0; + [[nodiscard]] virtual int64_t getInt64(std::string_view key) = 0; + [[nodiscard]] virtual uint64_t getUInt64(std::string_view key) = 0; + [[nodiscard]] virtual double getDouble(std::string_view key) = 0; + [[nodiscard]] virtual float getFloat(std::string_view key) = 0; + [[nodiscard]] virtual std::string getString(std::string_view key) = 0; + [[nodiscard]] virtual bool getBool(std::string_view key) = 0; + + [[nodiscard]] virtual int32_t getInt32() = 0; + [[nodiscard]] virtual uint32_t getUInt32() = 0; + [[nodiscard]] virtual int64_t getInt64() = 0; + [[nodiscard]] virtual uint64_t getUInt64() = 0; + [[nodiscard]] virtual double getDouble() = 0; + [[nodiscard]] virtual float getFloat() = 0; + [[nodiscard]] virtual std::string getString() = 0; + [[nodiscard]] virtual bool getBool() = 0; + }; + + template + T IJson::get(std::string_view key) + { + if constexpr (std::is_same::value) + return getDouble(key); + if constexpr (std::is_same::value) + return getFloat(key); + else if constexpr (std::is_same::value) + return getInt32(key); + else if constexpr (std::is_same::value) + return getUInt32(key); + else if constexpr (std::is_same::value) + return getInt64(key); + else if constexpr (std::is_same::value) + return getUInt64(key); + else if constexpr (std::is_same::value) + return getString(key); + else if constexpr (std::is_same::value) + return getBool(key); + else + return nullptr; + } + + template + T IJson::get() + { + if constexpr (std::is_same::value) + return getDouble(); + if constexpr (std::is_same::value) + return getFloat(); + else if constexpr (std::is_same::value) + return getInt32(); + else if constexpr (std::is_same::value) + return getUInt32(); + else if constexpr (std::is_same::value) + return getInt64(); + else if constexpr (std::is_same::value) + return getUInt64(); + else if constexpr (std::is_same::value) + return getString(); + else if constexpr (std::is_same::value) + return getBool(); + else + return nullptr; + } + +} + +#endif //TILESON_IJSON_HPP + +/*** End of inlined file: IJson.hpp ***/ + + + +/*** Start of inlined file: NlohmannJson.hpp ***/ +// +// Created by robin on 08.01.2021. +// + +#ifdef INCLUDE_NLOHMANN_JSON_HPP_ + +#ifndef TILESON_NLOHMANNJSON_HPP +#define TILESON_NLOHMANNJSON_HPP + +namespace tson +{ + class NlohmannJson : public tson::IJson + { + public: + inline NlohmannJson() = default; + + IJson &operator[](std::string_view key) override + { + if(m_arrayCache.count(key.data()) == 0) + m_arrayCache[key.data()] = std::make_unique(&m_json->operator[](key.data()));//.front()); + + return *m_arrayCache[key.data()].get(); + } + + inline explicit NlohmannJson(nlohmann::json *json) : m_json {json} + { + + } + + inline IJson& at(std::string_view key) override + { + if(m_arrayCache.count(key.data()) == 0) + m_arrayCache[key.data()] = std::make_unique(&m_json->operator[](key.data()));//.front()); + + return *m_arrayCache[key.data()].get(); + } + + inline IJson& at(size_t pos) override + { + if(m_arrayPosCache.count(pos) == 0) + m_arrayPosCache[pos] = std::make_unique(&m_json->at(pos)); + + return *m_arrayPosCache[pos]; + } + + std::vector> array() override + { + std::vector> vec; + for(auto &item : *m_json) + { + nlohmann::json *ptr = &item; + vec.emplace_back(std::make_unique(ptr)); + } + + return vec; + } + + inline std::vector> &array(std::string_view key) override + { + if(m_arrayListDataCache.count(key.data()) == 0) + { + if (m_json->count(key.data()) > 0 && m_json->operator[](key.data()).is_array()) + { + std::for_each(m_json->operator[](key.data()).begin(), m_json->operator[](key.data()).end(), [&](nlohmann::json &item) + { + nlohmann::json *ptr = &item; + m_arrayListDataCache[key.data()].emplace_back(std::make_unique(ptr)); + }); + } + } + + return m_arrayListDataCache[key.data()]; + } + + [[nodiscard]] inline size_t size() const override + { + return m_json->size(); + } + + inline bool parse(const fs::path &path) override + { + clearCache(); + m_data = nullptr; + m_json = nullptr; + if (fs::exists(path) && fs::is_regular_file(path)) + { + m_path = path.parent_path(); + m_data = std::make_unique(); + std::ifstream i(path.generic_string()); + try + { + i >> *m_data; + m_json = m_data.get(); + } + catch (const nlohmann::json::parse_error &error) + { + std::string message = "Parse error: "; + message += std::string(error.what()); + message += std::string("\n"); + std::cerr << message; + return false; + } + return true; + } + return false; + } + + inline bool parse(const void *data, size_t size) override + { + clearCache(); + m_json = nullptr; + m_data = std::make_unique(); + tson::MemoryStream mem{(uint8_t *) data, size}; + try + { + mem >> *m_data; + m_json = m_data.get(); + } + catch (const nlohmann::json::parse_error &error) + { + std::string message = "Parse error: "; + message += std::string(error.what()); + message += std::string("\n"); + std::cerr << message; + return false; + } + return true; + } + + [[nodiscard]] inline size_t count(std::string_view key) const override + { + return m_json->count(key); + } + + [[nodiscard]] inline bool any(std::string_view key) const override + { + return count(key) > 0; + } + + [[nodiscard]] inline bool isArray() const override + { + return m_json->is_array(); + } + + [[nodiscard]] inline bool isObject() const override + { + return m_json->is_object(); + } + + [[nodiscard]] inline bool isNull() const override + { + return m_json->is_null(); + } + + fs::path directory() const override + { + return m_path; + } + + void directory(const fs::path &directory) override + { + m_path = directory; + } + + protected: + [[nodiscard]] inline int32_t getInt32(std::string_view key) override + { + return m_json->operator[](key.data()).get(); + } + + [[nodiscard]] inline uint32_t getUInt32(std::string_view key) override + { + return m_json->operator[](key.data()).get(); + } + + [[nodiscard]] inline int64_t getInt64(std::string_view key) override + { + return m_json->operator[](key.data()).get(); + } + + [[nodiscard]] inline uint64_t getUInt64(std::string_view key) override + { + return m_json->operator[](key.data()).get(); + } + + [[nodiscard]] inline double getDouble(std::string_view key) override + { + return m_json->operator[](key.data()).get(); + } + + [[nodiscard]] inline std::string getString(std::string_view key) override + { + return m_json->operator[](key.data()).get(); + } + + [[nodiscard]] inline bool getBool(std::string_view key) override + { + return m_json->operator[](key.data()).get(); + } + + [[nodiscard]] float getFloat(std::string_view key) override + { + return m_json->operator[](key.data()).get(); + } + + [[nodiscard]] inline int32_t getInt32() override + { + return m_json->get(); + } + + [[nodiscard]] inline uint32_t getUInt32() override + { + return m_json->get(); + } + + [[nodiscard]] inline int64_t getInt64() override + { + return m_json->get(); + } + + [[nodiscard]] inline uint64_t getUInt64() override + { + return m_json->get(); + } + + [[nodiscard]] inline double getDouble() override + { + return m_json->get(); + } + + [[nodiscard]] inline std::string getString() override + { + return m_json->get(); + } + + [[nodiscard]] inline bool getBool() override + { + return m_json->get(); + } + + [[nodiscard]] float getFloat() override + { + return m_json->get(); + } + + private: + inline void clearCache() + { + m_arrayCache.clear(); + m_arrayPosCache.clear(); + m_arrayListDataCache.clear(); + } + + nlohmann::json *m_json = nullptr; + std::unique_ptr m_data = nullptr; //Only used if this is the owner json! + fs::path m_path; + + //Cache! + std::map> m_arrayCache; + std::map> m_arrayPosCache; + std::map>> m_arrayListDataCache; + + }; +} +#endif //TILESON_NLOHMANNJSON_HPP + +#endif //INCLUDE_NLOHMANN_JSON_HPP_ +/*** End of inlined file: NlohmannJson.hpp ***/ + + +/*** Start of inlined file: PicoJson.hpp ***/ +// +// Created by robin on 11.01.2021. +// + +#ifdef picojson_h +#ifndef TILESON_PICOJSON_HPP +#define TILESON_PICOJSON_HPP + +namespace tson +{ + class PicoJson : public tson::IJson + { + public: + inline PicoJson() = default; + + IJson &operator[](std::string_view key) override + { + if(m_arrayCache.count(key.data()) == 0) + { + if(m_json->is()) + { + picojson::object &o = m_json->get(); + m_arrayCache[key.data()] = std::make_unique(&o[key.data()]); + } + } + + return *m_arrayCache[key.data()].get(); + } + + inline explicit PicoJson(picojson::value *json) : m_json {json} + { + + } + + inline IJson& at(std::string_view key) override + { + if(m_arrayCache.count(key.data()) == 0) + { + if(m_json->is()) + { + picojson::object &o = m_json->get(); + m_arrayCache[key.data()] = std::make_unique(&o[key.data()]); + } + } + return *m_arrayCache[key.data()].get(); + } + + inline IJson& at(size_t pos) override + { + if(m_arrayPosCache.count(pos) == 0) + { + picojson::array &a = m_json->get(); + m_arrayPosCache[pos] = std::make_unique(&a.at(pos)); + } + + return *m_arrayPosCache[pos]; + } + + std::vector> array() override + { + std::vector> vec; + if(m_json->is()) + { + picojson::array &a = m_json->get(); + for (auto &item : a) + { + picojson::value *ptr = &item; + vec.emplace_back(std::make_unique(ptr)); + } + } + + return vec; + } + + inline std::vector> &array(std::string_view key) override + { + if(m_arrayListDataCache.count(key.data()) == 0) + { + if(count(key.data()) > 0) + { + if (isObject()) + { + picojson::object &obj = m_json->get(); + picojson::value &v = obj.at(key.data()); + bool isArray = v.is(); + if (isArray) + { + picojson::array &a = v.get(); + + std::for_each(a.begin(), a.end(), [&](picojson::value &item) + { + picojson::value *ptr = &item; + m_arrayListDataCache[key.data()].emplace_back(std::make_unique(ptr)); + }); + } + } + } + } + + return m_arrayListDataCache[key.data()]; + } + + [[nodiscard]] inline size_t size() const override + { + if (m_json->is()) + { + picojson::object obj = m_json->get(); + return obj.size(); + } + return 0; + } + + inline bool parse(const fs::path &path) override + { + clearCache(); + m_data = nullptr; + m_json = nullptr; + if (fs::exists(path) && fs::is_regular_file(path)) + { + m_path = path.parent_path(); + m_data = std::make_unique(); + std::ifstream i(path.generic_string()); + try + { + std::string error = picojson::parse(*m_data, i); + if(!error.empty()) + { + std::cerr << "PicoJson parse error: " << error << "\n"; + return false; + } + //i >> *m_data; + m_json = m_data.get(); + } + catch (const std::exception &error) + { + std::string message = "Parse error: "; + message += std::string(error.what()); + message += std::string("\n"); + std::cerr << message; + return false; + } + return true; + } + return false; + } + + inline bool parse(const void *data, size_t size) override + { + clearCache(); + m_json = nullptr; + m_data = std::make_unique(); + tson::MemoryStream mem{(uint8_t *) data, size}; + try + { + std::string error = picojson::parse(*m_data, mem); + if(!error.empty()) + { + std::cerr << "PicoJson parse error: " << error << "\n"; + return false; + } + //mem >> *m_data; + m_json = m_data.get(); + } + catch (const std::exception &error) + { + std::string message = "Parse error: "; + message += std::string(error.what()); + message += std::string("\n"); + std::cerr << message; + return false; + } + return true; + } + + [[nodiscard]] inline size_t count(std::string_view key) const override + { + if (isObject()) + { + picojson::object obj = m_json->get(); + return obj.count(key.data()); + } + + return m_json->contains(key.data()) ? 1 : 0; + } + + [[nodiscard]] inline bool any(std::string_view key) const override + { + return count(key) > 0; + } + + [[nodiscard]] inline bool isArray() const override + { + return m_json->is(); + } + + [[nodiscard]] inline bool isObject() const override + { + return m_json->is(); + } + + [[nodiscard]] inline bool isNull() const override + { + return m_json->is(); + } + + fs::path directory() const override + { + return m_path; + } + + void directory(const fs::path &directory) override + { + m_path = directory; + } + + protected: + [[nodiscard]] inline int32_t getInt32(std::string_view key) override + { + picojson::object obj = m_json->get(); + return static_cast(getDouble(key)); + } + + [[nodiscard]] inline uint32_t getUInt32(std::string_view key) override + { + picojson::object obj = m_json->get(); + return static_cast(getDouble(key)); + } + + [[nodiscard]] inline int64_t getInt64(std::string_view key) override + { + picojson::object obj = m_json->get(); + return static_cast(getDouble(key)); + } + + [[nodiscard]] inline uint64_t getUInt64(std::string_view key) override + { + picojson::object obj = m_json->get(); + return static_cast(getDouble(key)); + } + + [[nodiscard]] inline double getDouble(std::string_view key) override + { + picojson::object obj = m_json->get(); + return obj[key.data()].get(); + } + + [[nodiscard]] inline std::string getString(std::string_view key) override + { + picojson::object obj = m_json->get(); + return obj[key.data()].get(); + } + + [[nodiscard]] inline bool getBool(std::string_view key) override + { + picojson::object obj = m_json->get(); + return obj[key.data()].get(); + } + + [[nodiscard]] float getFloat(std::string_view key) override + { + picojson::object obj = m_json->get(); + return static_cast(getDouble(key)); + } + + [[nodiscard]] inline int32_t getInt32() override + { + return static_cast(getDouble()); + } + + [[nodiscard]] inline uint32_t getUInt32() override + { + return static_cast(getDouble()); + } + + [[nodiscard]] inline int64_t getInt64() override + { + return static_cast(getDouble()); + } + + [[nodiscard]] inline uint64_t getUInt64() override + { + return static_cast(getDouble()); + } + + [[nodiscard]] inline double getDouble() override + { + return m_json->get(); + } + + [[nodiscard]] inline std::string getString() override + { + return m_json->get(); + } + + [[nodiscard]] inline bool getBool() override + { + return m_json->get(); + } + + [[nodiscard]] float getFloat() override + { + return static_cast(getDouble()); + } + + private: + inline void clearCache() + { + m_arrayCache.clear(); + m_arrayPosCache.clear(); + m_arrayListDataCache.clear(); + } + + picojson::value *m_json = nullptr; + std::unique_ptr m_data = nullptr; //Only used if this is the owner json! + fs::path m_path; + + //Cache! + std::map> m_arrayCache; + std::map> m_arrayPosCache; + std::map>> m_arrayListDataCache; + + }; +} +#endif //TILESON_PICOJSON_HPP +#endif + +/*** End of inlined file: PicoJson.hpp ***/ + +//#include "../json/Gason.hpp" //Unsupported + +/*** Start of inlined file: Json11.hpp ***/ +// +// Created by robin on 16.01.2021. +// + +#ifndef TILESON_JSON11_HPP +#define TILESON_JSON11_HPP + +namespace tson +{ + class Json11 : public tson::IJson + { + public: + inline Json11() = default; + + IJson &operator[](std::string_view key) override + { + if(m_arrayCache.count(key.data()) == 0) + { + if(m_json->is_object()) + { + m_arrayCache[key.data()] = std::make_unique(m_json->operator[](key.data())); + } + } + + return *m_arrayCache[key.data()].get(); + } + + inline explicit Json11(const json11::Json &json) : m_json {&json} + { + + } + + inline IJson& at(std::string_view key) override + { + if(m_arrayCache.count(key.data()) == 0) + { + if(m_json->is_object()) + { + m_arrayCache[key.data()] = std::make_unique(m_json->operator[](key.data())); + } + } + return *m_arrayCache[key.data()].get(); + } + + inline IJson& at(size_t pos) override + { + if(m_arrayPosCache.count(pos) == 0) + { + const std::vector &a = m_json->array_items(); + m_arrayPosCache[pos] = std::make_unique(a.at(pos)); + } + + return *m_arrayPosCache[pos]; + } + + std::vector> array() override + { + std::vector> vec; + if(m_json->is_array()) + { + for (const json11::Json &item : m_json->array_items()) + { + vec.emplace_back(std::make_unique(item)); + } + } + + return vec; + } + + inline std::vector> &array(std::string_view key) override + { + if(m_arrayListDataCache.count(key.data()) == 0) + { + if(count(key.data()) > 0) + { + if(isObject()) + { + const json11::Json &v = m_json->operator[](key.data()); + if(v.is_array()) + { + for (const json11::Json &item : v.array_items()) + { + m_arrayListDataCache[key.data()].emplace_back(std::make_unique(item)); + } + } + } + } + } + + return m_arrayListDataCache[key.data()]; + } + + [[nodiscard]] inline size_t size() const override + { + if(m_json->is_object()) + return m_json->object_items().size(); + else if(m_json->is_array()) + return m_json->array_items().size(); + + return 0; + } + + inline bool parse(const fs::path &path) override + { + clearCache(); + m_data = nullptr; + m_json = nullptr; + if (fs::exists(path) && fs::is_regular_file(path)) + { + std::ifstream file(path.generic_string()); + std::string str; + m_path = path.parent_path(); + + file.seekg(0, std::ios::end); + str.reserve(static_cast(file.tellg())); + file.seekg(0, std::ios::beg); + + str.assign((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); + + m_data = std::make_unique(); + + try + { + std::string strError; + *m_data = json11::Json::parse(str, strError); + if(!strError.empty()) + { + std::cerr << strError << "\n"; + return false; + } + m_json = m_data.get(); + } + catch (const std::exception &error) + { + std::string message = "Json11 parse error: "; + message += std::string(error.what()); + message += std::string("\n"); + std::cerr << message; + return false; + } + return true; + } + return false; + } + + inline bool parse(const void *data, size_t size) override + { + clearCache(); + m_json = nullptr; + std::string str; + + str.reserve(size); + + tson::MemoryStream mem{(uint8_t *) data, size}; + + str.assign((std::istreambuf_iterator(mem)), + std::istreambuf_iterator()); + + m_data = std::make_unique(); + + try + { + std::string strError; + + *m_data = json11::Json::parse(str, strError); + if(!strError.empty()) + { + std::cout << strError << "\n"; + return false; + } + m_json = m_data.get(); + } + catch (const std::exception &error) + { + std::string message = "Json11 parse error: "; + message += std::string(error.what()); + message += std::string("\n"); + std::cerr << message; + return false; + } + return true; + } + + [[nodiscard]] inline size_t count(std::string_view key) const override + { + if (isObject()) + { + //const json11::Json &j = m_json->operator[](key.data()); + //size_t s1 = j.object_items().size(); + return m_json->object_items().count(key.data()); + } + + return 0; + } + + [[nodiscard]] inline bool any(std::string_view key) const override + { + return count(key) > 0; + } + + [[nodiscard]] inline bool isArray() const override + { + return m_json->is_array(); + } + + [[nodiscard]] inline bool isObject() const override + { + return m_json->is_object(); + } + + [[nodiscard]] inline bool isNull() const override + { + return m_json->is_null(); + } + + fs::path directory() const override + { + return m_path; + } + + void directory(const fs::path &directory) override + { + m_path = directory; + } + + protected: + [[nodiscard]] inline int32_t getInt32(std::string_view key) override + { + return static_cast(getDouble(key)); + } + + [[nodiscard]] inline uint32_t getUInt32(std::string_view key) override + { + return static_cast(getDouble(key)); + } + + [[nodiscard]] inline int64_t getInt64(std::string_view key) override + { + return static_cast(getDouble(key)); + } + + [[nodiscard]] inline uint64_t getUInt64(std::string_view key) override + { + return static_cast(getDouble(key)); + } + + [[nodiscard]] inline double getDouble(std::string_view key) override + { + return m_json->operator[](key.data()).number_value(); + } + + [[nodiscard]] inline std::string getString(std::string_view key) override + { + return m_json->operator[](key.data()).string_value(); // .get(); + } + + [[nodiscard]] inline bool getBool(std::string_view key) override + { + return m_json->operator[](key.data()).bool_value(); + } + + [[nodiscard]] float getFloat(std::string_view key) override + { + return static_cast(getDouble(key)); + } + + [[nodiscard]] inline int32_t getInt32() override + { + return static_cast(getDouble()); + } + + [[nodiscard]] inline uint32_t getUInt32() override + { + return static_cast(getDouble()); + } + + [[nodiscard]] inline int64_t getInt64() override + { + return static_cast(getDouble()); + } + + [[nodiscard]] inline uint64_t getUInt64() override + { + return static_cast(getDouble()); + } + + [[nodiscard]] inline double getDouble() override + { + return m_json->number_value(); + } + + [[nodiscard]] inline std::string getString() override + { + return m_json->string_value(); + } + + [[nodiscard]] inline bool getBool() override + { + return m_json->bool_value(); + } + + [[nodiscard]] float getFloat() override + { + return static_cast(getDouble()); + } + + private: + + inline void clearCache() + { + m_arrayCache.clear(); + m_arrayPosCache.clear(); + m_arrayListDataCache.clear(); + } + + //Owner values + std::unique_ptr m_data = nullptr; //Only used if this is the owner json! + + const json11::Json *m_json = nullptr; + fs::path m_path; + + //Cache! + std::map> m_arrayCache; + std::map> m_arrayPosCache; + std::map>> m_arrayListDataCache; + + }; +} + +#endif //TILESON_JSON11_HPP + +/*** End of inlined file: Json11.hpp ***/ + + + +/*** Start of inlined file: Layer.hpp ***/ +// +// Created by robin on 22.03.2020. +// + +#ifndef TILESON_LAYER_HPP +#define TILESON_LAYER_HPP + +#include +//#include "../external/json.hpp" + + +/*** Start of inlined file: Chunk.hpp ***/ +// +// Created by robin on 22.03.2020. +// + +#ifndef TILESON_CHUNK_HPP +#define TILESON_CHUNK_HPP + +//#include "../external/json.hpp" + +namespace tson +{ + class Chunk + { + public: + inline Chunk() = default; + inline explicit Chunk(IJson &json); + inline bool parse(IJson &json); + + [[nodiscard]] inline const std::vector &getData() const; + [[nodiscard]] inline const std::string &getBase64Data() const; + [[nodiscard]] inline const Vector2i &getSize() const; + [[nodiscard]] inline const Vector2i &getPosition() const; + + private: + std::vector m_data; /*! 'data' (when uint array): Array of unsigned int (GIDs) or base64-encoded data. tilelayer only. */ + std::string m_base64Data; /*! 'data' (when string): Array of unsigned int (GIDs) or base64-encoded data. */ + tson::Vector2i m_size; /*! x='width' (in tiles) and y='height' (in tiles): */ + tson::Vector2i m_position; /*! 'x' and 'y' position in tiles */ + }; +} + +#endif //TILESON_CHUNK_HPP + +/*! + * Parses 'chunk' data from Tiled json and stores the values in this class + * @param json json-data + */ +tson::Chunk::Chunk(IJson &json) +{ + parse(json); +} + +/*! + * Parses 'chunk' data from Tiled json and stores the values in this class + * @param json json-data + * @return true if all mandatory fields was found. false otherwise. + */ +bool tson::Chunk::parse(IJson &json) +{ + bool allFound = true; + + if(json.count("width") > 0 && json.count("height") > 0) + m_size = {json["width"].get(), json["height"].get()}; else allFound = false; + if(json.count("x") > 0 && json.count("y") > 0) + m_position = {json["x"].get(), json["y"].get()}; else allFound = false; + + //Handle DATA (Optional) + if(json.count("data") > 0) + { + if(json["data"].isArray()) + { + auto &data = json.array("data"); + std::for_each(data.begin(), data.end(), [&](std::unique_ptr &item) { m_data.push_back(item->get()); }); + } + else + m_base64Data = json["data"].get(); + } + + return allFound; +} + +/*! + * 'data' (when uint array): Array of unsigned int (GIDs) or base64-encoded data. tilelayer only. + * @return list of tile ids + */ +const std::vector &tson::Chunk::getData() const +{ + return m_data; +} + +/*! + * 'data' (when string): Array of unsigned int (GIDs) or base64-encoded data. + * @return base64 string + */ +const std::string &tson::Chunk::getBase64Data() const +{ + return m_base64Data; +} + +/*! + * x='width' (in tiles) and y='height' (in tiles). + * @return Size (x and y), containing the values from the fields 'width' and 'height' in Tiled + */ +const tson::Vector2i &tson::Chunk::getSize() const +{ + return m_size; +} + +/*! + * 'x' and 'y' position in tiles + * @return Position in int + */ +const tson::Vector2i &tson::Chunk::getPosition() const +{ + return m_position; +} +/*** End of inlined file: Chunk.hpp ***/ + + +/*** Start of inlined file: Object.hpp ***/ +// +// Created by robin on 22.03.2020. +// + +#ifndef TILESON_OBJECT_HPP +#define TILESON_OBJECT_HPP + +//#include "../external/json.hpp" + + +/*** Start of inlined file: PropertyCollection.hpp ***/ +// +// Created by robin on 22.03.2020. +// + +#ifndef TILESON_PROPERTYCOLLECTION_HPP +#define TILESON_PROPERTYCOLLECTION_HPP + + +/*** Start of inlined file: Property.hpp ***/ +// +// Created by robin on 22.03.2020. +// + +#ifndef TILESON_PROPERTY_HPP +#define TILESON_PROPERTY_HPP + +//#include "../../TilesonConfig.h" + +//#if USE_CPP17_FILESYSTEM + +#include +#include + +/*** Start of inlined file: Enums.hpp ***/ +// +// Created by robin on 22.03.2020. +// + +#ifndef TILESON_ENUMS_HPP +#define TILESON_ENUMS_HPP +#include + +/*** Start of inlined file: EnumBitflags.hpp ***/ +// +// Created by robin on 08.11.2020. +// + +#ifndef TILESON_ENUMBITFLAGS_HPP +#define TILESON_ENUMBITFLAGS_HPP + +#include +#include + +#define TILESON_ENABLE_BITMASK_OPERATORS(x) \ +namespace tson { \ + ENABLE_BITMASK_OPERATORS(x) \ +} + +namespace tson +{ + #define ENABLE_BITMASK_OPERATORS(x) \ + template<> \ + struct EnableBitMaskOperators \ + { \ + static const bool enable = true; \ + }; + + template + struct EnableBitMaskOperators + { + static const bool enable = false; + }; + + template + typename std::enable_if::enable, Enum>::type + operator |(Enum lhs, Enum rhs) + { + static_assert(std::is_enum::value, + "template parameter is not an enum type"); + + using underlying = typename std::underlying_type::type; + + return static_cast ( + static_cast(lhs) | + static_cast(rhs) + ); + } + + //Permissions operator &(Permissions lhs, Permissions rhs) + template + typename std::enable_if::enable, Enum>::type + operator &(Enum lhs, Enum rhs) + { + static_assert(std::is_enum::value, + "template parameter is not an enum type"); + + using underlying = typename std::underlying_type::type; + + return static_cast ( + static_cast(lhs) & + static_cast(rhs) + ); + } + + //Permissions operator ^(Permissions lhs, Permissions rhs) + template + typename std::enable_if::enable, Enum>::type + operator ^(Enum lhs, Enum rhs) + { + static_assert(std::is_enum::value, + "template parameter is not an enum type"); + + using underlying = typename std::underlying_type::type; + + return static_cast ( + static_cast(lhs) ^ + static_cast(rhs) + ); + } + + //Permissions operator ~(Permissions rhs) + template + typename std::enable_if::enable, Enum>::type + operator ~(Enum rhs) + { + static_assert(std::is_enum::value, + "template parameter is not an enum type"); + + using underlying = typename std::underlying_type::type; + + return static_cast ( + ~static_cast(rhs) + ); + } + + //Permissions& operator |=(Permissions &lhs, Permissions rhs) + template + typename std::enable_if::enable, Enum>::type + &operator |=(Enum &lhs, Enum rhs) + { + static_assert(std::is_enum::value, + "template parameter is not an enum type"); + + using underlying = typename std::underlying_type::type; + + lhs = static_cast ( + static_cast(lhs) | + static_cast(rhs) + ); + + return lhs; + } + + //Permissions& operator &=(Permissions &lhs, Permissions rhs) + template + typename std::enable_if::enable, Enum>::type + &operator &=(Enum &lhs, Enum rhs) + { + static_assert(std::is_enum::value, + "template parameter is not an enum type"); + + using underlying = typename std::underlying_type::type; + + lhs = static_cast ( + static_cast(lhs) & + static_cast(rhs) + ); + + return lhs; + } + + //Permissions& operator ^=(Permissions &lhs, Permissions rhs) + template + typename std::enable_if::enable, Enum>::type + &operator ^=(Enum &lhs, Enum rhs) + { + static_assert(std::is_enum::value, + "template parameter is not an enum type"); + + using underlying = typename std::underlying_type::type; + + lhs = static_cast ( + static_cast(lhs) ^ + static_cast(rhs) + ); + + return lhs; + } +} + +#endif //TILESON_ENUMBITFLAGS_HPP + +/*** End of inlined file: EnumBitflags.hpp ***/ + + +namespace tson +{ + /*! + * Type used in Property.hpp + */ + enum class Type : uint8_t + { + Undefined = 0, + Color = 1, /*! color */ + File = 2, /*! file */ + Int = 3, /*! int */ + Boolean = 4, /*! bool */ + Float = 5, /*! float */ + String = 6, /*! string */ + Class = 7, /*! class */ + Enum = 8, /*! 'string' or 'int' with a value in 'propertyType' */ + Object = 9 /*! object */ + }; + + /*! + * Layer.hpp - LayerType + * //'type': tilelayer, objectgroup, imagelayer or group + */ + enum class LayerType : uint8_t + { + Undefined = 0, + TileLayer = 1, + ObjectGroup = 2, + ImageLayer = 3, + Group = 4 + }; + + /*! + * Map.hpp - ParseStatus + */ + enum class ParseStatus : uint8_t + { + OK = 0, //OK unless otherwise stated + FileNotFound = 1, + ParseError = 2, + MissingData = 3, + DecompressionError = 4 + }; + + /*! + * Object.hpp - ObjectType + */ + enum class ObjectType : uint8_t + { + Undefined = 0, + Object = 1, + Ellipse = 2, + Rectangle = 3, + Point = 4, + Polygon = 5, + Polyline = 6, + Text = 7, + Template = 8 + }; + + static constexpr uint32_t FLIPPED_HORIZONTALLY_FLAG = 0x80000000; + static constexpr uint32_t FLIPPED_VERTICALLY_FLAG = 0x40000000; + static constexpr uint32_t FLIPPED_DIAGONALLY_FLAG = 0x20000000; + /*! + * Object.hpp - ObjectFlipFlags + */ + enum class TileFlipFlags : uint32_t + { + None = 0, + Diagonally = FLIPPED_DIAGONALLY_FLAG, + Vertically = FLIPPED_VERTICALLY_FLAG, + Horizontally = FLIPPED_HORIZONTALLY_FLAG + }; + + /*! + * Tileset.hpp - ObjectAlignment + */ + enum class ObjectAlignment : uint8_t + { + Unspecified = 0, //unspecified + TopLeft = 1, //topleft + Top = 2, //top + TopRight = 3, //topright + Left = 4, //left + Center = 5, //center + Right = 6, //right + BottomLeft = 7, //bottomleft + Bottom = 8, //bottom + BottomRight = 9 //bottomright + }; + + /*! + * Text.hpp - TextAlignment + */ + enum class TextAlignment : uint8_t + { + Unresolved = 0, + Left = 1, //left + Center = 2, //center + Right = 3, //right + Justify = 4, //justify + Top = 5, //top + Bottom = 6 //bottom + }; + + /*! + * Tileset.hpp - TileRenderSize - From Tiled v1.9 + */ + enum class TileRenderSize : uint8_t + { + Undefined = 0, + Tile = 1, //tile (default) + Grid = 2 //grid + }; + + /*! + * Tileset.hpp - FillMode - From Tiled v1.9 + */ + enum class FillMode : uint8_t + { + Undefined = 0, + Stretch = 1, //stretch (default) + PreserveAspectFit = 2 //preserve-aspect-fit + }; + + enum class EnumStorageType : uint8_t + { + Unspecified = 0, + Int = 1, + String = 2 + }; + + ENABLE_BITMASK_OPERATORS(TileFlipFlags) +} + +#endif //TILESON_ENUMS_HPP + +/*** End of inlined file: Enums.hpp ***/ + + +//#include "../external/json.hpp" + +namespace tson +{ + class Project; + + class Property + { + public: + + //enum class Type : uint8_t + //{ + // Undefined = 0, + // Color = 1, /*! color */ + // File = 2, /*! file */ + // Int = 3, /*! int */ + // Boolean = 4, /*! bool */ + // Float = 5, /*! float */ + // String = 6 /*! string */ + //}; + + inline Property(); + inline explicit Property(IJson &json, tson::Project *project = nullptr); + inline Property(std::string name, std::any value, Type type); + + inline void setValue(const std::any &value); + inline void setStrValue(const std::string &value); + inline void setName(const std::string &name); + + [[nodiscard]] inline const std::type_info& getValueType() const; + inline std::string getValueTypeInfo(); + [[nodiscard]]inline const std::any &getValue() const; + template + inline T getValue() const; + [[nodiscard]] inline const std::string &getName() const; + [[nodiscard]] inline Type getType() const; + [[nodiscard]] inline const std::string &getPropertyType() const; + + //Became public in v1.4.0 + inline void setValueByType(IJson &json); //Definition in tileson_forward.hpp + + protected: + inline void setTypeByString(const std::string &str); + + tson::Project *m_project = nullptr; //Used for resolving 'enum' and 'class' objects + Type m_type = Type::Undefined; + std::string m_name {}; + std::string m_propertyType {}; + std::any m_value; //Using std::any to assign any type + }; + + template + T Property::getValue() const + { + bool isCorrectType = (m_value.type() == typeid(T)); + + if(isCorrectType) + { + T value = std::any_cast(m_value); + return value; + } + else + { + static T defaultValue; + return defaultValue; + } + } +} + +tson::Property::Property() : m_name {"unnamed"} +{ + +} + +tson::Property::Property(IJson &json, tson::Project *project) : m_project {project} +{ + m_name = json["name"].get(); + if(json.count("propertytype") > 0) + m_propertyType = json["propertytype"].get(); + else if(json.count("propertyType") > 0) //Somehow Tiled's class objects uses propertyType with 'T'. + m_propertyType = json["propertyType"].get(); + + setTypeByString(json["type"].get()); + setValueByType(json["value"]); +} + +tson::Property::Property(std::string name, std::any value, Type type) : m_type {type}, m_name { move(name) }, m_value { move(value) } +{ + +} + +void tson::Property::setValue(const std::any &value) +{ + m_value = value; +} + +/*! + * Sets the value specifically as string. + * When not specified as std::string, the default is that the value will be set as char * when adding a value like "test" + * This function is to make sure the value is added as string. + * @param value + */ +void tson::Property::setStrValue(const std::string &value) +{ + m_value = value; +} + +const std::any &tson::Property::getValue() const +{ + return m_value; +} + +void tson::Property::setName(const std::string &name) +{ + m_name = name; +} + +const std::string &tson::Property::getName() const +{ + return m_name; +} + +/*! + * Gets the value type as std::value_info. + * This can easily be compared to types like this: + * Check if int: getValueType() == typeid(int) + * @return + */ + +const std::type_info &tson::Property::getValueType() const +{ + return m_value.type(); +} + +/*! + * Gets the value type as std::string + * Examples of known types: + * "i" = int + * "f" = float + * "b" = bool + * @return + */ +std::string tson::Property::getValueTypeInfo() +{ + return m_value.type().name(); +} + +tson::Type tson::Property::getType() const +{ + return m_type; +} + +void tson::Property::setTypeByString(const std::string &str) +{ + if(str == "color") + m_type = tson::Type::Color; + else if(str == "file") + m_type = tson::Type::File; + else if(str == "int") + m_type = tson::Type::Int; + else if(str == "bool") + m_type = tson::Type::Boolean; + else if(str == "float") + m_type = tson::Type::Float; + else if(str == "string") + m_type = tson::Type::String; + else if(str == "class") + m_type = tson::Type::Class; + else if(str == "object") + m_type = tson::Type::Object; + else + m_type = tson::Type::Undefined; +} + +const std::string &tson::Property::getPropertyType() const +{ + return m_propertyType; +} + +#endif //TILESON_PROPERTY_HPP + +/*** End of inlined file: Property.hpp ***/ + +//#include "../external/json.hpp" +#include + +namespace tson +{ + class PropertyCollection + { + public: + inline PropertyCollection() = default; + + inline explicit PropertyCollection(std::string id); + + inline tson::Property * add(const tson::Property &property); + inline tson::Property * add(IJson &json, tson::Project *project = nullptr); + inline tson::Property * add(const std::string &name, const std::any &value, tson::Type type); + + inline void remove(const std::string &name); + + inline void setValue(const std::string &name, const std::any &value); + inline void setProperty(const std::string &name, const tson::Property &value); + inline void setId(const std::string &id); + + inline bool hasProperty(const std::string &name); + inline tson::Property * getProperty(const std::string &name); + inline std::map &getProperties(); + inline std::vector get(); + template + inline T getValue(const std::string &name); + [[nodiscard]] inline const std::string &getId() const; + [[nodiscard]] inline size_t getSize() const; + + protected: + std::string m_id; + std::map m_properties; + }; +} + +template +T tson::PropertyCollection::getValue(const std::string &name) +{ + static T defaultT; + return (m_properties.count(name) > 0) ? m_properties[name].getValue() : defaultT; +} + +tson::PropertyCollection::PropertyCollection(std::string id) : m_id {std::move(id)} +{ + +} + +tson::Property *tson::PropertyCollection::add(const tson::Property &property) +{ + m_properties[property.getName()] = property; + return &m_properties[property.getName()]; +} + +tson::Property *tson::PropertyCollection::add(IJson &json, tson::Project *project) +{ + tson::Property property = tson::Property(json, project); + std::string name = property.getName(); + m_properties[name] = std::move(property); + return &m_properties[name]; +} + +tson::Property *tson::PropertyCollection::add(const std::string &name, const std::any &value, tson::Type type) +{ + m_properties[name] = {name, value, type}; + return &m_properties[name]; +} + +void tson::PropertyCollection::remove(const std::string &name) +{ + m_properties.erase(name); +} + +/*! + * Sets a value IF the property already exists. Does nothing otherwise. + * See add() for adding new properties + * @param name + * @param value + */ +void tson::PropertyCollection::setValue(const std::string &name, const std::any &value) +{ + if(m_properties.count(name) > 0) + m_properties[name].setValue(value); +} + +/*! + * Overwrites the current property if it exists, or adds it if it doesn't. + * @param name + * @param value + */ +void tson::PropertyCollection::setProperty(const std::string &name, const tson::Property &value) +{ + m_properties[name] = value; +} + +void tson::PropertyCollection::setId(const std::string &id) +{ + m_id = id; +} + +bool tson::PropertyCollection::hasProperty(const std::string &name) +{ + return m_properties.count(name) > 0; +} + +tson::Property *tson::PropertyCollection::getProperty(const std::string &name) +{ + return (m_properties.count(name) > 0) ? &m_properties[name] : nullptr; +} + +std::map &tson::PropertyCollection::getProperties() +{ + return m_properties; +} + +/*! + * Gets vector of pointers to all the existing properties + * @return + */ +std::vector tson::PropertyCollection::get() +{ + std::vector props; + for(auto &i : m_properties) + props.emplace_back(&i.second); + + return props; +} + +const std::string &tson::PropertyCollection::getId() const +{ + return m_id; +} + +size_t tson::PropertyCollection::getSize() const +{ + return m_properties.size(); +} + +#endif //TILESON_PROPERTYCOLLECTION_HPP + +/*** End of inlined file: PropertyCollection.hpp ***/ + + +/*** Start of inlined file: Text.hpp ***/ +// +// Created by robin on 05.08.2019. +// + +#ifndef TILESON_TEXT_HPP +#define TILESON_TEXT_HPP + +#include + +namespace tson +{ + class Text + { + public: + inline Text() = default; + /*! + * + * @param _text Text + * @param _wrap If the text is marked as wrapped + */ + //inline Text(std::string _text, bool _wrap, tson::Colori _color) : text {std::move(_text)}, wrap {_wrap}, color {_color} {}; + inline explicit Text(IJson &json) + { + bool hasColor = json.count("color") > 0; + tson::Color c = (hasColor) ? tson::Colori(json["color"].get()) : tson::Colori(); + color = c; + text = (json.count("text") > 0) ? json["text"].get() : ""; + wrap = (json.count("wrap") > 0) ? json["wrap"].get() : false; + + //Previously missing properties + bold = (json.count("bold") > 0) ? json["bold"].get() : false; + fontFamily = (json.count("fontfamily") > 0) ? json["fontfamily"].get() : "sans-serif"; + horizontalAlignment = (json.count("halign") > 0) ? resolveTextAlignmentByString(json["halign"].get()) : TextAlignment::Left; + italic = (json.count("italic") > 0) ? json["italic"].get() : false; + kerning = (json.count("kerning") > 0) ? json["kerning"].get() : true; + pixelSize = (json.count("pixelsize") > 0) ? json["pixelsize"].get() : 16; + strikeout = (json.count("strikeout") > 0) ? json["strikeout"].get() : false; + underline = (json.count("underline") > 0) ? json["underline"].get() : false; + verticalAlignment = (json.count("valign") > 0) ? resolveTextAlignmentByString(json["valign"].get()) : TextAlignment::Top; + }; + + //Just make it simple + std::string text {}; + tson::Colori color {}; + bool wrap{}; + + //Previously missing properties + bool bold {false}; + std::string fontFamily {"sans-serif"}; + TextAlignment horizontalAlignment {TextAlignment::Left}; + bool italic {false}; + bool kerning {true}; + int pixelSize {16}; + bool strikeout {false}; + bool underline {false}; + TextAlignment verticalAlignment {TextAlignment::Top}; + + private: + [[nodiscard]] TextAlignment resolveTextAlignmentByString(const std::string &str) const + { + if(str == "left") return TextAlignment::Left; + if(str == "center") return TextAlignment::Center; + if(str == "right") return TextAlignment::Right; + if(str == "justify") return TextAlignment::Justify; + if(str == "top") return TextAlignment::Top; + if(str == "bottom") return TextAlignment::Bottom; + + return TextAlignment::Unresolved; + } + }; +} + +#endif //TILESON_TEXT_HPP + +/*** End of inlined file: Text.hpp ***/ + +#include + +namespace tson +{ + class TiledClass; + class Map; + class Object + { + public: + //enum class Type : uint8_t + //{ + // Undefined = 0, + // Object = 1, + // Ellipse = 2, + // Rectangle = 3, + // Point = 4, + // Polygon = 5, + // Polyline = 6, + // Text = 7, + // Template = 8 + //}; + + inline Object() = default; + inline explicit Object(IJson &json, tson::Map *map); + inline bool parse(IJson &json, tson::Map *map); + + [[nodiscard]] inline ObjectType getObjectType() const; + [[nodiscard]] inline bool isEllipse() const; + [[nodiscard]] inline uint32_t getGid() const; + [[nodiscard]] inline const Vector2i &getSize() const; + [[nodiscard]] inline int getId() const; + [[nodiscard]] inline const std::string &getName() const; + [[nodiscard]] inline bool isPoint() const; + [[nodiscard]] inline float getRotation() const; + [[nodiscard]] inline const std::string &getTemplate() const; + [[nodiscard]] inline const std::string &getType() const; + [[nodiscard]] inline const std::string &getClassType() const; + [[nodiscard]] inline tson::TiledClass *getClass(); /*! Declared in tileson_forward.hpp */ + [[nodiscard]] inline bool isVisible() const; + [[nodiscard]] inline const Vector2i &getPosition() const; + + [[nodiscard]] inline const std::vector &getPolygons() const; + [[nodiscard]] inline const std::vector &getPolylines() const; + [[nodiscard]] inline PropertyCollection &getProperties(); + [[nodiscard]] inline const Text &getText() const; + + template + inline T get(const std::string &name); + inline tson::Property * getProp(const std::string &name); + + //v1.2.0-stuff + [[nodiscard]] inline TileFlipFlags getFlipFlags() const; + inline bool hasFlipFlags(TileFlipFlags flags); + + private: + inline void setObjectTypeByJson(IJson &json); + + ObjectType m_objectType = ObjectType::Undefined; /*! Says with object type this is */ + bool m_ellipse {}; /*! 'ellipse': Used to mark an object as an ellipse */ + uint32_t m_gid {}; /*! 'gid': GID, only if object comes from a Tilemap */ + tson::Vector2i m_size; /*! x = 'width' (Width in pixels), y = 'height' (Height in pixels). Ignored if using a gid.)*/ + int m_id{}; /*! 'id': Incremental id - unique across all objects */ + std::string m_name; /*! 'name': String assigned to name field in editor*/ + bool m_point {}; /*! 'point': Used to mark an object as a point */ + std::vector m_polygon; /*! 'polygon': A list of x,y coordinates in pixels */ + std::vector m_polyline; /*! 'polyline': A list of x,y coordinates in pixels */ + tson::PropertyCollection m_properties; /*! 'properties': A list of properties (name, value, type). */ + float m_rotation {}; /*! 'rotation': Angle in degrees clockwise */ + std::string m_template; /*! 'template': Reference to a template file, in case object is a template instance */ + tson::Text m_text; /*! first: 'text' second: 'wrap' */ + std::string m_type; /*! 'type': String assigned to type field in editor */ + bool m_visible {}; /*! 'visible': Whether object is shown in editor. */ + tson::Vector2i m_position; /*! 'x' and 'y': coordinate in pixels */ + + //v1.2.0-stuff + tson::TileFlipFlags m_flipFlags = TileFlipFlags::None; /*! Resolved using bit 32, 31 and 30 from gid */ + + tson::Map *m_map {nullptr}; + std::shared_ptr m_class {}; + }; + + /*! + * A shortcut for getting a property. Alternative to getProperties().getValue("") + * @tparam T The template value + * @param name Name of the property + * @return The actual value, if it exists. Otherwise: The default value of the type. + */ + template + T tson::Object::get(const std::string &name) + { + return m_properties.getValue(name); + } +} + +/*! + * Parses a json Tiled object + * @param json + */ +tson::Object::Object(IJson &json, tson::Map *map) +{ + parse(json, map); +} + +/*! + * Parses a json Tiled object and autoamtically determines the object type based on the data presented. + * Call getObjectType() to see what object type it is. + * @param json + * @return true if all mandatory fields was found. false otherwise. + */ +bool tson::Object::parse(IJson &json, tson::Map *map) +{ + m_map = map; + bool allFound = true; + + if(json.count("ellipse") > 0) m_ellipse = json["ellipse"].get(); //Optional + if(json.count("gid") > 0) + { + uint32_t gid = json["gid"].get(); //Optional + if (gid & FLIPPED_HORIZONTALLY_FLAG) m_flipFlags |= TileFlipFlags::Horizontally; + if (gid & FLIPPED_VERTICALLY_FLAG) m_flipFlags |= TileFlipFlags::Vertically; + if (gid & FLIPPED_DIAGONALLY_FLAG) m_flipFlags |= TileFlipFlags::Diagonally; + + // Clear flags + gid &= ~(FLIPPED_HORIZONTALLY_FLAG | FLIPPED_VERTICALLY_FLAG | FLIPPED_DIAGONALLY_FLAG); + + m_gid = gid; + } + if(json.count("id") > 0) m_id = json["id"].get(); else allFound = false; + if(json.count("name") > 0) m_name = json["name"].get(); else allFound = false; + if(json.count("point") > 0) m_point = json["point"].get(); //Optional + if(json.count("rotation") > 0) m_rotation = json["rotation"].get(); else allFound = false; + if(json.count("template") > 0) m_template = json["template"].get(); //Optional + + if(json.count("type") > 0) m_type = json["type"].get(); + else if(json.count("class") > 0) m_type = json["class"].get(); //Tiled v1.9 renamed 'type' to 'class' + else allFound = false; + + if(json.count("visible") > 0) m_visible = json["visible"].get(); else allFound = false; + + if(json.count("width") > 0 && json.count("height") > 0) + m_size = {json["width"].get(), json["height"].get()}; else allFound = false; + if(json.count("x") > 0 && json.count("y") > 0) + m_position = {json["x"].get(), json["y"].get()}; else allFound = false; + + if(json.count("text") > 0) + { + //Old logic + //bool hasColor = json["text"].count("color") > 0; + //tson::Color c = (hasColor) ? tson::Colori(json["text"]["color"].get()) : tson::Colori(); + //m_text = {json["text"]["text"].get(), json["text"]["wrap"].get(), c}; //Optional + m_text = tson::Text(json["text"]); + // + + } + + setObjectTypeByJson(json); + + if(m_objectType == ObjectType::Template) + allFound = true; //Just accept anything with this type + + //More advanced data + if(json.count("polygon") > 0 && json["polygon"].isArray()) + { + auto &polygon = json.array("polygon"); + std::for_each(polygon.begin(), polygon.end(),[&](std::unique_ptr &item) + { + IJson &j = *item; + m_polygon.emplace_back(j["x"].get(), j["y"].get()); + }); + + } + + if(json.count("polyline") > 0 && json["polyline"].isArray()) + { + auto &polyline = json.array("polyline"); + std::for_each(polyline.begin(), polyline.end(),[&](std::unique_ptr &item) + { + IJson &j = *item; + m_polyline.emplace_back(j["x"].get(), j["y"].get()); + }); + } + + if(json.count("properties") > 0 && json["properties"].isArray()) + { + auto &properties = json.array("properties"); + std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr &item) + { + m_properties.add(*item); + }); + } + + return allFound; +} + +/*! + * Sets an object type based on json data. + * @param json + */ +void tson::Object::setObjectTypeByJson(IJson &json) +{ + m_objectType = ObjectType::Undefined; + if(m_ellipse) + m_objectType = ObjectType::Ellipse; + else if(m_point) + m_objectType = ObjectType::Point; + else if(json.count("polygon") > 0) + m_objectType = ObjectType::Polygon; + else if(json.count("polyline") > 0) + m_objectType = ObjectType::Polyline; + else if(json.count("text") > 0) + m_objectType = ObjectType::Text; + else if(json.count("gid") > 0) + m_objectType = ObjectType::Object; + else if(json.count("template") > 0) + m_objectType = ObjectType::Template; + else + m_objectType = ObjectType::Rectangle; +} + +/*! + * Gets what type of object this is. + * @return + */ + +tson::ObjectType tson::Object::getObjectType() const +{ + return m_objectType; +} + +/*! + * 'ellipse': Used to mark an object as an ellipse + * @return + */ +bool tson::Object::isEllipse() const +{ + return m_ellipse; +} + +/*! + * 'gid': GID, only if object comes from a Tilemap + * @return + */ +uint32_t tson::Object::getGid() const +{ + return m_gid; +} + +/*! + * x = 'width' (Width in pixels), y = 'height' (Height in pixels). Ignored if using a gid.) + * @return + */ +const tson::Vector2i &tson::Object::getSize() const +{ + return m_size; +} + +/*! + * 'id': Incremental id - unique across all objects + * @return + */ +int tson::Object::getId() const +{ + return m_id; +} + +/*! + * 'name': String assigned to name field in editor + * @return + */ +const std::string &tson::Object::getName() const +{ + return m_name; +} + +/*! + * 'point': Used to mark an object as a point + * @return true if the object is of type point + */ +bool tson::Object::isPoint() const +{ + return m_point; +} + +/*! + * 'rotation': Angle in degrees clockwise + * @return + */ +float tson::Object::getRotation() const +{ + return m_rotation; +} + +/*! + * 'template': Reference to a template file, in case object is a template instance + * @return + */ +const std::string &tson::Object::getTemplate() const +{ + return m_template; +} + +/*! + * 'type': String assigned to type field in editor + * This was renamed to 'class' in Tiled v1.9 + * @return + */ +const std::string &tson::Object::getType() const +{ + return m_type; +} + +/*! + * 'class': String assigned to class field in editor + * This was renamed from 'type' to 'class' in Tiled v1.9 + * @return + */ +const std::string &tson::Object::getClassType() const +{ + return m_type; +} + +/*! + * 'visible': Whether object is shown in editor. + * @return + */ +bool tson::Object::isVisible() const +{ + return m_visible; +} + +/*! + * 'x' and 'y': coordinate in pixels + * @return + */ +const tson::Vector2i &tson::Object::getPosition() const +{ + return m_position; +} + +/*! + * 'polygon': A list of x,y coordinates in pixels. + * If this is a Polygon type, this function will return the points used to create it + * @return + */ +const std::vector &tson::Object::getPolygons() const +{ + return m_polygon; +} + +/*! + * 'polyline': A list of x,y coordinates in pixels + * If this is a Polyline type, this function will return the points used to create it + * @return + */ +const std::vector &tson::Object::getPolylines() const +{ + return m_polyline; +} + +/*! + * 'properties': A list of properties (name, value, type). + * @return + */ +tson::PropertyCollection &tson::Object::getProperties() +{ + return m_properties; +} + +/*! + * 'type': String assigned to type field in editor + * @return + */ +const tson::Text &tson::Object::getText() const +{ + return m_text; +} + +/*! + * Shortcut for getting a property object. Alternative to getProperties().getProperty(""); + * @param name Name of the property + * @return + */ +tson::Property *tson::Object::getProp(const std::string &name) +{ + if(m_properties.hasProperty(name)) + return m_properties.getProperty(name); + return nullptr; +} + +/*! + * Get all flip flags + * @return + */ +tson::TileFlipFlags tson::Object::getFlipFlags() const +{ + return m_flipFlags; +} + +/*! + * + * @param flags Which flags to check for. Several flags can be checked at once using the bitwise or operator. + * Example: + * hasFlipFlags(TileFlipFlags::Vertically | TileFlipFlags::Horizontally) + * + * @return true if the flag(s) specified are set + */ +bool tson::Object::hasFlipFlags(TileFlipFlags flags) +{ + return ((m_flipFlags & flags) == flags) ? true : false; +} + +#endif //TILESON_OBJECT_HPP + +/*** End of inlined file: Object.hpp ***/ + + +/*** Start of inlined file: TileObject.hpp ***/ +// +// Created by robin on 26.07.2020. +// + +#ifndef TILESON_TILEOBJECT_HPP +#define TILESON_TILEOBJECT_HPP + + +/*** Start of inlined file: Rect.hpp ***/ +// +// Created by robin on 24.07.2020. +// + +#ifndef TILESON_RECT_HPP +#define TILESON_RECT_HPP + +namespace tson +{ + class Rect + { + public: + + inline Rect() = default; + inline Rect(int x_, int y_, int width_, int height_); + + inline bool operator==(const Rect &rhs) const; + inline bool operator!=(const Rect &rhs) const; + + int x{}; + int y{}; + int width{}; + int height{}; + }; + + Rect::Rect(int x_, int y_, int width_, int height_) + { + x = x_; + y = y_; + width = width_; + height = height_; + } + + bool Rect::operator==(const Rect &rhs) const + { + return x == rhs.x && + y == rhs.y && + width == rhs.width && + height == rhs.height; + } + + bool Rect::operator!=(const Rect &rhs) const + { + return !(rhs == *this); + } +} + +#endif //TILESON_RECT_HPP + +/*** End of inlined file: Rect.hpp ***/ + +namespace tson +{ + class Tile; + class TileObject + { + public: + inline TileObject() = default; + inline TileObject(const std::tuple &posInTileUnits, tson::Tile *tile); + + inline void initialize(const std::tuple &posInTileUnits, tson::Tile *tile); //Defined in tileson_forward.hpp + + inline Tile *getTile(); + inline const Vector2i &getPositionInTileUnits() const; + inline const Vector2f &getPosition() const; + inline const tson::Rect &getDrawingRect() const; //Defined in tileson_forward.hpp + + private: + tson::Tile *m_tile; + tson::Vector2i m_posInTileUnits; + tson::Vector2f m_position; + + }; + + TileObject::TileObject(const std::tuple &posInTileUnits, tson::Tile *tile) + { + initialize(posInTileUnits, tile); + } + + /*! + * Get a pointer to the related tile + * @return + */ + Tile *TileObject::getTile() + { + return m_tile; + } + + /*! + * Gets the position of the tile in tile units + * @return + */ + const Vector2i &TileObject::getPositionInTileUnits() const + { + return m_posInTileUnits; + } + + /*! + * Gets the position of the tile in pixels. + * @return + */ + const Vector2f &TileObject::getPosition() const + { + return m_position; + } +} + +#endif //TILESON_TILEOBJECT_HPP + +/*** End of inlined file: TileObject.hpp ***/ + + +/*** Start of inlined file: FlaggedTile.hpp ***/ +// +// Created by robin on 13.11.2020. +// + +#ifndef TILESON_FLAGGEDTILE_HPP +#define TILESON_FLAGGEDTILE_HPP + +namespace tson +{ + class FlaggedTile + { + + public: + FlaggedTile(size_t x_, size_t y_, uint32_t id_, uint32_t tileId_) : x {x_}, y {y_}, id {id_}, tileId {tileId_} + { + + } + size_t x; + size_t y; + /*! Full ID, including flag */ + uint32_t id; + /*! ID of the flagged tile */ + uint32_t tileId; + }; +} +#endif //TILESON_FLAGGEDTILE_HPP + +/*** End of inlined file: FlaggedTile.hpp ***/ + +namespace tson +{ + class Tile; + class Map; + + class Layer + { + public: + inline Layer() = default; + inline Layer(IJson &json, tson::Map *map); + inline bool parse(IJson &json, tson::Map *map); //Defined in tileson_forward + + [[nodiscard]] inline const std::string &getCompression() const; + [[nodiscard]] inline const std::vector &getData() const; + [[nodiscard]] inline const std::string &getBase64Data() const; + [[nodiscard]] inline const std::string &getDrawOrder() const; + [[nodiscard]] inline const std::string &getEncoding() const; + [[nodiscard]] inline int getId() const; + [[nodiscard]] inline const std::string &getImage() const; + [[nodiscard]] inline const std::string &getName() const; + [[nodiscard]] inline const Vector2f &getOffset() const; + [[nodiscard]] inline float getOpacity() const; + [[nodiscard]] inline const Vector2i &getSize() const; + [[nodiscard]] inline const Colori &getTransparentColor() const; + [[nodiscard]] inline const Vector2f &getParallax() const; + [[nodiscard]] inline bool hasRepeatX() const; + [[nodiscard]] inline bool hasRepeatY() const; + + [[nodiscard]] inline LayerType getType() const; + [[nodiscard]] inline const std::string &getClassType() const; + [[nodiscard]] inline tson::TiledClass *getClass(); /*! Declared in tileson_forward.hpp */ + + [[nodiscard]] inline const std::string &getTypeStr() const; + [[nodiscard]] inline bool isVisible() const; + [[nodiscard]] inline int getX() const; + [[nodiscard]] inline int getY() const; + + [[nodiscard]] inline std::vector &getChunks(); + [[nodiscard]] inline std::vector &getLayers(); + [[nodiscard]] inline std::vector &getObjects(); + [[nodiscard]] inline PropertyCollection &getProperties(); + + inline tson::Object *getObj(int id); + inline tson::Object *firstObj(const std::string &name); + inline std::vector getObjectsByName(const std::string &name); + inline std::vector getObjectsByType(tson::ObjectType type); + + template + inline T get(const std::string &name); + inline tson::Property * getProp(const std::string &name); + + inline void assignTileMap(std::map *tileMap); + inline void createTileData(const Vector2i &mapSize, bool isInfiniteMap); + + [[nodiscard]] inline const std::map, tson::Tile *> &getTileData() const; + inline tson::Tile * getTileData(int x, int y); + + //v1.2.0-stuff + [[nodiscard]] inline const Colori &getTintColor() const; + [[nodiscard]] inline tson::Map *getMap() const; + + [[nodiscard]] inline std::map, tson::TileObject> &getTileObjects(); + inline tson::TileObject * getTileObject(int x, int y); + [[nodiscard]] inline const std::set &getUniqueFlaggedTiles() const; + inline void resolveFlaggedTiles(); + + private: + inline void setTypeByString(); + + std::vector m_chunks; /*! 'chunks': Array of chunks (optional). tilelayer only. */ + std::string m_compression; /*! 'compression': zlib, gzip or empty (default). tilelayer only. */ + std::vector m_data; /*! 'data' (when uint array): Array of unsigned int (GIDs) or base64-encoded + * data. tilelayer only. */ + std::string m_base64Data; /*! 'data' (when string): Array of unsigned int (GIDs) or base64-encoded + * data. tilelayer only. */ + std::string m_drawOrder; /*! 'draworder': topdown (default) or index. objectgroup only. */ + std::string m_encoding; /*! 'encoding': csv (default) or base64. tilelayer only. */ + int m_id{}; /*! 'id': Incremental id - unique across all layers */ + std::string m_image; /*! 'image': Image used by this layer. imagelayer only. */ + std::vector m_layers; /*! 'layers': Array of layers. group on */ + std::string m_name; /*! 'name': Name assigned to this layer */ + std::vector m_objects; /*! 'objects': Array of objects. objectgroup only. */ + tson::Vector2f m_offset; /*! 'offsetx' and 'offsety': Horizontal and Vertical layer offset in pixels + * (default: {0, 0}) */ + float m_opacity{}; /*! 'opacity': Value between 0 and 1 */ + tson::PropertyCollection m_properties; /*! 'properties': A list of properties (name, value, type). */ + tson::Vector2i m_size; /*! x = 'width': (Column count. Same as map width for fixed-size maps.) + y = 'height': Row count. Same as map height for fixed-size maps. */ + tson::Colori m_transparentColor; /*! 'transparentcolor': Hex-formatted color (#RRGGBB) (optional, imagelayer only */ + std::string m_typeStr; /*! 'type': tilelayer, objectgroup, imagelayer or group */ + LayerType m_type {LayerType::Undefined}; /*! Layer type as enum*/ + bool m_visible{}; /*! 'visible': Whether layer is shown or hidden in editor */ + int m_x{}; /*! 'x': Horizontal layer offset in tiles. Always 0. */ + int m_y{}; /*! 'y': Vertical layer offset in tiles. Always 0. */ + tson::Vector2f m_parallax{1.f, 1.f}; /*! Tiled v1.5: parallax factor for this layer. Defaults to 1. + x = 'parallaxx', y = 'parallaxy'*/ + bool m_repeatX {}; /*! 'repeatx': Whether the image drawn by this layer is repeated along the X axis. (since Tiled 1.8)*/ + bool m_repeatY {}; /*! 'repeaty': Whether the image drawn by this layer is repeated along the Y axis. (since Tiled 1.8)*/ + + std::map *m_tileMap; + std::map, tson::Tile*> m_tileData; /*! Key: Tuple of x and y pos in tile units. */ + + //v1.2.0-stuff + tson::Colori m_tintColor; /*! 'tintcolor': Hex-formatted color (#RRGGBB or #AARRGGBB) that is multiplied with + * any graphics drawn by this layer or any child layers (optional). */ + inline void decompressData(); /*! Defined in tileson_forward.hpp */ + inline void queueFlaggedTile(size_t x, size_t y, uint32_t id); /*! Queue a flagged tile */ + + tson::Map * m_map; /*! The map who owns this layer */ + std::map, tson::TileObject> m_tileObjects; + std::set m_uniqueFlaggedTiles; + std::vector m_flaggedTiles; + + std::string m_classType{}; /*! 'class': The class of this map (since 1.9, defaults to “”). */ + std::shared_ptr m_class {}; + }; + + /*! + * A shortcut for getting a property. Alternative to getProperties().getValue("") + * @tparam T The template value + * @param name Name of the property + * @return The actual value, if it exists. Otherwise: The default value of the type. + */ + template + T Layer::get(const std::string &name) + { + return m_properties.getValue(name); + } +} + +/*! + * Parses a Tiled layer from json + * @param json + */ +tson::Layer::Layer(IJson &json, tson::Map *map) +{ + parse(json, map); +} + +void tson::Layer::queueFlaggedTile(size_t x, size_t y, uint32_t id) +{ + uint32_t tileId = id; + tileId &= ~(FLIPPED_HORIZONTALLY_FLAG | FLIPPED_VERTICALLY_FLAG | FLIPPED_DIAGONALLY_FLAG); + m_uniqueFlaggedTiles.insert(id); + m_flaggedTiles.emplace_back(x, y, id, tileId); +} + +/*! + * Copies all objects with a name that equals the parameter + * @param name Name of the objects to return + * @return All objects with a matching name + */ +std::vector tson::Layer::getObjectsByName(const std::string &name) +{ + std::vector found; + + std::copy_if(m_objects.begin(), m_objects.end(), std::back_inserter(found), [&](const tson::Object &item) + { + return item.getName() == name; + }); + + return found; +} + +/*! + * Copies all objects with a type that equals the parameter + * @param type LayerType of the objects to return + * @return All objects with a matching type + */ +std::vector tson::Layer::getObjectsByType(tson::ObjectType type) +{ + std::vector found; + + std::copy_if(m_objects.begin(), m_objects.end(), std::back_inserter(found), [&](const tson::Object &item) + { + return item.getObjectType() == type; + }); + + return found; +} + +/*! + * Returns the first object with the given name + * @param name Name of the object to find. + * @return A pointer to the object if found. nullptr otherwise. + */ +tson::Object *tson::Layer::firstObj(const std::string &name) +{ + auto result = std::find_if(m_objects.begin(), m_objects.end(), [&](const tson::Object &obj){return obj.getName() == name; }); + if(result == m_objects.end()) + return nullptr; + + return &result.operator*(); +} + +/*! + * Get an object by ID + * @param id Unique ID of the object + * @return A pointer to the object if found. nullptr otherwise. + */ +tson::Object *tson::Layer::getObj(int id) +{ + auto result = std::find_if(m_objects.begin(), m_objects.end(), [&](const tson::Object &obj){return obj.getId() == id; }); + if(result == m_objects.end()) + return nullptr; + + return &result.operator*(); +} + +/*! + * Set type by string + * tilelayer, objectgroup, imagelayer or group + */ +void tson::Layer::setTypeByString() +{ + if(m_typeStr == "tilelayer") m_type = LayerType::TileLayer; + else if(m_typeStr == "objectgroup") m_type = LayerType::ObjectGroup; + else if(m_typeStr == "imagelayer") m_type = LayerType::ImageLayer; + else if(m_typeStr == "group") m_type = LayerType::Group; + else m_type = LayerType::Undefined; +} + +/*! + * 'compression': zlib, gzip or empty (default). tilelayer only. + * @return + */ +const std::string &tson::Layer::getCompression() const +{ + return m_compression; +} + +/*! + * 'data' (when uint array): Array of unsigned int (GIDs) or base64-encoded data. tilelayer only. + * @return + */ +const std::vector &tson::Layer::getData() const +{ + return m_data; +} + +/*! + * 'data' (when string): Array of unsigned int (GIDs) or base64-encoded data. tilelayer only. + * @return + */ +const std::string &tson::Layer::getBase64Data() const +{ + return m_base64Data; +} + +/*! + * 'draworder': topdown (default) or index. objectgroup only. + * @return + */ +const std::string &tson::Layer::getDrawOrder() const +{ + return m_drawOrder; +} + +/*! + * 'encoding': csv (default) or base64. tilelayer only. + * @return + */ +const std::string &tson::Layer::getEncoding() const +{ + return m_encoding; +} + +/*! + * 'id': Incremental id - unique across all layers + * @return + */ +int tson::Layer::getId() const +{ + return m_id; +} + +/*! + * 'image': Image used by this layer. imagelayer only. + * @return + */ +const std::string &tson::Layer::getImage() const +{ + return m_image; +} + +/*! + * 'name': Name assigned to this layer + * @return + */ +const std::string &tson::Layer::getName() const +{ + return m_name; +} + +/*! + * 'offsetx' and 'offsety': Horizontal and Vertical layer offset in pixels (default: {0, 0}) + * @return + */ +const tson::Vector2f &tson::Layer::getOffset() const +{ + return m_offset; +} + +/*! + * 'opacity': Value between 0 and 1 + * @return + */ +float tson::Layer::getOpacity() const +{ + return m_opacity; +} + +/*! + * x = 'width': (Column count. Same as map width for fixed-size maps.) + * y = 'height': Row count. Same as map height for fixed-size maps. + * @return width and height as a single size + */ +const tson::Vector2i &tson::Layer::getSize() const +{ + return m_size; +} + +/*! + * 'transparentcolor': Color created from a hex color (#RRGGBB) (optional, imagelayer only) + * @return color as color object with rgba channel. + */ +const tson::Colori &tson::Layer::getTransparentColor() const +{ + return m_transparentColor; +} + +/*! + * 'type': tilelayer, objectgroup, imagelayer or group + * @return string with the object type + */ +const std::string &tson::Layer::getTypeStr() const +{ + return m_typeStr; +} + +/*! + * 'visible': Whether layer is shown or hidden in editor + * @return + */ +bool tson::Layer::isVisible() const +{ + return m_visible; +} + +/*! + * 'x': Horizontal layer offset in tiles. Always 0. + * @return x value (always 0 for layer) + */ +int tson::Layer::getX() const +{ + return m_x; +} + +/*! + * 'y': Horizontal layer offset in tiles. Always 0. + * @return y value (always 0 for layer) + */ +int tson::Layer::getY() const +{ + return m_y; +} + +/*! + * 'chunks': Array of chunks (optional). tilelayer only. + * @return + */ +std::vector &tson::Layer::getChunks() +{ + return m_chunks; +} + +/*! + * 'layers': Array of layers. group on + * @return + */ +std::vector &tson::Layer::getLayers() +{ + return m_layers; +} + +/*! + * 'objects': Array of objects. objectgroup only. + * @return + */ +std::vector &tson::Layer::getObjects() +{ + return m_objects; +} + +/*! + * 'properties': A list of properties (name, value, type). + * @return + */ +tson::PropertyCollection &tson::Layer::getProperties() +{ + return m_properties; +} + +/*! + * Shortcut for getting a property object. Alternative to getProperties().getProperty(""); + * @param name Name of the property + * @return + */ +tson::Property *tson::Layer::getProp(const std::string &name) +{ + if(m_properties.hasProperty(name)) + return m_properties.getProperty(name); + return nullptr; +} + +/*! + * Get layer type + * @return Layer type as enum + */ +tson::LayerType tson::Layer::getType() const +{ + return m_type; +} + +/*! + * Assigns a tilemap of pointers to existing tiles. + * @param tileMap The tilemap. key: tile id, value: pointer to Tile. + */ +void tson::Layer::assignTileMap(std::map *tileMap) +{ + m_tileMap = tileMap; +} + +/*! + * Get tile data as some kind of map with x and y position with pointers to existing tiles. + * Map only contains tiles that are not empty. x and y position is in tile units. + * + * Example of getting tile from the returned map: + * + * Tile *tile = tileData[{0, 4}]; + * + * @return A map that represents the data returned from getData() in a 2D map with Tile pointers. + */ +const std::map, tson::Tile *> &tson::Layer::getTileData() const +{ + return m_tileData; +} + +/*! + * A safe way to get tile data + * Get tile data as some kind of map with x and y position with pointers to existing tiles. + * Map only contains tiles that are not empty. x and y position is in tile units. + * + * Example of getting tile: + * Tile *tile = layer->getTileData(0, 4) + * + * @param x X position in tile units + * @param y Y position in tile units + * @return pointer to tile, if it exists. nullptr otherwise. + */ +tson::Tile *tson::Layer::getTileData(int x, int y) +{ + return (m_tileData.count({x, y}) > 0) ? m_tileData[{x,y}] : nullptr; +} + +/*! + * Used for getting the tson::Map who is the parent of this Layer. + * @return a pointer to the tson::Map where this layer is contained. + */ +tson::Map *tson::Layer::getMap() const +{ + return m_map; +} + +/*! + * + * This is only supported for non-infinite maps! + * + * @param mapSize The size of the map + * @param isInfiniteMap Whether or not the current map is infinte. + */ +void tson::Layer::createTileData(const Vector2i &mapSize, bool isInfiniteMap) +{ + size_t x = 0; + size_t y = 0; + if(!isInfiniteMap) + { + std::for_each(m_data.begin(), m_data.end(), [&](uint32_t tileId) + { + if (static_cast(x) == mapSize.x) + { + ++y; + x = 0; + } + + if (tileId > 0 && m_tileMap->count(tileId) > 0) + { + m_tileData[{static_cast(x), static_cast(y)}] = m_tileMap->at(tileId); + m_tileObjects[{static_cast(x), static_cast(y)}] = {{static_cast(x), static_cast(y)}, m_tileData[{static_cast(x), static_cast(y)}]}; + } + else if(tileId > 0 && m_tileMap->count(tileId) == 0) //Tile with flip flags! + { + queueFlaggedTile(x, y, tileId); + } + x++; + }); + + } +} + +std::map, tson::TileObject> &tson::Layer::getTileObjects() +{ + return m_tileObjects; +} + +tson::TileObject *tson::Layer::getTileObject(int x, int y) +{ + return (m_tileObjects.count({x, y}) > 0) ? &m_tileObjects[{x,y}] : nullptr; +} + +const std::set &tson::Layer::getUniqueFlaggedTiles() const +{ + return m_uniqueFlaggedTiles; +} + +void tson::Layer::resolveFlaggedTiles() +{ + std::for_each(m_flaggedTiles.begin(), m_flaggedTiles.end(), [&](const tson::FlaggedTile &tile) + { + if (tile.id > 0 && m_tileMap->count(tile.id) > 0) + { + m_tileData[{static_cast(tile.x), static_cast(tile.y)}] = m_tileMap->at(tile.id); + m_tileObjects[{static_cast(tile.x), static_cast(tile.y)}] = {{static_cast(tile.x), static_cast(tile.y)}, m_tileData[{static_cast(tile.x), static_cast(tile.y)}]}; + } + }); +} + +/*! + * 'tintcolor': Hex-formatted color (#RRGGBB or #AARRGGBB) that is multiplied with any graphics drawn by this layer or any child layers (optional). + * + * @return tintcolor + */ +const tson::Colori &tson::Layer::getTintColor() const +{ + return m_tintColor; +} + +/*! + * New in Tiled v1.5 + * Gets the parallax factor for current layer. Defaults to 1. + * @return A vector with the x and y values of the parallax factor. + */ +const tson::Vector2f &tson::Layer::getParallax() const +{ + return m_parallax; +} + +/*! + * New in Tiled v1.8 + * 'repeatx': Whether the image drawn by this layer is repeated along the X axis. + * @return true if image layer is repeated along the X axis, false otherwise. + */ +bool tson::Layer::hasRepeatX() const +{ + return m_repeatX; +} + +/*! + * New in Tiled v1.8 + * 'repeatx': Whether the image drawn by this layer is repeated along the Y axis. + * @return true if image layer is repeated along the Y axis, false otherwise. + */ +bool tson::Layer::hasRepeatY() const +{ + return m_repeatY; +} + +const std::string &tson::Layer::getClassType() const +{ + return m_classType; +} + +#endif //TILESON_LAYER_HPP + +/*** End of inlined file: Layer.hpp ***/ + + +/*** Start of inlined file: Tileset.hpp ***/ +// +// Created by robin on 22.03.2020. +// + +#ifndef TILESON_TILESET_HPP +#define TILESON_TILESET_HPP + +//#include "../external/json.hpp" + + +/*** Start of inlined file: Transformations.hpp ***/ +// +// Created by robin on 04.04.2021. +// + +#ifndef TILESON_TRANSFORMATIONS_HPP +#define TILESON_TRANSFORMATIONS_HPP + +namespace tson +{ + class Transformations + { + public: + inline Transformations() = default; + inline explicit Transformations(IJson &json); + inline bool parse(IJson &json); + + inline bool allowHflip() const; + inline bool allowPreferuntransformed() const; + inline bool allowRotation() const; + inline bool allowVflip() const; + + private: + bool m_hflip {}; /*! hflip: Whether the tiles in this set can be flipped horizontally (default false) */ + bool m_preferuntransformed {}; /*! preferuntransformed: Whether untransformed tiles remain preferred, otherwise transformed tiles are used to produce more variations (default false) */ + bool m_rotate {}; /*! rotate: Whether the tiles in this set can be rotated in 90 degree increments (default false) */ + bool m_vflip {}; /*! vflip: Whether the tiles in this set can be flipped vertically (default false) */ + }; + + Transformations::Transformations(IJson &json) + { + parse(json); + } + + bool Transformations::parse(IJson &json) + { + if(json.count("hflip") > 0) m_hflip = json["hflip"].get(); //Optional + if(json.count("preferuntransformed") > 0) m_preferuntransformed = json["preferuntransformed"].get(); //Optional + if(json.count("rotate") > 0) m_rotate = json["rotate"].get(); //Optional + if(json.count("vflip") > 0) m_vflip = json["vflip"].get(); //Optional + + return true; + } + + /*! + * + * @return hflip: Whether the tiles in this set can be flipped horizontally (default false) + */ + bool Transformations::allowHflip() const + { + return m_hflip; + } + + /*! + * + * @return preferuntransformed: Whether untransformed tiles remain preferred, otherwise transformed tiles are used to produce more variations (default false) + */ + bool Transformations::allowPreferuntransformed() const + { + return m_preferuntransformed; + } + + /*! + * + * @return rotate: Whether the tiles in this set can be rotated in 90 degree increments (default false) + */ + bool Transformations::allowRotation() const + { + return m_rotate; + } + + /*! + * + * @return vflip: Whether the tiles in this set can be flipped vertically (default false) + */ + bool Transformations::allowVflip() const + { + return m_vflip; + } +} + +#endif //TILESON_TRANSFORMATIONS_HPP + +/*** End of inlined file: Transformations.hpp ***/ + + +/*** Start of inlined file: WangSet.hpp ***/ +// +// Created by robin on 22.03.2020. +// + +#ifndef TILESON_WANGSET_HPP +#define TILESON_WANGSET_HPP + +//#include "../external/json.hpp" + +/*** Start of inlined file: WangColor.hpp ***/ +// +// Created by robin on 22.03.2020. +// + +#ifndef TILESON_WANGCOLOR_HPP +#define TILESON_WANGCOLOR_HPP + +//#include "../external/json.hpp" + +namespace tson +{ + class WangColor + { + public: + inline WangColor() = default; + inline explicit WangColor(IJson &json, tson::Map *map); + inline bool parse(IJson &json, tson::Map *map); + + [[nodiscard]] inline const Colori &getColor() const; + [[nodiscard]] inline const std::string &getName() const; + [[nodiscard]] inline float getProbability() const; + [[nodiscard]] inline int getTile() const; + + inline PropertyCollection &getProperties(); + template + inline T get(const std::string &name); + inline tson::Property * getProp(const std::string &name); + + [[nodiscard]] inline const std::string &getClassType() const; + [[nodiscard]] inline tson::TiledClass *getClass(); /*! Declared in tileson_forward.hpp */ + + private: + tson::Colori m_color; /*! 'color': Hex-formatted color (#RRGGBB or #AARRGGBB) */ + std::string m_name; /*! 'name': Name of the Wang color */ + float m_probability{}; /*! 'probability': Probability used when randomizing */ + int m_tile{}; /*! 'tile': Local ID of tile representing the Wang color */ + + //New in Tiled v1.5 + tson::PropertyCollection m_properties; /*! 'properties': A list of properties (name, value, type). */ + tson::Map * m_map; + std::string m_classType {}; /*! 'class': The class of this map (since 1.9, defaults to “”). */ + std::shared_ptr m_class {}; + + }; +} + +tson::WangColor::WangColor(IJson &json, tson::Map *map) +{ + parse(json, map); +} + +bool tson::WangColor::parse(IJson &json, tson::Map *map) +{ + m_map = map; + bool allFound = true; + + if(json.count("color") > 0) m_color = tson::Colori(json["color"].get()); else allFound = false; + if(json.count("name") > 0) m_name = json["name"].get(); else allFound = false; + if(json.count("probability") > 0) m_probability = json["probability"].get(); else allFound = false; + if(json.count("tile") > 0) m_tile = json["tile"].get(); else allFound = false; + if(json.count("class") > 0) m_classType = json["class"].get(); //Optional + + if(json.count("properties") > 0 && json["properties"].isArray()) + { + auto &properties = json.array("properties"); + std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr &item) { m_properties.add(*item); }); + } + + return allFound; +} + +/*! + * 'color': Color object created from hex-formatted string (#RRGGBB or #AARRGGBB) + * @return + */ +const tson::Colori &tson::WangColor::getColor() const +{ + return m_color; +} + +/*! + * 'name': Name of the Wang color + * @return + */ +const std::string &tson::WangColor::getName() const +{ + return m_name; +} + +/*! + * 'probability': Probability used when randomizing + * @return + */ +float tson::WangColor::getProbability() const +{ + return m_probability; +} + +/*! + * 'tile': Local ID of tile representing the Wang color + * @return + */ +int tson::WangColor::getTile() const +{ + return m_tile; +} + +/*! + * New property in Tiled v1.5 when data is contained in 'colors' of a wangset + * 'properties': A list of properties (name, value, type). + * @return + */ +tson::PropertyCollection &tson::WangColor::getProperties() +{ + return m_properties; +} + +/*! + * A shortcut for getting a property. Alternative to getProperties().getValue("") + * @tparam T The template value + * @param name Name of the property + * @return The actual value, if it exists. Otherwise: The default value of the type. + */ +template +T tson::WangColor::get(const std::string &name) +{ + return m_properties.getValue(name); +} + +/*! + * Shortcut for getting a property object. Alternative to getProperties().getProperty(""); + * @param name Name of the property + * @return + */ +tson::Property *tson::WangColor::getProp(const std::string &name) +{ + if(m_properties.hasProperty(name)) + return m_properties.getProperty(name); + + return nullptr; +} + +const std::string &tson::WangColor::getClassType() const +{ + return m_classType; +} + +#endif //TILESON_WANGCOLOR_HPP + +/*** End of inlined file: WangColor.hpp ***/ + + + +/*** Start of inlined file: WangTile.hpp ***/ +// +// Created by robin on 22.03.2020. +// + +#ifndef TILESON_WANGTILE_HPP +#define TILESON_WANGTILE_HPP + +//#include "../external/json.hpp" + +namespace tson +{ + class WangTile + { + public: + inline WangTile() = default; + inline explicit WangTile(IJson &json); + inline bool parse(IJson &json); + + [[nodiscard]] inline bool hasDFlip() const; + [[nodiscard]] inline bool hasHFlip() const; + [[nodiscard]] inline uint32_t getTileid() const; + [[nodiscard]] inline bool hasVFlip() const; + + [[nodiscard]] inline const std::vector &getWangIds() const; + + private: + bool m_dflip{}; /*! 'dflip': Tile is flipped diagonally */ + bool m_hflip{}; /*! 'hflip': Tile is flipped horizontally */ + uint32_t m_tileid{}; /*! 'tileid': Local ID of tile */ + bool m_vflip{}; /*! 'vflip': Tile is flipped vertically */ + std::vector m_wangId; /*! 'wangid': Array of Wang color indexes (uchar[8])*/ + }; +} + +tson::WangTile::WangTile(IJson &json) +{ + parse(json); +} + +/*! + * Parses a wang tile from Tiled json. + * @param json A Tiled json file + * @return true if all mandatory fields were found. False otherwise. + */ +bool tson::WangTile::parse(IJson &json) +{ + bool allFound = true; + + if(json.count("dflip") > 0) m_dflip = json["dflip"].get(); //Removed in Tiled v1.5 and is now optional + if(json.count("hflip") > 0) m_hflip = json["hflip"].get(); //Removed in Tiled v1.5 and is now optional + if(json.count("vflip") > 0) m_vflip = json["vflip"].get(); //Removed in Tiled v1.5 and is now optional + + if(json.count("tileid") > 0) m_tileid = json["tileid"].get(); else allFound = false; + if(json.count("wangid") > 0 && json["wangid"].isArray()) + { + auto &wangid = json.array("wangid"); + std::for_each(wangid.begin(), wangid.end(), [&](std::unique_ptr &item) { m_wangId.emplace_back(item->get()); }); + } + + return allFound; +} + +/*! + * 'dflip': Tile is flipped diagonally + * + * NB! This property got removed in Tiled v1.5 + * @return + */ +bool tson::WangTile::hasDFlip() const +{ + return m_dflip; +} + +/*! + * 'hflip': Tile is flipped horizontally + * + * NB! This property got removed in Tiled v1.5 + * @return + */ +bool tson::WangTile::hasHFlip() const +{ + return m_hflip; +} + +/*! + * 'tileid': Local ID of tile + * @return + */ +uint32_t tson::WangTile::getTileid() const +{ + return m_tileid; +} + +/*! + * 'vflip': Tile is flipped vertically + * + * NB! This property got removed in Tiled v1.5 + * @return + */ +bool tson::WangTile::hasVFlip() const +{ + return m_vflip; +} + +/*! + * 'wangid': Array of Wang color indexes (uchar[8]) + * @return + */ +const std::vector &tson::WangTile::getWangIds() const +{ + return m_wangId; +} + +#endif //TILESON_WANGTILE_HPP + +/*** End of inlined file: WangTile.hpp ***/ + +namespace tson +{ + class WangSet + { + public: + inline WangSet() = default; + inline explicit WangSet(IJson &json, tson::Map *map); + inline bool parse(IJson &json, tson::Map *map); + + [[nodiscard]] inline const std::string &getName() const; + [[nodiscard]] inline int getTile() const; + + [[nodiscard]] inline const std::vector &getWangTiles() const; + [[nodiscard]] inline const std::vector &getCornerColors() const; + [[nodiscard]] inline const std::vector &getEdgeColors() const; + + inline tson::WangColor * getColor(const std::string &name); + inline const std::vector &getColors() const; + inline PropertyCollection &getProperties(); + + template + inline T get(const std::string &name); + inline tson::Property * getProp(const std::string &name); + + [[nodiscard]] inline const std::string &getClassType() const; + [[nodiscard]] inline tson::TiledClass *getClass(); /*! Declared in tileson_forward.hpp */ + + private: + + inline bool parseTiled15Props(IJson &json); + + std::string m_name; /*! 'name': Name of the Wang set */ + int m_tile{}; /*! 'tile': Local ID of tile representing the Wang set */ + std::vector m_wangTiles; /*! 'wangtiles': Array of Wang tiles */ + std::vector m_cornerColors; /*! 'cornercolors': Array of Wang colors */ + std::vector m_edgeColors; /*! 'edgecolors': Array of Wang colors */ + tson::PropertyCollection m_properties; /*! 'properties': A list of properties (name, value, type). */ + + //Tiled v1.5 + std::vector m_colors; /*! 'colors': */ + + tson::Map * m_map; + std::string m_classType {}; /*! 'class': The class of this map (since 1.9, defaults to “”). */ + std::shared_ptr m_class {}; + + }; + + /*! + * A shortcut for getting a property. Alternative to getProperties().getValue("") + * @tparam T The template value + * @param name Name of the property + * @return The actual value, if it exists. Otherwise: The default value of the type. + */ + template + T tson::WangSet::get(const std::string &name) + { + return m_properties.getValue(name); + } +} + +tson::WangSet::WangSet(IJson &json, tson::Map *map) +{ + parse(json, map); +} + +bool tson::WangSet::parse(IJson &json, tson::Map *map) +{ + m_map = map; + bool allFound = true; + + if(json.count("tile") > 0) m_tile = json["tile"].get(); else allFound = false; + if(json.count("name") > 0) m_name = json["name"].get(); else allFound = false; + + //More advanced data + if(json.count("wangtiles") > 0 && json["wangtiles"].isArray()) + { + auto &wangtiles = json.array("wangtiles"); + std::for_each(wangtiles.begin(), wangtiles.end(), [&](std::unique_ptr &item) { m_wangTiles.emplace_back(*item); }); + } + if(json.count("cornercolors") > 0 && json["cornercolors"].isArray()) + { + auto &cornercolors = json.array("cornercolors"); + std::for_each(cornercolors.begin(), cornercolors.end(), [&](std::unique_ptr &item) { m_cornerColors.emplace_back(*item, m_map); }); + } + if(json.count("edgecolors") > 0 && json["edgecolors"].isArray()) + { + auto &edgecolors = json.array("edgecolors"); + std::for_each(edgecolors.begin(), edgecolors.end(), [&](std::unique_ptr &item) { m_edgeColors.emplace_back(*item, m_map); }); + } + if(json.count("properties") > 0 && json["properties"].isArray()) + { + auto &properties = json.array("properties"); + std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr &item) { m_properties.add(*item); }); + } + + if(json.count("class") > 0) m_classType = json["class"].get(); //Optional + + if(!parseTiled15Props(json)) + allFound = false; + + return allFound; +} + +/*! + * Parsing data related to Tiled v1.5 changes + * @param json The json containing data + * @return Returns true if success + */ + +bool tson::WangSet::parseTiled15Props(tson::IJson &json) +{ + if(json.count("colors") > 0 && json["colors"].isArray()) + { + auto &colors = json.array("colors"); + std::for_each(colors.begin(), colors.end(), [&](std::unique_ptr &item) { m_colors.emplace_back(*item, m_map); }); + } + return true; +} + +/*! + * 'name': Name of the Wang set + * @return + */ +const std::string &tson::WangSet::getName() const +{ + return m_name; +} + +/*! + * 'tile': Local ID of tile representing the Wang set + * @return + */ +int tson::WangSet::getTile() const +{ + return m_tile; +} + +/*! + * 'wangtiles': Array of Wang tiles + * @return + */ +const std::vector &tson::WangSet::getWangTiles() const +{ + return m_wangTiles; +} + +/*! + * 'cornercolors': Array of Wang colors + * @return + */ +const std::vector &tson::WangSet::getCornerColors() const +{ + return m_cornerColors; +} + +/*! + * 'edgecolors': Array of Wang colors + * @return + */ +const std::vector &tson::WangSet::getEdgeColors() const +{ + return m_edgeColors; +} + +/*! + * 'properties': A list of properties (name, value, type). + * @return + */ +tson::PropertyCollection &tson::WangSet::getProperties() +{ + return m_properties; +} + +/*! + * Shortcut for getting a property object. Alternative to getProperties().getProperty(""); + * @param name Name of the property + * @return + */ +tson::Property *tson::WangSet::getProp(const std::string &name) +{ + if(m_properties.hasProperty(name)) + return m_properties.getProperty(name); + + return nullptr; +} + +/*! + * Get Wangset colors (new in Tiled v1.5) + * @return + */ +const std::vector &tson::WangSet::getColors() const +{ + return m_colors; +} + +/*! + * NB! Will only work with maps created/modified by Tiled v1.5 or later! + * Gets a color from a wangset by its name. + * + * @param name 'name' of WangColor + * @return The WangColor with the given name or nullptr if it doesn't exist. + */ +tson::WangColor *tson::WangSet::getColor(const std::string &name) +{ + auto color = std::find_if(m_colors.begin(), m_colors.end(), [&](const auto &c) { return c.getName() == name; }); + + if(color != m_colors.end()) + return &color.operator*(); + + return nullptr; +} + +const std::string &tson::WangSet::getClassType() const +{ + return m_classType; +} + +#endif //TILESON_WANGSET_HPP + +/*** End of inlined file: WangSet.hpp ***/ + + +/*** Start of inlined file: Tile.hpp ***/ +// +// Created by robin on 22.03.2020. +// + +#ifndef TILESON_TILE_HPP +#define TILESON_TILE_HPP + +//#include "../external/json.hpp" + + +/*** Start of inlined file: Frame.hpp ***/ +// +// Created by robin on 22.03.2020. +// + +#ifndef TILESON_FRAME_HPP +#define TILESON_FRAME_HPP + +//#include "../external/json.hpp" + +namespace tson +{ + class Frame + { + public: + inline Frame() = default; + inline Frame(int duration, uint32_t tileId); + inline explicit Frame(IJson &json); + + inline bool parse(IJson &json); + + [[nodiscard]] inline int getDuration() const; + [[nodiscard]] inline uint32_t getTileId() const; + + private: + int m_duration {}; /*! 'duration': Frame duration in milliseconds */ + uint32_t m_tileId {}; /*! 'tileid': Local tile ID representing this frame */ + }; +} + +/*! + * + * @param duration duration in milliseconds + * @param tileId TileId + */ +tson::Frame::Frame(int duration, uint32_t tileId) : m_duration {duration}, m_tileId {tileId} +{ + +} + +/*! + * Parses frame data from json + * @param json + */ +tson::Frame::Frame(IJson &json) +{ + parse(json); +} + +/*! + * Parses frame data from json + * @param json + * @return true if all mandatory fields was found. false otherwise. + */ +bool tson::Frame::parse(IJson &json) +{ + bool allFound = true; + + if(json.count("duration") > 0) m_duration = json["duration"].get(); else allFound = false; + if(json.count("tileid") > 0) m_tileId = json["tileid"].get() + 1; else allFound = false; + + return allFound; +} + +/*! + * 'duration': Frame duration in milliseconds + * @return Duration in milliseconds + */ +int tson::Frame::getDuration() const +{ + return m_duration; +} + +/*! + * 'tileid': Local tile ID representing this frame + * @return tile id + */ +uint32_t tson::Frame::getTileId() const +{ + return m_tileId; +} + +#endif //TILESON_FRAME_HPP + +/*** End of inlined file: Frame.hpp ***/ + + +/*** Start of inlined file: Animation.hpp ***/ +// +// Created by robin on 21.05.2021. +// + +#ifndef TILESON_ANIMATION_HPP +#define TILESON_ANIMATION_HPP + +namespace tson +{ + class Animation + { + public: + inline Animation() = default; + inline Animation(const std::vector &frames) : m_frames {frames} {}; + + inline void update(float timeDeltaMs); + inline void reset(); + + inline void setFrames(const std::vector &frames); + inline void setCurrentFrame(uint32_t currentFrame); + inline void setTimeDelta(float timeDelta); + + inline const std::vector &getFrames() const; + inline const tson::Frame *getCurrentFrame() const; + inline uint32_t getCurrentFrameNumber() const; + inline uint32_t getCurrentTileId() const; + inline float getTimeDelta() const; + + inline bool any() const; + inline size_t size() const; + + private: + inline int nextFrame(); + std::vector m_frames; + uint32_t m_currentFrame {0}; + float m_timeDelta {0}; + }; + + const std::vector &Animation::getFrames() const + { + return m_frames; + } + + /*! + * Resets the current frame and time delta to 0. + */ + void Animation::reset() + { + m_currentFrame = 0; + m_timeDelta = 0.f; + } + + /*! + * Gets the current frame or nullptr if no frame is found. + * @return + */ + const tson::Frame *Animation::getCurrentFrame() const + { + return (m_frames.size() == 0 || m_currentFrame >= m_frames.size()) ? nullptr : &m_frames.at(m_currentFrame); + } + + size_t Animation::size() const + { + return m_frames.size(); + } + + /*! + * Update animation based on the fra + * @param timedeltaMs Time in milliseconds + */ + void Animation::update(float timeDeltaMs) + { + const tson::Frame *frame = getCurrentFrame(); + if(frame != nullptr) + { + m_timeDelta += timeDeltaMs; + if(m_timeDelta >= frame->getDuration()) + { + m_timeDelta = static_cast((int32_t)m_timeDelta % frame->getDuration()); + m_currentFrame = nextFrame(); + } + } + } + + int Animation::nextFrame() + { + return (m_currentFrame+1 >= m_frames.size()) ? 0 : m_currentFrame + 1; + } + + float Animation::getTimeDelta() const + { + return m_timeDelta; + } + + uint32_t Animation::getCurrentFrameNumber() const + { + return m_currentFrame; + } + + uint32_t Animation::getCurrentTileId() const + { + return (getCurrentFrame() != nullptr) ? getCurrentFrame()->getTileId() : 0; + } + + void Animation::setFrames(const std::vector &frames) + { + m_frames = frames; + } + + void Animation::setCurrentFrame(uint32_t currentFrame) + { + m_currentFrame = currentFrame; + } + + void Animation::setTimeDelta(float timeDelta) + { + m_timeDelta = timeDelta; + } + + /*! + * True if any frames exists, false otherwise + * @return + */ + bool Animation::any() const + { + return m_frames.size() > 0; + } +} + +#endif //TILESON_ANIMATION_HPP + +/*** End of inlined file: Animation.hpp ***/ + +namespace tson +{ + class Tileset; + class TiledClass; + + class Tile + { + public: + inline Tile() = default; + inline Tile(IJson &json, tson::Tileset *tileset, tson::Map *map); + inline Tile(uint32_t id, tson::Tileset *tileset, tson::Map *map); + inline Tile(uint32_t id, tson::Map *map); //v1.2.0 + inline bool parse(IJson &json, tson::Tileset *tileset, tson::Map *map); + inline bool parseId(IJson &json); + + [[nodiscard]] inline uint32_t getId() const; + [[nodiscard]] inline const fs::path &getImage() const; + [[nodiscard]] inline const Vector2i &getImageSize() const; + [[nodiscard]] inline const std::string &getType() const; + [[nodiscard]] inline const std::string &getClassType() const; + [[nodiscard]] inline tson::TiledClass *getClass(); /*! Declared in tileson_forward.hpp */ + + //[[nodiscard]] inline const std::vector &getAnimation() const; + [[nodiscard]] inline tson::Animation &getAnimation(); + [[nodiscard]] inline Layer &getObjectgroup(); + [[nodiscard]] inline PropertyCollection &getProperties(); + [[nodiscard]] inline const std::vector &getTerrain() const; + + template + inline T get(const std::string &name); + inline tson::Property * getProp(const std::string &name); + + //v1.2.0-stuff + inline void setProperties(const tson::PropertyCollection &properties); + + [[nodiscard]] inline tson::Tileset * getTileset() const; + [[nodiscard]] inline tson::Map * getMap() const; + [[nodiscard]] inline const tson::Rect &getDrawingRect() const; + [[nodiscard]] inline const Rect &getSubRectangle() const; + + inline const tson::Vector2f getPosition(const std::tuple &tileDataPos); + inline const tson::Vector2i getPositionInTileUnits(const std::tuple &tileDataPos); + [[nodiscard]] inline const tson::Vector2i getTileSize() const; /*! Declared in tileson_forward.hpp */ + + [[nodiscard]] inline TileFlipFlags getFlipFlags() const; + inline bool hasFlipFlags(TileFlipFlags flags); + [[nodiscard]] inline uint32_t getGid() const; + + inline void addTilesetAndPerformCalculations(tson::Tileset *tileset); //v1.2.0 + + private: + tson::Animation m_animation{}; /*! 'animation': Array of Frames */ + uint32_t m_id {}; /*! 'id': Local ID of the tile */ + + fs::path m_image; /*! 'image': Image representing this tile (optional)*/ + + tson::Vector2i m_imageSize; /*! x = 'imagewidth' and y = 'imageheight': in pixels */ + tson::Layer m_objectgroup; /*! 'objectgroup': Layer with type objectgroup (optional) */ + tson::PropertyCollection m_properties; /*! 'properties': A list of properties (name, value, type). */ + std::vector m_terrain; /*! 'terrain': Index of terrain for each corner of tile */ + std::string m_type; /*! 'type': The type of the tile (optional) */ + + //v1.2.0-stuff + uint32_t m_gid {}; /*! id without flip flags */ + tson::Tileset * m_tileset; /*! A pointer to the tileset where this Tile comes from */ + tson::Map * m_map; /*! A pointer to the map where this tile is contained */ + tson::Rect m_drawingRect; /*! A rect that shows which part of the tileset that is used for this tile */ + tson::Rect m_subRect; /*! Tiled 1.9: Contains the newly added sub-rectangle variables: 'x', 'y', 'width' and 'height'*/ + tson::TileFlipFlags m_flipFlags = TileFlipFlags::None; /*! Resolved using bit 32, 31 and 30 from gid */ + inline void performDataCalculations(); /*! Declared in tileson_forward.hpp - Calculate all the values used in the tile class. */ + inline void manageFlipFlagsByIdThenRemoveFlags(uint32_t &id); + friend class Layer; + std::shared_ptr m_class {}; + }; + + /*! + * A shortcut for getting a property. Alternative to getProperties().getValue("") + * @tparam T The template value + * @param name Name of the property + * @return The actual value, if it exists. Otherwise: The default value of the type. + */ + template + T tson::Tile::get(const std::string &name) + { + return m_properties.getValue(name); + } +} + +tson::Tile::Tile(IJson &json, tson::Tileset *tileset, tson::Map *map) +{ + parse(json, tileset, map); +} + +/*! + * Used in cases where you have a tile without any property + * @param id + */ +tson::Tile::Tile(uint32_t id, tson::Tileset *tileset, tson::Map *map) : m_id {id}, m_gid {id} +{ + m_tileset = tileset; + m_map = map; + manageFlipFlagsByIdThenRemoveFlags(m_gid); + performDataCalculations(); +} + +/*! + * Used in cases where you have a FLIP FLAGGED tile + * @param id + */ +tson::Tile::Tile(uint32_t id, tson::Map *map) : m_id {id}, m_gid {id} +{ + m_map = map; + manageFlipFlagsByIdThenRemoveFlags(m_gid); +} + +/*! + * For flip flagged tiles, tilesets must be resolved later. + * @param tileset + */ +void tson::Tile::addTilesetAndPerformCalculations(tson::Tileset *tileset) +{ + m_tileset = tileset; + performDataCalculations(); +} + +/*! + * Parses a tile from a Tiled json. id on tile is store as id + 1 to match the references in data containers. + * @param json + * @return + */ +bool tson::Tile::parse(IJson &json, tson::Tileset *tileset, tson::Map *map) +{ + m_tileset = tileset; + m_map = map; + + if(json.count("image") > 0) m_image = fs::path(json["image"].get()); //Optional + + bool allFound = parseId(json); + + if(json.count("type") > 0) m_type = json["type"].get(); //Optional + else if(json.count("class") > 0) m_type = json["class"].get(); //Tiled v1.9 renamed 'type' to 'class' + + if(json.count("objectgroup") > 0) m_objectgroup = tson::Layer(json["objectgroup"], m_map); //Optional + + if(json.count("imagewidth") > 0 && json.count("imageheight") > 0) + m_imageSize = {json["imagewidth"].get(), json["imageheight"].get()}; //Optional + + m_subRect = {0,0, m_imageSize.x, m_imageSize.y}; + if(json.count("x") > 0) m_subRect.x = json["x"].get(); //Optional + if(json.count("y") > 0) m_subRect.y = json["y"].get(); //Optional + if(json.count("width") > 0) m_subRect.width = json["width"].get(); //Optional + if(json.count("height") > 0) m_subRect.height = json["height"].get(); //Optional + + //More advanced data + if(json.count("animation") > 0 && json["animation"].isArray()) + { + auto &animation = json.array("animation"); + std::vector frames; + std::for_each(animation.begin(), animation.end(), [&](std::unique_ptr &item) { frames.emplace_back(*item); }); + if(frames.size() > 0) + { + m_animation.setFrames(frames); + } + } + if(json.count("terrain") > 0 && json["terrain"].isArray()) + { + auto &terrain = json.array("terrain"); + std::for_each(terrain.begin(), terrain.end(), [&](std::unique_ptr &item) { m_terrain.emplace_back(item->get()); }); + } + + if(json.count("properties") > 0 && json["properties"].isArray()) + { + auto &properties = json.array("properties"); + std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr &item) { m_properties.add(*item); }); + } + + performDataCalculations(); + + return allFound; +} + +/*! + * 'id': Local ID of the tile + * @return + */ +uint32_t tson::Tile::getId() const +{ + return m_id; +} + +/*! + * 'image': Image representing this tile (optional) + * @return + */ + +const fs::path &tson::Tile::getImage() const { return m_image; } + +/*! + * x = 'imagewidth' and y = 'imageheight': in pixels + * @return + */ +const tson::Vector2i &tson::Tile::getImageSize() const +{ + return m_imageSize; +} + +/*! + * 'type': The type of the tile (optional) + * This was renamed to 'class' in Tiled v1.9 + * @return + */ +const std::string &tson::Tile::getType() const +{ + return m_type; +} + +/*! + * 'class': String assigned to class field in editor (optional) + * This was renamed from 'type' to 'class' in Tiled v1.9 + * @return + */ +const std::string &tson::Tile::getClassType() const +{ + return m_type; +} + +/*! + * 'animation': Array of Frames + * @return + */ +tson::Animation &tson::Tile::getAnimation() +{ + return m_animation; +} + +/*! + * 'objectgroup': Layer with type objectgroup (optional) + * @return + */ +tson::Layer &tson::Tile::getObjectgroup() +{ + return m_objectgroup; +} + +/*! + * 'properties': A list of properties (name, value, type). + * @return + */ +tson::PropertyCollection &tson::Tile::getProperties() +{ + return m_properties; +} + +/*! + * 'terrain': Index of terrain for each corner of tile + * @return + */ +const std::vector &tson::Tile::getTerrain() const +{ + return m_terrain; +} + +/*! + * Shortcut for getting a property object. Alternative to getProperties().getProperty(""); + * @param name Name of the property + * @return + */ +tson::Property *tson::Tile::getProp(const std::string &name) +{ + if(m_properties.hasProperty(name)) + return m_properties.getProperty(name); + + return nullptr; +} + +/*! + * Used for getting the tson::Tileset who is the parent of this Tile. + * @return a pointer to the tson::Tileset where this tile is contained. + */ +tson::Tileset *tson::Tile::getTileset() const +{ + return m_tileset; +} + +/*! + * Used for getting the tson::Map who is the parent of this Tile. + * @return a pointer to the tson::Map where this tile is contained. + */ +tson::Map *tson::Tile::getMap() const +{ + return m_map; +} + +/*! + * Get the information needed to draw the Tile based on its current tileset + * @return a tson::Rect containing the information needed to draw the tile. + */ +const tson::Rect &tson::Tile::getDrawingRect() const +{ + return m_drawingRect; +} + +/*! + * Helper function. + * + * Get the position of the tile in tile units. + * The size of each unit is determined by the tile size property of the map. + * Example: If the tile size is 16x16 in the map, a tile unit of [2, 4] would be [32, 64] in pixels. + * If you want the position in pixels: use getPosition() instead. + * + * @return Position of tile in tile units. + */ +const tson::Vector2i tson::Tile::getPositionInTileUnits(const std::tuple &tileDataPos) +{ + return {std::get<0>(tileDataPos), std::get<1>(tileDataPos)}; +} + +void tson::Tile::manageFlipFlagsByIdThenRemoveFlags(uint32_t &id) +{ + if (id & FLIPPED_HORIZONTALLY_FLAG) m_flipFlags |= TileFlipFlags::Horizontally; + if (id & FLIPPED_VERTICALLY_FLAG) m_flipFlags |= TileFlipFlags::Vertically; + if (id & FLIPPED_DIAGONALLY_FLAG) m_flipFlags |= TileFlipFlags::Diagonally; + + id &= ~(FLIPPED_HORIZONTALLY_FLAG | FLIPPED_VERTICALLY_FLAG | FLIPPED_DIAGONALLY_FLAG); +} + +tson::TileFlipFlags tson::Tile::getFlipFlags() const +{ + return m_flipFlags; +} + +/*! + * + * @param flags Which flags to check for. Several flags can be checked at once using the bitwise or operator. + * Example: + * hasFlipFlags(TileFlipFlags::Vertically | TileFlipFlags::Horizontally) + * + * @return true if the flag(s) specified are set + */ +bool tson::Tile::hasFlipFlags(tson::TileFlipFlags flags) +{ + return ((m_flipFlags & flags) == flags) ? true : false; +} + +uint32_t tson::Tile::getGid() const +{ + return m_gid; +} + +void tson::Tile::setProperties(const tson::PropertyCollection &properties) +{ + m_properties = properties; +} + +/*! + * Tiled 1.9: Contains the newly added sub-rectangle variables: 'x', 'y', 'width' and 'height' + * @return A tson::Rect with the 'x', 'y', 'width' and 'height' values + */ +const tson::Rect &tson::Tile::getSubRectangle() const +{ + return m_subRect; +} + +#endif //TILESON_TILE_HPP + +/*** End of inlined file: Tile.hpp ***/ + + +/*** Start of inlined file: Terrain.hpp ***/ +// +// Created by robin on 22.03.2020. +// + +#ifndef TILESON_TERRAIN_HPP +#define TILESON_TERRAIN_HPP + +//#include "../external/json.hpp" + +namespace tson +{ + class Terrain + { + public: + inline Terrain() = default; + inline Terrain(std::string name, int tile); + inline explicit Terrain(IJson &json); + + inline bool parse(IJson &json); + + [[nodiscard]] inline const std::string &getName() const; + [[nodiscard]] inline int getTile() const; + [[nodiscard]] inline PropertyCollection &getProperties(); + + template + inline T get(const std::string &name); + inline tson::Property * getProp(const std::string &name); + + private: + std::string m_name; /*! 'name': Name of terrain */ + int m_tile {}; /*! 'tile': Local ID of tile representing terrain */ + tson::PropertyCollection m_properties; /*! 'properties': A list of properties (name, value, type). */ + }; + + /*! + * A shortcut for getting a property. Alternative to getProperties().getValue("") + * @tparam T The template value + * @param name Name of the property + * @return The actual value, if it exists. Otherwise: The default value of the type. + */ + template + T tson::Terrain::get(const std::string &name) + { + return m_properties.getValue(name); + } +} + +tson::Terrain::Terrain(std::string name, int tile) : m_name {std::move(name)}, m_tile {tile} +{ + +} + +tson::Terrain::Terrain(IJson &json) +{ + parse(json); +} + +bool tson::Terrain::parse(IJson &json) +{ + bool allFound = true; + + if(json.count("name") > 0) m_name = json["name"].get(); else allFound = false; + if(json.count("tile") > 0) m_tile = json["tile"].get(); else allFound = false; + + if(json.count("properties") > 0 && json["properties"].isArray()) + { + auto &properties = json.array("properties"); + std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr &item) { m_properties.add(*item); }); + } + + return allFound; +} + +/*! + * 'name': Name of terrain + * @return + */ +const std::string &tson::Terrain::getName() const +{ + return m_name; +} + +/*! + * 'tile': Local ID of tile representing terrain + * @return + */ +int tson::Terrain::getTile() const +{ + return m_tile; +} + +/*! + * 'properties': A list of properties (name, value, type). *Missing from the official Tiled documentation...* + * @return + */ +tson::PropertyCollection &tson::Terrain::getProperties() +{ + return m_properties; +} + +/*! + * Shortcut for getting a property object. Alternative to getProperties().getProperty(""); + * @param name Name of the property + * @return + */ +tson::Property *tson::Terrain::getProp(const std::string &name) +{ + if(m_properties.hasProperty(name)) + return m_properties.getProperty(name); + return nullptr; +} + +#endif //TILESON_TERRAIN_HPP + +/*** End of inlined file: Terrain.hpp ***/ + + +/*** Start of inlined file: Grid.hpp ***/ +// +// Created by robin on 22.03.2020. +// + +#ifndef TILESON_GRID_HPP +#define TILESON_GRID_HPP + +#include +//#include "../external/json.hpp" + +namespace tson +{ + class Grid + { + public: + inline Grid() = default; + inline explicit Grid(IJson &json); + + inline bool parse(IJson &json); + + [[nodiscard]] inline const std::string &getOrientation() const; + [[nodiscard]] inline const Vector2i &getSize() const; + + private: + std::string m_orientation; /*! 'orientation': Orientation of the grid for the tiles in this tileset (orthogonal or isometric) */ + tson::Vector2i m_size; /*! 'width' and 'height': Size. */ + }; +} + +/*! + * Parses Tiled grid data from json + * @param json + */ +tson::Grid::Grid(IJson &json) +{ + parse(json); +} + +/*! + * Parses Tiled grid data from json + * @param json + * @return true if all mandatory fields was found. false otherwise. + */ +bool tson::Grid::parse(IJson &json) +{ + bool allFound = true; + + if(json.count("orientation") > 0) m_orientation = json["orientation"].get(); //Optional + + if(json.count("width") > 0 && json.count("height") > 0) + m_size = {json["width"].get(), json["height"].get()}; else allFound = false; + + return allFound; +} + +/*! + * 'orientation': Orientation of the grid for the tiles in this tileset (orthogonal or isometric) + * @return orientation as string + */ +const std::string &tson::Grid::getOrientation() const +{ + return m_orientation; +} + +/*! + * 'width' and 'height': Size. + * @return size as int + */ +const tson::Vector2i &tson::Grid::getSize() const +{ + return m_size; +} + +#endif //TILESON_GRID_HPP + +/*** End of inlined file: Grid.hpp ***/ + +#include + +namespace tson +{ + class Map; + class Tileset + { + public: + inline Tileset() = default; + inline explicit Tileset(IJson &json, tson::Map *map); + inline bool parse(IJson &json, tson::Map *map); + + [[nodiscard]] inline int getColumns() const; + [[nodiscard]] inline int getFirstgid() const; + + [[nodiscard]] inline const fs::path &getImagePath() const; + [[nodiscard]] inline const fs::path &getImage() const; + [[nodiscard]] inline const Vector2i &getImageSize() const; + [[nodiscard]] inline int getMargin() const; + [[nodiscard]] inline const std::string &getName() const; + [[nodiscard]] inline int getSpacing() const; + [[nodiscard]] inline int getTileCount() const; + [[nodiscard]] inline const Vector2i &getTileSize() const; + [[nodiscard]] inline const Colori &getTransparentColor() const; + [[nodiscard]] inline const std::string &getType() const; + [[nodiscard]] inline const std::string &getClassType() const; + [[nodiscard]] inline tson::TiledClass *getClass(); /*! Declared in tileson_forward.hpp */ + [[nodiscard]] inline std::vector &getTiles(); + [[nodiscard]] inline const std::vector &getWangsets() const; + [[nodiscard]] inline PropertyCollection &getProperties(); + [[nodiscard]] inline const std::vector &getTerrains() const; + [[nodiscard]] inline const Vector2i &getTileOffset() const; + [[nodiscard]] inline const Grid &getGrid() const; + [[nodiscard]] inline TileRenderSize getTileRenderSize() const; + [[nodiscard]] inline FillMode getFillMode() const; + + inline tson::Tile * getTile(uint32_t id); + inline tson::Terrain * getTerrain(const std::string &name); + + template + inline T get(const std::string &name); + inline tson::Property * getProp(const std::string &name); + + //v1.2.0-stuff + [[nodiscard]] inline tson::Map *getMap() const; + [[nodiscard]] inline ObjectAlignment getObjectAlignment() const; + + inline static tson::ObjectAlignment StringToAlignment(std::string_view str); + + //v1.3.0 + inline tson::Vector2i getMarginSpacingOffset(const tson::Vector2i &posInTileUnits); + inline tson::WangSet * getWangset(const std::string &name); + inline const Transformations &getTransformations() const; + + #ifndef TSON_TEST_ENABLED + private: + #endif + inline void generateMissingTiles(); + + int m_columns {}; /*! 'columns': The number of tile columns in the tileset */ + int m_firstgid {}; /*! 'firstgid': GID corresponding to the first tile in the set */ + + fs::path m_image; /*! 'image': Image used for tiles in this set */ + + tson::Vector2i m_imageSize; /*! x = 'imagewidth' and y = 'imageheight': in pixels */ + int m_margin {}; /*! 'margin': Buffer between image edge and first tile (pixels)*/ + std::string m_name; /*! 'name': Name given to this tileset */ + int m_spacing {}; /*! 'spacing': Spacing between adjacent tiles in image (pixels)*/ + int m_tileCount {}; /*! 'tilecount': The number of tiles in this tileset */ + tson::Vector2i m_tileSize; /*! x = 'tilewidth' and y = 'tileheight': Maximum size of tiles in this set */ + tson::Colori m_transparentColor; /*! 'transparentcolor': Hex-formatted color (#RRGGBB) (optional) */ + std::string m_type; /*! 'type': tileset (for tileset files, since 1.0) */ + + std::vector m_tiles; /*! 'tiles': Array of Tiles (optional) */ + std::vector m_wangsets; /*! 'wangsets':Array of Wang sets (since 1.1.5) */ + tson::PropertyCollection m_properties; /*! 'properties': A list of properties (name, value, type). */ + + std::vector m_terrains; /*! 'terrains': Array of Terrains (optional) */ + tson::Vector2i m_tileOffset; /*! 'x' and 'y': See (optional) */ + tson::Grid m_grid; /*! 'grid': This element is only used in case of isometric orientation, and determines + how tile overlays for terrain and collision information are rendered. */ + + //v1.2.0-stuff + tson::ObjectAlignment m_objectAlignment{tson::ObjectAlignment::Unspecified}; /*! 'objectalignment': Alignment to use for tile objects. Tiled 1.4.*/ + tson::Map * m_map; /*! The map who owns this tileset */ + + //v1.3.0-stuff + fs::path m_source {}; /*! 'source': exists only when tileset is contained in an external file*/ + fs::path m_path {}; /*! Has the full path to the tileset if 'source' has an existing value */ + Transformations m_transformations {}; /*! New in Tiled v1.5 - This element is used to describe which transformations can be applied to + the tiles (e.g. to extend a Wang set by transforming existing tiles).*/ + + //v1.4.0-stuff + TileRenderSize m_tileRenderSize {}; /*! 'tilerendersize': The size to use when rendering tiles from this tileset on a tile layer. Valid values are 'tile' (the default) and 'grid'. + * When set to 'grid', the tile is drawn at the tile grid size of the map. (since 1.9)*/ + FillMode m_fillMode {}; /*! 'fillmode': The fill mode to use when rendering tiles from this tileset. Valid values are 'stretch' (the default) and 'preserve-aspect-fit'. + * Only relevant when the tiles are not rendered at their native size, so this applies to resized tile objects or in combination with 'tilerendersize' set to 'grid'. (since 1.9)*/ + + std::string m_classType {}; /*! 'class': The class of this map (since 1.9, defaults to “”). */ + std::shared_ptr m_class {}; + }; + + /*! + * A shortcut for getting a property. Alternative to getProperties().getValue("") + * @tparam T The template value + * @param name Name of the property + * @return The actual value, if it exists. Otherwise: The default value of the type. + */ + template + T tson::Tileset::get(const std::string &name) + { + return m_properties.getValue(name); + } +} + +tson::Tileset::Tileset(IJson &json, tson::Map *map) +{ + parse(json, map); +} + +bool tson::Tileset::parse(IJson &json, tson::Map *map) +{ + m_map = map; + bool allFound = true; + + if(json.count("firstgid") > 0) m_firstgid = json["firstgid"].get(); else allFound = false; + + //Tileset is stored in external file if 'source' exists + if(json.count("source") > 0) + { + if(!allFound) + return allFound; + + std::string sourceStr = json["source"].get(); + m_source = fs::path(sourceStr); + m_path = json.directory() / m_source; + + if(!json.parse(m_path)) + return false; + } + + if(json.count("columns") > 0) m_columns = json["columns"].get(); else allFound = false; + + if(json.count("image") > 0) m_image = fs::path(json["image"].get()); else allFound = false; + + if(json.count("margin") > 0) m_margin = json["margin"].get(); else allFound = false; + if(json.count("name") > 0) m_name = json["name"].get(); else allFound = false; + if(json.count("spacing") > 0) m_spacing = json["spacing"].get(); else allFound = false; + if(json.count("tilecount") > 0) m_tileCount = json["tilecount"].get(); else allFound = false; + if(json.count("transparentcolor") > 0) m_transparentColor = tson::Colori(json["transparentcolor"].get()); //Optional + if(json.count("type") > 0) m_type = json["type"].get(); + if(json.count("grid") > 0) m_grid = tson::Grid(json["grid"]); + if(json.count("class") > 0) m_classType = json["class"].get(); //Optional + + if(json.count("imagewidth") > 0 && json.count("imageheight") > 0) + m_imageSize = {json["imagewidth"].get(), json["imageheight"].get()}; else allFound = false; + if(json.count("tilewidth") > 0 && json.count("tileheight") > 0) + m_tileSize = {json["tilewidth"].get(), json["tileheight"].get()}; else allFound = false; + if(json.count("tileoffset") > 0) + m_tileOffset = {json["tileoffset"]["x"].get(), json["tileoffset"]["y"].get()}; + + if(json.count("tilerendersize") > 0) + { + std::string tileRenderStr = json["tilerendersize"].get(); + if(tileRenderStr == "tile") m_tileRenderSize = TileRenderSize::Tile; + else if(tileRenderStr == "grid") m_tileRenderSize = TileRenderSize::Grid; + } + + if(json.count("fillmode") > 0) + { + std::string fillmode = json["fillmode"].get(); + if(fillmode == "stretch") m_fillMode = FillMode::Stretch; + else if(fillmode == "preserve-aspect-fit") m_fillMode = FillMode::PreserveAspectFit; + } + + //More advanced data + if(json.count("wangsets") > 0 && json["wangsets"].isArray()) + { + auto &wangsets = json.array("wangsets"); + std::for_each(wangsets.begin(), wangsets.end(), [&](std::unique_ptr &item) { m_wangsets.emplace_back(*item, m_map); }); + } + if(json.count("tiles") > 0 && json["tiles"].isArray()) + { + auto &tiles = json.array("tiles"); + std::for_each(tiles.begin(), tiles.end(), [&](std::unique_ptr &item) { m_tiles.emplace_back(*item, this, m_map); }); + } + if(json.count("terrains") > 0 && json["terrains"].isArray()) + { + auto &terrains = json.array("terrains"); + std::for_each(terrains.begin(), terrains.end(), [&](std::unique_ptr &item) { m_terrains.emplace_back(*item); }); + } + + if(json.count("properties") > 0 && json["properties"].isArray()) + { + auto &properties = json.array("properties"); + std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr &item) { m_properties.add(*item); }); + } + + if(json.count("objectalignment") > 0) + { + std::string alignment = json["objectalignment"].get(); + m_objectAlignment = StringToAlignment(alignment); + } + + if(json.count("transformations") > 0) + { + m_transformations.parse(json["transformations"]); + } + + generateMissingTiles(); + + return allFound; +} + +/*! + * 'columns': The number of tile columns in the tileset + * @return + */ +int tson::Tileset::getColumns() const +{ + return m_columns; +} + +/*! + * 'firstgid': GID corresponding to the first tile in the set + * @return + */ +int tson::Tileset::getFirstgid() const +{ + return m_firstgid; +} + +/*! + * 'image': Image used for tiles in this set + * @return + */ + +const fs::path &tson::Tileset::getImagePath() const { return m_image; } + +/*! + * x = 'imagewidth' and y = 'imageheight': in pixels + * @return + */ +const tson::Vector2i &tson::Tileset::getImageSize() const +{ + return m_imageSize; +} + +/*! + * 'margin': Buffer between image edge and first tile (pixels) + * @return + */ +int tson::Tileset::getMargin() const +{ + return m_margin; +} + +/*! + * 'name': Name given to this tileset + * @return + */ +const std::string &tson::Tileset::getName() const +{ + return m_name; +} + +/*! + * 'spacing': Spacing between adjacent tiles in image (pixels) + * @return + */ +int tson::Tileset::getSpacing() const +{ + return m_spacing; +} + +/*! + * 'tilecount': The number of tiles in this tileset + * @return + */ +int tson::Tileset::getTileCount() const +{ + return m_tileCount; +} + +/*! + * x = 'tilewidth' and y = 'tileheight': Maximum size of tiles in this set + * @return + */ +const tson::Vector2i &tson::Tileset::getTileSize() const +{ + return m_tileSize; +} + +/*! + * 'transparentcolor': Color object created by hex-formatted color (#RRGGBB) (optional) + * @return + */ +const tson::Colori &tson::Tileset::getTransparentColor() const +{ + return m_transparentColor; +} + +/*! + * 'type': tileset (for tileset files, since 1.0) + * @return + */ +const std::string &tson::Tileset::getType() const +{ + return m_type; +} + +/*! + * 'image': Image used for tiles in this set + * @return + */ + +const fs::path &tson::Tileset::getImage() const { return m_image; } + +/*! + * 'tiles': Array of Tiles (optional) + * @return + */ +std::vector &tson::Tileset::getTiles() +{ + return m_tiles; +} + +/*! + * 'wangsets':Array of Wang sets (since Tiled 1.1.5) + * @return + */ +const std::vector &tson::Tileset::getWangsets() const +{ + return m_wangsets; +} + +/*! + * 'properties': A list of properties (name, value, type). + * @return + */ +tson::PropertyCollection &tson::Tileset::getProperties() +{ + return m_properties; +} + +/*! + * 'terrains': Array of Terrains (optional) + * @return + */ +const std::vector &tson::Tileset::getTerrains() const +{ + return m_terrains; +} + +/*! + * 'x' and 'y': See (optional) + * @return + */ +const tson::Vector2i &tson::Tileset::getTileOffset() const +{ + return m_tileOffset; +} + +/*! + * 'grid': This element is only used in case of isometric orientation, and determines + * how tile overlays for terrain and collision information are rendered. + * @return + */ +const tson::Grid &tson::Tileset::getGrid() const +{ + return m_grid; +} + +/*! + * Gets a tile by ID (Tiled ID + 1) + * @param id The ID of the tile stored in Tiled map + 1. Example: If ID was stored in Tiled map as 0, the corresponding value in Tileson is 1. + * This is to make sure the IDs of tiles matches their references in containers. + * @return A pointer to the Tile if found. nullptr otherwise. + */ +tson::Tile *tson::Tileset::getTile(uint32_t id) +{ + auto result = std::find_if(m_tiles.begin(), m_tiles.end(), [&](const tson::Tile & item) { return item.getId() == id;}); + if(result == m_tiles.end()) + return nullptr; + + return &result.operator*(); +} + +/*! + * Get an existing Terrain object by name + * @param name + * @return A pointer to the Terrain if found. nullptr otherwise. + */ +tson::Terrain *tson::Tileset::getTerrain(const std::string &name) +{ + auto result = std::find_if(m_terrains.begin(), m_terrains.end(), [&](const tson::Terrain & item) { return item.getName() == name;}); + if(result == m_terrains.end()) + return nullptr; + + return &result.operator*(); +} + +/*! + * Shortcut for getting a property object. Alternative to getProperties().getProperty(""); + * @param name Name of the property + * @return + */ +tson::Property *tson::Tileset::getProp(const std::string &name) +{ + if(m_properties.hasProperty(name)) + return m_properties.getProperty(name); + + return nullptr; +} + +/*! + * Tiled only has tiles with a property stored in the map. This function makes sure even the ones with no properties will exist. + */ +void tson::Tileset::generateMissingTiles() +{ + std::vector tileIds; + for(auto &tile : m_tiles) + tileIds.push_back(tile.getId()); + + for(uint32_t i = m_firstgid; i < m_firstgid + (uint32_t) m_tileCount; ++i) + { + if(std::count(tileIds.begin(), tileIds.end(), i) == 0) + { + m_tiles.emplace_back(Tile(i, this, m_map)); + } + } +} + +/*! + * Used for getting the tson::Map who is the parent of this Tileset. + * @return a pointer to the tson::Map where this tileset is contained. + */ +tson::Map *tson::Tileset::getMap() const +{ + return m_map; +} + +/*! + * + * @param str The string you want to convert + * @return Alignment enum based on the string from the input. + */ +tson::ObjectAlignment tson::Tileset::StringToAlignment(std::string_view str) +{ + if(str == "unspecified") return tson::ObjectAlignment::Unspecified; + else if(str == "topleft") return tson::ObjectAlignment::TopLeft; + else if(str == "top") return tson::ObjectAlignment::Top; + else if(str == "topright") return tson::ObjectAlignment::TopRight; + else if(str == "left") return tson::ObjectAlignment::Left; + else if(str == "center") return tson::ObjectAlignment::Center; + else if(str == "right") return tson::ObjectAlignment::Right; + else if(str == "bottomleft") return tson::ObjectAlignment::BottomLeft; + else if(str == "bottom") return tson::ObjectAlignment::Bottom; + else if(str == "bottomright") return tson::ObjectAlignment::BottomRight; + else + return tson::ObjectAlignment::Unspecified; +} + +tson::ObjectAlignment tson::Tileset::getObjectAlignment() const +{ + return m_objectAlignment; +} + +/*! + * Helper function to calculate the correct additional offset when margin and/or spacing is used in a tileset + * Created to solve issue #31. + * + * @param posInTileUnits Position of the current tile in tile units. + * @return Calculated additional offset in pixels. + */ +tson::Vector2i tson::Tileset::getMarginSpacingOffset(const tson::Vector2i &posInTileUnits) +{ + if(m_margin == 0 && m_spacing == 0) + return {0,0}; + + tson::Vector2i offset {(posInTileUnits.x * m_spacing) + m_margin, (posInTileUnits.y * m_spacing) + m_margin}; + return offset; +} + +/*! + * Get a wangset by name + * @param name + * @return + */ +tson::WangSet *tson::Tileset::getWangset(const std::string &name) +{ + auto wangset = std::find_if(m_wangsets.begin(), m_wangsets.end(), [&](const auto &w) { return w.getName() == name; }); + + if(wangset != m_wangsets.end()) + return &wangset.operator*(); + + return nullptr; +} + +/*! + * New in Tiled v1.5 - This element is used to describe which transformations can be applied to + * the tiles (e.g. to extend a Wang set by transforming existing tiles). + * + * @return + */ +const tson::Transformations &tson::Tileset::getTransformations() const +{ + return m_transformations; +} + +tson::TileRenderSize tson::Tileset::getTileRenderSize() const +{ + return m_tileRenderSize; +} + +tson::FillMode tson::Tileset::getFillMode() const +{ + return m_fillMode; +} + +const std::string &tson::Tileset::getClassType() const +{ + return m_classType; +} + +#endif //TILESON_TILESET_HPP +/*** End of inlined file: Tileset.hpp ***/ + +namespace tson +{ + class Map + { + public: + inline Map() = default; + inline Map(ParseStatus status, std::string description); + inline explicit Map(IJson &json, tson::DecompressorContainer *decompressors, tson::Project *project); + inline bool parse(IJson &json, tson::DecompressorContainer *decompressors, tson::Project *project); + + [[nodiscard]] inline const Colori &getBackgroundColor() const; + [[nodiscard]] inline const Vector2i &getSize() const; + [[nodiscard]] inline int getHexsideLength() const; + [[nodiscard]] inline bool isInfinite() const; + [[nodiscard]] inline int getNextLayerId() const; + [[nodiscard]] inline int getNextObjectId() const; + [[nodiscard]] inline const std::string &getOrientation() const; + [[nodiscard]] inline const std::string &getRenderOrder() const; + [[nodiscard]] inline const std::string &getStaggerAxis() const; + [[nodiscard]] inline const std::string &getStaggerIndex() const; + [[nodiscard]] inline const std::string &getTiledVersion() const; + [[nodiscard]] inline const Vector2i &getTileSize() const; + [[nodiscard]] inline const std::string &getType() const; + [[nodiscard]] inline const std::string &getClassType() const; + [[nodiscard]] inline tson::TiledClass *getClass(); /*! Declared in tileson_forward.hpp */ + [[nodiscard]] inline const Vector2f &getParallaxOrigin() const; + //[[nodiscard]] inline int getVersion() const; //Removed - Tileson v1.3.0 + + [[nodiscard]] inline std::vector &getLayers(); + [[nodiscard]] inline PropertyCollection &getProperties(); + [[nodiscard]] inline std::vector &getTilesets(); + + [[nodiscard]] inline ParseStatus getStatus() const; + [[nodiscard]] inline const std::string &getStatusMessage() const; + [[nodiscard]] inline const std::map &getTileMap() const; + + inline Layer * getLayer(const std::string &name); + inline Tileset * getTileset(const std::string &name); + + template + inline T get(const std::string &name); + inline tson::Property * getProp(const std::string &name); + + //v1.2.0 + [[nodiscard]] inline int getCompressionLevel() const; + inline DecompressorContainer *getDecompressors(); + inline Project * getProject(); + inline Tileset * getTilesetByGid(uint32_t gid); + + private: + inline bool createTilesetData(IJson &json); + inline void processData(); + + Colori m_backgroundColor; /*! 'backgroundcolor': Hex-formatted color (#RRGGBB or #AARRGGBB) (optional)*/; + Vector2i m_size; /*! 'width' and 'height' of a Tiled map */ + int m_hexsideLength {}; /*! 'hexsidelength': Length of the side of a hex tile in pixels */ + bool m_isInfinite {}; /*! 'infinite': Whether the map has infinite dimensions*/ + std::vector m_layers; /*! 'layers': Array of layers. group on */ + int m_nextLayerId {}; /*! 'nextlayerid': Auto-increments for each layer */ + int m_nextObjectId {}; /*! 'nextobjectid': Auto-increments for each placed object */ + std::string m_orientation; /*! 'orientation': orthogonal, isometric, staggered or hexagonal */ + tson::PropertyCollection m_properties; /*! 'properties': A list of properties (name, value, type). */ + std::string m_renderOrder; /*! 'renderorder': Rendering direction (orthogonal maps only) */ + std::string m_staggerAxis; /*! 'staggeraxis': x or y (staggered / hexagonal maps only) */ + std::string m_staggerIndex; /*! 'staggerindex': odd or even (staggered / hexagonal maps only) */ + std::string m_tiledVersion; /*! 'tiledversion': The Tiled version used to save the file */ + Vector2i m_tileSize; /*! 'tilewidth': and 'tileheight' of a map */ + std::vector m_tilesets; /*! 'tilesets': Array of Tilesets */ + std::string m_type; /*! 'type': map (since 1.0) */ + tson::Vector2f m_parallaxOrigin; /*! Tiled v1.8: parallax origin in pixels. Defaults to 0. */ + //int m_version{}; /*! 'version': The JSON format version - Removed in Tileson v1.3.0*/ + + ParseStatus m_status {ParseStatus::OK}; + std::string m_statusMessage {"OK"}; + + std::map m_tileMap{}; /*! key: Tile ID. Value: Pointer to Tile*/ + + //v1.2.0 + int m_compressionLevel {-1}; /*! 'compressionlevel': The compression level to use for tile layer + * data (defaults to -1, which means to use the algorithm default) + * Introduced in Tiled 1.3*/ + tson::DecompressorContainer * m_decompressors {nullptr}; + tson::Project * m_project {nullptr}; + std::map m_flaggedTileMap{}; /*! key: Tile ID. Value: Tile*/ + + std::string m_classType{}; /*! 'class': The class of this map (since 1.9, defaults to “”). */ + std::shared_ptr m_class {}; + }; + + /*! + * A shortcut for getting a property. Alternative to getProperties().getValue("") + * @tparam T The template value + * @param name Name of the property + * @return The actual value, if it exists. Otherwise: The default value of the type. + */ + template + T tson::Map::get(const std::string &name) + { + return m_properties.getValue(name); + } +} + +/*! + * When errors have happened before the map starts parsing, just keep the statuses + * @param status The status + * @param description Description of the status + */ +tson::Map::Map(tson::ParseStatus status, std::string description) : m_status {status}, m_statusMessage { std::move(description) } +{ + +} + +/*! + * Parses a json of a Tiled map. + * @param json A json object with the format of Map + * @return true if all mandatory fields was found. false otherwise. + */ +tson::Map::Map(IJson &json, tson::DecompressorContainer *decompressors, tson::Project *project) +{ + parse(json, decompressors, project); +} + +/*! + * Parses a json of a Tiled map. + * @param json A json object with the format of Map + * @return true if all mandatory fields was found. false otherwise. + */ +bool tson::Map::parse(IJson &json, tson::DecompressorContainer *decompressors, tson::Project *project) +{ + m_decompressors = decompressors; + m_project = project; + + bool allFound = true; + if(json.count("compressionlevel") > 0) + m_compressionLevel = json["compressionlevel"].get(); //Tiled 1.3 - Optional + + if(json.count("backgroundcolor") > 0) m_backgroundColor = Colori(json["backgroundcolor"].get()); //Optional + if(json.count("width") > 0 && json.count("height") > 0 ) + m_size = {json["width"].get(), json["height"].get()}; else allFound = false; + if(json.count("hexsidelength") > 0) m_hexsideLength = json["hexsidelength"].get(); //Optional + if(json.count("infinite") > 0) m_isInfinite = json["infinite"].get(); //Optional + if(json.count("nextlayerid") > 0) m_nextLayerId = json["nextlayerid"].get(); //Optional + if(json.count("nextobjectid") > 0) m_nextObjectId = json["nextobjectid"].get(); else allFound = false; + if(json.count("orientation") > 0) m_orientation = json["orientation"].get(); else allFound = false; + if(json.count("renderorder") > 0) m_renderOrder = json["renderorder"].get(); //Optional + if(json.count("staggeraxis") > 0) m_staggerAxis = json["staggeraxis"].get(); //Optional + if(json.count("staggerindex") > 0) m_staggerIndex = json["staggerindex"].get(); //Optional + if(json.count("tiledversion") > 0) m_tiledVersion = json["tiledversion"].get(); else allFound = false; + if(json.count("tilewidth") > 0 && json.count("tileheight") > 0 ) + m_tileSize = {json["tilewidth"].get(), json["tileheight"].get()}; else allFound = false; + if(json.count("type") > 0) m_type = json["type"].get(); //Optional + if(json.count("class") > 0) m_classType = json["class"].get(); //Optional + + //Removed - Changed from a float to string in Tiled v1.6, and old spec said int. + //Reason for removal is that it seems to have no real use, as TiledVersion is stored in another variable. + //if(json.count("version") > 0) m_version = json["version"].get(); else allFound = false; + + //More advanced data + if(json.count("layers") > 0 && json["layers"].isArray()) + { + auto &array = json.array("layers"); + std::for_each(array.begin(), array.end(), [&](std::unique_ptr &item) + { + m_layers.emplace_back(*item, this); + }); + } + + if(json.count("properties") > 0 && json["properties"].isArray()) + { + auto &array = json.array("properties"); + std::for_each(array.begin(), array.end(), [&](std::unique_ptr &item) + { + m_properties.add(*item, m_project); + }); + } + + tson::Vector2f parallaxOrigin {0.f, 0.f}; + if(json.count("parallaxoriginx") > 0) + parallaxOrigin.x = json["parallaxoriginx"].get(); + if(json.count("parallaxoriginy") > 0) + parallaxOrigin.y = json["parallaxoriginy"].get(); + + m_parallaxOrigin = parallaxOrigin; + + if(!createTilesetData(json)) + allFound = false; + + processData(); + + return allFound; +} + +/*! + * Tileset data must be created in two steps to prevent malformed tson::Tileset pointers inside tson::Tile + */ +bool tson::Map::createTilesetData(IJson &json) +{ + bool ok = true; + if(json.count("tilesets") > 0 && json["tilesets"].isArray()) + { + //First created tileset objects + auto &tilesets = json.array("tilesets"); + std::for_each(tilesets.begin(), tilesets.end(), [&](std::unique_ptr &) + { + m_tilesets.emplace_back(); + }); + + int i = 0; + //Then do the parsing + std::for_each(tilesets.begin(), tilesets.end(), [&](std::unique_ptr &item) + { + item->directory(json.directory()); + if(!m_tilesets[i].parse(*item, this)) + ok = false; + + ++i; + }); + } + return ok; +} + +/*! + * Processes the parsed data and uses the data to create helpful objects, like tile maps. + */ +void tson::Map::processData() +{ + m_tileMap.clear(); + for(auto &tileset : m_tilesets) + { + std::set usedIds; + for(auto& tile : tileset.getTiles()) + { + if (usedIds.count(tile.getGid()) != 0) + { + continue; + } + usedIds.insert(tile.getGid()); + m_tileMap[tile.getGid()] = &tile; + } + } + std::for_each(m_layers.begin(), m_layers.end(), [&](tson::Layer &layer) + { + layer.assignTileMap(&m_tileMap); + layer.createTileData(m_size, m_isInfinite); + const std::set &flaggedTiles = layer.getUniqueFlaggedTiles(); + for(uint32_t ftile : flaggedTiles) + { + tson::Tile tile {ftile, layer.getMap()}; + if(m_tileMap.count(tile.getGid())) + { + tson::Tile *originalTile = m_tileMap[tile.getGid()]; + tile.addTilesetAndPerformCalculations(originalTile->getTileset()); + tile.setProperties(originalTile->getProperties()); + m_flaggedTileMap[ftile] = tile; + m_tileMap[ftile] = &m_flaggedTileMap[ftile]; + } + } + layer.resolveFlaggedTiles(); + }); +} + +/*! + * 'backgroundcolor': Color created from a hex-formatted color string (#RRGGBB or #AARRGGBB) (optional) + * @return string as color + */ +const tson::Colori &tson::Map::getBackgroundColor() const +{ + return m_backgroundColor; +} + +/*! + * 'width' and 'height' of a Tiled map + * @return + */ +const tson::Vector2 &tson::Map::getSize() const +{ + return m_size; +} + +/*! + * 'hexsidelength': Length of the side of a hex tile in pixels + * @return + */ +int tson::Map::getHexsideLength() const +{ + return m_hexsideLength; +} + +/*! + * 'infinite': Whether the map has infinite dimensions + * @return + */ +bool tson::Map::isInfinite() const +{ + return m_isInfinite; +} + +/*! + * 'nextlayerid': Auto-increments for each layer + * @return + */ +int tson::Map::getNextLayerId() const +{ + return m_nextLayerId; +} + +/*! + * 'nextobjectid': Auto-increments for each placed object + * @return + */ +int tson::Map::getNextObjectId() const +{ + return m_nextObjectId; +} + +/*! + * 'orientation': orthogonal, isometric, staggered or hexagonal + * @return + */ +const std::string &tson::Map::getOrientation() const +{ + return m_orientation; +} + +/*! + * 'renderorder': Rendering direction (orthogonal maps only) + * @return + */ +const std::string &tson::Map::getRenderOrder() const +{ + return m_renderOrder; +} + +/*! + * 'staggeraxis': x or y (staggered / hexagonal maps only) + * @return + */ +const std::string &tson::Map::getStaggerAxis() const +{ + return m_staggerAxis; +} + +/*! + * 'staggerindex': odd or even (staggered / hexagonal maps only) + * @return + */ +const std::string &tson::Map::getStaggerIndex() const +{ + return m_staggerIndex; +} + +/*! + * 'tiledversion': The Tiled version used to save the file + * @return + */ +const std::string &tson::Map::getTiledVersion() const +{ + return m_tiledVersion; +} + +/*! + * 'tilewidth': and 'tileheight' of a map + * @return + */ +const tson::Vector2 &tson::Map::getTileSize() const +{ + return m_tileSize; +} + +/*! + * 'type': map (since 1.0) + * @return + */ +const std::string &tson::Map::getType() const +{ + return m_type; +} + +/*! + * 'version': The JSON format version + * @return + */ +//int tson::Map::getVersion() const +//{ +// return m_version; +//} + +/*! + * 'layers': Array of layers. group on + * @return + */ +std::vector &tson::Map::getLayers() +{ + return m_layers; +} + +/*! + * 'properties': A list of properties (name, value, type). + * @return + */ +tson::PropertyCollection &tson::Map::getProperties() +{ + return m_properties; +} + +/*! + * 'tilesets': Array of Tilesets + * @return + */ +std::vector &tson::Map::getTilesets() +{ + return m_tilesets; +} + +tson::Layer *tson::Map::getLayer(const std::string &name) +{ + auto result = std::find_if(m_layers.begin(), m_layers.end(), [&](const tson::Layer &item) { return item.getName() == name; }); + if(result == m_layers.end()) + return nullptr; + + return &result.operator*(); +} + +/*! + * Gets a tileset by name + * + * @param name Name of the tileset + * @return tileset with the matching name + */ +tson::Tileset *tson::Map::getTileset(const std::string &name) +{ + auto result = std::find_if(m_tilesets.begin(), m_tilesets.end(), [&](const tson::Tileset &item) {return item.getName() == name; }); + if(result == m_tilesets.end()) + return nullptr; + + return &result.operator*(); +} + +/*! + * Gets a tileset by gid (graphical ID of a tile). These are always unique, no matter how many tilesets you have + * + * @param gid Graphical ID of a tile + * @return tileset related to the actual gid + */ +tson::Tileset *tson::Map::getTilesetByGid(uint32_t gid) +{ + auto result = std::find_if(m_tilesets.begin(), m_tilesets.end(), [&](const tson::Tileset &tileset) + { + auto const firstId = static_cast(tileset.getFirstgid()); //First tile id of the tileset + auto const lastId = static_cast((firstId + tileset.getTileCount()) - 1); + + return (gid >= firstId && gid <= lastId); + }); + if(result == m_tilesets.end()) + return nullptr; + + return &result.operator*(); +} + +/*! + * Shortcut for getting a property object. Alternative to getProperties().getProperty(""); + * @param name Name of the property + * @return + */ +tson::Property *tson::Map::getProp(const std::string &name) +{ + if(m_properties.hasProperty(name)) + return m_properties.getProperty(name); + return nullptr; +} + +tson::ParseStatus tson::Map::getStatus() const +{ + return m_status; +} + +const std::string &tson::Map::getStatusMessage() const +{ + return m_statusMessage; +} + +/*! + * Get a tile map with pointers to every existing tile. + * @return + */ +const std::map &tson::Map::getTileMap() const +{ + return m_tileMap; +} + +tson::DecompressorContainer *tson::Map::getDecompressors() +{ + return m_decompressors; +} + +/*! + * 'compressionlevel': The compression level to use for tile layer data (defaults to -1, which means to use the algorithm default) + * + * @return The compression level + */ +int tson::Map::getCompressionLevel() const +{ + return m_compressionLevel; +} + +/*! + * New in Tiled v1.8 + * Gets the parallax origin in pixels. Defaults to 0. + * @return A vector with the x and y values of the parallax origin. + */ +const tson::Vector2f &tson::Map::getParallaxOrigin() const +{ + return m_parallaxOrigin; +} + +tson::Project *tson::Map::getProject() +{ + return m_project; +} + +const std::string &tson::Map::getClassType() const +{ + return m_classType; +} + +#endif //TILESON_MAP_HPP + +/*** End of inlined file: Map.hpp ***/ + + +/*** Start of inlined file: TiledEnum.hpp ***/ +// +// Created by robin on 06.06.22. +// + +#ifndef TILESON_TILEDENUM_HPP +#define TILESON_TILEDENUM_HPP + +namespace tson +{ + class EnumDefinition + { + public: + inline explicit EnumDefinition(IJson &json); + inline uint32_t getValue(const std::string &str); + inline std::string getValue(uint32_t num); + inline std::vector getValues(uint32_t num); + inline bool exists(const std::string &str); + inline bool exists(uint32_t num); + + [[nodiscard]] inline uint32_t getId() const; + [[nodiscard]] inline uint32_t getMaxValue() const; + [[nodiscard]] inline const std::string &getName() const; + [[nodiscard]] inline EnumStorageType getStorageType() const; + [[nodiscard]] inline bool hasValuesAsFlags() const; + + private: + inline bool hasFlag(uint32_t value, uint32_t flag) const; + uint32_t m_id {}; + uint32_t m_maxValue {}; + std::string m_name {}; + std::map m_values {}; + bool m_valuesAsFlags {false}; + EnumStorageType m_storageType { EnumStorageType::Unspecified }; + }; + + EnumDefinition::EnumDefinition(IJson &json) + { + m_id = json.get("id"); + m_name = json.get("name"); + std::string type = json.get("storageType"); + m_storageType = (type == "int") ? EnumStorageType::Int : (type == "string") ? EnumStorageType::String : EnumStorageType::Unspecified; + m_valuesAsFlags = json.get("valuesAsFlags"); + + if(json.count("values") > 0 && json["values"].isArray()) + { + m_values[0] = "None"; + uint32_t valueCounter = (m_valuesAsFlags) ? 1 : 0; + uint8_t flagBit = 1; + auto &array = json.array("values"); + std::for_each(array.begin(), array.end(), [&](std::unique_ptr &item) + { + std::string v = item->get(); + m_values[valueCounter] = v; + if(m_valuesAsFlags) + { + valueCounter = 1 << flagBit; + ++flagBit; + } + else + { + ++valueCounter; + } + }); + + m_maxValue = valueCounter; + } + } + + uint32_t EnumDefinition::getValue(const std::string &str) + { + auto result = std::find_if(m_values.begin(), m_values.end(), [&](const std::pair &pair) + { + return pair.second == str; + }); + + if(result != m_values.end()) + return result->first; + + return 0; + } + + std::string EnumDefinition::getValue(uint32_t num) + { + return (m_values.count(num) > 0) ? m_values[num] : ""; + } + + bool EnumDefinition::hasValuesAsFlags() const + { + return m_valuesAsFlags; + } + + bool EnumDefinition::exists(const std::string &str) + { + auto result = std::find_if(m_values.begin(), m_values.end(), [&](const std::pair &pair) + { + return pair.second == str; + }); + + if(result != m_values.end()) + return true; + + return false; + } + + bool EnumDefinition::exists(uint32_t num) { return (m_values.count(num) > 0); } + + uint32_t EnumDefinition::getId() const + { + return m_id; + } + + const std::string &EnumDefinition::getName() const + { + return m_name; + } + + EnumStorageType EnumDefinition::getStorageType() const + { + return m_storageType; + } + + uint32_t EnumDefinition::getMaxValue() const + { + return m_maxValue; + } + + std::vector EnumDefinition::getValues(uint32_t num) + { + std::vector values; + if(m_valuesAsFlags) + { + uint32_t flag = 0; + uint32_t i = 0; + while(flag < m_maxValue) + { + flag = 1 << i; + ++i; + if(m_values.count(flag) > 0 && hasFlag(num, flag)) + { + values.emplace_back(m_values[flag]); + } + } + } + else + { + std::string v = getValue(num); + if(!v.empty()) + values.emplace_back(); + } + + return values; + } + + bool EnumDefinition::hasFlag(uint32_t value, uint32_t flag) const + { + return ((value & flag) == flag); + } + + class EnumValue + { + public: + inline EnumValue() = default; + inline EnumValue(uint32_t value, EnumDefinition *definition); + inline EnumValue(const std::string &value, EnumDefinition *definition); + + [[nodiscard]] inline uint32_t getValue() const; + inline std::string getValueName() const; + [[nodiscard]] inline std::vector getValueNames() const; + [[nodiscard]] inline EnumDefinition *getDefinition() const; + + inline bool hasFlagValue(uint32_t flag) const; + template + inline bool hasFlag(T flags) const; + inline bool hasAnyFlagValue(uint32_t flags) const; + template + inline bool hasAnyFlag(T flags) const; + [[nodiscard]] inline bool containsValueName(const std::string &value) const; + + private: + uint32_t m_value {0}; + EnumDefinition *m_definition = nullptr; + }; + + EnumValue::EnumValue(uint32_t value, EnumDefinition *definition) : m_value {value}, m_definition {definition} + { + + } + + EnumValue::EnumValue(const std::string &value, EnumDefinition *definition) : m_definition {definition} + { + if(!value.empty() && definition != nullptr) + { + std::vector values = Tools::SplitString(value, ','); + for(auto &item : values) + { + uint32_t v = definition->getValue(item); + m_value |= v; + } + } + } + + /*! + * Checks if uint32 value contains a single flag. If 'valuesAsFlags' is not a part of the EnumDefinition, a simple equality comparison will be done + * instead. + * @param flags The uint32 value of the flag you want to check + * @return 'true' if EnumValue has the requested bit activated. 'false' otherwise. + */ + bool EnumValue::hasFlagValue(uint32_t flags) const + { + if(m_definition->hasValuesAsFlags()) + return ((m_value & flags) == flags) ? true : false; + + return m_value == flags; + } + + /*! + * Checks if uint32 value contains one of several possible flags. If 'valuesAsFlags' is not a part of the EnumDefinition, a simple equality comparison will be done + * instead. + * @param flags The uint32 values of the flags you want to check + * @return 'true' if EnumValue has the requested bits activated. 'false' otherwise. + */ + bool EnumValue::hasAnyFlagValue(uint32_t flags) const + { + if(m_definition->hasValuesAsFlags()) + return ((m_value & flags) != 0); + + return m_value == flags; + } + + uint32_t EnumValue::getValue() const + { + return m_value; + } + + /*! + * Gets the single name of a value. + * This function is intended for enums that can only have one value (non-flag). + * If you want to get several value names (flags), use getValueNames() instead. + * @return A single name for a value + */ + std::string EnumValue::getValueName() const + { + return (m_definition == nullptr) ? "" : m_definition->getValue(m_value); + } + + /*! + * Checks if T value contains a single flag. If 'valuesAsFlags' is not a part of the EnumDefinition, a simple equality comparison will be done + * instead. + * @tparam T A uint32_t compatible type + * @param flags One or more flags you want to verify is included. + * @return true is all flags presented are set. false otherwise. + */ + template + bool EnumValue::hasFlag(T flags) const + { + return hasFlagValue(static_cast(flags)); + } + + /*! + * Checks if T value contains one of several possible flags. If 'valuesAsFlags' is not a part of the EnumDefinition, a simple equality comparison will be done + * instead. + * @tparam T A uint32_t compatible type + * @param flags One or more flags you want to verify is included. + * @return true is all flags presented are set. false otherwise. + */ + template + bool EnumValue::hasAnyFlag(T flags) const + { + return hasAnyFlagValue(static_cast(flags)); + } + + EnumDefinition *EnumValue::getDefinition() const + { + return m_definition; + } + + std::vector EnumValue::getValueNames() const + { + return (m_definition == nullptr) ? std::vector() : m_definition->getValues(m_value); + } + + /*! + * + * @param value + * @return + */ + bool EnumValue::containsValueName(const std::string &value) const + { + if(m_definition != nullptr) + { + if(m_definition->hasValuesAsFlags()) + { + std::vector values = m_definition->getValues(m_value); + auto it = std::find(values.begin(), values.end(), value); + return it != values.end(); + } + return m_definition->getValue(value) == m_value; + } + return false; + } +} + +#endif //TILESON_TILEDENUM_HPP + +/*** End of inlined file: TiledEnum.hpp ***/ + + +/*** Start of inlined file: TiledClass.hpp ***/ +// +// Created by robin on 06.06.22. +// + +#ifndef TILESON_TILEDCLASS_HPP +#define TILESON_TILEDCLASS_HPP + +namespace tson +{ + class TiledClass + { + public: + inline explicit TiledClass() = default; + inline explicit TiledClass(IJson &json, tson::Project *project = nullptr); + + [[nodiscard]] inline uint32_t getId() const; + [[nodiscard]] inline const std::string &getName() const; + [[nodiscard]] inline const std::string &getType() const; + [[nodiscard]] inline const PropertyCollection &getMembers() const; + inline void update(IJson &json); + inline void update(PropertyCollection &properties); + + template + inline T get(const std::string &name); + inline tson::Property *getMember(const std::string &name); + + private: + uint32_t m_id {}; + std::string m_name {}; + std::string m_type {}; + PropertyCollection m_members {}; + + }; + + TiledClass::TiledClass(IJson &json, tson::Project *project) + { + if(json.count("id") > 0) + m_id = json["id"].get(); + + if(json.count("name") > 0) + m_name = json["name"].get(); + if(json.count("type") > 0) + m_type = json["type"].get(); + + if(json.count("members") > 0 && json["members"].isArray()) + { + auto &array = json.array("members"); + std::for_each(array.begin(), array.end(), [&](std::unique_ptr &item) + { + m_members.add(*item, project); + }); + } + } + + uint32_t TiledClass::getId() const + { + return m_id; + } + + const std::string &TiledClass::getName() const + { + return m_name; + } + + const std::string &TiledClass::getType() const + { + return m_type; + } + + const PropertyCollection &TiledClass::getMembers() const + { + return m_members; + } + + template + T TiledClass::get(const std::string &name) + { + return m_members.getValue(name); + } + + tson::Property *TiledClass::getMember(const std::string &name) + { + if(m_members.hasProperty(name)) + return m_members.getProperty(name); + return nullptr; + } + + /*! + * Takes a json object from a particular map top update values if they differ from the original values of the class + * @param json + */ + void TiledClass::update(IJson &json) + { + for(Property *property : m_members.get()) + { + if(json.any(property->getName())) + { + property->setValueByType(json[property->getName()]); + } + } + } + + void TiledClass::update(PropertyCollection &properties) + { + std::vector toUpdate; + for(Property *member : m_members.get()) + { + if(properties.hasProperty(member->getName())) + { + Property *property = properties.getProperty(member->getName()); + if(member->getType() == property->getType()) + { + toUpdate.push_back(property); + } + } + } + + std::for_each(toUpdate.begin(), toUpdate.end(), [&](Property *p) + { + m_members.setProperty(p->getName(), *p); + }); + } +} + +#endif //TILESON_TILEDCLASS_HPP + +/*** End of inlined file: TiledClass.hpp ***/ + + +/*** Start of inlined file: Project.hpp ***/ +// +// Created by robin on 01.08.2020. +// + +#ifndef TILESON_PROJECT_HPP +#define TILESON_PROJECT_HPP + +#include +#include +#include + +/*** Start of inlined file: World.hpp ***/ +// +// Created by robin on 01.08.2020. +// + +#ifndef TILESON_WORLD_HPP +#define TILESON_WORLD_HPP + + +/*** Start of inlined file: WorldMapData.hpp ***/ +// +// Created by robin on 01.08.2020. +// + +#ifndef TILESON_WORLDMAPDATA_HPP +#define TILESON_WORLDMAPDATA_HPP + +namespace tson +{ + class WorldMapData + { + public: + inline WorldMapData(const fs::path &folder_, IJson &json); + inline void parse(const fs::path &folder_, IJson &json); + //inline WorldMapData(fs::path folder_, std::string fileName_) : folder {std::move(folder_)}, fileName {fileName_} + //{ + // path = folder / fileName; + //} + + fs::path folder; + fs::path path; + std::string fileName; + tson::Vector2i size; + tson::Vector2i position; + }; + + WorldMapData::WorldMapData(const fs::path &folder_, IJson &json) + { + parse(folder_, json); + } + + void WorldMapData::parse(const fs::path &folder_, IJson &json) + { + folder = folder_; + if(json.count("fileName") > 0) fileName = json["fileName"].get(); + if(json.count("height") > 0) size = {json["width"].get(), json["height"].get()}; + if(json.count("x") > 0) position = {json["x"].get(), json["y"].get()}; + + path = (!fileName.empty()) ? folder / fileName : folder; + } +} + +#endif //TILESON_WORLDMAPDATA_HPP +/*** End of inlined file: WorldMapData.hpp ***/ + +#include +namespace tson +{ + class Tileson; + class World + { + public: + #ifdef JSON11_IS_DEFINED + inline explicit World(std::unique_ptr jsonParser = std::make_unique()) : m_json {std::move(jsonParser)} + { + } + + inline explicit World(const fs::path &path, std::unique_ptr jsonParser = std::make_unique()); + #else + inline explicit World(std::unique_ptr jsonParser) : m_json {std::move(jsonParser)} + { + } + + inline explicit World(const fs::path &path, std::unique_ptr jsonParser); + #endif + inline bool parse(const fs::path &path); + inline std::size_t loadMaps(tson::Tileson *parser); //tileson_forward.hpp + inline bool contains(std::string_view filename); + inline const WorldMapData *get(std::string_view filename) const; + + [[nodiscard]] inline const fs::path &getPath() const; + [[nodiscard]] inline const fs::path &getFolder() const; + [[nodiscard]] inline const std::vector &getMapData() const; + [[nodiscard]] inline bool onlyShowAdjacentMaps() const; + [[nodiscard]] inline const std::string &getType() const; + [[nodiscard]] inline const std::vector> &getMaps() const; + + private: + inline void parseJson(IJson &json); + + std::unique_ptr m_json = nullptr; + fs::path m_path; + fs::path m_folder; + std::vector m_mapData; + std::vector> m_maps; + bool m_onlyShowAdjacentMaps; + std::string m_type; + }; + + World::World(const fs::path &path, std::unique_ptr jsonParser) : m_json {std::move(jsonParser)} + { + parse(path); + } + + bool World::parse(const fs::path &path) + { + m_path = path; + m_folder = m_path.parent_path(); + + if(!m_json->parse(path)) + return false; + + parseJson(*m_json); + return true; + } + + const fs::path &World::getPath() const + { + return m_path; + } + + const std::vector &World::getMapData() const + { + return m_mapData; + } + + bool World::onlyShowAdjacentMaps() const + { + return m_onlyShowAdjacentMaps; + } + + const std::string &World::getType() const + { + return m_type; + } + + void World::parseJson(IJson &json) + { + if(json.count("onlyShowAdjacentMaps") > 0) m_onlyShowAdjacentMaps = json["onlyShowAdjacentMaps"].get(); + if(json.count("type") > 0) m_type = json["type"].get(); + + if(json["maps"].isArray()) + { + auto &maps = json.array("maps"); + std::for_each(maps.begin(), maps.end(), [&](std::unique_ptr &item) { m_mapData.emplace_back(m_folder, *item); }); + } + } + + const fs::path &World::getFolder() const + { + return m_folder; + } + + /*! + * Check if there is WorldMapData in the world that contains the current filename. + * Filename = . + * @param filename + * @return + */ + bool World::contains(std::string_view filename) + { + //Note: might be moved to std::ranges from C++20. + return std::any_of(m_mapData.begin(), m_mapData.end(), [&](const auto &item) { return item.fileName == filename; }); + } + + /*! + * Get a map by its filename + * @param filename Filename (including extension) - (example: file.json) + * @return pointer to WorldMapData or nullptr if not exists + */ + const WorldMapData * World::get(std::string_view filename) const + { + auto iter = std::find_if(m_mapData.begin(), m_mapData.end(), [&](const auto &item) { return item.fileName == filename; }); + return (iter == m_mapData.end()) ? nullptr : iter.operator->(); + } + + /*! + * Get all maps that have been loaded by loadMaps(). + * NOTE: This is untested, and was a last second addition to Tileson 1.2.0, as I had forgot about the loadMaps() functionality (also untested) + * If you find anything malfunctioning - please report. + * @return All maps loaded by loadMaps() + */ + const std::vector> &World::getMaps() const + { + return m_maps; + } + +} + +#endif //TILESON_WORLD_HPP + +/*** End of inlined file: World.hpp ***/ + + + +/*** Start of inlined file: ProjectPropertyTypes.hpp ***/ +// +// Created by robin on 01.08.22. +// + +#ifndef TILESON_PROJECTPROPERTYTYPES_HPP +#define TILESON_PROJECTPROPERTYTYPES_HPP + +namespace tson +{ + class ProjectPropertyTypes + { + public: + inline ProjectPropertyTypes() = default; + inline bool parse(IJson &json, tson::Project *project); + + inline const std::vector &getEnums() const; + inline const std::vector &getClasses() const; + [[nodiscard]] inline tson::EnumDefinition* getEnumDefinition(std::string_view name); + [[nodiscard]] inline tson::TiledClass* getClass(std::string_view name); + inline bool isUnhandledContentFound() const; + + private: + std::vector m_enums; + std::vector m_classes; + bool m_unhandledContentFound {false}; + + }; + + bool ProjectPropertyTypes::parse(IJson &json, tson::Project *project) + { + m_enums.clear(); + m_classes.clear(); + m_unhandledContentFound = false; + + if(json.count("propertyTypes") > 0 && json["propertyTypes"].isArray()) + { + auto &array = json.array("propertyTypes"); + std::vector classes; //Classes must be handled after enums + std::vector other; //Unhandled stuff - just to keep track if something is missing... + std::for_each(array.begin(), array.end(), [&](std::unique_ptr &item) + { + IJson &j = *item; + if(j.count("type") > 0) + { + std::string t = j["type"].get(); + if(t == "enum") + { + m_enums.emplace_back(j); //Can be resolved directly + } + else if(t == "class") + { + classes.push_back(item.get()); //Must be resolved later + } + else + other.push_back(item.get()); //Only used to set flag for whether unhandled content was found. + } + }); + + std::for_each(classes.begin(), classes.end(), [&](IJson *item) + { + m_classes.emplace_back(*item, project); + }); + + if(!other.empty()) + m_unhandledContentFound = true; + + } + return false; + } + + const std::vector &ProjectPropertyTypes::getEnums() const + { + return m_enums; + } + + const std::vector &ProjectPropertyTypes::getClasses() const + { + return m_classes; + } + + bool ProjectPropertyTypes::isUnhandledContentFound() const + { + return m_unhandledContentFound; + } + + tson::EnumDefinition *ProjectPropertyTypes::getEnumDefinition(std::string_view name) + { + auto it = std::find_if(m_enums.begin(), m_enums.end(), [&](const EnumDefinition &def) + { + return def.getName() == name; + }); + + if(it != m_enums.end()) + return &it.operator*(); + + return nullptr; + } + + tson::TiledClass *ProjectPropertyTypes::getClass(std::string_view name) + { + auto it = std::find_if(m_classes.begin(), m_classes.end(), [&](const TiledClass &def) + { + return def.getName() == name; + }); + + if(it != m_classes.end()) + return &it.operator*(); + + return nullptr; + } +} + +#endif //TILESON_PROJECTPROPERTYTYPES_HPP + +/*** End of inlined file: ProjectPropertyTypes.hpp ***/ + + +/*** Start of inlined file: ProjectFolder.hpp ***/ +// +// Created by robin on 01.08.2020. +// + +#ifndef TILESON_PROJECTFOLDER_HPP +#define TILESON_PROJECTFOLDER_HPP + +namespace tson +{ + class ProjectFolder + { + public: + inline ProjectFolder(const fs::path &path); + + inline const fs::path &getPath() const; + inline bool hasWorldFile() const; + inline const std::vector &getSubFolders() const; + inline const std::vector &getFiles() const; + inline const World &getWorld() const; + + private: + inline void loadData(); + fs::path m_path; + bool m_hasWorldFile; + tson::World m_world; + std::vector m_subFolders; + std::vector m_files; + + }; + + ProjectFolder::ProjectFolder(const fs::path &path) : m_path {path} + { + loadData(); + } + + void ProjectFolder::loadData() + { + m_hasWorldFile = false; + m_subFolders.clear(); + m_files.clear(); + //Search and see if there is a World file .world file + fs::path worldPath; + for (const auto & entry : fs::directory_iterator(m_path)) + { + if(fs::is_regular_file(entry.path())) + { + if(entry.path().extension() == ".world") + { + m_hasWorldFile = true; + worldPath = entry.path(); + } + } + } + + if(m_hasWorldFile) + m_world.parse(worldPath); + + for (const auto & entry : fs::directory_iterator(m_path)) + { + if (fs::is_directory(entry.path())) + m_subFolders.emplace_back(entry.path());//.loadData(); - loadData() is called in the constructor, so don't call again. + else if (fs::is_regular_file(entry.path())) + { + if(m_hasWorldFile && m_world.contains(entry.path().filename().generic_string())) + m_files.emplace_back(entry.path()); + else if(!m_hasWorldFile) + m_files.emplace_back(entry.path()); + } + } + + } + + const fs::path &ProjectFolder::getPath() const + { + return m_path; + } + + bool ProjectFolder::hasWorldFile() const + { + return m_hasWorldFile; + } + + const std::vector &ProjectFolder::getSubFolders() const + { + return m_subFolders; + } + + const std::vector &ProjectFolder::getFiles() const + { + return m_files; + } + + /*! + * Only gives useful data if hasWorldFile() is true! + * @return + */ + const World &ProjectFolder::getWorld() const + { + return m_world; + } +} + +#endif //TILESON_PROJECTFOLDER_HPP +/*** End of inlined file: ProjectFolder.hpp ***/ + + +/*** Start of inlined file: ProjectData.hpp ***/ +// +// Created by robin on 01.08.2020. +// + +#ifndef TILESON_PROJECTDATA_HPP +#define TILESON_PROJECTDATA_HPP + +namespace tson +{ + class ProjectData + { + public: + ProjectData() = default; + std::string automappingRulesFile; + std::vector commands; + std::string extensionsPath; + std::vector folders; + std::string objectTypesFile; + ProjectPropertyTypes projectPropertyTypes; + + //Tileson specific + fs::path basePath; + std::vector folderPaths; + }; +} + +#endif //TILESON_PROJECTDATA_HPP +/*** End of inlined file: ProjectData.hpp ***/ + +namespace tson +{ + class Project + { + public: + #ifdef JSON11_IS_DEFINED + inline explicit Project(std::unique_ptr jsonParser = std::make_unique()) : m_json {std::move(jsonParser)} + { + + } + inline explicit Project(const fs::path &path, std::unique_ptr jsonParser = std::make_unique()); + #else + inline explicit Project(std::unique_ptr jsonParser) : m_json {std::move(jsonParser)} + { + + } + inline explicit Project(const fs::path &path, std::unique_ptr jsonParser); + #endif + inline bool parse(const fs::path &path); + inline void parse(); + + [[nodiscard]] inline const ProjectData &getData() const; + [[nodiscard]] inline const fs::path &getPath() const; + [[nodiscard]] inline const std::vector &getFolders() const; + [[nodiscard]] inline tson::EnumDefinition* getEnumDefinition(std::string_view name); + [[nodiscard]] inline tson::TiledClass* getClass(std::string_view name); + + private: + inline void parseJson(IJson &json); + fs::path m_path; + std::vector m_folders; + ProjectData m_data; + std::unique_ptr m_json = nullptr; + }; + + Project::Project(const fs::path &path, std::unique_ptr jsonParser) : m_json {std::move(jsonParser)} + { + parse(path); + } + + bool Project::parse(const fs::path &path) + { + m_path = path; + std::ifstream i(m_path.generic_string()); + + try + { + if(!m_json->parse(path)) + return false; + } + catch(const std::exception &error) + { + std::string message = "Parse error: "; + message += std::string(error.what()); + message += std::string("\n"); + return false; + } + parseJson(*m_json); + return true; + } + + const ProjectData &Project::getData() const + { + return m_data; + } + + void Project::parseJson(IJson &json) + { + m_data.basePath = (m_path.empty()) ? fs::path() : m_path.parent_path(); //The directory of the project file + + //Make sure these property types are read before any map is, so they can be resolved. + if(json.count("propertyTypes") > 0) + { + m_data.projectPropertyTypes.parse(json, this); + } + + if(json.count("automappingRulesFile") > 0) m_data.automappingRulesFile = json["automappingRulesFile"].get(); + if(json.count("commands") > 0) + { + m_data.commands.clear(); + auto &commands = json.array("commands"); + std::for_each(commands.begin(), commands.end(), [&](std::unique_ptr &item) + { + m_data.commands.emplace_back(item->get()); + }); + } + if(json.count("extensionsPath") > 0) m_data.extensionsPath = json["extensionsPath"].get(); + if(json.count("folders") > 0) + { + m_data.folders.clear(); + m_data.folderPaths.clear(); + auto &folders = json.array("folders"); + std::for_each(folders.begin(), folders.end(), [&](std::unique_ptr &item) + { + std::string folder = item->get(); + m_data.folders.emplace_back(folder); + m_data.folderPaths.emplace_back(m_data.basePath / folder); + m_folders.emplace_back(m_data.basePath / folder); + }); + } + if(json.count("objectTypesFile") > 0) m_data.objectTypesFile = json["objectTypesFile"].get(); + } + + const fs::path &Project::getPath() const + { + return m_path; + } + + const std::vector &Project::getFolders() const + { + return m_folders; + } + + tson::EnumDefinition *Project::getEnumDefinition(std::string_view name) + { + return m_data.projectPropertyTypes.getEnumDefinition(name); + } + + tson::TiledClass *Project::getClass(std::string_view name) + { + return m_data.projectPropertyTypes.getClass(name); + } + + /*! + * Parses preloaded json data. Only used during tests involving project jsons not actually read from files + * @return + */ + void Project::parse() + { + parseJson(*m_json); + } + +} + +#endif //TILESON_PROJECT_HPP + +/*** End of inlined file: Project.hpp ***/ + +namespace tson +{ + class Tileson + { + public: + #ifdef JSON11_IS_DEFINED + inline explicit Tileson(std::unique_ptr jsonParser = std::make_unique(), bool includeBase64Decoder = true); + inline explicit Tileson(tson::Project *project, std::unique_ptr jsonParser = std::make_unique(), bool includeBase64Decoder = true); + #else + inline explicit Tileson(std::unique_ptr jsonParser, bool includeBase64Decoder = true); + inline explicit Tileson(tson::Project *project, std::unique_ptr jsonParser, bool includeBase64Decoder = true); + #endif + + inline std::unique_ptr parse(const fs::path &path, std::unique_ptr, std::vector>> decompressor = nullptr); + inline std::unique_ptr parse(const void * data, size_t size, std::unique_ptr, std::vector>> decompressor = nullptr); + inline tson::DecompressorContainer *decompressors(); + + private: + inline std::unique_ptr parseJson(); + std::unique_ptr m_json; + tson::DecompressorContainer m_decompressors; + tson::Project *m_project {nullptr}; + }; +} + +/*! + * + * @param includeBase64Decoder Includes the base64-decoder from "Base64Decompressor.hpp" if true. + * Otherwise no other decompressors/decoders than whatever the user itself have added will be used. + */ +tson::Tileson::Tileson(std::unique_ptr jsonParser, bool includeBase64Decoder) : m_json {std::move(jsonParser)} +{ + if(includeBase64Decoder) + m_decompressors.add(); +} + +tson::Tileson::Tileson(tson::Project *project, std::unique_ptr jsonParser, bool includeBase64Decoder) : m_json {std::move(jsonParser)} +{ + m_project = project; + if(includeBase64Decoder) + m_decompressors.add(); +} + +/*! + * Parses Tiled json map data by file + * @param path path to file + * @return parsed data as Map + */ +std::unique_ptr tson::Tileson::parse(const fs::path &path, std::unique_ptr, std::vector>> decompressor) +{ + + bool result = false; + + if(decompressor != nullptr) + { + std::vector decompressed = decompressor->decompressFile(path); + result = (decompressed.empty()) ? false : true; + if(!result) + return std::make_unique(tson::ParseStatus::DecompressionError, "Error during decompression"); + + result = m_json->parse(&decompressed[0], decompressed.size()); + + if(result) + return parseJson(); + } + else if(m_json->parse(path)) + { + return parseJson(); + } + + std::string msg = "File not found: "; + msg += std::string(path.generic_string()); + return std::make_unique(tson::ParseStatus::FileNotFound, msg); +} + +/*! + * Parses Tiled json map data by memory + * @param data The data to parse + * @param size The size of the data to parse + * @return parsed data as Map + */ +std::unique_ptr tson::Tileson::parse(const void *data, size_t size, std::unique_ptr, std::vector>> decompressor) +{ + bool result = false; + + if(decompressor != nullptr) + { + std::vector decompressed = decompressor->decompress(data, size); + result = (decompressed.empty()) ? false : true; + if(!result) + return std::make_unique(tson::ParseStatus::DecompressionError, "Error during decompression"); + result = m_json->parse(&decompressed[0], decompressed.size()); + } + else + result = m_json->parse(data, size); + + if(!result) + return std::make_unique(tson::ParseStatus::ParseError, "Memory error"); + + return parseJson(); +} + +/*! + * Common parsing functionality for doing the json parsing + * @param json Tiled json to parse + * @return parsed data as Map + */ +std::unique_ptr tson::Tileson::parseJson() +{ + std::unique_ptr map = std::make_unique(); + + if(map->parse(*m_json, &m_decompressors, m_project)) + return map; + + return std::make_unique (tson::ParseStatus::MissingData, "Missing map data..."); +} + +/*! + * Gets the decompressor container used when something is either encoded or compressed (regardless: IDecompressor is used as base). + * These are used specifically for tile layers, and are connected by checking the name of the IDecompressor. If the name of a decompressor + * matches with an encoding or a compression, its decompress() function will be used. + * + * @return The container including all decompressors. + */ +tson::DecompressorContainer *tson::Tileson::decompressors() +{ + return &m_decompressors; +} + +#endif //TILESON_TILESON_PARSER_HPP + +/*** End of inlined file: tileson_parser.hpp ***/ + + +/*** Start of inlined file: tileson_forward.hpp ***/ +// +// Created by robin on 25.07.2020. +// + +#ifndef TILESON_TILESON_FORWARD_HPP +#define TILESON_TILESON_FORWARD_HPP +/*! + * T I L E S O N F O R W A R D D E C L A R A T I O N S + * ------------------------------------------------------- + * + * Due to cross-references we have forward declarations that cannot be resolved during the + * implementation, thus the implementations must be done later when the class definition itself is known. + * + * All those forward declarations can be found below. + */ + +// M a p . h p p +// ---------------- + +tson::TiledClass *tson::Map::getClass() +{ + if(m_class == nullptr) + { + TiledClass* baseClass = (m_project != nullptr) ? m_project->getClass(m_classType) : nullptr; + if(baseClass != nullptr) + { + m_class = std::make_shared(*baseClass); + m_class->update(m_properties); + } + } + return (m_class != nullptr) ? m_class.get() : nullptr; +} + +// T i l e . h p p +// --------------------- + +/*! + * Really just a shortcut to retrieve the tile size from the map. + * @return TileSize based on the map property for tile size. + */ +const tson::Vector2i tson::Tile::getTileSize() const +{ + if(m_map != nullptr) + return m_map->getTileSize(); + else + return {0,0}; +} + +bool tson::Tile::parseId(IJson &json) +{ + if(json.count("id") > 0) + { + m_id = json["id"].get() + 1; + if (m_tileset != nullptr) + m_gid = m_tileset->getFirstgid() + m_id - 1; + else + m_gid = m_id; + manageFlipFlagsByIdThenRemoveFlags(m_gid); + return true; + } + return false; +} + +/*! + * Uses tson::Tileset and tson::Map data to calculate related values for tson::Tile. + * Added in v1.2.0 + */ +void tson::Tile::performDataCalculations() +{ + if(m_tileset == nullptr || m_map == nullptr) + return; + + int firstId = m_tileset->getFirstgid(); //First tile id of the tileset + int columns = m_tileset->getColumns(); + int rows = m_tileset->getTileCount() / columns; + int lastId = (m_tileset->getFirstgid() + m_tileset->getTileCount()) - 1; + + int const gid = static_cast(getGid()); + if (gid >= firstId && gid <= lastId) + { + int const baseTilePosition = (gid - firstId); + + int const tileModX = (baseTilePosition % columns); + int const currentRow = (baseTilePosition / columns); + int const offsetX = (tileModX != 0) ? ((tileModX) * m_map->getTileSize().x) : (0 * m_map->getTileSize().x); + int const offsetY = (currentRow < rows-1) ? (currentRow * m_map->getTileSize().y) : ((rows-1) * m_map->getTileSize().y); + + tson::Vector2i spacing = m_tileset->getMarginSpacingOffset({tileModX, currentRow}); + m_drawingRect = { offsetX + spacing.x, offsetY + spacing.y, m_map->getTileSize().x, m_map->getTileSize().y }; + } + else + m_drawingRect = {0, 0, 0, 0}; +} + +/*! + * Get the position of the tile in pixels based on the tile data position from the current layer. + * @return The position of the tile in Pixels + */ +const tson::Vector2f tson::Tile::getPosition(const std::tuple &tileDataPos) +{ + return {((float) std::get<0>(tileDataPos)) * m_drawingRect.width, ((float) std::get<1>(tileDataPos)) * m_drawingRect.height}; +} + +/*! + * Gets the class information for the 'type'/'class' + * This may only give a valid result if the map is loaded through a tson::Project + * @return a tson::TiledClass object if related map was loaded through tson::Project + */ +tson::TiledClass *tson::Tile::getClass() +{ + if(m_class == nullptr) + { + TiledClass* baseClass = (m_map != nullptr && m_map->getProject() != nullptr) ? m_map->getProject()->getClass(m_type) : nullptr; + if(baseClass != nullptr) + { + m_class = std::make_shared(*baseClass); + m_class->update(m_properties); + } + } + return (m_class != nullptr) ? m_class.get() : nullptr; +} + +// T i l e s e t . h p p +// ------------------------ + +tson::TiledClass *tson::Tileset::getClass() +{ + if(m_class == nullptr) + { + TiledClass* baseClass = (m_map != nullptr && m_map->getProject() != nullptr) ? m_map->getProject()->getClass(m_classType) : nullptr; + if(baseClass != nullptr) + { + m_class = std::make_shared(*baseClass); + m_class->update(m_properties); + } + } + return (m_class != nullptr) ? m_class.get() : nullptr; +} + +// T i l e O b j e c t . h p p +// --------------------- + +/*! + * In cases where the empty constructor is called, this must be called manually + * for this class to make sense + * @param posInTileUnits + * @param tile + */ +void tson::TileObject::initialize(const std::tuple &posInTileUnits, tson::Tile *tile) +{ + m_tile = tile; + m_posInTileUnits = tile->getPositionInTileUnits(posInTileUnits); + m_position = tile->getPosition(posInTileUnits); +} + +const tson::Rect &tson::TileObject::getDrawingRect() const +{ + return m_tile->getDrawingRect(); +} + +// L a y e r . h p p +// ------------------- + +/*! + * Decompresses data if there are matching decompressors + */ +void tson::Layer::decompressData() +{ + + tson::DecompressorContainer *container = m_map->getDecompressors(); + if(container->empty()) + return; + + if(m_encoding.empty() && m_compression.empty()) + return; + + std::string data = m_base64Data; + bool hasBeenDecoded = false; + if(!m_encoding.empty() && container->contains(m_encoding)) + { + data = container->get(m_encoding)->decompress(data); + hasBeenDecoded = true; + } + + if(!m_compression.empty() && container->contains(m_compression)) + { + data = container->get(m_compression)->decompress(data); + } + + if(hasBeenDecoded) + { + std::vector bytes = tson::Tools::Base64DecodedStringToBytes(data); + m_data = tson::Tools::BytesToUnsignedInts(bytes); + } +} + +/*! + * Parses a Tiled layer from json + * @param json + * @return true if all mandatory fields was found. false otherwise. + */ +bool tson::Layer::parse(IJson &json, tson::Map *map) +{ + m_map = map; + + bool allFound = true; + if(json.count("tintcolor") > 0) m_tintColor = tson::Colori(json["tintcolor"].get()); //Optional + if(json.count("compression") > 0) m_compression = json["compression"].get(); //Optional + if(json.count("draworder") > 0) m_drawOrder = json["draworder"].get(); //Optional + if(json.count("encoding") > 0) m_encoding = json["encoding"].get(); //Optional + if(json.count("id") > 0) m_id = json["id"].get(); //Optional + if(json.count("image") > 0) m_image = json["image"].get(); //Optional + if(json.count("name") > 0) m_name = json["name"].get(); else allFound = false; + if(json.count("offsetx") > 0 && json.count("offsety") > 0) + m_offset = {json["offsetx"].get(), json["offsety"].get()}; //Optional + if(json.count("opacity") > 0) m_opacity = json["opacity"].get(); else allFound = false; + if(json.count("width") > 0 && json.count("height") > 0) + m_size = {json["width"].get(), json["height"].get()}; //else allFound = false; - Not mandatory for all layers! + if(json.count("transparentcolor") > 0) m_transparentColor = tson::Colori(json["transparentcolor"].get()); //Optional + if(json.count("type") > 0) m_typeStr = json["type"].get(); else allFound = false; + if(json.count("class") > 0) m_classType = json["class"].get(); //Optional + if(json.count("visible") > 0) m_visible = json["visible"].get(); else allFound = false; + if(json.count("x") > 0) m_x = json["x"].get(); else allFound = false; + if(json.count("y") > 0) m_y = json["y"].get(); else allFound = false; + if(json.count("repeatx") > 0) m_repeatX = json["repeatx"].get(); //Optional + if(json.count("repeaty") > 0) m_repeatY = json["repeaty"].get(); //Optional + + tson::Vector2f parallax {1.f, 1.f}; + if(json.count("parallaxx") > 0) + parallax.x = json["parallaxx"].get(); + if(json.count("parallaxy") > 0) + parallax.y = json["parallaxy"].get(); + + m_parallax = parallax; + + //Handle DATA (Optional) + if(json.count("data") > 0) + { + if(json["data"].isArray()) + { + auto &array = json.array("data"); + std::for_each(array.begin(), array.end(), [&](std::unique_ptr &item) { m_data.push_back(item->get()); }); + } + else + { + m_base64Data = json["data"].get(); + decompressData(); + } + } + + //More advanced data + if(json.count("chunks") > 0 && json["chunks"].isArray()) + { + auto &chunks = json.array("chunks"); + std::for_each(chunks.begin(), chunks.end(), [&](std::unique_ptr &item) { m_chunks.emplace_back(*item); }); + } + if(json.count("layers") > 0 && json["layers"].isArray()) + { + auto &layers = json.array("layers"); + std::for_each(layers.begin(), layers.end(), [&](std::unique_ptr &item) { m_layers.emplace_back(*item, m_map); }); + } + if(json.count("objects") > 0 && json["objects"].isArray()) + { + auto &objects = json.array("objects"); + std::for_each(objects.begin(), objects.end(), [&](std::unique_ptr &item) { m_objects.emplace_back(*item, m_map); }); + } + if(json.count("properties") > 0 && json["properties"].isArray()) + { + auto &properties = json.array("properties"); + tson::Project *project = (m_map != nullptr) ? m_map->getProject() : nullptr; + std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr &item) { m_properties.add(*item, project); }); + } + + setTypeByString(); + + return allFound; +} + +tson::TiledClass *tson::Layer::getClass() +{ + if(m_class == nullptr) + { + TiledClass* baseClass = (m_map != nullptr && m_map->getProject() != nullptr) ? m_map->getProject()->getClass(m_classType) : nullptr; + if(baseClass != nullptr) + { + m_class = std::make_shared(*baseClass); + m_class->update(m_properties); + } + } + return (m_class != nullptr) ? m_class.get() : nullptr; +} + +// O b j e c t . h p p +// -------------------- + +// W a n g s e t . h p p +// ---------------------- +tson::TiledClass *tson::WangSet::getClass() +{ + if(m_class == nullptr) + { + TiledClass* baseClass = (m_map != nullptr && m_map->getProject() != nullptr) ? m_map->getProject()->getClass(m_classType) : nullptr; + if(baseClass != nullptr) + { + m_class = std::make_shared(*baseClass); + m_class->update(m_properties); + } + } + return (m_class != nullptr) ? m_class.get() : nullptr; +} + +// W a n g c o l o r . h p p +// ---------------------- +tson::TiledClass *tson::WangColor::getClass() +{ + if(m_class == nullptr) + { + TiledClass* baseClass = (m_map != nullptr && m_map->getProject() != nullptr) ? m_map->getProject()->getClass(m_classType) : nullptr; + if(baseClass != nullptr) + { + m_class = std::make_shared(*baseClass); + m_class->update(m_properties); + } + } + return (m_class != nullptr) ? m_class.get() : nullptr; +} + +/*! + * Gets the class information for the 'type'/'class' + * This may only give a valid result if the map is loaded through a tson::Project + * @return a tson::TiledClass object if related map was loaded through tson::Project + */ +tson::TiledClass *tson::Object::getClass() +{ + if(m_class == nullptr) + { + TiledClass* baseClass = (m_map != nullptr && m_map->getProject() != nullptr) ? m_map->getProject()->getClass(m_type) : nullptr; + if(baseClass != nullptr) + { + m_class = std::make_shared(*baseClass); + m_class->update(m_properties); + } + } + return (m_class != nullptr) ? m_class.get() : nullptr; +} + +// W o r l d . h p p +// ------------------ + +/*! + * Loads the actual maps based on the world data. + * @param parser A Tileson object used for parsing the maps of the world. + * @return How many maps who were parsed. Remember to call getStatus() for the actual map to find out if everything went okay. + */ + +std::size_t tson::World::loadMaps(tson::Tileson *parser) +{ + m_maps.clear(); + std::for_each(m_mapData.begin(), m_mapData.end(), [&](const tson::WorldMapData &data) + { + if(fs::exists(data.path)) + { + std::unique_ptr map = parser->parse(data.path); + m_maps.push_back(std::move(map)); + } + }); + + return m_maps.size(); +} + +// P r o p e r t y . h p p +// ------------------ +void tson::Property::setValueByType(IJson &json) +{ + switch(m_type) + { + case Type::Color: + m_value = Colori(json.get()); + break; + + case Type::File: + m_value = fs::path(json.get()); + break; + + case Type::Int: + if(!m_propertyType.empty()) + { + m_type = Type::Enum; + tson::EnumDefinition *def = (m_project != nullptr) ? m_project->getEnumDefinition(m_propertyType) : nullptr; + if(def != nullptr) + { + uint32_t v = json.get(); + m_value = tson::EnumValue(v, def); + } + else + m_value = tson::EnumValue(); + } + else + m_value = json.get(); + + break; + + case Type::Boolean: + m_value = json.get(); + break; + + case Type::Float: + m_value = json.get(); + break; + + case Type::String: + if(!m_propertyType.empty()) + { + m_type = Type::Enum; + tson::EnumDefinition *def = (m_project != nullptr) ? m_project->getEnumDefinition(m_propertyType) : nullptr; + if(def != nullptr) + { + std::string v = json.get(); + m_value = tson::EnumValue(v, def); + } + else + m_value = tson::EnumValue(); + } + else + setStrValue(json.get()); + + break; + + case Type::Class: + { + tson::TiledClass *baseClass = (m_project != nullptr) ? m_project->getClass(m_propertyType) : nullptr; + if (baseClass != nullptr) + { + tson::TiledClass c = *baseClass; + c.update(json); + m_value = c; + } + } + break; + + case Type::Object: + m_value = json.get(); + break; + default: + setStrValue(json.get()); + break; + } +} + +#endif //TILESON_TILESON_FORWARD_HPP + +/*** End of inlined file: tileson_forward.hpp ***/ + +#endif //TILESON_TILESON_H + diff --git a/src/ui/UINineSlice.cpp b/src/ui/UINineSlice.cpp new file mode 100644 index 0000000..2999661 --- /dev/null +++ b/src/ui/UINineSlice.cpp @@ -0,0 +1,295 @@ +/* + * UINineSlice.cpp + * + * Created on: May 31, 2020 + * Author: ayoungblood + */ + +#include "../ui/UINineSlice.h" +#include "../game/Game.hpp" +#include "../ecs/ECS.h" +#include "../ecs/Components.h" +#include "string.h" +#include +#include + +extern Manager manager; + +UINineSlice::UINineSlice(std::string texID) +{ + textureID = texID; +} + +UINineSlice::~UINineSlice() +{ + +} + +void UINineSlice::MakeSlices(std::string texture, int srcW, int srcH, int x0, int x1, int y0, int y1, SDL_Rect finalRect,int scale, Game::groupLabels group) +{ +// Note about the variables: The source texture is what everything is drawn from, with srcW and srcH defining the size of that texture and x0 x1 being the vertical slices position across the x axis, and the y0, y1 being the horizontal slices. This is also in the project readme with an ASCII art diagram. finalRect refers to the Rectangle that describes the area of the screen in which we want the 9-sliced source to appear in it's final in-game rendering. + + SDL_Rect srcRect; + SDL_Rect destRect; + int cols; + int rows; + int colsRemainder; + int rowsRemainder; + + cols = ((finalRect.w-x0-(srcW-x1))/(x1-x0)); + rows = (finalRect.h-y0-(srcH-y1))/(y1-y0); +// Calculate a remainders + colsRemainder = ((finalRect.w-x0-(srcW-x1))%(x1-x0)); + rowsRemainder = ((finalRect.h-y0-(srcH-y1))%(y1-y0)); + +// printf("\n\nColumns: %d\nRows: %d\nColumn Remainder: %d\nRow Remainder: %d\n\n",cols,rows,colsRemainder,rowsRemainder); + + finalRect.x = finalRect.x*scale; + finalRect.y = finalRect.y*scale; + finalRect.w = finalRect.w*scale; + finalRect.h = finalRect.h*scale; + + for (int i=0; i<9; i++) + { + switch(i) + { + case 0: + srcRect.x = 0; + srcRect.y = 0; + srcRect.w = x0; + srcRect.h = y0; + destRect.w = srcRect.w; + destRect.h = srcRect.h; + break; + case 1: + srcRect.x = x0; + srcRect.y = 0; + srcRect.w = x1-x0; + srcRect.h = y0; + destRect.w = srcRect.w; + destRect.h = srcRect.h; + break; + case 2: + srcRect.x = x1; + srcRect.y = 0; + srcRect.w = srcW-x1; + srcRect.h = y0; + destRect.w = srcRect.w; + destRect.h = srcRect.h; + break; + case 3: + srcRect.x = 0; + srcRect.y = y0; + srcRect.w = x0; + srcRect.h = y1-y0; + destRect.w = srcRect.w; + destRect.h = srcRect.h; + break; + case 4: + srcRect.x = x0; + srcRect.y = y0; + srcRect.w = x1-x0; + srcRect.h = y1-y0; + destRect.w = srcRect.w; + destRect.h = srcRect.h; + break; + case 5: + srcRect.x = x1; + srcRect.y = y0; + srcRect.w = srcW-x1; + srcRect.h = y1-y0; + destRect.w = srcRect.w; + destRect.h = srcRect.h; + break; + case 6: + srcRect.x = 0; + srcRect.y = y1; + srcRect.w = x0; + srcRect.h = srcH-y1; + destRect.w = srcRect.w; + destRect.h = srcRect.h; + break; + case 7: + srcRect.x = x0; + srcRect.y = y1; + srcRect.w = x1-x0; + srcRect.h = srcH-y1; + destRect.w = srcRect.w; + destRect.h = srcRect.h; + break; + case 8: + srcRect.x = x1; + srcRect.y = y1; + srcRect.w = srcW-x1; + srcRect.h = srcH-y1; + destRect.w = srcRect.w; + destRect.h = srcRect.h; + break; + default: + srcRect.x = 0; + srcRect.y = 0; + srcRect.w = 0; + srcRect.h = 0; + destRect.w = 0; + destRect.h = 0; + } + +// Calculate where and how many tiles to place +// We only need one instance of each of these in each corner or slices 0,2,6,8 + if (i==0||i==2||i==6||i==8) + { + if (i==0) + { + destRect.x = finalRect.x; + destRect.y = finalRect.y; + } + if (i==2) + { + destRect.x = finalRect.x+(finalRect.w-srcRect.w*scale); + destRect.y = finalRect.y; + } + if (i==6) + { + destRect.x = finalRect.x; + destRect.y = finalRect.y+(finalRect.h-srcRect.h*scale); + } + if (i==8) + { + destRect.x = finalRect.x+(finalRect.w-srcRect.w*scale); + destRect.y = finalRect.y+(finalRect.h-srcRect.h*scale); + } + AddSlice(srcRect,destRect,scale,group); + } +// Slices 1,7 need to be repeated in a row + if (i==1||i==7) + { + if (i==1) + { + for (int c=0;c0){ + destRect.x = finalRect.x+(x0*scale+cols*(x1*scale-x0*scale)); + destRect.y = finalRect.y; + srcRect.w = colsRemainder; + destRect.w = colsRemainder; + AddSlice(srcRect,destRect,scale,group); + } + } + if (i==7) + { + for (int c=0;c0){ + destRect.x = finalRect.x+(x0*scale+cols*(x1*scale-x0*scale)); + destRect.y = finalRect.y+(finalRect.h-(srcH-y1)*scale); + srcRect.w = colsRemainder; + destRect.w = colsRemainder; + AddSlice(srcRect,destRect,scale,group); + } + } + } +// Slice 3,5 need to be repeated in a column + if (i==3||i==5) + { + if (i==3) + { + for (int r=0;r0){ + destRect.x = finalRect.x; + destRect.y = finalRect.y+(y0*scale+rows*(y1-y0)*scale); + srcRect.h = rowsRemainder; + destRect.h = rowsRemainder; + AddSlice(srcRect,destRect,scale,group); + } + } + if (i==5) + { + for (int r=0;r0){ + destRect.x = finalRect.x+(finalRect.w-(srcW-x1)*scale); + destRect.y = finalRect.y+(y0*scale+rows*(y1-y0)*scale); + srcRect.h = rowsRemainder; + destRect.h = rowsRemainder; + AddSlice(srcRect,destRect,scale,group); + } + } + } +// Slice 4 will need to be repeated in columns and rows + if (i==4) + { + int rowY; + for (int r=0;r0) + { + destRect.x = finalRect.x+(x0*scale+cols*(x1-x0)*scale); + destRect.y = finalRect.y+(y0*scale+rowY*scale); + srcRect.w = colsRemainder; + destRect.w = colsRemainder; + AddSlice(srcRect,destRect,scale,group); + } + } + if (rowsRemainder>0) + { + for (int c=0;c0&&colsRemainder>0) + { + destRect.x = finalRect.x+(x0*scale+cols*(x1-x0)*scale); + destRect.y = finalRect.y+(y0*scale+rows*(y1-y0)*scale); + srcRect.w = colsRemainder; + srcRect.h = rowsRemainder; + destRect.w = colsRemainder; + destRect.h = rowsRemainder; + AddSlice(srcRect,destRect,scale,group); + } + } + } +} + +void UINineSlice::AddSlice(SDL_Rect srcRect, SDL_Rect destRect, int scale, Game::groupLabels group) +{ + auto& slice(manager.addEntity()); + SDL_Rect scaledRect = SDL_Rect(); + scaledRect.x = destRect.x; + scaledRect.y = destRect.y; + scaledRect.w = destRect.w*scale; + scaledRect.h = destRect.h*scale; + slice.addComponent(scaledRect.x, scaledRect.y, scaledRect.w, scaledRect.h, 1); + slice.addComponent("textBox",SpriteComponent::spriteUIL0,srcRect,scaledRect); + slice.addGroup(group); +} diff --git a/src/ui/UINineSlice.h b/src/ui/UINineSlice.h new file mode 100644 index 0000000..5c0c219 --- /dev/null +++ b/src/ui/UINineSlice.h @@ -0,0 +1,29 @@ +/* + * UINineSlice.h + * + * Created on: May 31, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_UININESLICE_H_ +#define SRC_UININESLICE_H_ + +#include +#include "string" +#include "../game/Game.hpp" + +class UINineSlice +{ +public: + std::string textureID; + SDL_Rect destRect; + UINineSlice(std::string texID); + ~UINineSlice(); + void MakeSlices(std::string texture, int srcW, int srcH, int x0, int x1, int y0, int y1, SDL_Rect destRect, int scale, Game::groupLabels label); + void AddSlice(SDL_Rect srcRect, SDL_Rect destRect, int scale, Game::groupLabels label); +private: + +}; + + +#endif /* SRC_UININESLICE_H_ */ diff --git a/src/ui/UIText.cpp b/src/ui/UIText.cpp new file mode 100644 index 0000000..4900b44 --- /dev/null +++ b/src/ui/UIText.cpp @@ -0,0 +1,91 @@ +/* + * UIText.cpp + * + * Created on: May 21, 2020 + * Author: ayoungblood + */ + +#include "UIText.h" +#include "../game/Game.hpp" +#include "../ecs/ECS.h" +#include "../ecs/Components.h" +#include "string.h" +#include +#include +#include + +extern Manager manager; + +UIText::UIText(std::string text, std::string texId, int x, int y, int letterW, int letterH, int lScale, std::string tag, Game::groupLabels group) +{ + inputText = text; + textureID = texId; + posX = x; + posY = y; + letterWidth = letterW; + letterHeight = letterH; + scale = lScale; +// gameGroup = Game::groupLabels::groupUI_Layer3; + auto& uiLetters(manager.addEntity()); + uiLetters.setTag(tag); + uiLetters.addGroup(group); +} + +UIText::~UIText() +{ +} + +void UIText::ParseString(std::string inputText, int x, int y, int letterScale, std::string tag, Game::groupLabels group) +{ +// gameGroup = group; + //Parse input text into an array of char + int posX = x; + int posY = y; + int i = 0; + char current = inputText[i]; + int charsNumber = inputText.length(); +// printf("Counting string \'%s\': \n",inputText.c_str()); +// printf("%d\n",charsNumber); + + do + { + ++i; + if (strcmp(¤t,"\n")!=0) + { + posX += letterWidth; + } + else + { + posX = x; + posY += letterHeight; + } + UIText::AddLetter(posX, posY, current, tag, letterScale, group); + current = inputText[i]; + } while ((strcmp(¤t,"\0"))!=0); +} + + +void UIText::AddLetter(int xpos, int ypos, char crnt, std::string tag, int lttrScale, Game::groupLabels groupLabel) +{ +// =======THIS NEEDS TO BE REFACTORED TO NOT USE INDIVIDUAL ENTITIES FOR EACH LETTER============ +// auto& letter(manager.addEntity()); +// letter.addComponent(xpos*lttrScale, ypos*lttrScale, letterWidth, letterHeight, 1); +// letter.addComponent("font", SpriteComponent::spriteText, crnt, letterWidth, letterHeight, lttrScale); +// letter.setTag(tag); +// letter.addGroup(groupLabel); + SDL_Texture* letterTexture; + letterTexture = Game::assets->GetTexture(textureID); + SDL_Rect srcRect,destRect; + srcRect.x = ((crnt-ASCII_START_IDX) % ASCII_ROW_COUNT)*letterWidth; + srcRect.y = ((crnt-ASCII_START_IDX)/ASCII_ROW_COUNT)*letterHeight; + srcRect.w = letterWidth; + srcRect.h = letterHeight; + destRect.x = xpos; + destRect.y = ypos; + destRect.w = letterWidth*scale; + destRect.h = letterHeight*scale; + + TextureManager::Draw(letterTexture,srcRect,destRect,SDL_FLIP_NONE); +} + + diff --git a/src/ui/UIText.h b/src/ui/UIText.h new file mode 100644 index 0000000..8826def --- /dev/null +++ b/src/ui/UIText.h @@ -0,0 +1,54 @@ +/* + * UIText.h + * + * Created on: May 21, 2020 + * Author: ayoungblood + */ + +#ifndef SRC_UITEXT_H_ +#define SRC_UITEXT_H_ + +#define ASCII_START_IDX 32 +#define ASCII_COUNT 96 +#define ASCII_ROW_COUNT 16 + +#include +#include +#include "../game/Game.hpp" +#include "../assetmgr/TextureManager.h" +#include "../assetmgr/AssetManager.h" + +class UIText +{ +private: +// SDL_Texture *texture; +// SDL_Rect srcRect, destRect; + char letter; +// int frames = 0; +// int speed = 100; + int letterWidth, letterHeight; + int scale = 1; +public: + std::string inputText; +// int letterHeight; +// int letterWidth; +// virtual void init() {} +// virtual void update() {} +// virtual void draw() {} + + Game::groupLabels gameGroup; + int posX; + int posY; + std::string textureID; + UIText(std::string inputText, std::string texID, int x, int y, int letterW, int letterH, int lScale, std::string tag, Game::groupLabels group); + ~UIText(); + void init() {} + void update() {} + void draw() {} + void AddLetter(int xpos, int ypos, char crnt, std::string tag, int lttrScale, Game::groupLabels groupLabel); + void ParseString(std::string inputText, int x, int y, int letterScale, std::string tag, Game::groupLabels group); +// void setTex(std::string id); +// int scale; +}; + +#endif /* SRC_UITEXT_H_ */