diff --git a/CMakeLists.txt b/CMakeLists.txt index d238068a..099bfccc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,11 +26,17 @@ IF (NOT WIN32) ) ENDIF() +# fix issues on gh action where object files are too large +if (WIN32) + add_compile_options(/bigobj) +endif () + # Add google test to CMake add_subdirectory(dependencies/google-test) + # If we need any compiler / linker flags, add them here -SET(GCC_COMPILE_FLAGS "") + SET(GCC_COMPILE_FLAGS "") SET(GCC_LINK_FLAGS "") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COMPILE_FLAGS}") @@ -52,7 +58,7 @@ add_subdirectory(dependencies/glm) # include glm library add_subdirectory(src/shared) # define game_shared_lib add_subdirectory(src/client) # create client executable add_subdirectory(src/server) # create server executable -add_subdirectory(src/debugger) # create debugger executable +#add_subdirectory(src/debugger) # create debugger executable find_program(CPPCHECK "cppcheck") add_custom_target(lint diff --git a/assets/.DS_Store b/assets/.DS_Store index df5a4be4..b6cbd5eb 100644 Binary files a/assets/.DS_Store and b/assets/.DS_Store differ diff --git a/assets/imgs/arrowtrap.png b/assets/imgs/arrowtrap.png new file mode 100644 index 00000000..7feac0ce Binary files /dev/null and b/assets/imgs/arrowtrap.png differ diff --git a/assets/imgs/blade.png b/assets/imgs/blade.png new file mode 100644 index 00000000..f4b5e54f Binary files /dev/null and b/assets/imgs/blade.png differ diff --git a/assets/imgs/blank.png b/assets/imgs/blank.png new file mode 100644 index 00000000..41b430e3 Binary files /dev/null and b/assets/imgs/blank.png differ diff --git a/assets/imgs/compass.png b/assets/imgs/compass.png new file mode 100644 index 00000000..7d072f3a Binary files /dev/null and b/assets/imgs/compass.png differ diff --git a/assets/imgs/compasses/compass_0.png b/assets/imgs/compasses/compass_0.png new file mode 100644 index 00000000..63be983b Binary files /dev/null and b/assets/imgs/compasses/compass_0.png differ diff --git a/assets/imgs/compasses/compass_120.png b/assets/imgs/compasses/compass_120.png new file mode 100644 index 00000000..a6818ba4 Binary files /dev/null and b/assets/imgs/compasses/compass_120.png differ diff --git a/assets/imgs/compasses/compass_150.png b/assets/imgs/compasses/compass_150.png new file mode 100644 index 00000000..573d432d Binary files /dev/null and b/assets/imgs/compasses/compass_150.png differ diff --git a/assets/imgs/compasses/compass_180.png b/assets/imgs/compasses/compass_180.png new file mode 100644 index 00000000..7fb3ce4b Binary files /dev/null and b/assets/imgs/compasses/compass_180.png differ diff --git a/assets/imgs/compasses/compass_210.png b/assets/imgs/compasses/compass_210.png new file mode 100644 index 00000000..1f7efaf1 Binary files /dev/null and b/assets/imgs/compasses/compass_210.png differ diff --git a/assets/imgs/compasses/compass_240.png b/assets/imgs/compasses/compass_240.png new file mode 100644 index 00000000..04332838 Binary files /dev/null and b/assets/imgs/compasses/compass_240.png differ diff --git a/assets/imgs/compasses/compass_270.png b/assets/imgs/compasses/compass_270.png new file mode 100644 index 00000000..69a2214e Binary files /dev/null and b/assets/imgs/compasses/compass_270.png differ diff --git a/assets/imgs/compasses/compass_30.png b/assets/imgs/compasses/compass_30.png new file mode 100644 index 00000000..7bc49533 Binary files /dev/null and b/assets/imgs/compasses/compass_30.png differ diff --git a/assets/imgs/compasses/compass_300.png b/assets/imgs/compasses/compass_300.png new file mode 100644 index 00000000..4adf695f Binary files /dev/null and b/assets/imgs/compasses/compass_300.png differ diff --git a/assets/imgs/compasses/compass_330.png b/assets/imgs/compasses/compass_330.png new file mode 100644 index 00000000..83a8c4cf Binary files /dev/null and b/assets/imgs/compasses/compass_330.png differ diff --git a/assets/imgs/compasses/compass_60.png b/assets/imgs/compasses/compass_60.png new file mode 100644 index 00000000..afed363b Binary files /dev/null and b/assets/imgs/compasses/compass_60.png differ diff --git a/assets/imgs/compasses/compass_90.png b/assets/imgs/compasses/compass_90.png new file mode 100644 index 00000000..ecc8403c Binary files /dev/null and b/assets/imgs/compasses/compass_90.png differ diff --git a/assets/imgs/deathCountBG.png b/assets/imgs/deathCountBG.png new file mode 100644 index 00000000..40fedb85 Binary files /dev/null and b/assets/imgs/deathCountBG.png differ diff --git a/assets/imgs/defeat.png b/assets/imgs/defeat.png new file mode 100644 index 00000000..8100c5df Binary files /dev/null and b/assets/imgs/defeat.png differ diff --git a/assets/imgs/destroyedSkull.png b/assets/imgs/destroyedSkull.png new file mode 100644 index 00000000..c32f2ec3 Binary files /dev/null and b/assets/imgs/destroyedSkull.png differ diff --git a/assets/imgs/died.png b/assets/imgs/died.png new file mode 100644 index 00000000..3c95bacd Binary files /dev/null and b/assets/imgs/died.png differ diff --git a/assets/imgs/dmBackground.png b/assets/imgs/dmBackground.png new file mode 100644 index 00000000..4726bfd2 Binary files /dev/null and b/assets/imgs/dmBackground.png differ diff --git a/assets/imgs/dmEventBG.png b/assets/imgs/dmEventBG.png new file mode 100644 index 00000000..e838c850 Binary files /dev/null and b/assets/imgs/dmEventBG.png differ diff --git a/assets/imgs/dm_cooldown.png b/assets/imgs/dm_cooldown.png new file mode 100644 index 00000000..4ccb4c0f Binary files /dev/null and b/assets/imgs/dm_cooldown.png differ diff --git a/assets/imgs/dm_cooldown/dm_cooldown_1.png b/assets/imgs/dm_cooldown/dm_cooldown_1.png new file mode 100644 index 00000000..7b5262ac Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_cooldown_1.png differ diff --git a/assets/imgs/dm_cooldown/dm_cooldown_10.png b/assets/imgs/dm_cooldown/dm_cooldown_10.png new file mode 100644 index 00000000..4668e125 Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_cooldown_10.png differ diff --git a/assets/imgs/dm_cooldown/dm_cooldown_2.png b/assets/imgs/dm_cooldown/dm_cooldown_2.png new file mode 100644 index 00000000..b1965838 Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_cooldown_2.png differ diff --git a/assets/imgs/dm_cooldown/dm_cooldown_3.png b/assets/imgs/dm_cooldown/dm_cooldown_3.png new file mode 100644 index 00000000..72283620 Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_cooldown_3.png differ diff --git a/assets/imgs/dm_cooldown/dm_cooldown_4.png b/assets/imgs/dm_cooldown/dm_cooldown_4.png new file mode 100644 index 00000000..eb35910e Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_cooldown_4.png differ diff --git a/assets/imgs/dm_cooldown/dm_cooldown_5.png b/assets/imgs/dm_cooldown/dm_cooldown_5.png new file mode 100644 index 00000000..47523a4c Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_cooldown_5.png differ diff --git a/assets/imgs/dm_cooldown/dm_cooldown_6.png b/assets/imgs/dm_cooldown/dm_cooldown_6.png new file mode 100644 index 00000000..41268c64 Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_cooldown_6.png differ diff --git a/assets/imgs/dm_cooldown/dm_cooldown_7.png b/assets/imgs/dm_cooldown/dm_cooldown_7.png new file mode 100644 index 00000000..20573450 Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_cooldown_7.png differ diff --git a/assets/imgs/dm_cooldown/dm_cooldown_8.png b/assets/imgs/dm_cooldown/dm_cooldown_8.png new file mode 100644 index 00000000..844547d4 Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_cooldown_8.png differ diff --git a/assets/imgs/dm_cooldown/dm_cooldown_9.png b/assets/imgs/dm_cooldown/dm_cooldown_9.png new file mode 100644 index 00000000..acd4c929 Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_cooldown_9.png differ diff --git a/assets/imgs/dm_cooldown/dm_selected_cooldown_1.png b/assets/imgs/dm_cooldown/dm_selected_cooldown_1.png new file mode 100644 index 00000000..275723bb Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_selected_cooldown_1.png differ diff --git a/assets/imgs/dm_cooldown/dm_selected_cooldown_10.png b/assets/imgs/dm_cooldown/dm_selected_cooldown_10.png new file mode 100644 index 00000000..e327ff3f Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_selected_cooldown_10.png differ diff --git a/assets/imgs/dm_cooldown/dm_selected_cooldown_2.png b/assets/imgs/dm_cooldown/dm_selected_cooldown_2.png new file mode 100644 index 00000000..a4afef18 Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_selected_cooldown_2.png differ diff --git a/assets/imgs/dm_cooldown/dm_selected_cooldown_3.png b/assets/imgs/dm_cooldown/dm_selected_cooldown_3.png new file mode 100644 index 00000000..69463b70 Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_selected_cooldown_3.png differ diff --git a/assets/imgs/dm_cooldown/dm_selected_cooldown_4.png b/assets/imgs/dm_cooldown/dm_selected_cooldown_4.png new file mode 100644 index 00000000..9c552481 Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_selected_cooldown_4.png differ diff --git a/assets/imgs/dm_cooldown/dm_selected_cooldown_5.png b/assets/imgs/dm_cooldown/dm_selected_cooldown_5.png new file mode 100644 index 00000000..ed445947 Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_selected_cooldown_5.png differ diff --git a/assets/imgs/dm_cooldown/dm_selected_cooldown_6.png b/assets/imgs/dm_cooldown/dm_selected_cooldown_6.png new file mode 100644 index 00000000..ff686bec Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_selected_cooldown_6.png differ diff --git a/assets/imgs/dm_cooldown/dm_selected_cooldown_7.png b/assets/imgs/dm_cooldown/dm_selected_cooldown_7.png new file mode 100644 index 00000000..964553a6 Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_selected_cooldown_7.png differ diff --git a/assets/imgs/dm_cooldown/dm_selected_cooldown_8.png b/assets/imgs/dm_cooldown/dm_selected_cooldown_8.png new file mode 100644 index 00000000..262a61fe Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_selected_cooldown_8.png differ diff --git a/assets/imgs/dm_cooldown/dm_selected_cooldown_9.png b/assets/imgs/dm_cooldown/dm_selected_cooldown_9.png new file mode 100644 index 00000000..5e9cba2f Binary files /dev/null and b/assets/imgs/dm_cooldown/dm_selected_cooldown_9.png differ diff --git a/assets/imgs/dm_left.png b/assets/imgs/dm_left.png new file mode 100644 index 00000000..4f87cf28 Binary files /dev/null and b/assets/imgs/dm_left.png differ diff --git a/assets/imgs/dm_middle.png b/assets/imgs/dm_middle.png new file mode 100644 index 00000000..2e0c90bb Binary files /dev/null and b/assets/imgs/dm_middle.png differ diff --git a/assets/imgs/dm_right.png b/assets/imgs/dm_right.png new file mode 100644 index 00000000..01186bc6 Binary files /dev/null and b/assets/imgs/dm_right.png differ diff --git a/assets/imgs/dm_selected.png b/assets/imgs/dm_selected.png new file mode 100644 index 00000000..e327ff3f Binary files /dev/null and b/assets/imgs/dm_selected.png differ diff --git a/assets/imgs/eventBG.png b/assets/imgs/eventBG.png new file mode 100644 index 00000000..b2e8994a Binary files /dev/null and b/assets/imgs/eventBG.png differ diff --git a/assets/imgs/exit.png b/assets/imgs/exit.png new file mode 100644 index 00000000..fde9e2b2 Binary files /dev/null and b/assets/imgs/exit.png differ diff --git a/assets/imgs/exitBG.png b/assets/imgs/exitBG.png new file mode 100644 index 00000000..8bf16cc6 Binary files /dev/null and b/assets/imgs/exitBG.png differ diff --git a/assets/imgs/exitBGSelected.png b/assets/imgs/exitBGSelected.png new file mode 100644 index 00000000..c08b1221 Binary files /dev/null and b/assets/imgs/exitBGSelected.png differ diff --git a/assets/imgs/exitDMBG.png b/assets/imgs/exitDMBG.png new file mode 100644 index 00000000..8bf16cc6 Binary files /dev/null and b/assets/imgs/exitDMBG.png differ diff --git a/assets/imgs/exitDMBGSelected.png b/assets/imgs/exitDMBGSelected.png new file mode 100644 index 00000000..c08b1221 Binary files /dev/null and b/assets/imgs/exitDMBGSelected.png differ diff --git a/assets/imgs/exitSelected.png b/assets/imgs/exitSelected.png new file mode 100644 index 00000000..f9f9ca39 Binary files /dev/null and b/assets/imgs/exitSelected.png differ diff --git a/assets/imgs/fire_wand.png b/assets/imgs/fire_wand.png index 91a047d9..640b6898 100644 Binary files a/assets/imgs/fire_wand.png and b/assets/imgs/fire_wand.png differ diff --git a/assets/imgs/floorspiketrap.png b/assets/imgs/floorspiketrap.png new file mode 100644 index 00000000..fe22d0ef Binary files /dev/null and b/assets/imgs/floorspiketrap.png differ diff --git a/assets/imgs/frame.png b/assets/imgs/frame.png deleted file mode 100644 index c5ab3938..00000000 Binary files a/assets/imgs/frame.png and /dev/null differ diff --git a/assets/imgs/heal_wand.png b/assets/imgs/heal_wand.png index ab778a9d..fd8103fd 100644 Binary files a/assets/imgs/heal_wand.png and b/assets/imgs/heal_wand.png differ diff --git a/assets/imgs/healthbar.png b/assets/imgs/healthbar.png new file mode 100644 index 00000000..63bbff17 Binary files /dev/null and b/assets/imgs/healthbar.png differ diff --git a/assets/imgs/healthtick_empty.png b/assets/imgs/healthtick_empty.png new file mode 100644 index 00000000..4bd7a069 Binary files /dev/null and b/assets/imgs/healthtick_empty.png differ diff --git a/assets/imgs/healthtick_full.png b/assets/imgs/healthtick_full.png new file mode 100644 index 00000000..fa8501f4 Binary files /dev/null and b/assets/imgs/healthtick_full.png differ diff --git a/assets/imgs/helpBG.png b/assets/imgs/helpBG.png new file mode 100644 index 00000000..8d888792 Binary files /dev/null and b/assets/imgs/helpBG.png differ diff --git a/assets/imgs/helpDMBG.png b/assets/imgs/helpDMBG.png new file mode 100644 index 00000000..be2adc51 Binary files /dev/null and b/assets/imgs/helpDMBG.png differ diff --git a/assets/imgs/itemBackground.png b/assets/imgs/itemBackground.png new file mode 100644 index 00000000..75a0cd34 Binary files /dev/null and b/assets/imgs/itemBackground.png differ diff --git a/assets/imgs/left.png b/assets/imgs/left.png new file mode 100644 index 00000000..14dab20c Binary files /dev/null and b/assets/imgs/left.png differ diff --git a/assets/imgs/lightcut.png b/assets/imgs/lightcut.png new file mode 100644 index 00000000..d247d844 Binary files /dev/null and b/assets/imgs/lightcut.png differ diff --git a/assets/imgs/lightning.png b/assets/imgs/lightning.png new file mode 100644 index 00000000..38d00a07 Binary files /dev/null and b/assets/imgs/lightning.png differ diff --git a/assets/imgs/lobbyButton.png b/assets/imgs/lobbyButton.png new file mode 100644 index 00000000..1d74497b Binary files /dev/null and b/assets/imgs/lobbyButton.png differ diff --git a/assets/imgs/logo_animation/frame_1.png b/assets/imgs/logo_animation/frame_1.png new file mode 100644 index 00000000..f2919e9d Binary files /dev/null and b/assets/imgs/logo_animation/frame_1.png differ diff --git a/assets/imgs/logo_animation/frame_10.png b/assets/imgs/logo_animation/frame_10.png new file mode 100644 index 00000000..095773a6 Binary files /dev/null and b/assets/imgs/logo_animation/frame_10.png differ diff --git a/assets/imgs/logo_animation/frame_100.png b/assets/imgs/logo_animation/frame_100.png new file mode 100644 index 00000000..1f10a281 Binary files /dev/null and b/assets/imgs/logo_animation/frame_100.png differ diff --git a/assets/imgs/logo_animation/frame_101.png b/assets/imgs/logo_animation/frame_101.png new file mode 100644 index 00000000..f3edf850 Binary files /dev/null and b/assets/imgs/logo_animation/frame_101.png differ diff --git a/assets/imgs/logo_animation/frame_102.png b/assets/imgs/logo_animation/frame_102.png new file mode 100644 index 00000000..9489d4a2 Binary files /dev/null and b/assets/imgs/logo_animation/frame_102.png differ diff --git a/assets/imgs/logo_animation/frame_103.png b/assets/imgs/logo_animation/frame_103.png new file mode 100644 index 00000000..6462509a Binary files /dev/null and b/assets/imgs/logo_animation/frame_103.png differ diff --git a/assets/imgs/logo_animation/frame_104.png b/assets/imgs/logo_animation/frame_104.png new file mode 100644 index 00000000..b2f83734 Binary files /dev/null and b/assets/imgs/logo_animation/frame_104.png differ diff --git a/assets/imgs/logo_animation/frame_105.png b/assets/imgs/logo_animation/frame_105.png new file mode 100644 index 00000000..75d9d580 Binary files /dev/null and b/assets/imgs/logo_animation/frame_105.png differ diff --git a/assets/imgs/logo_animation/frame_106.png b/assets/imgs/logo_animation/frame_106.png new file mode 100644 index 00000000..f62ad903 Binary files /dev/null and b/assets/imgs/logo_animation/frame_106.png differ diff --git a/assets/imgs/logo_animation/frame_107.png b/assets/imgs/logo_animation/frame_107.png new file mode 100644 index 00000000..4eba2281 Binary files /dev/null and b/assets/imgs/logo_animation/frame_107.png differ diff --git a/assets/imgs/logo_animation/frame_108.png b/assets/imgs/logo_animation/frame_108.png new file mode 100644 index 00000000..84bf38cd Binary files /dev/null and b/assets/imgs/logo_animation/frame_108.png differ diff --git a/assets/imgs/logo_animation/frame_109.png b/assets/imgs/logo_animation/frame_109.png new file mode 100644 index 00000000..874192ee Binary files /dev/null and b/assets/imgs/logo_animation/frame_109.png differ diff --git a/assets/imgs/logo_animation/frame_11.png b/assets/imgs/logo_animation/frame_11.png new file mode 100644 index 00000000..5bcbf5f8 Binary files /dev/null and b/assets/imgs/logo_animation/frame_11.png differ diff --git a/assets/imgs/logo_animation/frame_110.png b/assets/imgs/logo_animation/frame_110.png new file mode 100644 index 00000000..d97abc4d Binary files /dev/null and b/assets/imgs/logo_animation/frame_110.png differ diff --git a/assets/imgs/logo_animation/frame_111.png b/assets/imgs/logo_animation/frame_111.png new file mode 100644 index 00000000..5e051b15 Binary files /dev/null and b/assets/imgs/logo_animation/frame_111.png differ diff --git a/assets/imgs/logo_animation/frame_112.png b/assets/imgs/logo_animation/frame_112.png new file mode 100644 index 00000000..62a04869 Binary files /dev/null and b/assets/imgs/logo_animation/frame_112.png differ diff --git a/assets/imgs/logo_animation/frame_113.png b/assets/imgs/logo_animation/frame_113.png new file mode 100644 index 00000000..2e3ffddb Binary files /dev/null and b/assets/imgs/logo_animation/frame_113.png differ diff --git a/assets/imgs/logo_animation/frame_114.png b/assets/imgs/logo_animation/frame_114.png new file mode 100644 index 00000000..0dbbf361 Binary files /dev/null and b/assets/imgs/logo_animation/frame_114.png differ diff --git a/assets/imgs/logo_animation/frame_115.png b/assets/imgs/logo_animation/frame_115.png new file mode 100644 index 00000000..56e059ba Binary files /dev/null and b/assets/imgs/logo_animation/frame_115.png differ diff --git a/assets/imgs/logo_animation/frame_116.png b/assets/imgs/logo_animation/frame_116.png new file mode 100644 index 00000000..4e4dcb16 Binary files /dev/null and b/assets/imgs/logo_animation/frame_116.png differ diff --git a/assets/imgs/logo_animation/frame_117.png b/assets/imgs/logo_animation/frame_117.png new file mode 100644 index 00000000..d827f02b Binary files /dev/null and b/assets/imgs/logo_animation/frame_117.png differ diff --git a/assets/imgs/logo_animation/frame_118.png b/assets/imgs/logo_animation/frame_118.png new file mode 100644 index 00000000..28e96aa7 Binary files /dev/null and b/assets/imgs/logo_animation/frame_118.png differ diff --git a/assets/imgs/logo_animation/frame_119.png b/assets/imgs/logo_animation/frame_119.png new file mode 100644 index 00000000..0e765c61 Binary files /dev/null and b/assets/imgs/logo_animation/frame_119.png differ diff --git a/assets/imgs/logo_animation/frame_12.png b/assets/imgs/logo_animation/frame_12.png new file mode 100644 index 00000000..58ffba7a Binary files /dev/null and b/assets/imgs/logo_animation/frame_12.png differ diff --git a/assets/imgs/logo_animation/frame_120.png b/assets/imgs/logo_animation/frame_120.png new file mode 100644 index 00000000..c0ef884e Binary files /dev/null and b/assets/imgs/logo_animation/frame_120.png differ diff --git a/assets/imgs/logo_animation/frame_121.png b/assets/imgs/logo_animation/frame_121.png new file mode 100644 index 00000000..e7ba6f06 Binary files /dev/null and b/assets/imgs/logo_animation/frame_121.png differ diff --git a/assets/imgs/logo_animation/frame_122.png b/assets/imgs/logo_animation/frame_122.png new file mode 100644 index 00000000..ce88bf42 Binary files /dev/null and b/assets/imgs/logo_animation/frame_122.png differ diff --git a/assets/imgs/logo_animation/frame_123.png b/assets/imgs/logo_animation/frame_123.png new file mode 100644 index 00000000..54679013 Binary files /dev/null and b/assets/imgs/logo_animation/frame_123.png differ diff --git a/assets/imgs/logo_animation/frame_124.png b/assets/imgs/logo_animation/frame_124.png new file mode 100644 index 00000000..29caacf8 Binary files /dev/null and b/assets/imgs/logo_animation/frame_124.png differ diff --git a/assets/imgs/logo_animation/frame_125.png b/assets/imgs/logo_animation/frame_125.png new file mode 100644 index 00000000..1fefe9eb Binary files /dev/null and b/assets/imgs/logo_animation/frame_125.png differ diff --git a/assets/imgs/logo_animation/frame_126.png b/assets/imgs/logo_animation/frame_126.png new file mode 100644 index 00000000..2b6105b6 Binary files /dev/null and b/assets/imgs/logo_animation/frame_126.png differ diff --git a/assets/imgs/logo_animation/frame_127.png b/assets/imgs/logo_animation/frame_127.png new file mode 100644 index 00000000..07453bcf Binary files /dev/null and b/assets/imgs/logo_animation/frame_127.png differ diff --git a/assets/imgs/logo_animation/frame_128.png b/assets/imgs/logo_animation/frame_128.png new file mode 100644 index 00000000..e0370d6a Binary files /dev/null and b/assets/imgs/logo_animation/frame_128.png differ diff --git a/assets/imgs/logo_animation/frame_129.png b/assets/imgs/logo_animation/frame_129.png new file mode 100644 index 00000000..3d6c2b27 Binary files /dev/null and b/assets/imgs/logo_animation/frame_129.png differ diff --git a/assets/imgs/logo_animation/frame_13.png b/assets/imgs/logo_animation/frame_13.png new file mode 100644 index 00000000..a2f91745 Binary files /dev/null and b/assets/imgs/logo_animation/frame_13.png differ diff --git a/assets/imgs/logo_animation/frame_130.png b/assets/imgs/logo_animation/frame_130.png new file mode 100644 index 00000000..4df66e00 Binary files /dev/null and b/assets/imgs/logo_animation/frame_130.png differ diff --git a/assets/imgs/logo_animation/frame_131.png b/assets/imgs/logo_animation/frame_131.png new file mode 100644 index 00000000..8be6481d Binary files /dev/null and b/assets/imgs/logo_animation/frame_131.png differ diff --git a/assets/imgs/logo_animation/frame_132.png b/assets/imgs/logo_animation/frame_132.png new file mode 100644 index 00000000..7a914e71 Binary files /dev/null and b/assets/imgs/logo_animation/frame_132.png differ diff --git a/assets/imgs/logo_animation/frame_133.png b/assets/imgs/logo_animation/frame_133.png new file mode 100644 index 00000000..c2c510a2 Binary files /dev/null and b/assets/imgs/logo_animation/frame_133.png differ diff --git a/assets/imgs/logo_animation/frame_134.png b/assets/imgs/logo_animation/frame_134.png new file mode 100644 index 00000000..d3554941 Binary files /dev/null and b/assets/imgs/logo_animation/frame_134.png differ diff --git a/assets/imgs/logo_animation/frame_135.png b/assets/imgs/logo_animation/frame_135.png new file mode 100644 index 00000000..99488f98 Binary files /dev/null and b/assets/imgs/logo_animation/frame_135.png differ diff --git a/assets/imgs/logo_animation/frame_136.png b/assets/imgs/logo_animation/frame_136.png new file mode 100644 index 00000000..7fdc2d30 Binary files /dev/null and b/assets/imgs/logo_animation/frame_136.png differ diff --git a/assets/imgs/logo_animation/frame_137.png b/assets/imgs/logo_animation/frame_137.png new file mode 100644 index 00000000..9d04350d Binary files /dev/null and b/assets/imgs/logo_animation/frame_137.png differ diff --git a/assets/imgs/logo_animation/frame_138.png b/assets/imgs/logo_animation/frame_138.png new file mode 100644 index 00000000..7798bcf7 Binary files /dev/null and b/assets/imgs/logo_animation/frame_138.png differ diff --git a/assets/imgs/logo_animation/frame_139.png b/assets/imgs/logo_animation/frame_139.png new file mode 100644 index 00000000..4e16a71c Binary files /dev/null and b/assets/imgs/logo_animation/frame_139.png differ diff --git a/assets/imgs/logo_animation/frame_14.png b/assets/imgs/logo_animation/frame_14.png new file mode 100644 index 00000000..f2f0e511 Binary files /dev/null and b/assets/imgs/logo_animation/frame_14.png differ diff --git a/assets/imgs/logo_animation/frame_140.png b/assets/imgs/logo_animation/frame_140.png new file mode 100644 index 00000000..fad67812 Binary files /dev/null and b/assets/imgs/logo_animation/frame_140.png differ diff --git a/assets/imgs/logo_animation/frame_141.png b/assets/imgs/logo_animation/frame_141.png new file mode 100644 index 00000000..d311b674 Binary files /dev/null and b/assets/imgs/logo_animation/frame_141.png differ diff --git a/assets/imgs/logo_animation/frame_142.png b/assets/imgs/logo_animation/frame_142.png new file mode 100644 index 00000000..2d06bf92 Binary files /dev/null and b/assets/imgs/logo_animation/frame_142.png differ diff --git a/assets/imgs/logo_animation/frame_143.png b/assets/imgs/logo_animation/frame_143.png new file mode 100644 index 00000000..f2018721 Binary files /dev/null and b/assets/imgs/logo_animation/frame_143.png differ diff --git a/assets/imgs/logo_animation/frame_144.png b/assets/imgs/logo_animation/frame_144.png new file mode 100644 index 00000000..b20981ac Binary files /dev/null and b/assets/imgs/logo_animation/frame_144.png differ diff --git a/assets/imgs/logo_animation/frame_145.png b/assets/imgs/logo_animation/frame_145.png new file mode 100644 index 00000000..a47ad595 Binary files /dev/null and b/assets/imgs/logo_animation/frame_145.png differ diff --git a/assets/imgs/logo_animation/frame_146.png b/assets/imgs/logo_animation/frame_146.png new file mode 100644 index 00000000..d29da883 Binary files /dev/null and b/assets/imgs/logo_animation/frame_146.png differ diff --git a/assets/imgs/logo_animation/frame_147.png b/assets/imgs/logo_animation/frame_147.png new file mode 100644 index 00000000..ff33a34f Binary files /dev/null and b/assets/imgs/logo_animation/frame_147.png differ diff --git a/assets/imgs/logo_animation/frame_148.png b/assets/imgs/logo_animation/frame_148.png new file mode 100644 index 00000000..65fb6866 Binary files /dev/null and b/assets/imgs/logo_animation/frame_148.png differ diff --git a/assets/imgs/logo_animation/frame_149.png b/assets/imgs/logo_animation/frame_149.png new file mode 100644 index 00000000..fc995609 Binary files /dev/null and b/assets/imgs/logo_animation/frame_149.png differ diff --git a/assets/imgs/logo_animation/frame_15.png b/assets/imgs/logo_animation/frame_15.png new file mode 100644 index 00000000..5d072d29 Binary files /dev/null and b/assets/imgs/logo_animation/frame_15.png differ diff --git a/assets/imgs/logo_animation/frame_150.png b/assets/imgs/logo_animation/frame_150.png new file mode 100644 index 00000000..9feebfe3 Binary files /dev/null and b/assets/imgs/logo_animation/frame_150.png differ diff --git a/assets/imgs/logo_animation/frame_151.png b/assets/imgs/logo_animation/frame_151.png new file mode 100644 index 00000000..a9708645 Binary files /dev/null and b/assets/imgs/logo_animation/frame_151.png differ diff --git a/assets/imgs/logo_animation/frame_152.png b/assets/imgs/logo_animation/frame_152.png new file mode 100644 index 00000000..8a16ffdc Binary files /dev/null and b/assets/imgs/logo_animation/frame_152.png differ diff --git a/assets/imgs/logo_animation/frame_153.png b/assets/imgs/logo_animation/frame_153.png new file mode 100644 index 00000000..a83685e9 Binary files /dev/null and b/assets/imgs/logo_animation/frame_153.png differ diff --git a/assets/imgs/logo_animation/frame_154.png b/assets/imgs/logo_animation/frame_154.png new file mode 100644 index 00000000..ae5f8757 Binary files /dev/null and b/assets/imgs/logo_animation/frame_154.png differ diff --git a/assets/imgs/logo_animation/frame_155.png b/assets/imgs/logo_animation/frame_155.png new file mode 100644 index 00000000..6fd24418 Binary files /dev/null and b/assets/imgs/logo_animation/frame_155.png differ diff --git a/assets/imgs/logo_animation/frame_156.png b/assets/imgs/logo_animation/frame_156.png new file mode 100644 index 00000000..bad5ecc5 Binary files /dev/null and b/assets/imgs/logo_animation/frame_156.png differ diff --git a/assets/imgs/logo_animation/frame_16.png b/assets/imgs/logo_animation/frame_16.png new file mode 100644 index 00000000..0cf32e72 Binary files /dev/null and b/assets/imgs/logo_animation/frame_16.png differ diff --git a/assets/imgs/logo_animation/frame_17.png b/assets/imgs/logo_animation/frame_17.png new file mode 100644 index 00000000..3e414bb0 Binary files /dev/null and b/assets/imgs/logo_animation/frame_17.png differ diff --git a/assets/imgs/logo_animation/frame_18.png b/assets/imgs/logo_animation/frame_18.png new file mode 100644 index 00000000..2dd66b6b Binary files /dev/null and b/assets/imgs/logo_animation/frame_18.png differ diff --git a/assets/imgs/logo_animation/frame_19.png b/assets/imgs/logo_animation/frame_19.png new file mode 100644 index 00000000..5f32bba9 Binary files /dev/null and b/assets/imgs/logo_animation/frame_19.png differ diff --git a/assets/imgs/logo_animation/frame_2.png b/assets/imgs/logo_animation/frame_2.png new file mode 100644 index 00000000..b0a5236e Binary files /dev/null and b/assets/imgs/logo_animation/frame_2.png differ diff --git a/assets/imgs/logo_animation/frame_20.png b/assets/imgs/logo_animation/frame_20.png new file mode 100644 index 00000000..6ad0e56c Binary files /dev/null and b/assets/imgs/logo_animation/frame_20.png differ diff --git a/assets/imgs/logo_animation/frame_21.png b/assets/imgs/logo_animation/frame_21.png new file mode 100644 index 00000000..1fdb8a49 Binary files /dev/null and b/assets/imgs/logo_animation/frame_21.png differ diff --git a/assets/imgs/logo_animation/frame_22.png b/assets/imgs/logo_animation/frame_22.png new file mode 100644 index 00000000..911b0477 Binary files /dev/null and b/assets/imgs/logo_animation/frame_22.png differ diff --git a/assets/imgs/logo_animation/frame_23.png b/assets/imgs/logo_animation/frame_23.png new file mode 100644 index 00000000..3ec69849 Binary files /dev/null and b/assets/imgs/logo_animation/frame_23.png differ diff --git a/assets/imgs/logo_animation/frame_24.png b/assets/imgs/logo_animation/frame_24.png new file mode 100644 index 00000000..70b9ab1c Binary files /dev/null and b/assets/imgs/logo_animation/frame_24.png differ diff --git a/assets/imgs/logo_animation/frame_25.png b/assets/imgs/logo_animation/frame_25.png new file mode 100644 index 00000000..4661314f Binary files /dev/null and b/assets/imgs/logo_animation/frame_25.png differ diff --git a/assets/imgs/logo_animation/frame_26.png b/assets/imgs/logo_animation/frame_26.png new file mode 100644 index 00000000..b00ee02f Binary files /dev/null and b/assets/imgs/logo_animation/frame_26.png differ diff --git a/assets/imgs/logo_animation/frame_27.png b/assets/imgs/logo_animation/frame_27.png new file mode 100644 index 00000000..770559f5 Binary files /dev/null and b/assets/imgs/logo_animation/frame_27.png differ diff --git a/assets/imgs/logo_animation/frame_28.png b/assets/imgs/logo_animation/frame_28.png new file mode 100644 index 00000000..627862b5 Binary files /dev/null and b/assets/imgs/logo_animation/frame_28.png differ diff --git a/assets/imgs/logo_animation/frame_29.png b/assets/imgs/logo_animation/frame_29.png new file mode 100644 index 00000000..e482b138 Binary files /dev/null and b/assets/imgs/logo_animation/frame_29.png differ diff --git a/assets/imgs/logo_animation/frame_3.png b/assets/imgs/logo_animation/frame_3.png new file mode 100644 index 00000000..55453c6a Binary files /dev/null and b/assets/imgs/logo_animation/frame_3.png differ diff --git a/assets/imgs/logo_animation/frame_30.png b/assets/imgs/logo_animation/frame_30.png new file mode 100644 index 00000000..4a33d34d Binary files /dev/null and b/assets/imgs/logo_animation/frame_30.png differ diff --git a/assets/imgs/logo_animation/frame_31.png b/assets/imgs/logo_animation/frame_31.png new file mode 100644 index 00000000..ae065daf Binary files /dev/null and b/assets/imgs/logo_animation/frame_31.png differ diff --git a/assets/imgs/logo_animation/frame_32.png b/assets/imgs/logo_animation/frame_32.png new file mode 100644 index 00000000..29cf1186 Binary files /dev/null and b/assets/imgs/logo_animation/frame_32.png differ diff --git a/assets/imgs/logo_animation/frame_33.png b/assets/imgs/logo_animation/frame_33.png new file mode 100644 index 00000000..b0676b01 Binary files /dev/null and b/assets/imgs/logo_animation/frame_33.png differ diff --git a/assets/imgs/logo_animation/frame_34.png b/assets/imgs/logo_animation/frame_34.png new file mode 100644 index 00000000..fd54e619 Binary files /dev/null and b/assets/imgs/logo_animation/frame_34.png differ diff --git a/assets/imgs/logo_animation/frame_35.png b/assets/imgs/logo_animation/frame_35.png new file mode 100644 index 00000000..06a60381 Binary files /dev/null and b/assets/imgs/logo_animation/frame_35.png differ diff --git a/assets/imgs/logo_animation/frame_36.png b/assets/imgs/logo_animation/frame_36.png new file mode 100644 index 00000000..3497cd9f Binary files /dev/null and b/assets/imgs/logo_animation/frame_36.png differ diff --git a/assets/imgs/logo_animation/frame_37.png b/assets/imgs/logo_animation/frame_37.png new file mode 100644 index 00000000..9a55b305 Binary files /dev/null and b/assets/imgs/logo_animation/frame_37.png differ diff --git a/assets/imgs/logo_animation/frame_38.png b/assets/imgs/logo_animation/frame_38.png new file mode 100644 index 00000000..2c7fa18e Binary files /dev/null and b/assets/imgs/logo_animation/frame_38.png differ diff --git a/assets/imgs/logo_animation/frame_39.png b/assets/imgs/logo_animation/frame_39.png new file mode 100644 index 00000000..a4e12e39 Binary files /dev/null and b/assets/imgs/logo_animation/frame_39.png differ diff --git a/assets/imgs/logo_animation/frame_4.png b/assets/imgs/logo_animation/frame_4.png new file mode 100644 index 00000000..6d0b91f9 Binary files /dev/null and b/assets/imgs/logo_animation/frame_4.png differ diff --git a/assets/imgs/logo_animation/frame_40.png b/assets/imgs/logo_animation/frame_40.png new file mode 100644 index 00000000..ecb0c483 Binary files /dev/null and b/assets/imgs/logo_animation/frame_40.png differ diff --git a/assets/imgs/logo_animation/frame_41.png b/assets/imgs/logo_animation/frame_41.png new file mode 100644 index 00000000..eba963fd Binary files /dev/null and b/assets/imgs/logo_animation/frame_41.png differ diff --git a/assets/imgs/logo_animation/frame_42.png b/assets/imgs/logo_animation/frame_42.png new file mode 100644 index 00000000..5a555207 Binary files /dev/null and b/assets/imgs/logo_animation/frame_42.png differ diff --git a/assets/imgs/logo_animation/frame_43.png b/assets/imgs/logo_animation/frame_43.png new file mode 100644 index 00000000..223de386 Binary files /dev/null and b/assets/imgs/logo_animation/frame_43.png differ diff --git a/assets/imgs/logo_animation/frame_44.png b/assets/imgs/logo_animation/frame_44.png new file mode 100644 index 00000000..ada96613 Binary files /dev/null and b/assets/imgs/logo_animation/frame_44.png differ diff --git a/assets/imgs/logo_animation/frame_45.png b/assets/imgs/logo_animation/frame_45.png new file mode 100644 index 00000000..ddc5f1a2 Binary files /dev/null and b/assets/imgs/logo_animation/frame_45.png differ diff --git a/assets/imgs/logo_animation/frame_46.png b/assets/imgs/logo_animation/frame_46.png new file mode 100644 index 00000000..2e36309c Binary files /dev/null and b/assets/imgs/logo_animation/frame_46.png differ diff --git a/assets/imgs/logo_animation/frame_47.png b/assets/imgs/logo_animation/frame_47.png new file mode 100644 index 00000000..1b24afa2 Binary files /dev/null and b/assets/imgs/logo_animation/frame_47.png differ diff --git a/assets/imgs/logo_animation/frame_48.png b/assets/imgs/logo_animation/frame_48.png new file mode 100644 index 00000000..5ef5cf38 Binary files /dev/null and b/assets/imgs/logo_animation/frame_48.png differ diff --git a/assets/imgs/logo_animation/frame_49.png b/assets/imgs/logo_animation/frame_49.png new file mode 100644 index 00000000..ab6605fe Binary files /dev/null and b/assets/imgs/logo_animation/frame_49.png differ diff --git a/assets/imgs/logo_animation/frame_5.png b/assets/imgs/logo_animation/frame_5.png new file mode 100644 index 00000000..2a24be0b Binary files /dev/null and b/assets/imgs/logo_animation/frame_5.png differ diff --git a/assets/imgs/logo_animation/frame_50.png b/assets/imgs/logo_animation/frame_50.png new file mode 100644 index 00000000..1ef7a3d8 Binary files /dev/null and b/assets/imgs/logo_animation/frame_50.png differ diff --git a/assets/imgs/logo_animation/frame_51.png b/assets/imgs/logo_animation/frame_51.png new file mode 100644 index 00000000..f9df6982 Binary files /dev/null and b/assets/imgs/logo_animation/frame_51.png differ diff --git a/assets/imgs/logo_animation/frame_52.png b/assets/imgs/logo_animation/frame_52.png new file mode 100644 index 00000000..eb8007b6 Binary files /dev/null and b/assets/imgs/logo_animation/frame_52.png differ diff --git a/assets/imgs/logo_animation/frame_53.png b/assets/imgs/logo_animation/frame_53.png new file mode 100644 index 00000000..f56c89a3 Binary files /dev/null and b/assets/imgs/logo_animation/frame_53.png differ diff --git a/assets/imgs/logo_animation/frame_54.png b/assets/imgs/logo_animation/frame_54.png new file mode 100644 index 00000000..cd377a07 Binary files /dev/null and b/assets/imgs/logo_animation/frame_54.png differ diff --git a/assets/imgs/logo_animation/frame_55.png b/assets/imgs/logo_animation/frame_55.png new file mode 100644 index 00000000..fe2d358f Binary files /dev/null and b/assets/imgs/logo_animation/frame_55.png differ diff --git a/assets/imgs/logo_animation/frame_56.png b/assets/imgs/logo_animation/frame_56.png new file mode 100644 index 00000000..7d4dff31 Binary files /dev/null and b/assets/imgs/logo_animation/frame_56.png differ diff --git a/assets/imgs/logo_animation/frame_57.png b/assets/imgs/logo_animation/frame_57.png new file mode 100644 index 00000000..1980eecc Binary files /dev/null and b/assets/imgs/logo_animation/frame_57.png differ diff --git a/assets/imgs/logo_animation/frame_58.png b/assets/imgs/logo_animation/frame_58.png new file mode 100644 index 00000000..b1f863f1 Binary files /dev/null and b/assets/imgs/logo_animation/frame_58.png differ diff --git a/assets/imgs/logo_animation/frame_59.png b/assets/imgs/logo_animation/frame_59.png new file mode 100644 index 00000000..ec3f25de Binary files /dev/null and b/assets/imgs/logo_animation/frame_59.png differ diff --git a/assets/imgs/logo_animation/frame_6.png b/assets/imgs/logo_animation/frame_6.png new file mode 100644 index 00000000..95ef9348 Binary files /dev/null and b/assets/imgs/logo_animation/frame_6.png differ diff --git a/assets/imgs/logo_animation/frame_60.png b/assets/imgs/logo_animation/frame_60.png new file mode 100644 index 00000000..e5f0202a Binary files /dev/null and b/assets/imgs/logo_animation/frame_60.png differ diff --git a/assets/imgs/logo_animation/frame_61.png b/assets/imgs/logo_animation/frame_61.png new file mode 100644 index 00000000..41327991 Binary files /dev/null and b/assets/imgs/logo_animation/frame_61.png differ diff --git a/assets/imgs/logo_animation/frame_62.png b/assets/imgs/logo_animation/frame_62.png new file mode 100644 index 00000000..65e663d8 Binary files /dev/null and b/assets/imgs/logo_animation/frame_62.png differ diff --git a/assets/imgs/logo_animation/frame_63.png b/assets/imgs/logo_animation/frame_63.png new file mode 100644 index 00000000..b725bfed Binary files /dev/null and b/assets/imgs/logo_animation/frame_63.png differ diff --git a/assets/imgs/logo_animation/frame_64.png b/assets/imgs/logo_animation/frame_64.png new file mode 100644 index 00000000..b2088edd Binary files /dev/null and b/assets/imgs/logo_animation/frame_64.png differ diff --git a/assets/imgs/logo_animation/frame_65.png b/assets/imgs/logo_animation/frame_65.png new file mode 100644 index 00000000..586b2d63 Binary files /dev/null and b/assets/imgs/logo_animation/frame_65.png differ diff --git a/assets/imgs/logo_animation/frame_66.png b/assets/imgs/logo_animation/frame_66.png new file mode 100644 index 00000000..f633b180 Binary files /dev/null and b/assets/imgs/logo_animation/frame_66.png differ diff --git a/assets/imgs/logo_animation/frame_67.png b/assets/imgs/logo_animation/frame_67.png new file mode 100644 index 00000000..fc7540fa Binary files /dev/null and b/assets/imgs/logo_animation/frame_67.png differ diff --git a/assets/imgs/logo_animation/frame_68.png b/assets/imgs/logo_animation/frame_68.png new file mode 100644 index 00000000..412b1d4e Binary files /dev/null and b/assets/imgs/logo_animation/frame_68.png differ diff --git a/assets/imgs/logo_animation/frame_69.png b/assets/imgs/logo_animation/frame_69.png new file mode 100644 index 00000000..07b18b12 Binary files /dev/null and b/assets/imgs/logo_animation/frame_69.png differ diff --git a/assets/imgs/logo_animation/frame_7.png b/assets/imgs/logo_animation/frame_7.png new file mode 100644 index 00000000..ac550080 Binary files /dev/null and b/assets/imgs/logo_animation/frame_7.png differ diff --git a/assets/imgs/logo_animation/frame_70.png b/assets/imgs/logo_animation/frame_70.png new file mode 100644 index 00000000..c51e8db2 Binary files /dev/null and b/assets/imgs/logo_animation/frame_70.png differ diff --git a/assets/imgs/logo_animation/frame_71.png b/assets/imgs/logo_animation/frame_71.png new file mode 100644 index 00000000..54f57a85 Binary files /dev/null and b/assets/imgs/logo_animation/frame_71.png differ diff --git a/assets/imgs/logo_animation/frame_72.png b/assets/imgs/logo_animation/frame_72.png new file mode 100644 index 00000000..1539d225 Binary files /dev/null and b/assets/imgs/logo_animation/frame_72.png differ diff --git a/assets/imgs/logo_animation/frame_73.png b/assets/imgs/logo_animation/frame_73.png new file mode 100644 index 00000000..ca2a8150 Binary files /dev/null and b/assets/imgs/logo_animation/frame_73.png differ diff --git a/assets/imgs/logo_animation/frame_74.png b/assets/imgs/logo_animation/frame_74.png new file mode 100644 index 00000000..dba797ae Binary files /dev/null and b/assets/imgs/logo_animation/frame_74.png differ diff --git a/assets/imgs/logo_animation/frame_75.png b/assets/imgs/logo_animation/frame_75.png new file mode 100644 index 00000000..c81795d1 Binary files /dev/null and b/assets/imgs/logo_animation/frame_75.png differ diff --git a/assets/imgs/logo_animation/frame_76.png b/assets/imgs/logo_animation/frame_76.png new file mode 100644 index 00000000..2079d305 Binary files /dev/null and b/assets/imgs/logo_animation/frame_76.png differ diff --git a/assets/imgs/logo_animation/frame_77.png b/assets/imgs/logo_animation/frame_77.png new file mode 100644 index 00000000..840229bd Binary files /dev/null and b/assets/imgs/logo_animation/frame_77.png differ diff --git a/assets/imgs/logo_animation/frame_78.png b/assets/imgs/logo_animation/frame_78.png new file mode 100644 index 00000000..61f869a4 Binary files /dev/null and b/assets/imgs/logo_animation/frame_78.png differ diff --git a/assets/imgs/logo_animation/frame_79.png b/assets/imgs/logo_animation/frame_79.png new file mode 100644 index 00000000..9f7ee469 Binary files /dev/null and b/assets/imgs/logo_animation/frame_79.png differ diff --git a/assets/imgs/logo_animation/frame_8.png b/assets/imgs/logo_animation/frame_8.png new file mode 100644 index 00000000..d678aabd Binary files /dev/null and b/assets/imgs/logo_animation/frame_8.png differ diff --git a/assets/imgs/logo_animation/frame_80.png b/assets/imgs/logo_animation/frame_80.png new file mode 100644 index 00000000..2e2e9b1e Binary files /dev/null and b/assets/imgs/logo_animation/frame_80.png differ diff --git a/assets/imgs/logo_animation/frame_81.png b/assets/imgs/logo_animation/frame_81.png new file mode 100644 index 00000000..c9609808 Binary files /dev/null and b/assets/imgs/logo_animation/frame_81.png differ diff --git a/assets/imgs/logo_animation/frame_82.png b/assets/imgs/logo_animation/frame_82.png new file mode 100644 index 00000000..f0d89e9f Binary files /dev/null and b/assets/imgs/logo_animation/frame_82.png differ diff --git a/assets/imgs/logo_animation/frame_83.png b/assets/imgs/logo_animation/frame_83.png new file mode 100644 index 00000000..773f9fdd Binary files /dev/null and b/assets/imgs/logo_animation/frame_83.png differ diff --git a/assets/imgs/logo_animation/frame_84.png b/assets/imgs/logo_animation/frame_84.png new file mode 100644 index 00000000..f9ca7768 Binary files /dev/null and b/assets/imgs/logo_animation/frame_84.png differ diff --git a/assets/imgs/logo_animation/frame_85.png b/assets/imgs/logo_animation/frame_85.png new file mode 100644 index 00000000..0f8a80b5 Binary files /dev/null and b/assets/imgs/logo_animation/frame_85.png differ diff --git a/assets/imgs/logo_animation/frame_86.png b/assets/imgs/logo_animation/frame_86.png new file mode 100644 index 00000000..bc185804 Binary files /dev/null and b/assets/imgs/logo_animation/frame_86.png differ diff --git a/assets/imgs/logo_animation/frame_87.png b/assets/imgs/logo_animation/frame_87.png new file mode 100644 index 00000000..e144fda7 Binary files /dev/null and b/assets/imgs/logo_animation/frame_87.png differ diff --git a/assets/imgs/logo_animation/frame_88.png b/assets/imgs/logo_animation/frame_88.png new file mode 100644 index 00000000..3053411b Binary files /dev/null and b/assets/imgs/logo_animation/frame_88.png differ diff --git a/assets/imgs/logo_animation/frame_89.png b/assets/imgs/logo_animation/frame_89.png new file mode 100644 index 00000000..f8946e91 Binary files /dev/null and b/assets/imgs/logo_animation/frame_89.png differ diff --git a/assets/imgs/logo_animation/frame_9.png b/assets/imgs/logo_animation/frame_9.png new file mode 100644 index 00000000..7b7f4c1f Binary files /dev/null and b/assets/imgs/logo_animation/frame_9.png differ diff --git a/assets/imgs/logo_animation/frame_90.png b/assets/imgs/logo_animation/frame_90.png new file mode 100644 index 00000000..a785100c Binary files /dev/null and b/assets/imgs/logo_animation/frame_90.png differ diff --git a/assets/imgs/logo_animation/frame_91.png b/assets/imgs/logo_animation/frame_91.png new file mode 100644 index 00000000..cd68f84e Binary files /dev/null and b/assets/imgs/logo_animation/frame_91.png differ diff --git a/assets/imgs/logo_animation/frame_92.png b/assets/imgs/logo_animation/frame_92.png new file mode 100644 index 00000000..35e15fec Binary files /dev/null and b/assets/imgs/logo_animation/frame_92.png differ diff --git a/assets/imgs/logo_animation/frame_93.png b/assets/imgs/logo_animation/frame_93.png new file mode 100644 index 00000000..eee6a192 Binary files /dev/null and b/assets/imgs/logo_animation/frame_93.png differ diff --git a/assets/imgs/logo_animation/frame_94.png b/assets/imgs/logo_animation/frame_94.png new file mode 100644 index 00000000..6800e154 Binary files /dev/null and b/assets/imgs/logo_animation/frame_94.png differ diff --git a/assets/imgs/logo_animation/frame_95.png b/assets/imgs/logo_animation/frame_95.png new file mode 100644 index 00000000..dd71e19c Binary files /dev/null and b/assets/imgs/logo_animation/frame_95.png differ diff --git a/assets/imgs/logo_animation/frame_96.png b/assets/imgs/logo_animation/frame_96.png new file mode 100644 index 00000000..96d6b963 Binary files /dev/null and b/assets/imgs/logo_animation/frame_96.png differ diff --git a/assets/imgs/logo_animation/frame_97.png b/assets/imgs/logo_animation/frame_97.png new file mode 100644 index 00000000..1954662e Binary files /dev/null and b/assets/imgs/logo_animation/frame_97.png differ diff --git a/assets/imgs/logo_animation/frame_98.png b/assets/imgs/logo_animation/frame_98.png new file mode 100644 index 00000000..04adde09 Binary files /dev/null and b/assets/imgs/logo_animation/frame_98.png differ diff --git a/assets/imgs/logo_animation/frame_99.png b/assets/imgs/logo_animation/frame_99.png new file mode 100644 index 00000000..15f18b7b Binary files /dev/null and b/assets/imgs/logo_animation/frame_99.png differ diff --git a/assets/imgs/manabar.png b/assets/imgs/manabar.png new file mode 100644 index 00000000..d0a5d495 Binary files /dev/null and b/assets/imgs/manabar.png differ diff --git a/assets/imgs/manatick_empty.png b/assets/imgs/manatick_empty.png new file mode 100644 index 00000000..3716fed3 Binary files /dev/null and b/assets/imgs/manatick_empty.png differ diff --git a/assets/imgs/manatick_full.png b/assets/imgs/manatick_full.png new file mode 100644 index 00000000..febddef0 Binary files /dev/null and b/assets/imgs/manatick_full.png differ diff --git a/assets/imgs/middle.png b/assets/imgs/middle.png new file mode 100644 index 00000000..cf33ec37 Binary files /dev/null and b/assets/imgs/middle.png differ diff --git a/assets/imgs/mirror.png b/assets/imgs/mirror.png new file mode 100644 index 00000000..1f3d0104 Binary files /dev/null and b/assets/imgs/mirror.png differ diff --git a/assets/imgs/needle.png b/assets/imgs/needle.png new file mode 100644 index 00000000..f4b5e54f Binary files /dev/null and b/assets/imgs/needle.png differ diff --git a/assets/imgs/normalSkull.png b/assets/imgs/normalSkull.png new file mode 100644 index 00000000..cfc6697b Binary files /dev/null and b/assets/imgs/normalSkull.png differ diff --git a/assets/imgs/orb.png b/assets/imgs/orb.png index 6e6b6d82..24b197f3 100644 Binary files a/assets/imgs/orb.png and b/assets/imgs/orb.png differ diff --git a/assets/imgs/player.png b/assets/imgs/player.png new file mode 100644 index 00000000..3aaf2d15 Binary files /dev/null and b/assets/imgs/player.png differ diff --git a/assets/imgs/playerSelected.png b/assets/imgs/playerSelected.png new file mode 100644 index 00000000..4c1b53a7 Binary files /dev/null and b/assets/imgs/playerSelected.png differ diff --git a/assets/imgs/pot_health.png b/assets/imgs/pot_health.png index a6968c37..9c110fdb 100644 Binary files a/assets/imgs/pot_health.png and b/assets/imgs/pot_health.png differ diff --git a/assets/imgs/pot_invisibility.png b/assets/imgs/pot_invisibility.png index 6c98b3f1..2ea06549 100644 Binary files a/assets/imgs/pot_invisibility.png and b/assets/imgs/pot_invisibility.png differ diff --git a/assets/imgs/pot_unknown.png b/assets/imgs/pot_unknown.png index 63bc788b..8c192eba 100644 Binary files a/assets/imgs/pot_unknown.png and b/assets/imgs/pot_unknown.png differ diff --git a/assets/imgs/readyPlayer.png b/assets/imgs/readyPlayer.png new file mode 100644 index 00000000..7ed0731e Binary files /dev/null and b/assets/imgs/readyPlayer.png differ diff --git a/assets/imgs/readyPlayerSelected.png b/assets/imgs/readyPlayerSelected.png new file mode 100644 index 00000000..e66402d7 Binary files /dev/null and b/assets/imgs/readyPlayerSelected.png differ diff --git a/assets/imgs/readyZeus.png b/assets/imgs/readyZeus.png new file mode 100644 index 00000000..bdbe7dfb Binary files /dev/null and b/assets/imgs/readyZeus.png differ diff --git a/assets/imgs/readyZeusSelected.png b/assets/imgs/readyZeusSelected.png new file mode 100644 index 00000000..81eafb8f Binary files /dev/null and b/assets/imgs/readyZeusSelected.png differ diff --git a/assets/imgs/respawn.png b/assets/imgs/respawn.png new file mode 100644 index 00000000..ba26d395 Binary files /dev/null and b/assets/imgs/respawn.png differ diff --git a/assets/imgs/right.png b/assets/imgs/right.png new file mode 100644 index 00000000..e45f893b Binary files /dev/null and b/assets/imgs/right.png differ diff --git a/assets/imgs/rowBG.png b/assets/imgs/rowBG.png new file mode 100644 index 00000000..ae8f9992 Binary files /dev/null and b/assets/imgs/rowBG.png differ diff --git a/assets/imgs/scroll.png b/assets/imgs/scroll.png index 09aab44b..851b06a2 100644 Binary files a/assets/imgs/scroll.png and b/assets/imgs/scroll.png differ diff --git a/assets/imgs/selected_frame.png b/assets/imgs/selected_frame.png deleted file mode 100644 index 3b450cd2..00000000 Binary files a/assets/imgs/selected_frame.png and /dev/null differ diff --git a/assets/imgs/selected_middle.png b/assets/imgs/selected_middle.png new file mode 100644 index 00000000..19d746df Binary files /dev/null and b/assets/imgs/selected_middle.png differ diff --git a/assets/imgs/spiketrap.png b/assets/imgs/spiketrap.png new file mode 100644 index 00000000..0f45dd73 Binary files /dev/null and b/assets/imgs/spiketrap.png differ diff --git a/assets/imgs/start.png b/assets/imgs/start.png new file mode 100644 index 00000000..d44b67bc Binary files /dev/null and b/assets/imgs/start.png differ diff --git a/assets/imgs/startBG.png b/assets/imgs/startBG.png new file mode 100644 index 00000000..f8a85bdc Binary files /dev/null and b/assets/imgs/startBG.png differ diff --git a/assets/imgs/startHover.png b/assets/imgs/startHover.png new file mode 100644 index 00000000..94cf7f8d Binary files /dev/null and b/assets/imgs/startHover.png differ diff --git a/assets/imgs/sungod.png b/assets/imgs/sungod.png new file mode 100644 index 00000000..9dc660cf Binary files /dev/null and b/assets/imgs/sungod.png differ diff --git a/assets/imgs/teleporter.png b/assets/imgs/teleporter.png new file mode 100644 index 00000000..6a33e5c0 Binary files /dev/null and b/assets/imgs/teleporter.png differ diff --git a/assets/imgs/title.png b/assets/imgs/title.png new file mode 100644 index 00000000..83bff7d7 Binary files /dev/null and b/assets/imgs/title.png differ diff --git a/assets/imgs/victory.png b/assets/imgs/victory.png new file mode 100644 index 00000000..6725c739 Binary files /dev/null and b/assets/imgs/victory.png differ diff --git a/assets/imgs/weapon_dagger.png b/assets/imgs/weapon_dagger.png index d6e224f1..6e4d65ec 100644 Binary files a/assets/imgs/weapon_dagger.png and b/assets/imgs/weapon_dagger.png differ diff --git a/assets/imgs/weapon_hammer.png b/assets/imgs/weapon_hammer.png index d410dfbd..04a4dcc8 100644 Binary files a/assets/imgs/weapon_hammer.png and b/assets/imgs/weapon_hammer.png differ diff --git a/assets/imgs/weapon_sword.png b/assets/imgs/weapon_sword.png index 553ba9ed..5af3545b 100644 Binary files a/assets/imgs/weapon_sword.png and b/assets/imgs/weapon_sword.png differ diff --git a/assets/imgs/zeus.png b/assets/imgs/zeus.png new file mode 100644 index 00000000..25a39476 Binary files /dev/null and b/assets/imgs/zeus.png differ diff --git a/assets/imgs/zeusSelected.png b/assets/imgs/zeusSelected.png new file mode 100644 index 00000000..c587d427 Binary files /dev/null and b/assets/imgs/zeusSelected.png differ diff --git a/assets/sounds/client_music/maze_exploration_dm.flac b/assets/sounds/client_music/maze_exploration_dm.flac new file mode 100644 index 00000000..db8b4e1c Binary files /dev/null and b/assets/sounds/client_music/maze_exploration_dm.flac differ diff --git a/assets/sounds/client_music/maze_exploration_dm.mp3 b/assets/sounds/client_music/maze_exploration_dm.mp3 new file mode 100644 index 00000000..6cfaf216 Binary files /dev/null and b/assets/sounds/client_music/maze_exploration_dm.mp3 differ diff --git a/assets/sounds/client_music/maze_exploration_players.flac b/assets/sounds/client_music/maze_exploration_players.flac new file mode 100644 index 00000000..b4da5439 Binary files /dev/null and b/assets/sounds/client_music/maze_exploration_players.flac differ diff --git a/assets/sounds/client_music/maze_exploration_players.mp3 b/assets/sounds/client_music/maze_exploration_players.mp3 new file mode 100644 index 00000000..0762f3b1 Binary files /dev/null and b/assets/sounds/client_music/maze_exploration_players.mp3 differ diff --git a/assets/sounds/client_music/menu.flac b/assets/sounds/client_music/menu.flac new file mode 100644 index 00000000..49d0339f Binary files /dev/null and b/assets/sounds/client_music/menu.flac differ diff --git a/assets/sounds/client_music/menu.mp3 b/assets/sounds/client_music/menu.mp3 new file mode 100644 index 00000000..71ad849f Binary files /dev/null and b/assets/sounds/client_music/menu.mp3 differ diff --git a/assets/sounds/client_music/mono-retrowave.mp3 b/assets/sounds/client_music/mono-retrowave.mp3 deleted file mode 100644 index 3b1fe995..00000000 Binary files a/assets/sounds/client_music/mono-retrowave.mp3 and /dev/null differ diff --git a/assets/sounds/client_music/relay_race_dm.flac b/assets/sounds/client_music/relay_race_dm.flac new file mode 100644 index 00000000..bf9183f3 Binary files /dev/null and b/assets/sounds/client_music/relay_race_dm.flac differ diff --git a/assets/sounds/client_music/relay_race_dm.mp3 b/assets/sounds/client_music/relay_race_dm.mp3 new file mode 100644 index 00000000..f0fa44d3 Binary files /dev/null and b/assets/sounds/client_music/relay_race_dm.mp3 differ diff --git a/assets/sounds/client_music/piano.wav b/assets/sounds/client_music/relay_race_players.flac similarity index 62% rename from assets/sounds/client_music/piano.wav rename to assets/sounds/client_music/relay_race_players.flac index 88544b02..017df2fa 100644 Binary files a/assets/sounds/client_music/piano.wav and b/assets/sounds/client_music/relay_race_players.flac differ diff --git a/assets/sounds/client_music/relay_race_players.mp3 b/assets/sounds/client_music/relay_race_players.mp3 new file mode 100644 index 00000000..fbae0f3c Binary files /dev/null and b/assets/sounds/client_music/relay_race_players.mp3 differ diff --git a/assets/sounds/client_sfx/victory_dm.flac b/assets/sounds/client_sfx/victory_dm.flac new file mode 100644 index 00000000..976a9914 Binary files /dev/null and b/assets/sounds/client_sfx/victory_dm.flac differ diff --git a/assets/sounds/client_sfx/victory_dm.mp3 b/assets/sounds/client_sfx/victory_dm.mp3 new file mode 100644 index 00000000..c984c9c0 Binary files /dev/null and b/assets/sounds/client_sfx/victory_dm.mp3 differ diff --git a/assets/sounds/client_sfx/victory_players.flac b/assets/sounds/client_sfx/victory_players.flac new file mode 100644 index 00000000..eda2a473 Binary files /dev/null and b/assets/sounds/client_sfx/victory_players.flac differ diff --git a/assets/sounds/client_sfx/victory_players.mp3 b/assets/sounds/client_sfx/victory_players.mp3 new file mode 100644 index 00000000..f362a5ab Binary files /dev/null and b/assets/sounds/client_sfx/victory_players.mp3 differ diff --git a/assets/sounds/client_sfx/vine-boom-mono.mp3 b/assets/sounds/client_sfx/vine-boom-mono.mp3 deleted file mode 100644 index e4a8e70b..00000000 Binary files a/assets/sounds/client_sfx/vine-boom-mono.mp3 and /dev/null differ diff --git a/assets/sounds/server_sfx/cutscene_gate_open.wav b/assets/sounds/server_sfx/cutscene_gate_open.wav new file mode 100644 index 00000000..e9762c23 Binary files /dev/null and b/assets/sounds/server_sfx/cutscene_gate_open.wav differ diff --git a/assets/sounds/server_sfx/dagger.wav b/assets/sounds/server_sfx/dagger.wav new file mode 100644 index 00000000..726a376b Binary files /dev/null and b/assets/sounds/server_sfx/dagger.wav differ diff --git a/assets/sounds/server_sfx/electric_hum.wav b/assets/sounds/server_sfx/electric_hum.wav new file mode 100644 index 00000000..04510a73 Binary files /dev/null and b/assets/sounds/server_sfx/electric_hum.wav differ diff --git a/assets/sounds/server_sfx/hammer.wav b/assets/sounds/server_sfx/hammer.wav new file mode 100644 index 00000000..61afb437 Binary files /dev/null and b/assets/sounds/server_sfx/hammer.wav differ diff --git a/assets/sounds/server_sfx/itemdrop.wav b/assets/sounds/server_sfx/itemdrop.wav new file mode 100644 index 00000000..354d8ef9 Binary files /dev/null and b/assets/sounds/server_sfx/itemdrop.wav differ diff --git a/assets/sounds/server_sfx/itempickup.wav b/assets/sounds/server_sfx/itempickup.wav new file mode 100644 index 00000000..e08a5f42 Binary files /dev/null and b/assets/sounds/server_sfx/itempickup.wav differ diff --git a/assets/sounds/server_sfx/minotaur.wav b/assets/sounds/server_sfx/minotaur.wav new file mode 100644 index 00000000..f046b56f Binary files /dev/null and b/assets/sounds/server_sfx/minotaur.wav differ diff --git a/assets/sounds/server_sfx/minotaur_death.wav b/assets/sounds/server_sfx/minotaur_death.wav new file mode 100755 index 00000000..bba0e2b5 Binary files /dev/null and b/assets/sounds/server_sfx/minotaur_death.wav differ diff --git a/assets/sounds/server_sfx/mirror_shatter.mp3 b/assets/sounds/server_sfx/mirror_shatter.mp3 new file mode 100644 index 00000000..9bbf22b7 Binary files /dev/null and b/assets/sounds/server_sfx/mirror_shatter.mp3 differ diff --git a/assets/sounds/server_sfx/players_start_theme.mp3 b/assets/sounds/server_sfx/players_start_theme.mp3 new file mode 100644 index 00000000..d6435138 Binary files /dev/null and b/assets/sounds/server_sfx/players_start_theme.mp3 differ diff --git a/assets/sounds/server_sfx/players_start_theme_old.mp3 b/assets/sounds/server_sfx/players_start_theme_old.mp3 new file mode 100644 index 00000000..e09e53a3 Binary files /dev/null and b/assets/sounds/server_sfx/players_start_theme_old.mp3 differ diff --git a/assets/sounds/server_sfx/potion.wav b/assets/sounds/server_sfx/potion.wav new file mode 100644 index 00000000..f771ce88 Binary files /dev/null and b/assets/sounds/server_sfx/potion.wav differ diff --git a/assets/sounds/server_sfx/python.wav b/assets/sounds/server_sfx/python.wav new file mode 100644 index 00000000..242b0cbc Binary files /dev/null and b/assets/sounds/server_sfx/python.wav differ diff --git a/assets/sounds/server_sfx/spell.wav b/assets/sounds/server_sfx/spell.wav new file mode 100644 index 00000000..381d112f Binary files /dev/null and b/assets/sounds/server_sfx/spell.wav differ diff --git a/assets/sounds/server_sfx/start_game_dm.flac b/assets/sounds/server_sfx/start_game_dm.flac new file mode 100644 index 00000000..f5be7143 Binary files /dev/null and b/assets/sounds/server_sfx/start_game_dm.flac differ diff --git a/assets/sounds/server_sfx/start_game_dm.mp3 b/assets/sounds/server_sfx/start_game_dm.mp3 new file mode 100644 index 00000000..ec55190e Binary files /dev/null and b/assets/sounds/server_sfx/start_game_dm.mp3 differ diff --git a/assets/sounds/server_sfx/sword.wav b/assets/sounds/server_sfx/sword.wav new file mode 100644 index 00000000..7844fa6f Binary files /dev/null and b/assets/sounds/server_sfx/sword.wav differ diff --git a/assets/sounds/server_sfx/teleport.wav b/assets/sounds/server_sfx/teleport.wav new file mode 100644 index 00000000..247282a4 Binary files /dev/null and b/assets/sounds/server_sfx/teleport.wav differ diff --git a/assets/sounds/server_sfx/thunder.wav b/assets/sounds/server_sfx/thunder.wav new file mode 100644 index 00000000..de80bfd2 Binary files /dev/null and b/assets/sounds/server_sfx/thunder.wav differ diff --git a/assets/sounds/server_sfx/wind.wav b/assets/sounds/server_sfx/wind.wav new file mode 100644 index 00000000..e588adf1 Binary files /dev/null and b/assets/sounds/server_sfx/wind.wav differ diff --git a/assets/sounds/server_sfx/zeus_start_theme.mp3 b/assets/sounds/server_sfx/zeus_start_theme.mp3 new file mode 100644 index 00000000..b1180b71 Binary files /dev/null and b/assets/sounds/server_sfx/zeus_start_theme.mp3 differ diff --git a/config.json b/config.json index 8ab750c4..2359dd97 100644 --- a/config.json +++ b/config.json @@ -1,25 +1,30 @@ { - "game": { - "maze": { - "directory": "maps", - "procedural": true, - "maze_file": "test/itemRoom.maze" - } + "game": { + "maze": { + "directory": "maps", + "procedural": false, + "maze_file": "demo/game1_player_pov.maze" }, - "network": { - "server_ip": "localhost", - "server_port": 2355 - }, - "server": { - "lobby_name": "Hope you're doing well!", - "lobby_broadcast": true, - "max_players": 1 - }, - "client": { - "default_name": "Conan O'Brien", - "lobby_discovery": true, - "window_width": 2000, - "draw_bboxes": false, - "fps_counter": true - } + "disable_enemies": false + }, + "network": { + "server_ip": "localhost", + "server_port": 2355 + }, + "server": { + "lobby_name": "Funny Lobby Name Here", + "lobby_broadcast": true, + "max_players": 4, + "disable_dm": false, + "skip_intro": false + }, + "client": { + "default_name": "Conan O'Brien", + "lobby_discovery": true, + "fullscreen": true, + "draw_bboxes": false, + "fps_counter": true, + "presentation": false, + "render": 80 + } } diff --git a/dependencies/boost/CMakeLists.txt b/dependencies/boost/CMakeLists.txt index f9fcda05..6ee3de41 100644 --- a/dependencies/boost/CMakeLists.txt +++ b/dependencies/boost/CMakeLists.txt @@ -1,9 +1,9 @@ set(BOOST_INCLUDE_LIBRARIES thread filesystem system program_options asio date_time serialization dll) set(BOOST_ENABLE_CMAKE ON) -IF(MSVC) - ADD_DEFINITIONS("/EHsc") -ENDIF(MSVC) +#IF(MSVC) +# ADD_DEFINITIONS("/EHsc") +#ENDIF(MSVC) include(FetchContent) FetchContent_Declare( diff --git a/dependencies/freetype/CMakeLists.txt b/dependencies/freetype/CMakeLists.txt index b62c76f2..9ad7ab9f 100644 --- a/dependencies/freetype/CMakeLists.txt +++ b/dependencies/freetype/CMakeLists.txt @@ -2,7 +2,7 @@ FetchContent_Declare( freetype - GIT_REPOSITORY https://gitlab.freedesktop.org/freetype/freetype.git + GIT_REPOSITORY https://github.com/freetype/freetype.git GIT_TAG VER-2-13-2 ) diff --git a/include/client/animation.hpp b/include/client/animation.hpp new file mode 100644 index 00000000..51ae5f43 --- /dev/null +++ b/include/client/animation.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "client/bone.hpp" +#include "client/model.hpp" +#include "client/util.hpp" + +struct AssimpNodeData { + glm::mat4 transformation; + std::string name; + int numChildren; + std::vector children; +}; + +class Animation { +public: + Animation() = default; + + Animation(const std::string& animationPath, Model* model); + + Animation(const std::string& animationDirPath, const std::string& animName, int frames); + + ~Animation() {} + + Bone* findBone(const std::string& name); + + inline float getTicksPerSecond() { return m_ticksPerSecond; } + inline float getDuration() { return m_duration;} + inline const AssimpNodeData& getRootNode() { return m_rootNode; } + inline const std::map& getBoneIDMap() { return m_boneInfoMap; } + inline Model* getFrame(int frame) { return model_keyframes[(frame % model_keyframes.size())]; } + inline int getKeyframeSize() { return model_keyframes.size(); } + +private: + void readMissingBones(const aiAnimation* animation, Model& model); + + void readHierarchyData(AssimpNodeData& dest, const aiNode* src); + + float m_duration; + int m_ticksPerSecond; + std::vector m_bones; + AssimpNodeData m_rootNode; + std::map m_boneInfoMap; + std::vector model_keyframes; +}; diff --git a/include/client/animationmanager.hpp b/include/client/animationmanager.hpp new file mode 100644 index 00000000..13d075ec --- /dev/null +++ b/include/client/animationmanager.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "client/animation.hpp" +#include "client/bone.hpp" +#include "shared/game/sharedobject.hpp" + +#define MAX_FRAME_TIME 0.033 + +class AnimationManager +{ +public: + explicit AnimationManager(); + + void updateAnimation(float dt); + + Model* updateFrameAnimation(float dt); + + void playAnimation(Animation* pAnimation); + + void calculateBoneTransform(const AssimpNodeData* node, glm::mat4 parentTransform); + + void addAnimation(Animation* anim, ModelType modelType, AnimState animState); + + void setAnimation(EntityID id, ModelType modelType, AnimState animState); + + void setFrameAnimation(EntityID id, ModelType modelType, AnimState animState); + + std::vector getFinalBoneMatrices() { return m_finalBoneMatrices; } + +private: + std::vector m_finalBoneMatrices; + std::unordered_map> entityAnimFrameMap; + std::unordered_map> entityAnimMap; + std::unordered_map> objAnimMap; + Animation* m_currentAnimation; + EntityID currEntity; + float m_currentTime; + float m_deltaTime; + double lastFrameTime; + int currFrame; +}; \ No newline at end of file diff --git a/include/client/bone.hpp b/include/client/bone.hpp new file mode 100644 index 00000000..eff336aa --- /dev/null +++ b/include/client/bone.hpp @@ -0,0 +1,69 @@ +#pragma once + +/* Container for bone data */ + +#include +#include +#include +#include + +#define GLM_ENABLE_EXPERIMENTAL +#include +#include "client/util.hpp" + +struct keyPosition +{ + glm::vec3 position; + float timeStamp; +}; + +struct keyRotation +{ + glm::quat orientation; + float timeStamp; +}; + +struct keyScale +{ + glm::vec3 scale; + float timeStamp; +}; + +class Bone +{ +public: + Bone(const std::string& name, int ID, const aiNodeAnim* channel); + + void update(float animationTime); + + glm::mat4 getLocalTransform() { return m_localTransform; } + std::string getBoneName() const { return m_name; } + int getBoneID() { return m_ID; } + + int getPositionIndex(float animationTime); + + int getRotationIndex(float animationTime); + + int getScaleIndex(float animationTime); + +private: + + float getScaleFactor(float lastTimeStamp, float nextTimeStamp, float animationTime); + + glm::mat4 interpolatePosition(float animationTime); + + glm::mat4 interpolateRotation(float animationTime); + + glm::mat4 interpolateScaling(float animationTime); + + std::vector m_positions; + std::vector m_rotations; + std::vector m_scales; + int m_numPositions; + int m_numRotations; + int m_numScalings; + + glm::mat4 m_localTransform; + std::string m_name; + int m_ID; +}; diff --git a/include/client/camera.hpp b/include/client/camera.hpp index b3f7f60f..a83d7e05 100644 --- a/include/client/camera.hpp +++ b/include/client/camera.hpp @@ -89,6 +89,8 @@ class Camera { glm::mat4 getView(); + void setPitch(float pitch); + protected: // Perspective controls float FOV; // Field of View Angle (degrees) diff --git a/include/client/client.hpp b/include/client/client.hpp index f212a9fe..72847beb 100644 --- a/include/client/client.hpp +++ b/include/client/client.hpp @@ -11,8 +11,6 @@ #include #include -#include "client/cube.hpp" -#include "client/lightsource.hpp" #include "client/shader.hpp" #include "client/model.hpp" #include "client/util.hpp" @@ -21,6 +19,9 @@ #include "client/camera.hpp" #include "client/audio/audiomanager.hpp" #include "client/constants.hpp" +#include "client/animation.hpp" +#include "client/animationmanager.hpp" +#include "client/bone.hpp" #include "shared/game/sharedgamestate.hpp" #include "shared/game/sharedobject.hpp" @@ -88,10 +89,13 @@ class Client { * @brief Callback which handles all updates to the local SharedGameState, and sends * events to the server based on any local inputs. All logic relating to state updates * shoud go in here. - * - * @param context */ - void idleCallback(boost::asio::io_context& context); + void idleCallback(); + + /** + * @brief sends all queued packets to server + */ + void sendPacketsToServer(); /** * @brief Callback which handles keyboard inputs to the GLFWwindow. Binds to the GLFWwindow. @@ -113,6 +117,15 @@ class Client { */ void mouseCallback(GLFWwindow* window, double xposIn, double yposIn); + /** + * @brief Callback which handles scrolling movement. + * + * @param window The GLFWwindow being monitered. + * @param xposIn The current scroll up value. + * @param yposIn The current scroll down value. + */ + void scrollCallback(GLFWwindow* window, double xposIn, double yposIn); + /** * @brief Callback which handles mouse button presses. * @@ -158,26 +171,71 @@ class Client { * * @param ip_addr */ - bool connectAndListen(std::string ip_addr); + bool connect(std::string ip_addr); + /** + * @brief get the reference to the Audio manager + */ AudioManager* getAudioManager(); + /** + * @brief get the reference to the Animation manager + */ + AnimationManager* getAnimManager() { return animManager; } + + /** + * @brief the current position in the world the player is looking at + */ void setWorldPos(); + /** + * @brief Send down a trap event to the server + * + * @param hover boolean to indicate the DM is only hovering and has not placed yet + * @param place boolean to indicate the DM would like to place a type + * @param trapType ModelType to indicate the type of the trap the DM wants to place + */ + void sendTrapEvent(bool hover, bool place, ModelType trapType); + int curr_fps; private: /** * @brief Processes all data received from the server and updates the SharedGameState. * - * @param context + * @param allow_defer whether or not you are allowed to defer packets until the next frame + * IMPORTANT: this is a performance optimization for more unstable networks, but it must + * be set to false until the ServerAssignEID packet has been received because then it + * guarantees the game has been fully loaded before trying to render things */ - void processServerInput(boost::asio::io_context& context); + void processServerInput(bool allow_defer); + + GLuint gBuffer; + GLuint gPosition, gNormal, gAlbedoSpec; + GLuint quadVAO = 0; + GLuint quadVBO; + GLuint cubeVAO = 0; + GLuint cubeVBO = 0; + + void configureGBuffer(); /** * @brief Draws all objects in the SharedGameState. */ void draw(); + /** + * @brief Goes through all objects and render them to + * G-Buffer. Does not handle lighting. Only stores + * each object's positions, normals and textures. + */ + void geometryPass(); + + /** + * @brief Loop through visible pixels and render + * lighting effects for the object at each pixel. + */ + void lightingPass(); + /** * @brief Draw bounding box around a given SharedObject * only if the client.draw_bboxes field is set to true @@ -187,22 +245,42 @@ class Client { /* Current game state */ SharedGameState gameState; + /* Stuff for the intro cutscene, contains a whole other SharedGameState */ + std::optional intro_cutscene; + /* Shader objects for various */ - std::shared_ptr cube_shader; - std::shared_ptr dm_cube_shader; - std::shared_ptr model_shader; - std::shared_ptr light_source_shader; - std::shared_ptr solid_surface_shader; - std::shared_ptr wall_shader; + std::shared_ptr deferred_geometry_shader; + std::shared_ptr deferred_lighting_shader; + std::shared_ptr dm_deferred_lighting_shader; + std::shared_ptr deferred_light_box_shader; /* Character models and lighting objects, might need to move to different classes later */ - std::unique_ptr cube_model; - std::unique_ptr player_model; + std::unique_ptr fire_player_model; + std::unique_ptr lightning_player_model; + std::unique_ptr water_player_model; + std::unique_ptr bear_model; - std::unique_ptr light_source; std::unique_ptr torchlight_model; + std::unique_ptr torchpost_model; std::unique_ptr wall_model; std::unique_ptr pillar_model; + std::unique_ptr sungod_model; + std::unique_ptr slime_model; + std::unique_ptr python_model; + std::unique_ptr item_model; + std::unique_ptr spike_trap_model; + std::unique_ptr orb_model; + std::unique_ptr exit_model; + std::unique_ptr floor_model; + std::unique_ptr arrow_model; + std::unique_ptr arrow_trap_model; + std::unique_ptr lava_cross_model; + std::unique_ptr lava_vertical_model; + std::unique_ptr lava_horizontal_model; + std::unique_ptr lightning_model; + std::unique_ptr chest_model; + std::unique_ptr teleport_model; + std::unique_ptr mirror_model; GLFWwindow *window; @@ -211,8 +289,45 @@ class Client { gui::GUI gui; gui::GUIState gui_state; + /** + * @brief Enum that describes this client's player's lobby player state + * (only relevant when GUIState is set to GUIState::Lobby) + */ + enum class LobbyPlayerState { + Connected, + SelectedRole, + Ready + }; + + /** + * @brief This client's player's lobby player state + * (only relevant when GUIState is set to GUIState::Lobby) + */ + LobbyPlayerState lobbyPlayerState; + + /** + * @brief Radio button state enum for a radio button GUI + * (represents currently selected radio button) + */ + enum class RadioButtonState { + NoneSelected, + FirstOption, + SecondOption + }; + + /** + * @brief This client's player's lobby player role radio + * button selection + */ + RadioButtonState roleSelection; + + /** + * @brief Audio Manager to play Client sounds + */ AudioManager* audioManager; + AnimationManager* animManager; + /* Camera object representing player's current position & orientation */ std::unique_ptr cam; @@ -229,17 +344,23 @@ class Client { bool is_held_left = false; bool is_held_space = false; + /* DM zooming in and out flags */ bool is_held_i = false; bool is_held_o = false; - bool is_pressed_p = false; - bool is_left_mouse_down = false; + bool is_right_mouse_down = false; + + /* DM Trap Orientation Traps With Directions */ + int orientation = 0; /* Mouse position coordinates */ float mouse_xpos = 0.0f; float mouse_ypos = 0.0f; + double lastTime = 0.0; + double lastFrameTime = 0.0; + GameConfig config; tcp::resolver resolver; tcp::socket socket; @@ -253,5 +374,15 @@ class Client { glm::vec3 world_pos; // stored world pause, calculated before the GUI is rendered std::array, MAX_POINT_LIGHTS> closest_light_sources; + + std::deque events_received; + + /** + * @brief boolean to see if a phase change from LOBBY to GAME already happened + */ + bool phase_change; + + // id of last known player which is holding the orb + EntityID player_has_orb_global_id; }; diff --git a/include/client/constants.hpp b/include/client/constants.hpp index ccd388ec..b758d2aa 100644 --- a/include/client/constants.hpp +++ b/include/client/constants.hpp @@ -1,15 +1,11 @@ #pragma once -#define MIN_WINDOW_WIDTH 900 -#define MIN_WINDOW_HEIGHT 600 +#define MIN_WINDOW_WIDTH 1280 +#define MIN_WINDOW_HEIGHT 720 // The screen width that's defined as 1:1, from which other // resolutions can calculate pixel sizes -#define UNIT_WINDOW_WIDTH 1500 -#define UNIT_WINDOW_HEIGHT 1000 +#define UNIT_WINDOW_WIDTH 1920 +#define UNIT_WINDOW_HEIGHT 1080 -#define PLAYER_EYE_LEVEL 2.35f - -// distance threshold of which objects -// to render -#define RENDER_DISTANCE 100.0f +#define PLAYER_EYE_LEVEL 3.45f diff --git a/include/client/cube.hpp b/include/client/cube.hpp deleted file mode 100644 index c25e5086..00000000 --- a/include/client/cube.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include "client/core.hpp" -#include "client/renderable.hpp" - -#define GLM_ENABLE_EXPERIMENTAL -#include -#include -#include - -#include -#include -#include - -class Cube : public Renderable { -public: - explicit Cube(glm::vec3 newColor); - ~Cube(); - - void draw(Shader* shader, - glm::mat4 viewProj, - glm::vec3 camPos, - std::array, MAX_POINT_LIGHTS> lightSources, - bool fill) override; -private: - GLuint VAO; - GLuint VBO_positions, VBO_normals, EBO; - - glm::vec3 color; - - // Cube Information - std::vector positions; - std::vector normals; - std::vector indices; - -}; \ No newline at end of file diff --git a/include/client/gui/font/font.hpp b/include/client/gui/font/font.hpp index 46babc45..842018f7 100644 --- a/include/client/gui/font/font.hpp +++ b/include/client/gui/font/font.hpp @@ -11,15 +11,17 @@ namespace gui::font { /** * Abstract representation of the different fonts to use in our game - * - * NOTE: currently I haven't found a good font for "Text", so both of these - * map to the same font. */ enum class Font { MENU, - TEXT + TEXT, + TITLE, }; +#define ALL_FONTS() { \ + Font::MENU, Font::TEXT, Font::TITLE \ +} + /** * Mappings from our specified abstract fonts to the file to load */ @@ -31,10 +33,11 @@ std::string getFilepath(Font font); enum class Color { BLACK, RED, + GREEN, BLUE, GRAY, WHITE, - TORCHLIGHT_GAMES + YELLOW, }; /** @@ -45,15 +48,17 @@ glm::vec3 getRGB(Color color); const int UNIT_LARGE_SIZE_PX = 128; // how many pixels a small font is on the unit screen size enum class Size { SMALL, + SMALLMEDIUM, MEDIUM, LARGE, XLARGE }; const std::unordered_map SIZE_TO_SCALE = { - {Size::SMALL, 0.25f}, - {Size::MEDIUM, 0.50f}, - {Size::LARGE, 1.0f}, - {Size::XLARGE, 2.0f}, + {Size::SMALL, 0.25f}, + {Size::SMALLMEDIUM, 0.35f}, + {Size::MEDIUM, 0.50f}, + {Size::LARGE, 1.0f}, + {Size::XLARGE, 2.0f}, }; int getFontSizePx(Size size); @@ -67,5 +72,6 @@ float getScaleFactor(Size size); */ float getRelativePixels(float pixels); +float getRelativePixelsHorizontal(float pixels); } diff --git a/include/client/gui/gui.hpp b/include/client/gui/gui.hpp index 434e8f3b..e3aa78bd 100644 --- a/include/client/gui/gui.hpp +++ b/include/client/gui/gui.hpp @@ -9,14 +9,23 @@ #include "client/gui/widget/staticimg.hpp" #include "client/gui/widget/centertext.hpp" #include "client/gui/widget/textinput.hpp" +#include "client/gui/widget/empty.hpp" #include "client/gui/font/font.hpp" #include "client/gui/font/loader.hpp" #include "client/gui/img/img.hpp" #include "client/gui/img/loader.hpp" +#include "server/game/constants.hpp" +#include "client/gui/img/logo.hpp" +#include "shared/utilities/config.hpp" #include +#include #include -#include +#include +#include + +// Forward declarration of LobbyPlayer struct +struct LobbyPlayer; class Client; @@ -33,6 +42,7 @@ enum class GUIState { TITLE_SCREEN, LOBBY_BROWSER, LOBBY, + INTRO_CUTSCENE, GAME_HUD, GAME_ESC_MENU, DEAD_SCREEN, @@ -67,7 +77,7 @@ class GUI { * @param client Pointer to the client object. Note that GUI is a friend class to client, so GUI can * access private client data members for ease of use. */ - explicit GUI(Client* client); + explicit GUI(Client* client, const GameConfig& config); /** * @brief Initializes all of the necessary file loading for all of the GUI elements, and * registers all of the static shader variables for each of the derived widget classes @@ -120,8 +130,12 @@ class GUI { * is down. Note: this is a reference so that if a click is captured it can toggle the * mouse down flag to false, so that click doesn't get "double counted" in subsequent * frames. + * @param is_right_mouse_down reference to flag which stores whether or not the right mouse + * is down. Note: this is a reference so that if a click is captured it can toggle the + * mouse down flag to false, so that click doesn't get "double counted" in subsequent + * frames. */ - void handleInputs(float mouse_xpos, float mouse_ypos, bool& is_left_mouse_down); + void handleInputs(float mouse_xpos, float mouse_ypos, bool& is_left_mouse_down, bool& is_right_mouse_down); /** * Renders the current state of the GUI to the screen, based on all of the widgets * that have been added and any changes caused by inputs. @@ -251,6 +265,10 @@ class GUI { * @brief Wipes all of the captured keyboard input from the internal state. */ void clearCapturedKeyboardInput(); + /** + * @brief Displays controls for the player + */ + void displayControl(); /** * @brief Returns all of the captured keyboard input without clearing it. * @@ -272,7 +290,7 @@ class GUI { private: widget::Handle next_handle {0}; - std::unordered_map widgets; + std::map widgets; std::shared_ptr fonts; img::Loader images; @@ -282,6 +300,13 @@ class GUI { Client* client; + GameConfig config; + bool controlDisplayed; + + std::vector recentEvents; + + img::Logo logo; + /// =========================================================== /** * @brief Performs a click on the specied coordinate in the GUI coordinate frame. @@ -347,6 +372,19 @@ class GUI { * Displays the lobby name and all of the players who are currently in the lobby. */ void _layoutLobby(); + + /** + * @brief Creates a player status row (which is represented as a Flexbox widget) for the + * given player. This function generates the player status row such that the 3 columns + * have the specified width (this is done using the Empty widget). + * @param lobbyPlayer Player for whom the player status row is created. + * @param columnWidths Widths for the 3 columns of the player status row. + * @param origin Origin for this player status row's Flexbox. + * @param playerIndex 1-indexed player index for the given LobbyPlayer. + * @return gui::widget::Flexbox::Ptr of a Flexbox storing the generated player status row. + */ + gui::widget::Flexbox::Ptr _createPlayerStatusRow(boost::optional lobbyPlayer, + glm::vec3 columnWidths, glm::vec2 origin, int playerIndex); /** * @brief Displays the Game HUD layout * diff --git a/include/client/gui/img/img.hpp b/include/client/gui/img/img.hpp index 8c3b9661..28817459 100644 --- a/include/client/gui/img/img.hpp +++ b/include/client/gui/img/img.hpp @@ -17,24 +17,106 @@ namespace gui::img { enum class ImgID { Yoshi, AwesomeSauce, - ItemFrame, - SelectedFrame, HealthPotion, UnknownPotion, InvisPotion, FireSpell, HealSpell, Orb, + Mirror, Crosshair, Scroll, Dagger, Sword, Hammer, + Title, + LeftHotbar, + RightHotbar, + MiddleHotbar, + MiddleSelected, + DMLeftHotbar, + DMRightHotbar, + DMMiddleHotbar, + DMMiddleSelected, + DMMiddleCooldown, + HealthBar, + HealthTickEmpty, + HealthTickFull, + ManaBar, + ManaTickEmpty, + ManaTickFull, + Needle, + ItemBG, + DMTrapBG, + EventBG, + DMEventBG, + Compass0, Compass30, Compass60, + Compass90, Compass120, Compass150, + Compass180, Compass210, Compass240, + Compass270, Compass300, Compass330, + FloorSpikeTrap, + Sungod, + Teleporter, + Lightning, + LightCut, + ArrowTrap, + SpikeTrap, + DMCD_10, DMCD_9, DMCD_8, DMCD_7, DMCD_6, DMCD_5, DMCD_4, DMCD_3, DMCD_2, DMCD_1, + DMCD_Selected_10, DMCD_Selected_9, DMCD_Selected_8, DMCD_Selected_7, DMCD_Selected_6, + DMCD_Selected_5, DMCD_Selected_4, DMCD_Selected_3, DMCD_Selected_2, DMCD_Selected_1, + Skull, + DestroyedSkull, + SkullBG, + HelpBG, + HelpDMBG, + ExitBG, + ExitDMBG, + ExitBGSelected, + ExitDMBGSelected, + ExitSelected, + Exit, + LobbyButton, + Victory, + Defeat, + Death, + Respawn, + StartGame, + StartGameSelected, + Player, PlayerSelected, + Zeus, ZeusSelected, + ReadyPlayer, ReadyPlayerSelected, + ReadyZeus, ReadyZeusSelected, + RowBG, + Blank, }; + +// Sorry for whoever has to look at this :) - ted #define GET_ALL_IMG_IDS() \ - {ImgID::Yoshi, ImgID::AwesomeSauce, ImgID::ItemFrame, ImgID::SelectedFrame, ImgID::HealthPotion, \ - ImgID::UnknownPotion, ImgID::InvisPotion, ImgID::FireSpell, ImgID::HealSpell, ImgID::Orb, \ - ImgID::Crosshair, ImgID::Scroll, ImgID::Dagger, ImgID::Sword, ImgID::Hammer} + {ImgID::Yoshi, ImgID::AwesomeSauce, ImgID::HealthPotion, ImgID::UnknownPotion, \ + ImgID::InvisPotion, ImgID::FireSpell, ImgID::HealSpell, ImgID::Orb, ImgID::Mirror, \ + ImgID::Crosshair, ImgID::Scroll, ImgID::Dagger, ImgID::Sword, ImgID::Hammer, \ + ImgID::LeftHotbar, ImgID::RightHotbar, ImgID::MiddleHotbar, ImgID::Blank, ImgID::Title, \ + ImgID::MiddleSelected, ImgID::HealthBar, ImgID::HealthTickEmpty, ImgID::HealthTickFull, \ + ImgID::ManaBar, ImgID::ManaTickEmpty, ImgID::ManaTickFull, ImgID::ItemBG, \ + ImgID::DMTrapBG, ImgID::Needle, ImgID::EventBG, ImgID::DMEventBG, \ + ImgID::Compass0, ImgID::Compass30, ImgID::Compass60, ImgID::Compass90, \ + ImgID::Compass120, ImgID::Compass150, ImgID::Compass180, ImgID::Compass210, \ + ImgID::Compass240, ImgID::Compass270, ImgID::Compass300, ImgID::Compass330, \ + ImgID::FloorSpikeTrap, ImgID::Sungod, ImgID::Teleporter, ImgID::Lightning, ImgID::LightCut, \ + ImgID::ArrowTrap, ImgID::SpikeTrap, ImgID::DMTrapBG, ImgID::Needle, \ + ImgID::EventBG, ImgID::DMEventBG, \ + ImgID::DMLeftHotbar, ImgID::DMRightHotbar, ImgID::DMMiddleHotbar, ImgID::DMMiddleSelected, ImgID::DMMiddleCooldown, \ + ImgID::DMCD_10, ImgID::DMCD_9, ImgID::DMCD_8, ImgID::DMCD_7, ImgID::DMCD_6, \ + ImgID::DMCD_5, ImgID::DMCD_4, ImgID::DMCD_3, ImgID::DMCD_2, ImgID::DMCD_1, \ + ImgID::DMCD_Selected_10, ImgID::DMCD_Selected_9, ImgID::DMCD_Selected_8, ImgID::DMCD_Selected_7, ImgID::DMCD_Selected_6, \ + ImgID::DMCD_Selected_5, ImgID::DMCD_Selected_4, ImgID::DMCD_Selected_3, ImgID::DMCD_Selected_2, ImgID::DMCD_Selected_1, \ + ImgID::Skull, ImgID::DestroyedSkull, ImgID::SkullBG, ImgID::HelpBG, ImgID::HelpDMBG, \ + ImgID::ExitBG, ImgID::ExitDMBG, ImgID::LobbyButton, ImgID::Victory, ImgID::Defeat, \ + ImgID::ExitBGSelected, ImgID::ExitDMBGSelected, ImgID::ExitSelected, ImgID::Exit, \ + ImgID::Respawn, ImgID::Death, ImgID::StartGame, ImgID::StartGameSelected, ImgID::RowBG, \ + ImgID::Player, ImgID::PlayerSelected, ImgID::Zeus, ImgID::ZeusSelected, \ + ImgID::ReadyPlayer, ImgID::ReadyPlayerSelected, ImgID::ReadyZeus, ImgID::ReadyZeusSelected, \ + } /** * Representation of a loaded image diff --git a/include/client/gui/img/loader.hpp b/include/client/gui/img/loader.hpp index fe85d567..b0110237 100644 --- a/include/client/gui/img/loader.hpp +++ b/include/client/gui/img/loader.hpp @@ -20,7 +20,7 @@ class Loader { private: std::unordered_map img_map; - bool _loadImg(ImgID id); + bool _loadImg(ImgID img_id); }; } diff --git a/include/client/gui/img/logo.hpp b/include/client/gui/img/logo.hpp new file mode 100644 index 00000000..145ce630 --- /dev/null +++ b/include/client/gui/img/logo.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "client/gui/img/img.hpp" + +namespace gui::img { + +class Logo { +public: + static const inline std::size_t NUM_FRAMES = 156; + + Logo() = default; + bool init(); + + Img getNextFrame(); +private: + bool _loadFrame(std::size_t index); + + std::vector frames; + std::size_t curr_frame; +}; + +} diff --git a/include/client/gui/widget/dyntext.hpp b/include/client/gui/widget/dyntext.hpp index e6f189a8..7c0f98e0 100644 --- a/include/client/gui/widget/dyntext.hpp +++ b/include/client/gui/widget/dyntext.hpp @@ -18,11 +18,13 @@ class DynText : public Widget { struct Options { Options(font::Font font, font::Size size, font::Color color): + font(font), size(size), color(font::getRGB(color)) {} + Options(font::Font font, font::Size size, glm::vec3 color): font(font), size(size), color(color) {} font::Font font {font::Font::TEXT}; font::Size size {font::Size::SMALL}; - font::Color color {font::Color::BLACK}; + glm::vec3 color; }; /** @@ -47,6 +49,8 @@ class DynText : public Widget { void changeColor(font::Color new_color); + void changeText(const std::string& new_text); + private: Options options; std::string text; @@ -54,6 +58,13 @@ class DynText : public Widget { unsigned int VAO; unsigned int VBO; + + /** + * @brief Calculates the size of this DynText widget based on + * its stored text and sets this DynText widget's height and width + * fields. + */ + void _calculateSize(); }; } diff --git a/include/client/gui/widget/empty.hpp b/include/client/gui/widget/empty.hpp new file mode 100644 index 00000000..bb48325a --- /dev/null +++ b/include/client/gui/widget/empty.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "client/gui/widget/widget.hpp" + +namespace gui::widget { +class Empty : public Widget { +public: + using Ptr = std::unique_ptr; + + template + static Ptr make(Params&&... params) { + return std::make_unique(std::forward(params)...); + } + + Empty(glm::vec2 origin, glm::vec2 size); + explicit Empty(glm::vec2 size) : Empty(glm::vec2(0, 0), size) {} + Empty(float width, float height) : Empty(glm::vec2(width, height)) {} + explicit Empty(float width) : Empty(width, 0.0f) {} + + void render() override; +private: +}; +} \ No newline at end of file diff --git a/include/client/gui/widget/staticimg.hpp b/include/client/gui/widget/staticimg.hpp index 73c58035..ab25590e 100644 --- a/include/client/gui/widget/staticimg.hpp +++ b/include/client/gui/widget/staticimg.hpp @@ -22,7 +22,8 @@ class StaticImg : public Widget { public: using Ptr = std::unique_ptr; static std::unique_ptr shader; - int size; + float size_width; + float size_height; /** * @brief creates a StaticImg unique ptr widget @@ -37,11 +38,11 @@ class StaticImg : public Widget { return std::make_unique(std::forward(params)...); } - StaticImg(glm::vec2 origin, gui::img::Img img, int size); - StaticImg(glm::vec2 origin, gui::img::Img img); + StaticImg(glm::vec2 origin, gui::img::Img img, float size = 1.0f); explicit StaticImg(gui::img::Img img); ~StaticImg(); + void changeImage(gui::img::Img img); void render() override; private: diff --git a/include/client/gui/widget/type.hpp b/include/client/gui/widget/type.hpp index efb920d7..10e80f55 100644 --- a/include/client/gui/widget/type.hpp +++ b/include/client/gui/widget/type.hpp @@ -3,7 +3,7 @@ namespace gui::widget { enum class Type { - DynText, Flexbox, StaticImg, TextInput + DynText, Flexbox, StaticImg, TextInput, Empty }; } diff --git a/include/client/lightsource.hpp b/include/client/lightsource.hpp deleted file mode 100644 index a7256107..00000000 --- a/include/client/lightsource.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "client/core.hpp" -#include "client/shader.hpp" - -#define GLM_ENABLE_EXPERIMENTAL -#include -#include -#include - -#include -#include -#include -#include - -class LightSource { -public: - LightSource(); - void draw(std::shared_ptr shader, glm::mat4 viewProj); - void TranslateTo(const glm::vec3& new_pos); - - glm::vec3 lightPos; -private: - GLuint VAO, VBO; - - glm::mat4 model; - glm::vec3 color; - - // Cube Information - std::vector positions; - std::vector normals; - std::vector indices; - -}; diff --git a/include/client/model.hpp b/include/client/model.hpp index e1c6084f..522359c6 100644 --- a/include/client/model.hpp +++ b/include/client/model.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -16,6 +17,9 @@ #include "client/renderable.hpp" #include "client/shader.hpp" #include "client/util.hpp" +#include "client/bone.hpp" + +#define MAX_BONE_INFLUENCE 4 /** * Stores position, normal vector, and coordinates @@ -25,6 +29,10 @@ struct Vertex { glm::vec3 position; glm::vec3 normal; glm::vec2 textureCoords; + int m_boneIDs[MAX_BONE_INFLUENCE]; /* Bone indices which influence the vertex */ + float m_weights[MAX_BONE_INFLUENCE]; /* Weights for each bone */ + glm::vec3 tangent; + glm::vec3 bitangent; }; class Texture { @@ -60,6 +68,14 @@ struct Material { float shininess; }; +struct BoneInfo { + /*id is index in finalBoneMatrices*/ + int id; + + /*offset matrix transforms vertex from model space to bone space*/ + glm::mat4 offset; +}; + /** * Mesh holds the data needed to render a mesh (collection of triangles). * @@ -88,9 +104,7 @@ class Mesh : public Renderable { * mesh */ void draw(Shader* shader, - glm::mat4 viewProj, glm::vec3 camPos, - std::array, MAX_POINT_LIGHTS> lightSources, bool fill) override; @@ -103,6 +117,7 @@ class Mesh : public Renderable { // render data opengl needs GLuint VAO, VBO, EBO; + void setupNormalMaps(); }; @@ -115,7 +130,7 @@ class Model : public Renderable { * * @param Filepath to model file. */ - explicit Model(const std::string& filepath); + explicit Model(const std::string& filepath, bool flip_uvs); /** * Draws all the meshes of a given model @@ -124,11 +139,20 @@ class Model : public Renderable { * meshes of the model */ void draw(Shader* shader, - glm::mat4 viewProj, glm::vec3 camPos, - std::array, MAX_POINT_LIGHTS> lightSources, bool fill) override; + /** + * Draws all the meshes of a given model + * + * @param Shader to use while drawing all the + * meshes of the model + */ + void draw(Shader* shader, + glm::vec3 camPos, + bool fill, + glm::vec3 color); + /** * Sets the position of the Model to the given x,y,z * values @@ -188,6 +212,36 @@ class Model : public Renderable { */ void scaleRelative(const glm::vec3& scale) override; + /** + * @brief Rotates the item along the specified axis. If + * no axis is specified, then assumes a rotation on the + * y-axis. This will not stack upon previous rotations. + * + * @param angle The angle of rotation + * @param axis The axis of rotation + */ + void rotateAbsolute(const glm::vec3& dir, bool is_player = false, const glm::vec3& axis = glm::vec3(0.0f, 1.0f, 0.0f)) override; + + /** + * @brief Rotates the item along the specified axis. If + * no axis is specified, then assumes a rotation on the + * y-axis. This will not stack upon previous rotations. + * + * @param angle The angle of rotation + * @param axis The axis of rotation + */ + void rotateAbsolute(const float& angle, const glm::vec3& axis = glm::vec3(0.0f, 1.0f, 0.0f)) override; + + /** + * @brief Rotates the item along the specified axis. If + * no axis is specified, then assumes a rotation on the + * y-axis. This will stack upon previous rotations. + * + * @param angle The angle of rotation + * @param axis The axis of rotation + */ + void rotateRelative(const glm::vec3& dir, const glm::vec3& axis = glm::vec3(0.0f, 1.0f, 0.0f)) override; + /** * Clear transformations and reset the model matrix * to the identity. @@ -223,13 +277,26 @@ class Model : public Renderable { void overrideSolidColor(std::optional color); + + auto& getBoneInfoMap() { return m_boneInfoMap; } + int& getBoneCount() { return m_boneCounter; } + + private: std::vector meshes; Bbox bbox; glm::vec3 dimensions; + + std::map m_boneInfoMap; + int m_boneCounter = 0; + void processNode(aiNode* node, const aiScene* scene); Mesh processMesh(aiMesh* mesh, const aiScene* scene); + void setDefaultVertexBoneData(Vertex& vertex); + void setVertexBoneData(Vertex& vertex, int boneID, float weight); + void extractBoneWeight(std::vector& vertices, aiMesh* mesh, const aiScene* scene); + std::vector loadMaterialTextures(aiMaterial* mat, const aiTextureType& type); diff --git a/include/client/renderable.hpp b/include/client/renderable.hpp index 18520235..7cb122b5 100644 --- a/include/client/renderable.hpp +++ b/include/client/renderable.hpp @@ -4,13 +4,21 @@ #include #include +#define GLM_ENABLE_EXPERIMENTAL #include +#include + + +#define GLM_ENABLE_EXPERIMENTAL +#include #include "client/shader.hpp" #include "client/util.hpp" #include "shared/game/sharedobject.hpp" #include "shared/utilities/constants.hpp" + + class Renderable { public: Renderable(); @@ -22,9 +30,7 @@ class Renderable { * @param */ virtual void draw(Shader* shader, - glm::mat4 viewProj, glm::vec3 camPos, - std::array, MAX_POINT_LIGHTS> lightSources, bool fill) = 0; /** @@ -86,6 +92,38 @@ class Renderable { */ virtual void scaleRelative(const glm::vec3& scale); + /** + * @brief Rotates the item along the specified axis. If + * no axis is specified, then assumes a rotation on the + * y-axis. This will not stack upon previous rotations. + * + * @param dir The direction the model is facing + * @param change behavior if rotating the player model + * @param axis The axis of rotation + */ + virtual void rotateAbsolute(const glm::vec3& dir, bool is_player = false, const glm::vec3& axis = glm::vec3(0.0f, 1.0f, 0.0f)); + + /** + * @brief Rotates the item along the specified axis. If + * no axis is specified, then assumes a rotation on the + * y-axis. This will not stack upon previous rotations. + * + * @param angle The angle of rotation + * @param axis The axis of rotation + */ + virtual void rotateAbsolute(const float& angle, const glm::vec3& axis = glm::vec3(0.0f, 1.0f, 0.0f)); + + /** + * @brief Rotates the item along the specified axis. If + * no axis is specified, then assumes a rotation on the + * y-axis. This will stack upon previous rotations. + * + * @param angle The angle of rotation + * @param axis The axis of rotation + */ + virtual void rotateRelative(const glm::vec3& dir, const glm::vec3& axis = glm::vec3(0.0f, 1.0f, 0.0f)); + + /** * Gets the model matrix given all the transformations * applied to it @@ -109,6 +147,8 @@ class Renderable { * Reset translation to position (0, 0, 0) */ virtual void clearPosition(); + private: glm::mat4 model; + glm::quat rotation; }; diff --git a/include/client/shader.hpp b/include/client/shader.hpp index 17284347..83e3ad60 100644 --- a/include/client/shader.hpp +++ b/include/client/shader.hpp @@ -29,6 +29,8 @@ class Shader { */ void use(); + static void clear() { glUseProgram(0); } + /* * Sets a boolean unform variable of the shader program * with the specified value diff --git a/include/client/util.hpp b/include/client/util.hpp index d189dc32..fe9b948d 100644 --- a/include/client/util.hpp +++ b/include/client/util.hpp @@ -6,6 +6,9 @@ #include "shared/game/sharedobject.hpp" +#define GLM_ENABLE_EXPERIMENTAL +#include + /** * Convert ASSIMP color to a glm::vec3 * @@ -14,6 +17,9 @@ */ glm::vec3 aiColorToGLM(const aiColor3D& color); +glm::mat4 matrixToGLM(const aiMatrix4x4& from); + +glm::vec3 getGLMVec(const aiVector3D& vec); /** * Bbox struct which stores the bounding box * of an object. @@ -41,3 +47,8 @@ Bbox aiBboxToGLM(const aiAABB& bbox); */ Bbox combineBboxes(const Bbox& bbox1, const Bbox& bbox2); +glm::quat getGLMQuat(const aiQuaternion& pOrientation); + +// thx chat gpt +glm::vec3 rotate90DegreesAroundYAxis(const glm::vec3& direction); +glm::vec3 rotate90DegreesAroundXAxis(const glm::vec3& direction); diff --git a/include/server/audio/soundtable.hpp b/include/server/audio/soundtable.hpp index 5339f28e..d0e7060c 100644 --- a/include/server/audio/soundtable.hpp +++ b/include/server/audio/soundtable.hpp @@ -19,7 +19,7 @@ class SoundTable { void addStaticSoundSource(const SoundSource& source); void addNewSoundSource(const SoundSource& source); - std::unordered_map> getCommandsPerPlayer(SmartVector& players); + std::unordered_map> getCommandsPerPlayer(const std::vector& players); void tickSounds(); const std::unordered_map& data() const; diff --git a/include/server/game/arrowtrap.hpp b/include/server/game/arrowtrap.hpp index 159e74fc..68223bd5 100644 --- a/include/server/game/arrowtrap.hpp +++ b/include/server/game/arrowtrap.hpp @@ -19,22 +19,15 @@ */ class ArrowTrap: public Trap { public: - enum class Direction { - LEFT, - UP, - DOWN, - RIGHT - }; - /** * @param corner Corner position of the spike trap - * @param dimensions TODO: remove once we use real model with size * @param dir What direction it should shoot in */ - ArrowTrap(glm::vec3 corner, glm::vec3 dimensions, Direction dir); + ArrowTrap(glm::vec3 corner, Direction dir); /// how long from initial activation until it can activate again - const static std::chrono::seconds TIME_UNTIL_RESET; + const static inline std::chrono::seconds TIME_UNTIL_RESET = 4s; + const static inline float SIGHTLINE_M = 15; bool shouldTrigger(ServerGameState& state) override; void trigger(ServerGameState& state) override; diff --git a/include/server/game/constants.hpp b/include/server/game/constants.hpp index f2a30fd7..c9d130df 100644 --- a/include/server/game/constants.hpp +++ b/include/server/game/constants.hpp @@ -4,16 +4,9 @@ #include #include -#define NUM_PLAYERS 4 -#define MAX_ENTITIES 1000 -#define MAX_COMPONENTS 32 -#define MAX_WALLS 1000 -#define MAX_TRAPS 10 -#define MAX_SPELLS 4 - - /* ServerGameState Constants */ -#define MAX_ALIVE_ENEMIES 15 +#define MAX_ENEMY_VALUE 500 +#define NUM_PLAYERS 4 /* Maze Constants */ #define MAX_MAZE_COLUMNS 10000 @@ -22,6 +15,7 @@ /* GridCell Constants */ #define DEFAULT_GRIDCELL_WIDTH 3 #define DM_Z_DISCOUNT 0.2 +#define GRIDS_PER_FLOOR_OBJECT 5 // Player Stat Constants #define INITIAL_HEALTH 100 @@ -31,7 +25,7 @@ /* Spell Constants */ #define FIRE_LIMIT 50 -#define HEAL_LIMIT 50 +#define HEAL_LIMIT 3 #define TELEPORT_LIMIT 1 #define TELEPORT_RANGE 15 @@ -39,7 +33,7 @@ #define RESTORE_HEALTH 20 #define HEALTH_DURATION 0 -#define NAUSEA_SCALAR -1.0f +#define NAUSEA_SCALAR -1 #define NAUSEA_DURATION 10 #define INVIS_DURATION 15 @@ -48,9 +42,10 @@ #define INVINCIBLITY_DUR 15 /* Weapon Stats */ -#define SWORD_DMG 15 -#define DAGGER_DMG 10 -#define HAMMER_DMG 30 +#define SWORD_DMG 5 +#define DAGGER_DMG 7 +#define HAMMER_DMG 15 +#define LIGHTNING_DMG 99 #define SWORD_PREP 200 #define SWORD_DUR 300 @@ -60,21 +55,35 @@ #define DAGGER_DUR 150 #define DAGGER_TOTAL 300 -#define HAMMER_PREP 400 +#define HAMMER_PREP 500 #define HAMMER_DUR 350 -#define HAMMER_TOTAL 750 +#define HAMMER_TOTAL 850 + +#define LIGHTNING_PREP 1000 +#define LIGHTNING_DUR 400 + +#define DM_MANA_TOTAL 30 +#define DM_MANA_REGEN 1 +#define LIGHTNING_MANA 6 +#define LIGHT_CUT_MANA 3 + +/* Mirror Item */ +// Mirror use duration in seconds +#define MIRROR_USE_DURATION 30 /* Game */ #define GRAVITY 0.03f -#define PLAYER_SPEED 1.5f -#define JUMP_SPEED 0.5f - -/* Default model sizes */ -#define BEAR_DIMENSIONS glm::vec3(14.163582, 17.914591, 10.655818) -#define FIRE_PLAYER_DIMENSIONS glm::vec3(8.008834, 10.069769, 2.198592) +#define PLAYER_SPEED 1.65f +#define JUMP_SPEED 0.59f /* DM Constants */ #define MAX_TRAPS 10 -#define TRAP_INVENTORY_SIZE 10 +#define TRAP_INVENTORY_SIZE 7 #define TRAP_TIME 10 -#define TRAP_COOL_DOWN 5 \ No newline at end of file +#define TRAP_COOL_DOWN 5 +#define ITEM_SPAWN_PROB 0.1 +#define ITEM_SPAWN_BOUND 3 +#define LIGHTNING_LIGHT_CUT_TICKS 100 +#define LIGHT_CUT_TICKS 200 +#define LIGHT_CUT_RANGE 60.0 +#define LIGHT_CUT_RANGE_LIGHTNING 20.0 \ No newline at end of file diff --git a/include/server/game/dungeonmaster.hpp b/include/server/game/dungeonmaster.hpp index d543eeef..dc2ef3a2 100644 --- a/include/server/game/dungeonmaster.hpp +++ b/include/server/game/dungeonmaster.hpp @@ -4,20 +4,97 @@ #include "server/game/object.hpp" #include "server/game/creature.hpp" #include "shared/game/sharedobject.hpp" +#include +class Weapon; class DungeonMaster : public Creature { public: SharedTrapInventory sharedTrapInventory; + SharedDMInfo dmInfo; DungeonMaster(glm::vec3 corner, glm::vec3 facing); ~DungeonMaster(); virtual SharedObject toShared() override; + /** + * @brief get the number of traps the DM currently has placed + */ int getPlacedTraps(); + /** + * @brief set the number of traps the DM currently has placed + * @param placedTraps the number of traps the DM has placed + */ void setPlacedTraps(int placedTraps); + + /** + * @brief For lightning and light-cut usage + * @param mana the amount of mana the specific DM action takes + */ + void useMana(int mana); + + /** + * @brief mana regeneration function + */ + void manaRegen(); + + /** + * @brief Sets the whether the DungeonMaster is paralyzed. If isParalyzed + * is true, then this sets the paralysis duration and marks the timestamp for + * the paralysis start event. + * @param isParalyzed Whether the DungeonMaster should now be paralyzed. + * @param paralysis_duration How long the DungeonMaster should be paralyzed for + * (ignored if isParalyzed is false) + */ + void setParalysis(bool isParalyzed, double paralysis_duration); + + /** + * @brief Getter for whether the DungeonMaster is paralyzed. + * @return true if the DungeonMaster is paralyzed and false otherwise. + */ + bool isParalyzed() const; + + /** + * @brief Getter for the DungeonMaster's paralysis duration. + * (this value should be ignored if the DungeonMaster's paralyzed boolean + * is false) + * @return double representing the number of seconds that the DungeonMaster + * should be paralyzed since the paralysis_start_time timestamp. + */ + double getParalysisDuration() const; + + /** + * @brief Getter for the timestamp of the last time the DungeonMaster was + * paralyzed. + * @return std::chrono::time_point timestamp of + * the last time the DungeonMaster became paralyzed. + */ + std::chrono::time_point getParalysisStartTime() const; + + /** + * @brief The DM's lightning weapon + */ + Weapon* lightning; + private: + /** + * @brief Duration, in seconds, of the Dungeon Master's current paralysis + * (this value should be ignored if paralyzed is false) + */ + double paralysisDuration; + + /** + * @brief Timestamp for the last time the DungeonMaster became paralyzed. + * Set by setParalysis(). + */ + std::chrono::time_point paralysis_start_time; + + /** + * @brief the number of traps the DM has placed + */ int placedTraps; + + std::chrono::system_clock::time_point mana_used; }; \ No newline at end of file diff --git a/include/server/game/enemy.hpp b/include/server/game/enemy.hpp index ff99d689..86deb6ed 100644 --- a/include/server/game/enemy.hpp +++ b/include/server/game/enemy.hpp @@ -25,7 +25,7 @@ class Enemy : public Creature { /** * @return true if the enemy should be deleted, false otherwise */ - virtual bool doDeath(ServerGameState& state) {return true; }; + virtual bool doDeath(ServerGameState& state); virtual SharedObject toShared() override; private: diff --git a/include/server/game/exit.hpp b/include/server/game/exit.hpp index ffa43eb1..52de0881 100644 --- a/include/server/game/exit.hpp +++ b/include/server/game/exit.hpp @@ -1,15 +1,21 @@ #pragma once #include "server/game/object.hpp" +#include "shared/game/point_light.hpp" #include "shared/game/sharedobject.hpp" class Exit : public Object { public: SharedExit shared; - Exit(bool open, glm::vec3 corner, glm::vec3 dimensions); + Exit(bool open, glm::vec3 corner, glm::vec3 dimensions, const PointLightProperties& properties); void doCollision(Object* other, ServerGameState& state) override; virtual SharedObject toShared() override; + + void setIntensity(float val); +private: + float intensity; + PointLightProperties properties; }; \ No newline at end of file diff --git a/include/server/game/fireballtrap.hpp b/include/server/game/fireballtrap.hpp index 67762b3b..02e11264 100644 --- a/include/server/game/fireballtrap.hpp +++ b/include/server/game/fireballtrap.hpp @@ -13,9 +13,9 @@ class FireballTrap: public Trap { public: /** * @param corner Corner position of the fireball trap - * @param dimensions dimensions of the fireball trap (probably will change once we use a non cube model to not have this) + * @param dir is the direction the fireball trap is pointing at */ - FireballTrap(glm::vec3 corner, glm::vec3 dimensions); + FireballTrap(glm::vec3 corner, Direction dir); const static std::chrono::seconds TIME_UNTIL_RESET; // how long from initial activation until it can activate again const static int SHOOT_DIST; // how many grid cells away it can shoot you from diff --git a/include/server/game/floorspike.hpp b/include/server/game/floorspike.hpp index 8742baa0..fd296294 100644 --- a/include/server/game/floorspike.hpp +++ b/include/server/game/floorspike.hpp @@ -11,12 +11,6 @@ */ class FloorSpike : public Trap { public: - enum class Orientation { - Full, // take up whole grid cell - Vertical, // 1/2 x, take up full z - Horizontal // 1/2 z, take up full x - }; - static const int DAMAGE; /** @@ -24,7 +18,7 @@ class FloorSpike : public Trap { * @param orientation what orientation the floorspike should be in * @param grid_width or how wide the longer axis should be (e.g. z if vertical, x if horizontal) */ - FloorSpike(glm::vec3 corner, Orientation dimensions, float grid_width); + FloorSpike(glm::vec3 corner, float grid_width); bool shouldTrigger(ServerGameState& state) override; diff --git a/include/server/game/introcutscene.hpp b/include/server/game/introcutscene.hpp new file mode 100644 index 00000000..9db08ede --- /dev/null +++ b/include/server/game/introcutscene.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "server/game/servergamestate.hpp" +#include "shared/utilities/serialize_macro.hpp" +#include "shared/game/sharedgamestate.hpp" + +#include + +/** + * @brief config to use for the servergamestate used for the cutscene, + * This mainly just needs to set the room file to load the cutscene room + * from. I dont think the other values will really matter. + */ +GameConfig getCutsceneConfig(); + +class IntroCutscene { +public: + IntroCutscene(); + + /** + * update to the next frame of the cutscene + * @returns true if the cutscene is over + */ + bool update(); + + LoadIntroCutsceneEvent toNetwork(); + + inline static const int START_TICK = 1; + inline static const int STOP_MOVING_TICK = START_TICK + 250; + inline static const int GATE_RAISE_TICK = STOP_MOVING_TICK + 30; + inline static const int GATE_STOP_RAISE_TICK = GATE_RAISE_TICK + 200; + inline static const int LIGHTNING_1_TICK = GATE_STOP_RAISE_TICK + 80; + inline static const int LIGHTNING_2_TICK = LIGHTNING_1_TICK + 50; + inline static const int LIGHTNING_3_TICK = LIGHTNING_2_TICK + 40; + inline static const int START_PLAYER_THEME_TICK = LIGHTNING_3_TICK + 110; + inline static const int EXIT_CUTSCENE_TICK = START_PLAYER_THEME_TICK + 240; + + int ticks; + + // just making everything public bc lazy + ServerGameState state; + EntityID pov_eid; + EntityID dm_eid; + std::array, MAX_POINT_LIGHTS> lights; +}; diff --git a/include/server/game/lava.hpp b/include/server/game/lava.hpp new file mode 100644 index 00000000..cc989666 --- /dev/null +++ b/include/server/game/lava.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include "server/game/trap.hpp" +#include "server/game/servergamestate.hpp" +#include "server/game/object.hpp" +#include "server/game/servergamestate.hpp" +#include "shared/game/point_light.hpp" + +class Lava : public Trap { +public: + static const int DAMAGE; + + /** + * @param corner Corner position of the floor spike trap + * @param model_type type of model + * @param grid_width or how wide the longer axis should be (e.g. z if vertical, x if horizontal) + */ + Lava(glm::vec3 corner, ModelType model_type, float grid_width, const PointLightProperties& light_properties); + + bool shouldTrigger(ServerGameState& state) override; + + bool shouldReset(ServerGameState& state) override; + + void doCollision(Object* other, ServerGameState& state) override; + + virtual SharedObject toShared() override; +private: + std::chrono::time_point shoot_time; + + PointLightProperties light_properties; +}; diff --git a/include/server/game/mazegenerator.hpp b/include/server/game/mazegenerator.hpp index 3bbc3ebf..723ef6dd 100644 --- a/include/server/game/mazegenerator.hpp +++ b/include/server/game/mazegenerator.hpp @@ -14,7 +14,7 @@ #define GLM_ENABLE_EXPERIMENTAL #include "glm/gtx/hash.hpp" -#define REQUIRED_NUM_ROOMS 30 +#define REQUIRED_NUM_ROOMS 100 enum class RoomType { EMPTY, // testing @@ -165,4 +165,9 @@ class MazeGenerator { int _num_rooms_placed; std::deque _policy; + + // ids that should not be reused + // currently, only ids of 20x20 and 40x40 rooms will be placed in here to prevent them from being placed + // twice in the same maze + std::unordered_set used_room_ids; }; diff --git a/include/server/game/minotaur.hpp b/include/server/game/minotaur.hpp new file mode 100644 index 00000000..f65f482d --- /dev/null +++ b/include/server/game/minotaur.hpp @@ -0,0 +1,23 @@ +#include "server/game/enemy.hpp" +#include + +using namespace std::chrono_literals; + +class Minotaur : public Enemy { +public: + inline static const float SIGHT_LIMIT_GRID_CELLS = 10.0f; + + Minotaur(glm::vec3 corner, glm::vec3 facing); + + bool doBehavior(ServerGameState& state) override; + + void doCollision(Object* other, ServerGameState& state) override; + + bool doDeath(ServerGameState& state) override; + +private: + std::chrono::system_clock::time_point last_charge_time; + int chargeDelay; + int chargeDuration; + bool stopped; +}; diff --git a/include/server/game/mirror.hpp b/include/server/game/mirror.hpp new file mode 100644 index 00000000..78d23557 --- /dev/null +++ b/include/server/game/mirror.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include "server/game/item.hpp" +#include + +class Mirror : public Item { +public: + + /** + * @brief Mirror constructor + * @param corner Corner position of the Mirror + * @param dimensions Dimensions of the Mirror + */ + Mirror(glm::vec3 corner, glm::vec3 dimensions); + + /** + * @brief Using the mirror causes it to be held for a few seconds + * @param other Player object that uses the mirror + * @param state Reference to the server's ServerGameState instance + * @param itemSelected Mirror's item index in the player's inventory + */ + void useItem(Object* other, ServerGameState& state, int itemSelected) override; + + void dropItem(Object* other, ServerGameState& state, int itemSelected, float dropDistance) override; + + /** + * @brief Determines whether the mirror has been used for the mirror holding + * duration and updated the remaining time it will be used. + * @return true if the mirror has been used for the specified duration. + */ + bool timeOut(); + + /** + * @brief Stop holding the mirror. + * @note This should be called when the use duration times out OR when the player + * selects a different item in their inventory. + * @param state Reference to the server's ServerGameState instance + */ + void revertEffect(ServerGameState& state); +private: + /** + * @brief The time at which the mirror was last used + */ + std::chrono::time_point used_time; + + /** + * @brief The last Player that used this mirror object. + */ + Player* used_player; +}; \ No newline at end of file diff --git a/include/server/game/object.hpp b/include/server/game/object.hpp index 9b4a8021..1896fd52 100644 --- a/include/server/game/object.hpp +++ b/include/server/game/object.hpp @@ -39,8 +39,8 @@ struct Physics { glm::vec3 corner, glm::vec3 facing, glm::vec3 dimensions = glm::vec3(1.0f)): shared{.corner=corner, .facing=facing, .dimensions=dimensions}, - movable(movable), feels_gravity(true), velocity(glm::vec3(0.0f)), velocityMultiplier(glm::vec3(1.0f)), nauseous(1.0f), - collider(collider) + movable(movable), feels_gravity(true), velocity(glm::vec3(0.0f)), velocityMultiplier(glm::vec3(1.0f)), \ + currTickVelocity(glm::vec3(0.0f)), nauseous(1.0f), collider(collider) {} /** @@ -72,6 +72,11 @@ struct Physics { */ glm::vec3 velocityMultiplier; + /** + * @brief Tick velocity for knockbacks + */ + glm::vec3 currTickVelocity; + /** * @brief Factor for potion of nausea */ @@ -123,6 +128,18 @@ class Object { */ ModelType modelType; + /** + * @brief Object's animation state and current action. For non-animated + * objects, this defaults as AnimState::IdleAnim. + * + */ + AnimState animState; + + /** + * @brief used to determine if the player is sprinting for animation purposes + */ + bool is_sprinting; + /** * @brief Vector of (x, y) positions of GridCells currently occupied by this * object @@ -177,3 +194,12 @@ class Object { std::string to_string(unsigned int tab_offset); std::string to_string() { return this->to_string(0); } }; + +enum class Direction { + LEFT, + UP, + DOWN, + RIGHT +}; + +glm::vec3 directionToFacing(const Direction& direction); diff --git a/include/server/game/objectmanager.hpp b/include/server/game/objectmanager.hpp index 5ea5acea..89f38586 100644 --- a/include/server/game/objectmanager.hpp +++ b/include/server/game/objectmanager.hpp @@ -37,7 +37,12 @@ class ObjectManager { * SpecificID to the newly created object (NOTE: the Object constructor does * NOT do this!) * - * @param type the type of object to create + * @note this is a public wrapper for the _createObject() method. + * + * @param object pointer to the newly created object to add to the ObjectManager. + * @param id boost::optional which is by default boost::none. If given + * a value for a specific EntityID, the new object will be added with the given + * EntityID. Note that this may * @return the SpecificID of the newly created object */ SpecificID createObject(Object* object); @@ -60,6 +65,16 @@ class ObjectManager { */ bool removeObject(Object** object_dbl_ptr); + /** + * @brief Replaces the object with the given EntityID with the given + * Object, if there currently exists an object with the given EntityID. + * The original object is deleted. + * @param globalID EntityID of object to replace. + * @param object Pointer to new object that will replace the existing one. + * @return true if replacement was successful and false otherwise. + */ + bool replaceObject(EntityID globalID, Object* object); + /** * @brief Attempts to retrieve the object with the given EntityID. * @param globalID EntityID of the object to retrieve @@ -259,6 +274,23 @@ class ObjectManager { std::vector> toShared(); private: + + /** + * @brief Creates a new object (adds the given object pointer to the ObjectManager). + * + * @note This method assigns a unique global EntityID and a type-specific + * SpecificID to the newly created object (NOTE: the Object constructor does + * NOT do this!) + * + * @param object pointer to the newly created object to add to the ObjectManager. + * @param id boost::optional which is by default boost::none. If given + * a value for a specific EntityID, the new object will be added with the given + * EntityID. Note that this may overwrite an existing object, so only specify the + * EntityID if you know that that EntityID is available! + * @return the SpecificID of the newly created object + */ + SpecificID _createObject(Object* object, boost::optional id = boost::none); + /* * Note on how Objects are stored: * diff --git a/include/server/game/orb.hpp b/include/server/game/orb.hpp index 2af22495..2fb62faa 100644 --- a/include/server/game/orb.hpp +++ b/include/server/game/orb.hpp @@ -4,6 +4,7 @@ #include "server/game/object.hpp" #include "server/game/servergamestate.hpp" #include "server/game/item.hpp" +#include "shared/game/point_light.hpp" #include class Orb : public Item { @@ -11,13 +12,17 @@ class Orb : public Item { /** * @param corner Corner position of the Orb * @param dimensions Dimensions applied for the Orb + * @param properties are point light properties of how the + * orb will light it's surroundings */ - Orb(glm::vec3 corner, glm::vec3 dimensions); + Orb(glm::vec3 corner, glm::vec3 dimensions, const PointLightProperties& properties); void doCollision(Object* other, ServerGameState& state) override; void useItem(Object* other, ServerGameState& state, int itemSelected) override; void dropItem(Object* other, ServerGameState& state, int itemSelected, float dropDistance) override; + SharedObject toShared() override; private: + PointLightProperties properties; }; \ No newline at end of file diff --git a/include/server/game/player.hpp b/include/server/game/player.hpp index 94b92b77..6502f254 100644 --- a/include/server/game/player.hpp +++ b/include/server/game/player.hpp @@ -10,6 +10,7 @@ class Player : public Creature { public: SharedPlayerInfo info; SharedInventory sharedInventory; + SharedCompass compass; std::vector inventory; @@ -22,5 +23,63 @@ class Player : public Creature { virtual SharedObject toShared() override; + bool canBeTargetted() const; + + /** + * @brief This sets the Player as invulnerable to lightning and marks + * the timestamp for this event if the value is set to true. + * @param isInvulnerable whether the Player should now be invulnerable to lightning + * @param duration how long the Player should be invulnerable to lightning (ignored + * if isInvulnerable is false) + */ + void setInvulnerableToLightning(bool isInvulnerable, double duration); + + /** + * @brief Getter for whether this Player is invulnerable to lightning. + * @return true if this Player is invulnerable to lightning and false + * otherwise. + */ + bool isInvulnerableToLightning() const; + + /** + * @brief Getter for this Player's lightning invulnerability duration + * (this value should be ignored if the Player's invulnerableToLightning + * boolean is false) + * @return double representing the number of seconds that this Player + * should be invulnerable to lightning since the + * lightning_invulnerability_start_time timestamp. + */ + double getLightningInvulnerabilityDuration() const; + + /** + * @brief Returns the timestamp for the last time that this Player became invulnerable + * to lightning. + * @return std::chrono::time_point timestamp of the last time + * this Player became invulnerable to lightning. + */ + std::chrono::time_point getLightningInvulnerabilityStartTime() const; + private: + /** + * @brief Whether or not this Player is currently invulnerable to lightning. + * This can occur after a Player used a Mirror to effectively reflect a + * lightning bolt (this is to ensure that the player isn't harmed by that + * lightning bolt in collisions in subsequent ticks and protects the player + * from other lightning bolts in the vicinity for a short time to avoid + * a double lightning bolt attack by the DM or something like this). + */ + bool invulnerableToLightning; + + /** + * @brief Duration, in seconds, of this Player's current lightning invulnerability + * (this value should be ignored if invulnerableToLightning is false) + */ + double lightningInvulnerabilityDuration; + + /** + * @brief Timestamp for the last time this Player gained an invulnerability + * to lightning. + * Set by setInvulnerableToLightning(). + */ + std::chrono::time_point lightning_invulnerability_start_time; }; \ No newline at end of file diff --git a/include/server/game/projectile.hpp b/include/server/game/projectile.hpp index 838a4fa3..3e43a98e 100644 --- a/include/server/game/projectile.hpp +++ b/include/server/game/projectile.hpp @@ -2,10 +2,12 @@ #include "server/game/constants.hpp" #include "server/game/arrowtrap.hpp" #include "server/game/spell.hpp" +#include "shared/game/sharedmodel.hpp" #include #include #include +#include /** * Class for any possible Projectile. @@ -25,15 +27,16 @@ class Projectile : public Object { * @param v_mult vertical velocity multiplier * @param homing whether or not the projectile homes in on a specified target * @param homing_strength value from 0-1, where the closer to 1 the more strong the homing is + * @param homing_duration how many ticks it should home for * @param target Target towards which the projectile homes, if it is homing */ Options(bool isSpell, int damage, float h_mult, float v_mult, - bool homing, float homing_strength, + bool homing, float homing_strength, int homing_duration, std::optional target ): isSpell(isSpell), damage(damage), h_mult(h_mult), v_mult(v_mult), homing(homing), homing_strength(homing_strength), - target(target) + homing_duration(homing_duration), target(target) { if (homing && !target.has_value()) { std::cerr << "FATAL: homing projectile created without target.\n" @@ -49,6 +52,7 @@ class Projectile : public Object { int damage; bool homing; float homing_strength; + int homing_duration; std::optional target; }; @@ -72,22 +76,30 @@ class Projectile : public Object { */ bool doTick(ServerGameState& state); + virtual SharedObject toShared() override; + + private: Options opt; std::optional destroy_sound; + PointLightProperties properties; + }; class HomingFireball : public Projectile { public: - inline static const int DAMAGE = 25; + inline static const int DAMAGE = 15; inline static const float H_MULT = 0.4; - inline static const float V_MULT = 0.0; // not affected by gravity + inline static const float V_MULT = 0.1; inline static const float HOMING_STRENGTH = 0.1f; + inline static const int HOMING_DURATION_TICKS = 80; // 2.4s HomingFireball(glm::vec3 corner, glm::vec3 facing, std::optional target): - Projectile(corner, facing, glm::vec3(0.4f, 0.4f, 0.4f), ModelType::Cube, ServerSFX::FireballImpact, - Options(false, DAMAGE, H_MULT, V_MULT, true, HOMING_STRENGTH, target)) - {} + Projectile(corner, facing, glm::vec3(0.4f, 0.4f, 0.4f), ModelType::Fireball, ServerSFX::FireballImpact, + Options(false, DAMAGE, H_MULT, V_MULT, true, HOMING_STRENGTH, HOMING_DURATION_TICKS, target)) + { + this->physics.feels_gravity = false; + } }; /** @@ -98,37 +110,38 @@ class HomingFireball : public Projectile { class Arrow : public Projectile { public: inline static const int DAMAGE = 10; - inline static const float H_MULT = 1.20f; + inline static const float H_MULT = 0.55f; inline static const float V_MULT = 0.0f; // not affected by gravity - Arrow(glm::vec3 corner, glm::vec3 facing, ArrowTrap::Direction dir): - Projectile(corner, facing, glm::vec3(0.0f, 0.0f, 0.0f), ModelType::Cube, ServerSFX::ArrowImpact, - Options(false, DAMAGE, H_MULT, V_MULT, false, 0.0f, {})) + Arrow(glm::vec3 corner, glm::vec3 facing, Direction dir): + Projectile(corner, facing, glm::vec3(0.0f, 0.0f, 0.0f), ModelType::Arrow, ServerSFX::ArrowImpact, + Options(false, DAMAGE, H_MULT, V_MULT, false, 0.0f, 0, {})), dir(dir) { - // temp hack to get the correct direction until we load in a model and can rotate it - - const float ARROW_WIDTH = 0.2f; - const float ARROW_LENGTH = 1.0f; - const float ARROW_HEIGHT = 0.2f; - - float arrow_x_dim; - float arrow_z_dim; - + const float clear_model_nudge = 1.5f; switch (dir) { - case ArrowTrap::Direction::UP: - case ArrowTrap::Direction::DOWN: - arrow_x_dim = ARROW_WIDTH; - arrow_z_dim = ARROW_LENGTH; + case Direction::LEFT: + std::swap(this->physics.shared.dimensions.x, this->physics.shared.dimensions.z); + this->physics.shared.facing = glm::vec3(0.0f, 0.0f, -1.0f); + this->physics.shared.corner.x -= clear_model_nudge; + break; + case Direction::RIGHT: + std::swap(this->physics.shared.dimensions.x, this->physics.shared.dimensions.z); + this->physics.shared.facing = glm::vec3(0.0f, 0.0f, 1.0f); + // right doesn't need nudge for some reason + // this->physics.shared.corner.x += clear_model_nudge; break; - case ArrowTrap::Direction::LEFT: - case ArrowTrap::Direction::RIGHT: - arrow_x_dim = ARROW_LENGTH; - arrow_z_dim = ARROW_WIDTH; + case Direction::UP: + this->physics.shared.facing = glm::vec3(1.0f, 0.0f, 0.0f); + this->physics.shared.corner.z -= (2.0f * clear_model_nudge); + break; + case Direction::DOWN: + this->physics.shared.facing = glm::vec3(-1.0f, 0.0f, 0.0f); + this->physics.shared.corner.z += clear_model_nudge; break; } - - this->physics.shared.dimensions = glm::vec3(arrow_x_dim, ARROW_HEIGHT, arrow_z_dim); } + + Direction dir; }; class SpellOrb : public Projectile { @@ -141,8 +154,8 @@ class SpellOrb : public Projectile { SpellType sType; SpellOrb(glm::vec3 corner, glm::vec3 facing, SpellType type) : - Projectile(corner, facing, glm::vec3(0.4f, 0.4f, 0.4f), ModelType::Cube, ServerSFX::FireballImpact, - Options(true, DAMAGE, H_MULT, V_MULT, false, 0.0f, {})) + Projectile(corner, facing, glm::vec3(0.4f, 0.4f, 0.4f), ModelType::SpellOrb, ServerSFX::FireballImpact, + Options(true, DAMAGE, H_MULT, V_MULT, false, 0.0f, 0, {})) { this->sType = type; } diff --git a/include/server/game/python.hpp b/include/server/game/python.hpp new file mode 100644 index 00000000..d5b750d4 --- /dev/null +++ b/include/server/game/python.hpp @@ -0,0 +1,24 @@ +#include "server/game/enemy.hpp" +#include + +using namespace std::chrono_literals; + +class Python : public Enemy { +public: + inline static const float SIGHT_LIMIT_GRID_CELLS = 6.0f; + + Python(glm::vec3 corner, glm::vec3 facing); + + bool doBehavior(ServerGameState& state) override; + + void doCollision(Object* other, ServerGameState& state) override; + + bool doDeath(ServerGameState& state) override; + +private: + std::chrono::system_clock::time_point last_move_time; + int moveDelay; + int moveDuration; + bool diagonal; + bool stopped; +}; diff --git a/include/server/game/servergamestate.hpp b/include/server/game/servergamestate.hpp index e692958c..0151f522 100644 --- a/include/server/game/servergamestate.hpp +++ b/include/server/game/servergamestate.hpp @@ -1,5 +1,6 @@ #pragma once +#include "server/game/gridcell.hpp" #include "shared/utilities/constants.hpp" #include "shared/utilities/typedefs.hpp" #include "shared/game/sharedgamestate.hpp" @@ -11,6 +12,7 @@ #include "shared/game/event.hpp" #include "server/game/grid.hpp" #include "server/game/objectmanager.hpp" +#include "server/game/spawner.hpp" #include #include @@ -46,6 +48,11 @@ class ServerGameState { */ ObjectManager objects; + /** + * @brief Controls the spawns for the enemies + */ + std::unique_ptr spawner; + /** * @brief Creates a ServerGameState instance. The intial GamePhase is set to * Lobby. @@ -123,12 +130,31 @@ class ServerGameState { void handleRespawns(); + void handleDM(); + + void updateCompass(); + void tickStatuses(); void spawnEnemies(); + void handleTickVelocity(); + void deleteEntities(); + /** + * @brief Updates player's lightning invulnerability status + * (sets to false once the player's lightning invulnerability duration + * is past) + */ + void updatePlayerLightningInvulnerabilityStatus(); + + /** + * @brief Updates the DungeonMaster's paralysis status + * (sets to false once the DungeonMaster's paralysis duration is past) + */ + void updateDungeonMasterParalysis(); + /* SharedGameState generation */ // TODO: Modify this function to dynamically allocate a SharedGameState @@ -195,7 +221,27 @@ class ServerGameState { * player is already in the mapping, as nothing will happen. If a player's name * has changed, then this will update their name as well. */ - void addPlayerToLobby(EntityID id, const std::string& name); + + /** + * @brief Adds a new LobbyPlayer to this ServerGameState's Lobby struct. + * This method will assign the new player's LobbyPlayer to the first free + * index in the Lobby.players vector. + * Note: this method will CAUSE THE SERVER TO CRASH if all LobbyPlayer indices + * are currently in use (i.e., if Lobby.max_players players have already + * connected to this server's lobby). + * @param player LobbyPlayer struct of the new player to add to this + * ServerGameState instance's Lobby. + */ + void addPlayerToLobby(LobbyPlayer player); + + /** + * @brief Updates the LobbyPlayer with the given EntityID to the given + * LobbyPlayer struct + * @param id EntityID of the LobbyPlayer to update + * @param player LobbyPlayer struct to copy to the current lobby player + */ + void updateLobbyPlayer(EntityID id, LobbyPlayer player); + /** * Removes a player from the lobby with the specified id. */ @@ -229,8 +275,6 @@ class ServerGameState { */ std::string to_string(); - Trap* createTrap(CellType type, glm::vec3 corner); - private: /** * list of entities to delete at the end of the tick @@ -266,11 +310,11 @@ class ServerGameState { MatchPhase matchPhase; /** - * @brief Amount of time, in timesteps, left until the end of the match - * This value only becomes relevant when matchPhase is set to + * @brief epoch timestamp of when the match will end + * This value only becomes set when matchPhase is set to * MatchPhase::RelayRace */ - unsigned int timesteps_left; + time_t relay_finish_time; /** * @brief Player victory is by default false - only becomes true if a Player @@ -307,10 +351,30 @@ class ServerGameState { */ std::unordered_set, pair_hash> collidedObjects; + /** + * @brief Field that stores the current trap the DM is hovering (not placed yet) + */ + Trap* currentGhostTrap; - std::unordered_map, std::vector, IntPairHash> solidSurfaceInGridCells; + /** + * @brief Field that stores the lightning pos for cutting lights + */ + std::optional dmLightningCutLights; + + /** + * @brief Field that stores the light cut action for cutting lights + */ + std::optional dmActionCutLights; - std::vector previouslyHighlighted; + /** + * @brief last light cut for light cut action + */ + unsigned int lastLightCut; + + /** + * @brief last light cut for lightning action + */ + unsigned int lastLightningLightCut; /** /** @@ -326,11 +390,32 @@ class ServerGameState { * cell * @param cell is a single cell of the maze's grid where a wall + torch * should be placed + * @param orb_pos position of the orb so that we can calculate the distance + * for coloring purposes + * @param exit_pos position of the exit so that we can calculate the distance + * for coloring purposes * @param cellType determines which direction the torch should face. * This means that only TorchUp, TorchDown, TorchRight, and TorchLeft * are acceptable values of cellType */ - void spawnTorch(GridCell *cell); + void spawnTorch(GridCell *cell, glm::vec3 orb_pos, glm::vec3 exit_pos); + + /** + * @brief helper function to spawn a fireball trap + * @param cell is a single cell of the maze's grid where the fireball + * trap should be placed + * @param cellType determines which direction the trap should face. + * This means that only FireballTrapUp, FireballTrapDown, FireballTrapRight, and FireballTrapLeft + * are acceptable values of cellType + * @return a pointer to the fireball trap that was spawned + */ + Trap* spawnFireballTrap(GridCell *cell); + + Trap* spawnArrowTrap(GridCell* cell); + + Trap* spawnFloorSpike(GridCell* cell); + + Trap* spawnLava(GridCell* cell); std::unordered_map, MAX_POINT_LIGHTS>> lightSourcesPerPlayer; @@ -338,8 +423,6 @@ class ServerGameState { * @brief table of all currently playing sounds */ SoundTable sound_table; - /** - * @brief number of enemies currently in the maze - */ - int alive_enemy_weight; + + GameConfig config; }; \ No newline at end of file diff --git a/include/server/game/slime.hpp b/include/server/game/slime.hpp index 6c6efb50..536b9296 100644 --- a/include/server/game/slime.hpp +++ b/include/server/game/slime.hpp @@ -6,6 +6,7 @@ using namespace std::chrono_literals; class Slime : public Enemy { public: inline static const float SIGHT_LIMIT_GRID_CELLS = 8.0f; // can see you within 8 grid cells + int size; Slime(glm::vec3 corner, glm::vec3 facing, int size); @@ -23,7 +24,6 @@ class Slime : public Enemy { void increaseJumpIndex(); std::size_t jump_index; - int size; bool landed; }; diff --git a/include/server/game/solidsurface.hpp b/include/server/game/solidsurface.hpp index 0b7513c6..1c10ee32 100644 --- a/include/server/game/solidsurface.hpp +++ b/include/server/game/solidsurface.hpp @@ -19,6 +19,4 @@ class SolidSurface : public Object { SharedSolidSurface shared{}; virtual SharedObject toShared() override; - - void setDMHighlight(bool highlight); }; \ No newline at end of file diff --git a/include/server/game/spawner.hpp b/include/server/game/spawner.hpp new file mode 100644 index 00000000..490a1976 --- /dev/null +++ b/include/server/game/spawner.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include "shared/game/sharedobject.hpp" +#include "shared/utilities/typedefs.hpp" + +#include +#include +#include + +class ServerGameState; +class Item; + +/** + * Spawner Class manages the number of enemies in the maze + * + * Only the enemy value cap has to be set in constants.hpp for + * managing the max number of enemies to spawn + */ +class Spawner { +public: + Item* dummyItem; + + Item* smallDummyItem; + + Spawner(); + + /* + * General spawn method to manage number of enemies + */ + void spawn(ServerGameState& state); + + /* + * Selects and spawns the enemy with amount of value remaining + */ + void spawnEnemy(ServerGameState& state, int valueRemaining); + + /* + * Decreases the enemy value when enemies are killed/dead + */ + void decreaseValue(SpecificID id); + + /* + * Method to manually add enemies for tracking if not spawned by spawner + * e.g slime creating its own clone + */ + void addEnemy(ServerGameState& state, SpecificID id); + + /* + * Find empty positions that enemies can safely spawn + */ + glm::vec3 findEmptyPosition(ServerGameState& state); + + /* + * General spawn method to manage number of enemies + */ + void spawnDummy(ServerGameState& state); + + + /* + * Smaller dummy for empty check + */ + void spawnSmallDummy(ServerGameState& state); + + +private: + int enemyValueCap; + int currentEnemyValue; + std::vector valueMap; + std::unordered_map enemiesAlive; +}; diff --git a/include/server/game/torchlight.hpp b/include/server/game/torchlight.hpp index 6fc20ffb..bfa600d3 100644 --- a/include/server/game/torchlight.hpp +++ b/include/server/game/torchlight.hpp @@ -2,48 +2,25 @@ #include "server/game/constants.hpp" #include "server/game/object.hpp" +#include "shared/game/point_light.hpp" #include "shared/game/sharedobject.hpp" -struct TorchlightProperties { - // if the light should flicker or not. if set - // to false, it will be constantly at the - // max_intensity - bool flickering; - // lowest intensity the light should reach during flickering. - // range is from 0-1 - float min_intensity; - // highest intensity the light should reach during flickering. - // range is from 0-1. if flickering is false, this intensity - // will be used. - float max_intensity; - - // lighting properties of that the light source - // emits - glm::vec3 ambient_color; - glm::vec3 diffuse_color; - glm::vec3 specular_color; // shiny effects - - // these two affect the dropoff of the light intensity - // as distance increases - float attenuation_linear; - float attenuation_quadratic; -}; - - class Torchlight : public Object { public: /** * Creates a torchight with default lighting properties. * @param corner Corner position of the surface + * @param dist_orb distance to orb, to see if it should be shaded blue + * @param dist_exit distance to the exit, to see if it should be shaded white */ - explicit Torchlight(glm::vec3 corner); + explicit Torchlight(glm::vec3 corner, float dist_orb, float dist_exit); /** * @param corner Corner position of the surface * @param properties allows for customization of lighting * and flickering properties */ - Torchlight(glm::vec3 corner, const TorchlightProperties& properties); + Torchlight(glm::vec3 corner, const PointLightProperties& properties); ~Torchlight(); SharedObject toShared() override; @@ -52,15 +29,25 @@ class Torchlight : public Object { * @brief runs on every server tick to update torchlight flickering * animations * @parm current ServerGameState + * @param the position the lightning hit (if exists) + * @param the position of the light cut action (if exists) + */ + bool doTick(ServerGameState& state, std::optional lightning_light_cut_pos, std::optional action_light_cut_pos); + + /** + * @brief manually set torchlight intensity, for use in intro cutscene */ - void doTick(ServerGameState& state); + void overrideIntensity(float val); /** * @brief get current intensity of torch from 0-1 */ float getIntensity() const; + + bool is_cut; private: - TorchlightProperties properties; + PointLightProperties properties; + // current intensity from 0-1 that gets // sent to client @@ -70,8 +57,10 @@ class Torchlight : public Object { // curr_step from 0-1 in flickering animation float curr_step; + // how much the intensity should change on every server tick float flickering_speed; + // if the flickering animation is inceasing in // intensity or decreasing bool inc_intensity; diff --git a/include/server/game/trap.hpp b/include/server/game/trap.hpp index 3fe508eb..930aaaae 100644 --- a/include/server/game/trap.hpp +++ b/include/server/game/trap.hpp @@ -16,7 +16,7 @@ class Trap : public Object { * @param model What model should be used to render the trap * @param dimensions Dimensions to use for the trap, if it won't be overridden by the model info */ - Trap(ObjectType type, bool movable, glm::vec3 corner, Collider collider, ModelType model, glm::vec3 dimensions); + Trap(ObjectType type, bool movable, glm::vec3 corner, Collider collider, ModelType model, glm::vec3 dimensions = glm::vec3(1.0f)); /** * Determines if the trap should be triggered @@ -60,15 +60,51 @@ class Trap : public Object { SharedObject toShared() override; + /** + * Set the is_dm_trap field + * + * @param boolean for if this trap is a DM trap or not + */ void setIsDMTrap(bool is_dm_trap); + /** + * Set the SharedTrapInfo info dm_hover field + * + * @param boolean for if this trap is a a DM hover or not + */ + void setIsDMTrapHover(bool is_dm_trap_hover); + + /** + * Set the expiration of this trap + * + * @param expiration time + */ void setExpiration(std::chrono::time_point expiration); + /** + * Gets if this trap is a DM trap or not + * + * @returns True if the trap is a DM trap and false otherwise + */ bool getIsDMTrap(); + /** + * Gets the expiration time of this trap + * + * @returns the expiration time of this trap + */ std::chrono::time_point getExpiration(); + protected: + /** + * is this trap a DM trap? + */ bool is_dm_trap; + + /** + * the expiration time of this trap + */ std::chrono::time_point expiration; + SharedTrapInfo info; }; \ No newline at end of file diff --git a/include/server/game/weapon.hpp b/include/server/game/weapon.hpp index c405a858..9e61a3f5 100644 --- a/include/server/game/weapon.hpp +++ b/include/server/game/weapon.hpp @@ -12,6 +12,7 @@ enum class WeaponType { Dagger, Sword, Hammer, + Lightning, }; class Weapon : public Item { @@ -29,6 +30,9 @@ class Weapon : public Item { void useItem(Object* other, ServerGameState& state, int itemSelected) override; void reset(ServerGameState& state); + // for DM + void useLightning(Object* other, ServerGameState& state, glm::vec3 corner); + private: int delay; bool resetAttack; diff --git a/include/server/game/weaponcollider.hpp b/include/server/game/weaponcollider.hpp index b8bfc4c8..bdad7b32 100644 --- a/include/server/game/weaponcollider.hpp +++ b/include/server/game/weaponcollider.hpp @@ -1,6 +1,9 @@ #include "server/game/object.hpp" #include "server/game/constants.hpp" #include "server/game/player.hpp" +#include "shared/audio/soundtype.hpp" +#include "shared/game/point_light.hpp" +#include "shared/game/sharedmodel.hpp" #include /* @@ -8,15 +11,17 @@ */ class WeaponCollider : public Object { public: + ServerSFX sound; struct WeaponOptions { - WeaponOptions(int damage, float timeUntilAttack, int attackDuration) : - damage(damage), timeUntilAttack(timeUntilAttack), attackDuration(attackDuration) + WeaponOptions(int damage, int timeUntilAttack, int attackDuration, bool followPlayer) : + damage(damage), timeUntilAttack(timeUntilAttack), attackDuration(attackDuration), followPlayer(followPlayer) {} int damage; int timeUntilAttack; //in milliseconds int attackDuration; //in milliseconds + bool followPlayer; }; WeaponCollider(Player* usedPlayer, glm::vec3 corner, glm::vec3 facing, \ @@ -24,16 +29,17 @@ class WeaponCollider : public Object { void doCollision(Object* other, ServerGameState& state) override; void updateMovement(ServerGameState& state); - bool readyTime(); + bool readyTime(ServerGameState& state); bool timeOut(ServerGameState& state); virtual SharedObject toShared() override; -private: +protected: std::chrono::time_point preparing_time; std::chrono::time_point attacked_time; Player* usedPlayer; SharedWeaponInfo info; WeaponOptions opt; + bool playSound; }; class ShortAttack : public WeaponCollider { @@ -42,8 +48,10 @@ class ShortAttack : public WeaponCollider { ShortAttack(Player* usedPlayer, glm::vec3 corner, glm::vec3 facing): WeaponCollider(usedPlayer, corner, facing, DIMENSION, ModelType::Cube, - WeaponOptions(DAGGER_DMG, DAGGER_PREP, DAGGER_DUR)) - {} + WeaponOptions(DAGGER_DMG, DAGGER_PREP, DAGGER_DUR, true)) + { + this->sound = ServerSFX::Dagger; + } }; class MediumAttack : public WeaponCollider { @@ -52,8 +60,10 @@ class MediumAttack : public WeaponCollider { MediumAttack(Player* usedPlayer, glm::vec3 corner, glm::vec3 facing) : WeaponCollider(usedPlayer, corner, facing, DIMENSION, ModelType::Cube, - WeaponOptions(SWORD_DMG, SWORD_PREP, SWORD_DUR)) - {} + WeaponOptions(SWORD_DMG, SWORD_PREP, SWORD_DUR, true)) + { + this->sound = ServerSFX::Sword; + } }; class BigAttack : public WeaponCollider { @@ -62,6 +72,35 @@ class BigAttack : public WeaponCollider { BigAttack(Player* usedPlayer, glm::vec3 corner, glm::vec3 facing) : WeaponCollider(usedPlayer, corner, facing, DIMENSION, ModelType::Cube, - WeaponOptions(HAMMER_DMG, HAMMER_PREP, HAMMER_DUR)) - {} + WeaponOptions(HAMMER_DMG, HAMMER_PREP, HAMMER_DUR, true)) + { + this->sound = ServerSFX::Hammer; + } +}; + +class Lightning : public WeaponCollider { +public: + inline static const glm::vec3 DIMENSION = glm::vec3(3.0f, 100.0f, 3.0f); + + Lightning(glm::vec3 corner, glm::vec3 facing, const PointLightProperties& properties) : + WeaponCollider(nullptr, corner, facing, DIMENSION, ModelType::Lightning, + WeaponOptions(LIGHTNING_DMG, LIGHTNING_PREP, LIGHTNING_DUR, false)), + properties(properties) + { + this->sound = ServerSFX::Thunder; + } + virtual SharedObject toShared() override { + auto so = WeaponCollider::toShared(); + so.pointLightInfo = SharedPointLightInfo { + .intensity = (this->info.attacked) ? 1.0f : 0.3f, + .ambient_color = this->properties.ambient_color, + .diffuse_color = this->properties.diffuse_color, + .specular_color = this->properties.specular_color, + .attenuation_linear = this->properties.attenuation_linear, + .attenuation_quadratic = this->properties.attenuation_quadratic, + }; + return so; + } +private: + PointLightProperties properties; }; \ No newline at end of file diff --git a/include/server/server.hpp b/include/server/server.hpp index 23f93db5..81c17659 100644 --- a/include/server/server.hpp +++ b/include/server/server.hpp @@ -10,6 +10,7 @@ #include #include "server/lobbybroadcaster.hpp" +#include "server/game/introcutscene.hpp" #include "shared/network/session.hpp" #include "shared/utilities/config.hpp" #include "shared/utilities/typedefs.hpp" @@ -41,6 +42,8 @@ class Server { void sendLightSourceUpdates(EntityID playerID); + void sendSoundCommands(); + private: /// @brief EID that is reserved for the Server / World itself. EntityID world_eid; @@ -73,4 +76,10 @@ class Server { /// @brief Master copy of the ServerGameState, living on the server ServerGameState state; + + /// @brief config + GameConfig config; + + /// @brief game state used to render the intro cutscene + IntroCutscene intro_cutscene; }; diff --git a/include/shared/audio/constants.hpp b/include/shared/audio/constants.hpp index 945663ef..d1e3d787 100644 --- a/include/shared/audio/constants.hpp +++ b/include/shared/audio/constants.hpp @@ -14,6 +14,6 @@ #define MEDIUM_ATTEN 5.0f #define MEDIUM_DIST 20.0f -// Sound values fitting for something that should be heard from pretty far (large room) -#define FAR_ATTEN 3.0f -#define FAR_DIST 35.0f \ No newline at end of file +// Sound values fitting for something that should be heard from very far +#define FAR_ATTEN 1.0f +#define FAR_DIST 80.0f \ No newline at end of file diff --git a/include/shared/audio/soundtype.hpp b/include/shared/audio/soundtype.hpp index 2f172a0f..ad3e49fc 100644 --- a/include/shared/audio/soundtype.hpp +++ b/include/shared/audio/soundtype.hpp @@ -9,23 +9,29 @@ using namespace std::chrono_literals; // Sounds that the client can decide to play on its own // (e.g. BGM, clicking on UI elements...) enum class ClientMusic { - TitleTheme, - GameTheme, + MenuTheme, + MazeExplorationPlayersTheme, + MazeExplorationDMTheme, + RelayRacePlayersTheme, + RelayRaceDMTheme // make sure to add to macro below! }; #define GET_CLIENT_MUSICS() { \ - ClientMusic::TitleTheme, ClientMusic::GameTheme \ + ClientMusic::MenuTheme, ClientMusic::MazeExplorationPlayersTheme, \ + ClientMusic::MazeExplorationDMTheme, ClientMusic::RelayRacePlayersTheme, \ + ClientMusic::RelayRaceDMTheme \ } enum class ClientSFX { // TODO: decide what these are - TEMP, + VictoryThemePlayers, + VictoryThemeDM // make sure to add to macro below! }; #define GET_CLIENT_SFXS() { \ - ClientSFX::TEMP, \ + ClientSFX::VictoryThemePlayers, ClientSFX::VictoryThemeDM, \ } // Sounds that correspond to something in the game world @@ -46,6 +52,25 @@ enum class ServerSFX { CeilingSpikeTrigger, CeilingSpikeImpact, TorchLoop, + Thunder, + Dagger, + Sword, + Hammer, + Minotaur, + Python, + Teleport, + Potion, + Spell, + ItemPickUp, + ItemDrop, + MirrorShatter, + TEMP, + PlayersStartTheme, + ZeusStartTheme, + ElectricHum, + IntroGateOpen, + Wind, + MinotaurDeath // make sure to add to server sfx len map! // make sure to add to macro below! }; @@ -64,9 +89,30 @@ const std::unordered_map SERVER_SFX_LENS = {ServerSFX::PlayerWalk3, 500ms}, {ServerSFX::PlayerWalk4, 500ms}, {ServerSFX::PlayerWalk5, 500ms}, + {ServerSFX::Dagger, 500ms}, + {ServerSFX::Sword, 500ms}, + {ServerSFX::Hammer, 1000ms}, + {ServerSFX::Minotaur, 1000ms}, + {ServerSFX::Python, 1000ms}, {ServerSFX::CeilingSpikeTrigger, 380ms}, {ServerSFX::CeilingSpikeImpact, 1180ms}, - {ServerSFX::TorchLoop, 9999ms} // wont actually be used because it loops + {ServerSFX::Thunder, 2500ms}, + + // used not for in game, but for the intro cutscene, so this is the duration of sound in intro cutscene + {ServerSFX::TorchLoop, 30000ms}, + {ServerSFX::Wind, 14000ms}, + + {ServerSFX::PlayersStartTheme, 8000ms}, + {ServerSFX::ElectricHum, 1500ms}, + {ServerSFX::IntroGateOpen, 9000ms}, + {ServerSFX::ZeusStartTheme, 12000ms}, + {ServerSFX::Teleport, 500ms}, + {ServerSFX::Potion, 500ms}, + {ServerSFX::Spell, 500ms}, + {ServerSFX::ItemPickUp, 500ms}, + {ServerSFX::ItemDrop, 500ms}, + {ServerSFX::MirrorShatter, 2000ms}, + {ServerSFX::MinotaurDeath, 3100ms}, // dont forget macro below! }; @@ -74,7 +120,12 @@ const std::unordered_map SERVER_SFX_LENS = ServerSFX::ArrowShoot, ServerSFX::FireballShoot, ServerSFX::ArrowImpact, ServerSFX::FireballImpact, \ ServerSFX::SlimeJump, ServerSFX::SlimeLand, ServerSFX::PlayerJump, ServerSFX::PlayerLand, \ ServerSFX::PlayerWalk1, ServerSFX::PlayerWalk2, ServerSFX::PlayerWalk3, ServerSFX::PlayerWalk4, ServerSFX::PlayerWalk5, \ - ServerSFX::CeilingSpikeTrigger, ServerSFX::CeilingSpikeImpact, ServerSFX::TorchLoop, \ + ServerSFX::CeilingSpikeTrigger, ServerSFX::CeilingSpikeImpact, ServerSFX::TorchLoop, ServerSFX::Thunder,\ + ServerSFX::Dagger, ServerSFX::Sword, ServerSFX::Hammer, ServerSFX::Minotaur, ServerSFX::Python, \ + ServerSFX::PlayersStartTheme, ServerSFX::ElectricHum, ServerSFX::IntroGateOpen, ServerSFX::ZeusStartTheme, \ + ServerSFX::Wind, \ + ServerSFX::Teleport, ServerSFX::Potion, ServerSFX::Spell, ServerSFX::ItemPickUp, ServerSFX::ItemDrop, \ + ServerSFX::MirrorShatter, ServerSFX::MinotaurDeath \ } // const std::unordered_map serverSoundTickLengths = { diff --git a/include/shared/game/celltype.hpp b/include/shared/game/celltype.hpp index 5e36140d..15bb5a67 100644 --- a/include/shared/game/celltype.hpp +++ b/include/shared/game/celltype.hpp @@ -7,10 +7,16 @@ enum class CellType { Spawn, Enemy, SpikeTrap, - FireballTrap, + FireballTrapLeft, + FireballTrapRight, + FireballTrapUp, + FireballTrapDown, + FloorSpikeFull, FloorSpikeHorizontal, FloorSpikeVertical, - FloorSpikeFull, + LavaCross, + LavaHorizontal, + LavaVertical, FakeWall, ArrowTrapUp, ArrowTrapDown, @@ -37,5 +43,8 @@ enum class CellType { OutsideTheMaze, TeleporterTrap, Exit, + Lightning, + Mirror, + LightCut, Unknown }; \ No newline at end of file diff --git a/include/shared/game/constants.hpp b/include/shared/game/constants.hpp index c6f4bb4a..2da0a230 100644 --- a/include/shared/game/constants.hpp +++ b/include/shared/game/constants.hpp @@ -7,9 +7,21 @@ /* Game phase information */ // Time limit initially set to 5 minutes -#define TIME_LIMIT_MS std::chrono::milliseconds(300000) -//#define TIME_LIMIT_MS std::chrono::milliseconds(5 * 60 * 1000) +#define TIME_LIMIT_S std::chrono::seconds(300) + +/* Default model sizes */ +#define BEAR_DIMENSIONS glm::vec3(14.163582, 17.914591, 10.655818) +#define FIRE_PLAYER_DIMENSIONS glm::vec3(4.0f, 10.069769, 4.0f) +#define LIGHTNING_PLAYER_DIMENSIONS glm::vec3(4.0f, 10.069769, 4.0f) +#define WATER_PLAYER_DIMENSIONS glm::vec3(4.0f, 10.069769, 4.0f) +#define SUNGOD_DIMENSIONS glm::vec3(3.281404, 9.543382, 7.974873) +#define ARROW_DIMENSIONS glm::vec3(25.025101, 68.662003, 333.333333) +#define ARROW_TRAP_DIMENSIONS glm::vec3(2.815553, 3.673665, 1.588817) +#define FLOOR_SPIKE_DIMENSIONS glm::vec3(3.0f, 1.0f, 3.0f) +#define LAVA_DIMENSIONS glm::vec3(3.0f, 0.01f, 3.0f) + +#define PLAYER_BBOX_SCALE 0.35f +#define PLAYER_MODEL_SCALE 0.004f /* Number of player deaths to update match state to MatchState::RelayRace */ -#define PLAYER_DEATHS_TO_RELAY_RACE 5 -//#define PLAYER_DEATHS_TO_RELAY_RACE 15 +#define PLAYER_DEATHS_TO_RELAY_RACE 3 diff --git a/include/shared/game/dir_light.hpp b/include/shared/game/dir_light.hpp new file mode 100644 index 00000000..1a591647 --- /dev/null +++ b/include/shared/game/dir_light.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +/** + * @brief DirLight holds lighting information + * for directional light source. Note that this + * is different than point lights which emit from + * a certain point. + */ +struct DirLight { + glm::vec3 direction; + + glm::vec3 ambient_color; + glm::vec3 diffuse_color; + glm::vec3 specular_color; +}; diff --git a/include/shared/game/event.hpp b/include/shared/game/event.hpp index 0a5b0ae8..ba443681 100644 --- a/include/shared/game/event.hpp +++ b/include/shared/game/event.hpp @@ -42,7 +42,8 @@ enum class EventType { UseItem, DropItem, UpdateLightSources, - TrapPlacement + TrapPlacement, + LoadIntroCutscene, }; enum class ActionType { @@ -73,23 +74,38 @@ struct ChangeFacingEvent { } }; + +/** + * Enum representing a player's desired role (as selected with the radio buttons + * in the client lobby screen (in GUIState::Lobby) + */ +enum class PlayerRole { + Player, + DungeonMaster, + Unknown +}; + /** - * Event representing an action a player can take during the lobby screen + * Event representing an action a player can take during the Lobby GUI + * screen. + * (Only handle this event in the server while the game phase is set to + * GamePhase::LOBBY) */ struct LobbyActionEvent { enum class Action { - LEAVE, - READY_UP, - UNREADY + Ready, + StartGame }; LobbyActionEvent() {} - explicit LobbyActionEvent(Action action) : action(action) {} + explicit LobbyActionEvent(Action action, PlayerRole role) : action(action), + role(role) {} Action action; + PlayerRole role; DEF_SERIALIZE(Archive& ar, const unsigned int version) { - ar& action; + ar& action & role; } }; @@ -272,8 +288,9 @@ struct UpdateLightSourcesEvent { struct UpdatedLightSource { EntityID eid; float intensity; + bool is_cut; DEF_SERIALIZE(Archive& ar, const unsigned int version) { - ar & eid & intensity; + ar & eid & intensity & is_cut; } }; std::array, MAX_POINT_LIGHTS> lightSources; @@ -283,6 +300,25 @@ struct UpdateLightSourcesEvent { } }; +struct LoadIntroCutsceneEvent { + LoadIntroCutsceneEvent() = default; + + explicit LoadIntroCutsceneEvent( + const SharedGameState& state, + EntityID pov_eid, + EntityID dm_eid, + const std::array, MAX_POINT_LIGHTS>& lights + ) : state(state), pov_eid(pov_eid), dm_eid(dm_eid), lights(lights) {} + + SharedGameState state; + EntityID pov_eid; + EntityID dm_eid; + std::array, MAX_POINT_LIGHTS> lights; + + DEF_SERIALIZE(Archive& ar, const unsigned int version) { + ar & state & lights & pov_eid & dm_eid; + } +}; /** * All of the different kinds of events in a tagged union, so we can @@ -302,7 +338,8 @@ using EventData = boost::variant< UseItemEvent, UpdateLightSourcesEvent, DropItemEvent, - TrapPlacementEvent + TrapPlacementEvent, + LoadIntroCutsceneEvent >; /** diff --git a/include/shared/game/point_light.hpp b/include/shared/game/point_light.hpp new file mode 100644 index 00000000..a05af462 --- /dev/null +++ b/include/shared/game/point_light.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +struct PointLightProperties { + // if the light should flicker or not. if set + // to false, it will be constantly at the + // max_intensity + bool flickering; + // lowest intensity the light should reach during flickering. + // range is from 0-1 + float min_intensity; + // highest intensity the light should reach during flickering. + // range is from 0-1. if flickering is false, this intensity + // will be used. + float max_intensity; + + // lighting properties of that the light source + // emits + glm::vec3 ambient_color; + glm::vec3 diffuse_color; + glm::vec3 specular_color; // shiny effects + + // these two affect the dropoff of the light intensity + // as distance increases + float attenuation_linear; + float attenuation_quadratic; +}; diff --git a/include/shared/game/sharedgamestate.hpp b/include/shared/game/sharedgamestate.hpp index 504cd060..5c30d952 100644 --- a/include/shared/game/sharedgamestate.hpp +++ b/include/shared/game/sharedgamestate.hpp @@ -14,10 +14,13 @@ //#include "server/game/constants.hpp" #include "shared/game/constants.hpp" +// Forward declaration of PlayerRole enum to avoid circular reference +enum class PlayerRole; enum class GamePhase { TITLE_SCREEN, LOBBY, + INTRO_CUTSCENE, GAME, RESULTS }; @@ -27,6 +30,50 @@ enum class MatchPhase { RelayRace }; +/** + * @brief Information about a player that has connected to the + * current lobby. + */ +struct LobbyPlayer { + /** + * @brief Player's EntityID (this is the EntityID of their Player + * or DungeonMaster object in the game state) + */ + EntityID id; + + /** + * @brief This is the player's desired role (though they may not + * actually play as this role - the server must settle ties if + * multiple (or no) players want to play as the Dungeon Master + * for instance) + */ + PlayerRole desired_role; + + /** + * @brief Whether the player is in the Ready state on the lobby + * screen. + */ + bool ready; + + LobbyPlayer() {} + + LobbyPlayer(EntityID id, PlayerRole desired_role, bool ready) + : id(id), desired_role(desired_role), ready(ready) {} + + DEF_SERIALIZE(Archive& ar, unsigned int version) { + ar& id& desired_role & ready; + } + + /* Debug Methods */ + + /** + * @brief Generates a string representation of this Lobby Player struct. + * @return std::string representation of this Lobby Player struct. + */ + std::string to_string(unsigned int tab_offset); + std::string to_string() { return this->to_string(0); } +}; + /** * @brief Information about the current lobby of players. */ @@ -37,15 +84,63 @@ struct Lobby { std::string name; /** - * @brief A hash table that maps from player's EntityID to their names. + * @brief A vector of length max_players that maps a player's index + * (technically, index - 1 as player indices are 1-indexed) to their + * LobbyPlayer info struct (though since a player index could at + * present not be assigned, this maps to a boost::optional) */ - std::unordered_map players; + std::vector> players; /** * @brief The maximum number of players that this game instance can support. */ int max_players; + /** + * @brief Default Lobby constructor supports exactly 4 players + */ + Lobby() : Lobby(4) {} + + explicit Lobby(int max_players) : Lobby("Default Lobby Name", max_players) {} + + Lobby(const std::string& name, int max_players) : name(name), max_players(max_players) { + // Reserve max_players slots in the players vector + for (int i = 0; i < max_players; i++) { + this->players.push_back(boost::none); + } + } + + /** + * @brief Given a player's 1-indexed player index, this method returns + * a reference to the boost::optional that will contain the + * LobbyPlayer struct for that player if that player index has been assigned. + * @param playerIndex 1-indexed player index of the player whose LobbyPlayer + * struct is requested. (I.e., Player 1's 1-indexed player index is 1 though it + * will be stored at index 0 in the players vector) + * @return Reference to the boost::optional that contains the LobbyPlayer + * struct of the player with the requested 1-indexed player index. If that player + * index has not been assigned, the boost::optional will be empty. + */ + const boost::optional& getPlayer(int playerIndex) const; + + /** + * @brief Returns a player's LobbyPlayer struct by the player's EntityID. + * @param id EntityID of the player whose LobbyPlayer struct is requested. + * @return Reference to the boost::optional that contains the LobbyPlayer + * struct of the player with the given EntityID; if no player in the lobby has this + * EntityID, the boost::optional will be empty. + */ + const boost::optional& getPlayer(EntityID id) const; + + /** + * @brief Returns the number of players in the lobby + * Note: use this method and note players.size() to determine the number of + * current players in the lobby! players.size() should always equal + * max_players + * @return Number of current players in the lobby + */ + int numPlayersInLobby() const; + DEF_SERIALIZE(Archive& ar, unsigned int version) { ar & name & players & max_players; @@ -53,6 +148,15 @@ struct Lobby { // TODO: Add a player role listing? I.e., which player is playing which // character and which player is playing as the Dungeon Master? + + /* Debug Methods */ + + /** + * @brief Generates a string representation of this Lobby struct. + * @return std::string representation of this Lobby struct + */ + std::string to_string(unsigned int tab_offset) const; + std::string to_string() const { return this->to_string(0); } }; /** @@ -71,7 +175,7 @@ struct SharedGameState { MatchPhase matchPhase; - unsigned int timesteps_left; + time_t relay_finish_time; bool playerVictory; @@ -84,7 +188,7 @@ struct SharedGameState { this->timestep = FIRST_TIMESTEP; this->lobby.max_players = MAX_PLAYERS; this->matchPhase = MatchPhase::MazeExploration; - this->timesteps_left = TIME_LIMIT_MS / TIMESTEP_LEN; + this->relay_finish_time = 0; this->playerVictory = false; this->numPlayerDeaths = 0; } @@ -97,14 +201,14 @@ struct SharedGameState { this->lobby.max_players = config.server.max_players; this->lobby.name = config.server.lobby_name; this->matchPhase = MatchPhase::MazeExploration; - this->timesteps_left = TIME_LIMIT_MS / TIMESTEP_LEN; + this->relay_finish_time = 0; this->playerVictory = false; this->numPlayerDeaths = 0; } DEF_SERIALIZE(Archive& ar, const unsigned int version) { ar & objects & timestep & lobby & phase & matchPhase - & timesteps_left & playerVictory & numPlayerDeaths; + & relay_finish_time & playerVictory & numPlayerDeaths; } /** diff --git a/include/shared/game/sharedmodel.hpp b/include/shared/game/sharedmodel.hpp index f5f3c8eb..3c737da1 100644 --- a/include/shared/game/sharedmodel.hpp +++ b/include/shared/game/sharedmodel.hpp @@ -5,7 +5,9 @@ */ enum class ModelType { Cube, - Player, + PlayerFire, + PlayerLightning, + PlayerWater, WarrenBear, HealthPotion, InvisibilityPotion, @@ -15,19 +17,35 @@ enum class ModelType { FireSpell, HealSpell, TeleportSpell, + SpellOrb, Frame, Orb, FloorSpikeHorizontal, FloorSpikeVertical, - FloorSpikeFull, + FloorSpikeFull, + LavaCross, + LavaHorizontal, + LavaVertical, ArrowTrapUp, ArrowTrapDown, ArrowTrapLeft, ArrowTrapRight, SpikeTrap, - FireballTrap, TeleporterTrap, Dagger, Sword, Hammer, + Lightning, + Arrow, + ArrowTrap, + Fireball, + FireballTrapLeft, + FireballTrapRight, + FireballTrapUp, + FireballTrapDown, + SunGod, + Mirror, + LightCut, + TorchPost, + Chest }; \ No newline at end of file diff --git a/include/shared/game/sharedobject.hpp b/include/shared/game/sharedobject.hpp index f3265dbc..838ac045 100644 --- a/include/shared/game/sharedobject.hpp +++ b/include/shared/game/sharedobject.hpp @@ -28,16 +28,20 @@ enum class ObjectType { FireballTrap, Projectile, FloorSpike, + Lava, FakeWall, ArrowTrap, TeleporterTrap, Spell, Slime, + Minotaur, + Python, Item, Exit, Orb, Weapon, WeaponCollider, + Mirror }; /** @@ -124,9 +128,11 @@ struct SharedTrapInventory { int inventory_size; std::vector inventory; std::unordered_map trapsInCooldown; + std::unordered_map trapsCooldown; + int trapsPlaced; DEF_SERIALIZE(Archive& ar, const unsigned int version) { - ar& selected& inventory_size& inventory& trapsInCooldown; + ar& selected& inventory_size& inventory& trapsInCooldown& trapsPlaced & trapsCooldown; } }; @@ -142,9 +148,10 @@ struct SharedItemInfo { struct SharedWeaponInfo { bool attacked; + bool lightning; DEF_SERIALIZE(Archive& ar, const unsigned int version) { - ar& attacked; + ar& attacked& lightning; } }; @@ -162,11 +169,10 @@ struct SharedSolidSurface { */ SurfaceType surfaceType; - bool dm_highlight; bool is_internal; DEF_SERIALIZE(Archive& ar, const unsigned int version) { - ar & surfaceType & dm_highlight & is_internal; + ar & surfaceType & is_internal; } }; @@ -203,9 +209,10 @@ struct SharedPhysics { struct SharedTrapInfo { bool triggered; + bool dm_hover; DEF_SERIALIZE(Archive& ar, const unsigned int version) { - ar & triggered; + ar & triggered & dm_hover; } }; @@ -213,9 +220,10 @@ struct SharedPlayerInfo { bool is_alive; time_t respawn_time; // unix timestamp in ms when the player will be respawned bool render; // for invis potion + bool used_mirror_to_reflect_lightning; // To tell the player that they successfully reflected lightning DEF_SERIALIZE(Archive& ar, const unsigned int version) { - ar & is_alive & respawn_time & render; + ar & is_alive & respawn_time & render & used_mirror_to_reflect_lightning; } }; @@ -231,9 +239,11 @@ struct SharedPointLightInfo { float attenuation_linear; float attenuation_quadratic; + bool is_cut = false; + DEF_SERIALIZE(Archive& ar, const int version) { ar & intensity & ambient_color & diffuse_color & specular_color & - attenuation_linear & attenuation_quadratic; + attenuation_linear & attenuation_quadratic & is_cut; } }; @@ -248,6 +258,43 @@ struct SharedExit { } }; +enum class AnimState { + IdleAnim, + WalkAnim, + SprintAnim, + JumpAnim, + LandAnim, + AttackAnim, + DrinkPotionAnim, + DeathAnim +}; + +struct SharedDMInfo { + /** + * @brief The Dungeon Master can become paralyzed if a player used a Mirror + * object to reflect a lightning bolt back at the Dungeon Master. + * When the Dungeon Master is paralyzed, all input events from the DM should + * be ignored for the duration of the paralysis. + */ + bool paralyzed; + + int mana_remaining; + + DEF_SERIALIZE(Archive& ar, const unsigned int version) { + ar & paralyzed & mana_remaining; + } +}; + + +struct SharedCompass { + + float angle; + + DEF_SERIALIZE(Archive& ar, const unsigned int version) { + ar& angle; + } +}; + /** * @brief Representation of the Object class used by ServerGameState, containing * exactly the subset of Object data required by the client. @@ -258,6 +305,7 @@ class SharedObject { ObjectType type; SharedPhysics physics; ModelType modelType; + AnimState animState; boost::optional stats; boost::optional iteminfo; @@ -270,15 +318,17 @@ class SharedObject { boost::optional statuses; boost::optional exit; boost::optional weaponInfo; + boost::optional DMInfo; + boost::optional compass; SharedObject() {} // cppcheck-suppress uninitMemberVar ~SharedObject() {} DEF_SERIALIZE(Archive& ar, const unsigned int version) { - ar & globalID & type & physics & modelType & stats & + ar & globalID & type & physics & modelType & animState & stats & iteminfo & solidSurface & trapInfo & playerInfo & inventoryInfo & statuses & trapInventoryInfo & pointLightInfo & - exit & weaponInfo; + exit & weaponInfo & DMInfo & compass; } private: }; diff --git a/include/shared/network/constants.hpp b/include/shared/network/constants.hpp index 3483ae10..34f8fc42 100644 --- a/include/shared/network/constants.hpp +++ b/include/shared/network/constants.hpp @@ -8,4 +8,4 @@ // How many objects we send in one LoadGameState packet // If there are more objects that need to be sent, then // they are split up into multiple LoadGameState packets -#define OBJECTS_PER_UPDATE 80 +#define OBJECTS_PER_UPDATE 250 diff --git a/include/shared/network/packet.hpp b/include/shared/network/packet.hpp index f9dd8140..8881cf3d 100644 --- a/include/shared/network/packet.hpp +++ b/include/shared/network/packet.hpp @@ -166,13 +166,6 @@ class PackagedPacket { /** * Converts the PackagedPacket into asio::buffer format. - * Note: it is important when doing an async write THAT THE PackagedPacket DOES NOT - * GET DESTROYED BEFORE THE ASYNC WRITE ACTUALLY OCCURS. If the PackagedPacket is - * destroyed before the buffer is read from, the underlying data will be deleted - * and garbage will be written to the network socket. - * - * Since we only expose a way to make this class as a shared_ptr, you should pass - * this shared_ptr into any callback that needs to read from the packet * * @return The packet in buffer format, which can easily be passed into boost::asio::write * or similar function. diff --git a/include/shared/network/session.hpp b/include/shared/network/session.hpp index 3c562603..8833f328 100644 --- a/include/shared/network/session.hpp +++ b/include/shared/network/session.hpp @@ -7,7 +7,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -71,31 +73,32 @@ class Session : public std::enable_shared_from_this { ~Session(); /** - * Starts listening for packets on the socket + * @returns true if there has been no fatal socket error, false otherwise */ - void startListen(); + bool isOkay() const; /** - * Connects to the ip + * handles all of the incoming packets and puts the events in the event queue, which you + * can get by calling getEvents * - * @returns true/false if the connection was successful + * @returns list of all received events */ - [[nodiscard("You should check if the connection was successful")]] - bool connectTo(basic_resolver_results endpoints); + std::vector handleAllReceivedPackets(); /** - * Gets all of the received packets since the last time this function was called. + * Connects to the ip * - * @returns received packets on the socket + * @returns true/false if the connection was successful */ - std::vector getEvents(); + [[nodiscard("You should check if the connection was successful")]] + bool connectTo(basic_resolver_results endpoints); /** - * Sends a packet on the socket + * Puts a packet in the queue of packets to send * * @param packet The packet to send. */ - void sendPacketAsync(std::shared_ptr packet); + void sendPacket(std::shared_ptr packet); /** * Sends an event on the socket after packaging it into @@ -103,17 +106,26 @@ class Session : public std::enable_shared_from_this { * * @param evt The event object to send */ - void sendEventAsync(Event evt); + void sendEvent(Event evt); /** * Get the information associated with this session. */ const SessionInfo& getInfo() const; + void setDM(bool is_dm); + private: + /// @brief true until there is a fatal error on the socket + bool okay; + + std::optional prev_hdr; + tcp::socket socket; - std::vector received_events; + std::vector> packets_to_send; + + std::array buffer; SessionInfo info; @@ -124,16 +136,9 @@ class Session : public std::enable_shared_from_this { * * @param type Type of the packet received * @param data Serialized format of the data received on the network. + * @returns Event if the packet was an event, nullopt otherwise */ - void _handleReceivedPacket(PacketType type, const std::string& data); - - /** - * Sets up one async callback to receive a packet. This callback ends up calling - * receivePacketAsync again, if there was no FATAL error, meaning this only needs - * be called once, and then another time if there was a FATAL error and we are - * sure we fixed the error on the socket. - */ - void _receivePacketAsync(); + std::optional _handleReceivedPacket(PacketType type, const std::string& data); /** * Classifies the error reported by boost::asio based on how we should respond to it. @@ -143,6 +148,12 @@ class Session : public std::enable_shared_from_this { * be displayed in an error message. */ SocketError _classifySocketError(boost::system::error_code ec, const char* where); + + /** + * @param bytes number of bytes to check for + * @returns true if the socket has received enough bytes, false otherwise + */ + bool socketHasEnoughBytes(std::size_t bytes); }; /** @@ -158,7 +169,7 @@ struct SessionEntry { EntityID id; bool is_dungeon_master; boost::asio::ip::address ip; - std::weak_ptr session; + std::shared_ptr session; }; /** diff --git a/include/shared/utilities/config.hpp b/include/shared/utilities/config.hpp index ca321938..5a7f0c8d 100644 --- a/include/shared/utilities/config.hpp +++ b/include/shared/utilities/config.hpp @@ -31,6 +31,7 @@ struct GameConfig { std::string maze_file; } maze; + bool disable_enemies; } game; /// @brief Shared config settings for the network struct { @@ -48,6 +49,10 @@ struct GameConfig { bool lobby_broadcast; /// @brief max number of players this server allows int max_players; + /// @brief whether or not the server will spawn a DM + bool disable_dm; + /// @brief whether or not to skip the intro cutscene + bool skip_intro; } server; /// @brief Config settings for the client struct { @@ -55,9 +60,11 @@ struct GameConfig { std::string default_name; /// @brief Whether or not the client should listen for server lobby broadcasts bool lobby_discovery; - int window_width; + bool fullscreen; bool draw_bboxes; bool fps_counter; + bool presentation; + int render; } client; /** @@ -89,4 +96,4 @@ struct GameConfig { * Note: Not using a constructor as then aggregate initialization will not be * possible for GameConfig structs */ -GameConfig getDefaultConfig(); \ No newline at end of file +GameConfig getDefaultConfig(); diff --git a/include/shared/utilities/constants.hpp b/include/shared/utilities/constants.hpp index ae1f1cf6..8ad3d6eb 100644 --- a/include/shared/utilities/constants.hpp +++ b/include/shared/utilities/constants.hpp @@ -4,4 +4,4 @@ * must match the corresponding macro * in the fragment shader */ -#define MAX_POINT_LIGHTS 16 \ No newline at end of file +#define MAX_POINT_LIGHTS 32 \ No newline at end of file diff --git a/include/shared/utilities/smartvector.hpp b/include/shared/utilities/smartvector.hpp index 95a9599e..a3901465 100644 --- a/include/shared/utilities/smartvector.hpp +++ b/include/shared/utilities/smartvector.hpp @@ -56,6 +56,31 @@ class SmartVector { return index; } + /** + * @brief Overwrites the object at the specified index with the + * given object. + * @param object object to place at the given index. + * @param index Index at which the new object will be placed. + * @return true if successfully placed the object at the given + * index, and false if the index is out of bounds. + */ + bool set(T object, size_t index) { + if (index >= wrapped_vector.size()) + return false; + + // Remove index from free list if the specified index is currently + // empty + auto it = freelist.find(index); + + if (it != freelist.end()) { + freelist.erase(*it); + } + + wrapped_vector[index] = object; + + return true; + } + /** * @brief Adds an empty object to the end of the wrapped vector (by adding * the new index to the freelist. diff --git a/include/shared/utilities/time.hpp b/include/shared/utilities/time.hpp index fc13a7b2..3dd8cd1e 100644 --- a/include/shared/utilities/time.hpp +++ b/include/shared/utilities/time.hpp @@ -1,3 +1,5 @@ #pragma once -long getMsSinceEpoch(); \ No newline at end of file +long long getMsSinceEpoch(); + +long long getSecSinceEpoch(); \ No newline at end of file diff --git a/maps/cutscene/intro.maze b/maps/cutscene/intro.maze new file mode 100644 index 00000000..49d9604b --- /dev/null +++ b/maps/cutscene/intro.maze @@ -0,0 +1,6 @@ +################[########## +.........................!o +..........@..............!o +.........................!o +.........................!o +################]########## \ No newline at end of file diff --git a/maps/demo/1717736322059.maze b/maps/demo/1717736322059.maze new file mode 100644 index 00000000..6319da36 --- /dev/null +++ b/maps/demo/1717736322059.maze @@ -0,0 +1,130 @@ + ########## ############################## + ########## #>......w#####vv############## + ###.3.##### ####..########..#########..T## + ##....![#!# ##[#!..!###![##..##[!#[##!...## + ##2.......# #.XXXXXXXX...................## + ##........# #.XXXXXXXX.................!### + ###!..!##!# ####!..!#]#!##!..!##!####..}### + ####..##### ####..########..########..#### + ###{..#### ##p.....<#####..}#######..#### + ###### ###!..!### #############!..!#######..#### + ###!..!#######..############################..#### #### + ###{..#####..|..|..########################{..#### + #####..#####..|..|..##############.3.########..#### + #!##!..!##!#--+--+--#####!##[#!##....![#!###!..!### + #.............|]!|...###!......##2.......####...<## + #.............|![|...####......##........##T...#### + #!##!##!]#!#--+--+--#####..!##!###!..!##!###!..!### + ############..|..|..#####..########..########..#### + ###########..|..|..####{..#######{..########..}### + ##### ##############..#######!..!######!..!#######..#### ###### +#######################!..#######!..!######!..!#######..########..#######!..!####################### ###!..!### +######################....}######{..#####vv#..#vv#####..########..}######{..##################vv#### ###[..6..# +######################2...########..#####..#..#..####{..########..########..##################..#### ####..6..# +##############[######......![#!##!..!##!!..!..!..!###!..!######!..!#######..!##!![###!####![##..#### ###!..!55## +##vvv!vvv!............................................................####............!###......!### ####......# +##...!...!............................................................###!............####......#### ####......# +##...!...!.......####]!......#!##!##!]#!!..!..!..!###!..!##XX#]!..!#######!##]#!!##!..####!##!..#### ###!..!55!# +#{...............}######...!#############........#####..###XX###..##################..}#######..}### ####..6..## +#!..............!######{...###############]#^^#]######..##w..w##..##################..########..#### ####..6..# +#*..............|.T#####..!###########################..#>....<#..#################!..!######!..!### ###### ###!..!### +#!..............!### ##### ###!..!#######..#>....<#..#######!..!######!..!#######..###########################!..!######!..!############# +#{...............### ###[..6..#####..##w..w##..#######{..#######[..#######{..##################vv#######{..#####..6..}############# +##...............}## ####..6..#####..###XX###..########..########..########..##################..########..#####..6..############### +##...!...!.......#### ###!..!55##[#!55!##XX##!..!###!##!..!#[!###!..!#{####!..!#######!##[#!![##..####!##!..!##!!55!..!#######!##[#!# +##...!...!..........# ####....................................####......####..#######!............!###................#######!......# +##^^^!^^^!..........# ####....................................####......####..########............####................########......# +##############]###### ###!..!55!###!55!##]###!..!###!]#!..!##!###!..!##!###!..!#######..!##!!##!..####!##!##!]#!!55!..!#######..!##!# +#################### ####..6..#####..########..}#######..########..########..########..########..}##############..6..########..##### +#################### ####..6..####{..########..########..}#######..]#######..}######{..########..###############..6..}######{..#### +#################### ###!..!#######..########..#######!..!######!..!#######..#######!..!######!..!################!..!######!..!### + #######################!..!##########################!..!#######..########..#######!..!##########################!..!######!..!### + ###########X.F..F.p####[..###########################{..########..########..#######{..###########################{..########..}### + #############!..!#######..############################..########..########..########..############################..########..#### + ####!##[#!###!..!######!..!#{#![######[!#[#!##!###!##!..!##!####..##[##[##..####!##!..########!##[#!![######[!!##!..!##!!##!..!### + ###!................####..............................................................#######!................................#### + ####................####..............................................................!#######................................#### + ####..!##!###!..!######!..!##!!##!..!##!###!##!#]#!##!##!]#!##....!######!....##!##]#!########..!##!!##!..!##!!##!##!]#!!]#!..!### + ####..#######!..!#######..########..##########################....#......#....################..########..##################..#### + ###{..#####w.F..F.s#####..]#######..}#########################....#.@..@.#....###############{..########..}#################..}### + ###!..!#######..#######!..!######!..!#########################....{......}....###############!..!######!..!################!..!### + ###!..!######!..!######!..!#######..########..#######!..!#####....#......#....#####!..########..!################!..!### ###### + ###[..#######{..#######{..#####vv#...|XXXX|......}####..######....#.@..@.#....######............}####>......w####{..#### + ####..########..########..#####..#...|XXXX|......####{..######....#......#....#####{............########..########..#### + ###!..!#{#!##!..!##!!##!..!#[!#..![#########..!..#!##!55!##!##....!##..##!....##!##!..!......!..!##!#[#!..!###!##!..#### + ####.................................|XXXX|..........6XX6............................................XXXXXXXX.......#### + ####p................................|XXXX|..........6XX6............................................XXXXXXXX.......!### + ###!..!##!!##!##!]#!!]#!..!##!#..!..###[####..!..#!##!55!##!####..##]##]##..####!.....55555555.....!###!..!#]#!##]#!#### + ####..##################..#####..#....|..|....#..#####..}#######..########..####{.....6..4.!.6.....#####..############## + ####..]#################..}####..#....|..|....#..#####..########..########..#####.....6!!!!!!6...!.###p.....<########### + ###!..!################!..!####..{....|..|....#..####!..!#######..########..#####.!...6!!!!>.6.....##################### + ####..#### ###!..!####..#....|..|....}..####!..!#######..########..#####.....6...............<######............}#############..############..#### + ####..#### ##############..########..##################..#]#..###..#######!..!######!..!#############..###########!..!### + #### ###!..!######!..!######!..!#######..###..###..#################!..!#########[###..###########!..!### + ###[..6..####{..#######{..######>..............<###############{..#########...##..############..}### + ####..6..#####..########..########..###..#]#..##################..########.....!..############..#### + ###!..!55#####..!##!!##!..!#[!####..###..###..#####[#!##!#######..!##!##..........!##[####!##!..!### + ####......####..............................................####...........#!#..................#### + ####......###!.............................X................###!..........#####.................#### + ###!..!55!####!##]#!!]#!..!##!#]##..###..###..##]####!##!#]#####!##]#!#############.....##!]#!..!### + ####..6..###############..########..###^^###..######################################.4.#######..#### + ####..6..###############..}#######..########..################################################..}### + ###!..!################!..!#######..########..###############################################!..!### + ####..########..########..#######!..!#######..########..#### ###!..!################################# + ###{..########..########..#######[..6..####{..#######{..#### ###[..################################## + ####..########..########..########..6..#####..########..#### ####..################################## + ###!..!#####[#..########..#######!..!55####!..!######!..!### ###!..!#{##[#!##!####################### + ####...<##.......X............####......####...<######..#### ####...................!################ + ##T...####............X.......####......##T...########..#### ####....................!############### + ###!..!#######..###..###..#######!..!55!###!..!######!..!### ###!..!##!###!##!#]##....####vv###vv#### + ####..########..###.X##{.X########..6..#####..########..#### ####..################...!##!..!#!..!### + ####..}#######X.###..###..########..6..#####..}#######..}### ####..]###############!........X|X....o# + ####..########..###..###..#######!..!#######..########..#### ###!..!################........X|X....o# + ###!..!#######..###..###..#######!..!#######..#######!..!### ###!..!### ###........X|X....o# + ###{..########..}##X.###..#######{..#####..|..|..####{..#### ###{..#### ##!........X|X....o# + ####..########.X###..###..########..#####..|..|..#####..#### #####..##### ##...!##!..!#!..!### + ####..!##!####..###..###X.####!##!..#####--+--+--#!##!..#### #!##!..!#[!# ##....####^^###^^#### + ####.............X..................####...|]!|.........#### #..........# #....!############### + ###!...................X............!###...|![|.........!### #..........# #...!################ + ####!##]#!####..########..#]##!##]#!#####--+--+--#!##]#!#### #!]#!..!##!# ##################### + ##############..########..###############..|..|..########### #####..##### #################### + ##############..########..###############..|..|..########### ####..}### #################### + ##############..########..##################..############## ###!..!### #################### + ###!..!######!..!################!..!### ###### + ###[..6..####{..##################..}### + ####..6..#####..##################..#### + ###!..!55#!##!..!#[!#[#!##!###!##!..!### + ####................................#### + ####................................#### + ###!..!55!!]#!..!##!###!##!#]#!]#!..!### + ####..6..#####..##################..#### + ####..6..#####..}#################..}### + ###!..!######!..!################!..!### + ####..#######!..!######!..!######!..!### + ###{..#######{..########..#####..6..}### + ####..########..#######{..#####..6..#### + ###!..!###!##!..!##!!##!55!##!!55!..!### + ####..####.............6XX6.........#### + ####..####.............6XX6.........#### + ###!..!###!##!##!]#!!##!55!##!!55!..!### + ####..##################..}####..6..#### + ####..}#################..#####..6..}### + ####..#################!..!######!..!### + ###!..!### ###### ###!..!### + ###{..#### #vv#..#vv# + #####..#### ##..#..#..## + #!##!..#### #!..!..!..!# + #......#### #..........# + #......!### #..........# + #!##]#!#### #!..!..!..!# + ########### ##........## + ########## ##]#^^#]## + ########## ########## \ No newline at end of file diff --git a/maps/demo/1717736328207.maze b/maps/demo/1717736328207.maze new file mode 100644 index 00000000..1177ca05 --- /dev/null +++ b/maps/demo/1717736328207.maze @@ -0,0 +1,130 @@ + ########## ########## + ####vv#### ###[..#### + #####..#### ####..#### + #![##..#### ###!..!#{## + #......!### ####......# + #......#### ####p.....# + #!##!..#### ###!..!##!# + #####..}### ####..##### + ####..#### ####..]### + ##### ###!..!### ###!..!### ###### + ###!..#######!..!###########################..###########################!..!############# + ##....}######[..#####X.F..F.p##############{..###########################{..############## + ##2...########..#######!..!#################..#################.3.########..######!#[#!### + ##......![####!..!#{####!..!###![###!#######!..!###![###!######....![#!!##!..!#[!!.......o# + #..........####......................!#######..####......!#####2.........................o# + #..........####......................########..####......######..........................o# + ##]!......####!..!##!###!..!###!##!..#######!..!###!##!..#######!..!##!!]#!..!##!!.......o# + ####...!######..#######!..!#######..}#######..########..}#######..########..######!#]#!### + ###{...#######..]####w.F..F.s#####..########..}#######..#######{..########..}############# + ####..!######!..!#######..#######!..!#######..#######!..!######!..!######!..!############# + ###!..!##############vv#..########..########..########..########..########..############## + ###[..6..#####vv#####--#..########..########..########..########..##[##[##..############## + ####..6..#####..#####.....########..########..########..########.....##.....#########..T## + ###!..!55#![##..##[!#..###########..#[######..########..##########...##...#######[##!...## + ####................#..###########..........X....##..................##T................## + ####................#..##........................##...X.........T....##...T...........!### + ###!..!55!!##!..!##!#...#.....#######--#####..X..##.....######....T..##.T.....######..}### + ####..6..#####..#####..##........XX....####{.....##.X...}#####.......!!........!####..#### + ####..6..#####..}###{..##.....##[####..#####.X....X.....#####{.T....T...T...T.1#####..#### + ###### ###!..!######!..!####..##..............########......#########....T...T...T....!####..#### + ###!..!### ####..#######!..!####...#.....#######--########....X.##F..p#!..T..............#####!..!### + ####..}### #..|..|..####[..#####..##.........###..#####...X.....X..#####2....T....T..T...}####{..#### + #####..#### #..|..|..#####..#####..##.....##..###..####{.....##.....}###!........!!....T..######..#### + #!##!..!### ##--+--+--####!..!#{##..##.....##..###--#####..X..##X....######.T..T..##......T##!##!..#### + #......#### #...|]!|...####......#...#.....##....|............##..................##..T............#### + #......#### #...|![|...####......#..#####..##....|............##...X.............T##...............!### + #!]#!..!### ##--+--+--####!..!##!#..#[###..#######..#####..########..##########...##...######!##]#!#### + #####..#### #..|..|..#####..#####.........|<##.....#####..########..########.....##.....############## + ####..}### #..|..|..#####..]####.........|<##.....#####..########..########..##]##]##..############## + ###!..!### ####..#######!..!#######..########..#]######..########..########..########..############## + #############!..!##########################!..########..!################!..!######!..!######!..!######!..!### + ##############..############################............}################{..#######{..########..}######{..#### + #############{..###########################{............##################..########..########..########..##### + ####!##[#!!##!55!##!#[#!##!###![######[!!##!..!......!..!##!#[#!##!###!##!..!##!!##!..!##!!##!..!#######..!##!# + ###!.........6XX6...............................................................................########......# + ####.........6XX6...............................................................................#######!......# + ####..!##!!##!55!##!###!##!#]#!##!..!##!!.....55555555.....!###!##!#]#!##!##!]#!!##!##!]#!!]#!..!#######!##]#!# + ####..########..}#################..####{.....6..4.!.6.....###################################..############### + ###{..########..##################..}####.....6!!!!!!6...!.###################################..}############# + ###!..!######!..!################!..!####.!...6!!!!>.6.....##################################!..!############# + ###### ##############..###############.....6.......w###....#.@..@.#....#####{..########..}##X.###..########..########..}### + #####..##### ####..########..######....#......#....######..########.X###..###..#######{..########..#### + #!##!..!##!# ###!..!#{##[#!..!#####....!##..##!....##!##!..!##!####..###..###X.####!##!55!##!!##!..!### + #..........# ####.......XXXXXXXX......................................X...............6XX6.........#### + #..........# ####.......XXXXXXXX............................................X.........6XX6.........#### + #!##!##!]#!# ###!..!##!###!..!#]#####..##]##]##..####!##!##!]#!####..########..#]##!##!55!##!!]#!..!### + ############ ####..########..########..########..##################..########..########..}#######..#### + ########## ####..]#####p.....<#####..########..##################..########..########..########..}### + ########## ###!..!#################..########..##################..########..#######!..!######!..!### + ###!..!################!..!######!..!######!..!######!..!#################..########..#######!..!### ###### + ###[..#################{..#######{..#######[..6..####[..##################..########..#######[..6..# + ####..##################..########..########..6..#####..#################...########..}#######..6..# + ###!..!#{#![######[!!##!..!##!!##!..#######!..!55####!..!#{#![###!######...#########..#######!..!55## + ####................................########......####............!###{...##########...#######......# + ####................................!#######......####............#####...###########...######......# + ###!..!##!!##!..!##!!##!##!]#!!##]#!#######!..!55!###!..!##!!##!..######...###########...####!..!55!# + ####..########..############################..6..#####..########..}######...##########...#####..6..## + ####..]#######..}###########################..6..#####..]#######..########...#########...#####..6..# + ###!..!######!..!##########################!..!######!..!######!..!########...#######...#####!..!### + ###### ###!..!### ###!..!######!..!######!..!#########...!.3.!...######!..!### + #..6..}### ###[..#####..6..}######{..###########.........########..#### + ##..6..#### ####..#####..6..########..###########........########{..##### + #!55!..!### ###!..!#{#!55!..!###!##!..##########........######!##!55!##!# + #......#### ####............####......#########...!]!...######...6XX6...# + #......#### ####............####......!#######...####...######...6XX6...# + #!55!..!### ###!..!##!!55!..!###!##]#!########..######...#####!##!55!##!# + ##..6..#### ####..#####..6..#################{..#######...}#######..}#### + #..6..}### ####..]####..6..}#################..########..########..#### + ###!..!### ###!..!######!..!#################..########..#######!..!### + ###### ####..#######!..!######!..!######!..!######!..!############# + ###{..#######{..#######{..#######{..#######{..########vv#### + ####..########..########..########..########..########..#### + ###!..!###!##!..!#[!!##!..!##!!##!..!#[!!##!..!#[!![##..#### + ####..####..............................................!### + ####..####..............................................#### + ###!..!###!]#!..!##!!##!##!]#!!]#!..!##!!]#!..!##!!##!..#### + ####..########..##################..########..########..}### + ####..}#######..}#################..}#######..}#######..#### + ####..#######!..!################!..!######!..!######!..!### + #############!..!######!..!#######..########..########..#### ###### + ###########..6..}####vv#..#vv##..|..|..##..|..|..##..|..|..# + ############..6..#####..#..#..##..|..|..##..|..|..##..|..|..# + #![######[!!55!..!###!..!..!..!#--+--+--##--+--+--##--+--+--## + #................####.............|]!|......|]!|......|]!|...# + #................####.............|![|......|![|......|![|...# + #!##!..!##!!55!..!###!..!..!..!#--+--+--##--+--+--##--+--+--## + #####..#####..6..#####........##..|..|..##..|..|..##..|..|..# + ####..}####..6..}#####]#^^#]###..|..|..##..|..|..##..|..|..# + ###!..!######!..!#################..########..########..#### + ###### ####..#### #### #### #### + #..|..|..# + #..|..|..# + ##--+--+--## + #...|]!|...# + #...|![|...# + ##--+--+--## + #..|..|..# + #..|..|..# + ########## \ No newline at end of file diff --git a/maps/demo/1717736331254.maze b/maps/demo/1717736331254.maze new file mode 100644 index 00000000..9a58130f --- /dev/null +++ b/maps/demo/1717736331254.maze @@ -0,0 +1,150 @@ + ########## ########## ########## + ###[..6..# #..6..}### ##........## + ####..6..# ##..6..#### #{..####..}# + ###!..!55## #!55!..!### #!..![[!..!# + ####......# #......#### #....*.....# + ####......# #......#### #..........# + ###!..!55!# #!55!..!### #!..!]]!..!# + ####..6..## ##..6..#### #{..####..}# + ####..6..# #..6..}### ##........## + ###!..!### ###!..!### ###### ###!..!### + ###!..!#################..#### ###!..!#######..#### + ###[..6..##############{..#### ###[..6..####{..#### + ####..6..###############..#### ####..6..#####..#### + ###!..!55#![###!#######!..!### ###!..!55####!..!### + ####............!#######...<## ####......####...<## + ####............######T...#### ####......##T...#### + ###!..!55!!##!..#######!..!### ###!..!55!###!..!### + ####..6..#####..}#######..#### ####..6..#####..#### + ####..6..#####..########..}### ####..6..#####..}### + ###!..!######!..!#######..#### ###### ###!..!#######..#### + ##############..########..#######!..!################!..!#######..########..############## + ##############..########..#######[..#################{..#####vv#...|XXXX|......}########## + ##############..###vv###..########..##################..#####..#...|XXXX|......########### + ####!##[#!####..###..###..#######!..!#{##[#!##!###!##!..!#[!#..![#########..!..#![###!#### + ###!..........................####.................................|XXXX|.............!### + ####..........................####.................................|XXXX|.............#### + ####..!##!####............#######!..!##!###!##!#]#!]#!..!##!#..!..###[####..!..#!##!..#### + ####..########.............<######..##################..#####..#....|..|....#..#####..}### + ###{..######>......]!.....########..]#################..}####..#....|..|....#..#####..#### + ###### ###!..!#######....!##!.....<#####!..!################!..!####..{....|..|....#..####!..!### + ###!..!###############>.....!##!....#######!..!######!..!##############..#....|..|....}..####!..!### + ###[..6..###############.....![......<#####[..#######{..##############{..#....|..|....#..#####..#### + ####..6..#############>.............########..########..###############..#....|..|....#..####{..#### + ###!..!55#![######[!####............#######!..!#{#!##!..!#[!####!##[#!#..!..####]###..!..#!##!55!### + ####....................................####................###!.............|XXXX|..........6XX6..# + ####....................................####................####.............|XXXX|..........6XX6..# + ###!..!55!!##!..!##!####..###..###..#######!..!##!!]#!..!##!####..!##!#..!##[##########..#!##!55!### + ####..6..#####..########..###^^###..########..########..########..#####......|XXXX|...#..#####..}### + ####..6..#####..}#######..########..########..]#######..}######{..#####......|XXXX|...#^^#####..#### + ###!..!######!..!#######..########..#######!..!######!..!######!..!#######..########..#######!..!### + ###### #######################!..!######!..!################!..!#######..#######!..!### ###### + ##############vv########..}######[..##################..}######{..########..}### + ##############..########..########..##################..########..########..#### + ####!##[#!![##..####!##!..!######!..!#{#####!##[#!!##!..!######!..!###!##!..!### + ###!............!###......########......###!............########...<##......#### + ####............####......########......####............######T...####......#### + ####..!##!!##!..####!]#!..!######!..!##!####..!##!!]#!..!######!..!###!]#!..!### + ####..########..}#######..########..########..########..########..########..#### + ###{..########..########..}#######..]######{..########..}#######..}#######..}### + ###!..!######!..!######!..!######!..!######!..!######!..!#######..#######!..!### + ###!..!#######..########..########..########..########..########..############## + ###[..6..#####..########..########..########..#######{..########..}############# + ####..6..#####..########..########..########..########..########..#########..T## + ###!..!55###[#..########..########..##[##[##..########..######[#..#####[##!...## + ####.............X....................................X.......................## + ####..................X.....................................................!### + ###!..!55!####..###..###..######....!######!....####]#..########..########..}### + ####..6..#####..###.X##{.X######....#......#....######..########..########..#### + ####..6..#####X.###..###..######....#.@..@.#....######..########..}#######..#### + ###### ###!..!#######..###..###..######....{......}....#####>............########..#### + ###!..!######!..!#######..###..###..######....#......#....######..##..#############!..!### + ###{..#######{..########..}##X.###..######....#.@..@.#....######..##..#####v########..}### + #####..########..########.X###..###..######....#......#....######..##........########..#### + #!##!..!#[!####..!##!####..###..###X.######....!##..##!....####[#..########..##[#!##!..!### + #..........####.............X..........................................................#### + #..........###!...................X....................................................#### + #!]#!..!##!####!##]#!####..########..#]######..##]##]##..#############]##########!]#!..!### + #####..##################..########..########..########..############################..#### + ####..}#################..########..########..########..############################..}### + ###### ###!..!#################..########..########..########..###########################!..!### + ###!..!#######..#################!..!######!..!#######..########..#######!..!################!..!### + ####..}######{..#################{..#######{..########..########..#####..6..}#######vv########..}### + #####..########..##################..########..########..###vv###..#####..6..########..########..#### + #!##!..!######!..!###![###!########..!##!!##!..!##!#[##..###..###..##[#!55!..!###![##..##[!!##!..!### + #......########..####......!#######..........................................####................#### + #......########..####......#######!..........................................####................#### + #!]#!..!######!..!###!##!..########!##]#!!##!##!]#!####..###..###..####!55!..!###!##!..!##!!]#!..!### + #####..########..########..}###########################..###..###..#####..6..########..########..#### + ####..}#######..}#######..##########################>..............<###..6..}#######..}#######..}### + ###### ###!..!#######..#######!..!###########################..#]#..###..#######!..!######!..!######!..!### +###!..!#######..########..########..########..########..########..###..###..################################## +###[..6..##..|..|..#####..########..########..########..}#####>..............<#############>......w########### +####..6..##..|..|..#####..########..#######{..########..########..###..#]#..##################..############## +###!..!55##--+--+--#####..########..#######!..!######!55!#######..###..###..####![######[!#[#!..!####[#!##!#### +####.........|]!|.......X....##............6..6............................................XXXXXXXX...........# +####.........|![|............##...X........6..6..........................X.................XXXXXXXX...........# +###!..!55!#--+--+--#####..X..##.....#######!..!#####]!55!####]##..###..###..##]#!##!..!##!###!..!#]####!##!#]## +####..6..##..|..|..####{.....##.X...}#######..########..########..###^^###..########..########..############## +####..6..##..|..|..#####.X....X.....########..##p..p##..########..########..########..}#####p.....<########### +###!..!#######..###########......###########..#!....!#..########..########..#######!..!####################### +#############!..!##########....X.##F..p#####..#!....!#..########..#######!..!######!..!############# +#############{..########...X.....X..########..##s..p##..#####..|..|..####{..########..############## +##############..#######{.....##.....}#######..###FF###..#####..|..|..#####..#######{..############## +#[######[!!##!..!#[!####..X..##X....#####[#!55!##FF##!..!####--+--+--#####..!##!!##!55!##!#[#!##!#### +#............................##......................6..6......|]!|...####.........6XX6.............# +#............................##...X..................6..6......|![|...###!.........6XX6.............# +###!..!##!!]#!..!##!####..########..#######!55!##]###!..!####--+--+--#####!##]#!!##!55!##!###!##!#]## +####..########..########..########..########..########..}####..|..|..###############..}############# +####..}#######..}#######..########..#######{..########..#####..|..|..###############..############## +###!..!######!..!#######..########..########..########..########..#################!..!############# + ###### ###!..#######!..!######!..!######!..!######!..!######!..!######!..!######!..!### + ##....}######[..#######{..########..#######{..#######{..########..#######[..6..# + ##2...########..########..#######{..########..########..#######{..########..6..# + ##......![####!..!#{#!##!..!##!!##!55!##!!##!..!##!!##!..!##!!##!55!##!###!..!55## + #..........####...................6XX6..........................6XX6...####......# + #..........####...................6XX6..........................6XX6...####......# + ##]!......####!..!##!!##!##!]#!!##!55!##!!##!##!]#!!##!##!]#!!##!55!##!###!..!55!# + ####...!######..##################..}###########################..}#######..6..## + ###{...#######..]#################..############################..########..6..# + ####..!######!..!################!..!##########################!..!######!..!### + ##### ###!..##################..########..############## ###### ###### + ##....}####>......w#####..########..}############# + ##2...########..#######{..########..############## + ##......![##[#!..!######!..!######!..!###![###!#### + #...........XXXXXXXX...........................!### + #...........XXXXXXXX...........................#### + ##]!......####!..!#]####!..!##XX#]!..!###!##!..#### + ####...!######..########..###XX###..########..}### + ###{...#####p.....<#####..##w..w##..########..#### + ####..!#################..#>....<#..#######!..!### + ##### ###!..!#######..#>....<#..############## + #vv#..#vv#####..##w..w##..############## + ##..#..#..#####..###XX###..#########..T## + #!..!..!..!#[#!55!##XX##!..!####[##!...## + #......................................## + #....................................!### + #!..!..!..!###!55!##]###!..!#######..}### + ##........#####..########..}#######..#### + ##]#^^#]#####{..########..########..#### + ##############..########..########..#### + ###!..!######!..!### #### + ###[..6..####{..#### + ####..6..#####..##### + ###!..!55#!##!..!#[!# + ####................# + ####................# + ###!..!55!!]#!..!##!# + ####..6..#####..##### + ####..6..#####..}### + ###!..!######!..!### + ####..#### ###### + ###!..!### + ##......## + #!......!# + #{......}# + #!......!# + ##......## + ##!....!## + ###oooo### + ########## \ No newline at end of file diff --git a/maps/demo/1717736332102.maze b/maps/demo/1717736332102.maze new file mode 100644 index 00000000..61fa2f2e --- /dev/null +++ b/maps/demo/1717736332102.maze @@ -0,0 +1,140 @@ + #################### + #################### + #################### + ##############[###### + ##vvv!vvv!..........# + ##...!...!..........# + ##...!...!.......#### + #{...............}## + #!..............!### + #*..............|.T# #### ###### ###### #### ###### + #!..............!#################..#######!..!################!..!### ####..#######!..!### + #{...............##############..|..|..#####..###############vv#..#vv# #..|..|..#####..#### + ##...............}#######..T###..|..|..####{..###############..#..#..## #..|..|..####{..##### + ##...!...!.......####[##!...###--+--+--#!##!55!##!####!##[#!!..!..!..!# ##--+--+--#!##!55!##!# + ##...!...!..................##...|]!|......6XX6...###!................# #...|]!|......6XX6...# + ##^^^!^^^!................!###...|![|......6XX6...####................# #...|![|......6XX6...# + ##############]#########..}####--+--+--#!##!55!##!####..!##!!..!..!..!# ##--+--+--#!##!55!##!# + ########################..#####..|..|..#####..}#######..#####........## #..|..|..#####..}#### + ########################..#####..|..|..#####..#######{..######]#^^#]## #..|..|..#####..#### + ########################..########..#######!..!######!..!############# #### ####..#######!..!### + ##############..########..#######!..!######!..!#################..########..########..############## + #>......w#####..########..#######{..########..}##############..|..|..#####..########..}#######vv#### + ####..########..###vv###..########..########..###############..|..|..####{..########..########..##### + ##[#!..!#######..###..###..####!##!..!##!!##!..!###![###!#####--+--+--####!..!######!55!###![##..##[!# + #.XXXXXXXX.....................................####......!###...|]!|......6..6.......................# + #.XXXXXXXX.....................................####......####...|![|......6..6.......................# + ####!..!#]#####............####!##!##!]#!!]#!..!###!##!..#####--+--+--####!..!#####]!55!###!##!..!##!# + ####..########.............<################..########..}####..|..|..#####..########..########..##### + ##p.....<###>......]!.....##################..}#######..#####..|..|..#####..##p..p##..########..}### + ##############....!##!.....<###############!..!######!..!#######..########..#!....!#..#######!..!### + ############>.....!##!....#################!..!######!..##################..#!....!#..#######!..!### + ####vv########.....![......<###############[..6..###....}#################..##s..p##..#####vv#..#vv# + #####..######>.............##################..6..###2...##################..###FF###..#####..#..#..## + #![##..##[!####............####![###!#######!..!55##......![##[#!##!####[#!55!##FF##!..!###!..!..!..!# + #....................................!#######.......................................6..6.............# + #....................................########.......................................6..6.............# + #!##!..!##!####..###..###..####!##!..#######!..!55!#]!......####!##!#]####!55!##]###!..!###!..!..!..!# + #####..########..###^^###..########..}#######..6..#####...!################..########..}####........## + ####..}#######..########..########..########..6..####{...################{..########..######]#^^#]## + ###!..!#######..########..#######!..!######!..!#######..!#################..########..############## + ####..#######!..!################!..!#######..###########################!..!#######..#### + #..|..|..####[..#################{..#######{..########vv#################{..#####..|..|..# + #..|..|..#####..##################..########..########..##################..#####..|..|..# + ##--+--+--####!..!#{#![######[!!##!..!#[!###!..!###![##..##[!#[#!##!###!##!..#####--+--+--## + #...|]!|...####..........................####...<##..........................####...|]!|...# + #...|![|...####..........................##T...####..........................!###...|![|...# + ##--+--+--####!..!##!!##!..!##!!]#!..!##!###!..!###!##!..!##!###!##!#]#!##]#!#####--+--+--## + #..|..|..#####..########..########..########..########..#########################..|..|..# + #..|..|..#####..]#######..}#######..}#######..}#######..}########################..|..|..# + ####..#######!..!######!..!######!..!#######..#######!..!###########################..#### ###### + ###!..!######!..!################!..!#######..########..#####################################!..!### + ###[..6..####[..##################..########..########..#####################################{..#### + ####..6..#####..#################{..########..########..######################################..##### + ###!..!55####!..!#{##[#!##!###!##!55!##!####..##[##[##..#####[#!##!###![######[!#[#!##!###!##!..!#[!# + ####......####...................6XX6...............................................................# + ####......####...................6XX6...............................................................# + ###!..!55!###!..!##!###!##!#]#!##!55!##!##....!######!....#####!##!#]#!##!..!##!###!##!#]#!]#!..!##!# + ####..6..#####..##################..}#####....#......#....################..##################..##### + ####..6..#####..]#################..######....#.@..@.#....################..}#################..}### + ###!..!######!..!################!..!#####....{......}....###############!..!################!..!### ###### + ###!..!#######..########..################....#......#....#####!..!#######..########..#######!..!######!..!### + ###{..########..########..}###############....#.@..@.#....###vv#..#vv#####..########..########..########..}### + ####..#######{..########..################....#......#....###..#..#..#####..########..#######{..########..#### + ####..!##!###!..!######!..!####[#!##!#####....!##..##!....##!..!..!..!##[#..########..####!##!55!##!!##!..!### + ####.........................................................................X...............6XX6.........#### + ###!..............................................................................X..........6XX6.........#### + ####!##]#!###!..!##XX#]!..!######!##!#]#####..##]##]##..####!..!..!..!####..###..###..####!##!55!##!!]#!..!### + ##############..###XX###..##################..########..#####........#####..###.X##{.X########..}#######..#### + ##############..##w..w##..##################..########..######]#^^#]######X.###..###..########..########..}### + ##############..#>....<#..##################..########..##################..###..###..#######!..!######!..!### +###############################[############..#>....<#..#################!..!#######..########..########..###..###..############## ###### +###########>......w#####..............######..##w..w##..#################[..6..#####..########..########..}##X.###..############## +##############..########............X.######..###XX###..##################..6..####...########..}#######.X###..###..############## +####!##[#!#[#!..!####[##..!####]###!..###[#!55!##XX##!..!####[#!##!######!..!55###...#########..########..###..###X.####![###!#### +###!.......XXXXXXXX.......######vv##..................................####......{...##########...###.......X..................!### +####.......XXXXXXXX.......##w.....##..................................####......#...###########...##.............X............#### +####..!##!###!..!#]##.....######..##..#####!55!##]###!..!######!##!#]####!..!55!##...###########...#####..########..#]##!##!..#### +####..########..#####.....!###[#XX#!..######..########..}#################..6..####...##########...#####..########..########..}### +###{..######p.....<##...............X.#####{..########..##################..6..#####...#########...#####..########..########..#### +###!..!##############..X..............######..########..#################!..!########...#######...######..########..#######!..!### + ###### ###########...!######!..##..#####!..!######!..!#######..########..##########...!.3.!...#################..############## + ###########...########..##..###vv#..#vv##vv#..#vv#####..########..###########.........###############..|..|..########### + ###########...#######{..##..###..#..#..##..#..#..#####..########..###########........##########..T###..|..|..############ + ##[#!##!####...########..##..##!..!..!..!!..!..!..!####..########..##########........#######[##!...###--+--+--#![######[!# + #..............########..##............................X....##.........#####...!]!...######........##...|]!|.............# + #..............}#######..#{.................................##...X.....####...####...######......!###...|![|.............# + ####!##!#]##...!######!..##..##!..!..!..!!..!..!..!####..X..##.....########..######...#########..}####--+--+--#!##!..!##!# + ###########..X............X.###........##........####{.....##.X...}######{..#######...}#######..#####..|..|..#####..##### + ###########.................####]#^^#]####]#^^#]######.X....X.....########..########..########..#####..|..|..#####..}### + #######################]#################################......###########..########..########..########..#######!..!### + ### ###!..#####################....X.##F..p#####..########..########..#######!..!######!..!### + ##....}#################...X.....X..#######{..#######{..########..}######{..#######{..#### + ##2...#################{.....##.....}#######..########..########..########..########..##### + ##......![##[#!##!#######..X..##X....#######!..!#######..######[#..####!##!..!##!!##!..!##!# + #.............................##.........####..####....X...................................# + #.............................##...X.....####..####........................................# + ##]!......####!##!#]#####..########..#######!..!#####]#..########..####!##!##!]#!!##!##!]#!# + ####...!################..########..########..########..########..######################### + ###{...#################..########..########..}#######..########..}####################### + ####..!#################..########..########..#######>............######################## + ##### ###!..!######!..!#######..########..##..#############!..!### + ###[..6..####{..#######{..########..##..#####v########..}### + ####..6..#####..########..########..##........########..#### + ###!..!55#!##!..!#[!###!..!#####[#..########..##[#!##!..!### + ####................####...<##..........................#### + ####................##T...####..........................#### + ###!..!55!!]#!..!##!###!..!############]##########!]#!..!### + ####..6..#####..########..############################..#### + ####..6..#####..}#######..}###########################..}### + ###!..!######!..!#######..###########################!..!### + ###!..!######!..########..#### ####..#### + ###{..######....}######{..#### ###{..#### + #####..######2...########..#### ####..#### + #!##!..#####......![####!..!### ###!..!### + #......####..........####...<## ####...<## + #......!###..........##T...#### ##T...#### + #!##]#!#####]!......####!..!### ###!..!### + ###############...!######..#### ####..#### + #############{...#######..}### ####..}### + ##############..!#######..#### ####..#### #### + ##### #### ####..########..#### + ####.....!!.....#### + ####!..........!#### + ######!......!###### + #######{....}####### + #######!....!####### + ########XXXX######## + #######!....!####### + #######{....}####### + #######!----!####### + ########XXXX######## + #######!----!####### + #######{....}####### + #######!....!####### + ########XXXX######## + #######!....!####### + #######{....}####### + #######!....!####### + ########oooo######## + #################### \ No newline at end of file diff --git a/maps/demo/1717736332741.maze b/maps/demo/1717736332741.maze new file mode 100644 index 00000000..8362297f --- /dev/null +++ b/maps/demo/1717736332741.maze @@ -0,0 +1,130 @@ + #################### #################### + ###{..############## ####..}######{..#### + ####..#########..T## #####..########..##### + ###!..!####[##!...## #!##!..!###!##!..!#[!# + ####...<##........## #......####..........# + ##T...####......!### #......####..........# + ###!..!#######..}### #!]#!..!###!]#!..!##!# + ####..########..#### #####..########..##### + ####..}#######..#### ####..}#######..}### + ####..########..#### ###### ###!..!######!..!### ###### + ###!..!######!..!######!..!######!..########..!######!..!### + ###[..6..####[..#######{..########............}######{..#### + ####..6..#####..########..#######{............########..#### + ###!..!55####!..!#{#####..!##!!##!..!......!..!##!!##!..#### + ####......####......####................................#### + ####......####p.....###!................................!### + ###!..!55!###!..!##!####!##]#!!.....55555555.....!!##]#!#### + ####..6..#####..##############{.....6..4.!.6.....########### + ####..6..#####..]##############.....6!!!!!!6...!.########### + ###!..!######!..!##############.!...6!!!!>.6.....########### + ##############..########..###############.....6.......w##vv#..#vv##################### + ##..6..#### ####..########..########..#####..6..#######{..#######{..########..########..#####..#..#..##################### + #!55!..!### ###!..!#{####!..!#{#!##!..!#[!!55!..!###!##!55!##!!##!55!##!!##!..!#[!#[#!..!###!..!..!..!#####[############## + #......#### ####......####......................####...6XX6......6XX6..............XXXXXXXX.....................!vvv!vvv## + #......#### ####......####......................####...6XX6......6XX6..............XXXXXXXX.....................!...!...## + #!55!..!### ###!..!##!###!..!##!!]#!..!##!!55!..!###!##!55!##!!##!55!##!!]#!..!##!###!..!#]#!..!..!..!###.......!...!...## + ##..6..#### ####..########..########..#####..6..########..}#######..}#######..########..#####........###{...............## + #..6..}### ####..]#######..]#######..}####..6..}#######..########..########..}#####p.....<###]#^^#]#####...............}# + ###!..!### ### ###!..!######!..!######!..!######!..!######!..!######!..!######!..!##########################!..............!# + ###!..!########################[############..########..#######!..!#######..########..########..########..############## #T.|..............*# + ###{..##################..............#####{..########..}####........##vv#...|XXXX|......}####..########..############## ###!..............!# + ####..##################............X.######..########..####!.@....@.!#..#...|XXXX|......#####..###vv###..############## ##{...............}# + ####..!##!![######[!#[##..!####]###!..######..######[#..####{........}#..![#########..!..#####..###..###..####![###!#### ####.......!...!...## + ####......................######vv##........X...................!!...........|XXXX|.................................!### #..........!...!...## + ###!......................##w.....##............................!!...........|XXXX|.................................#### #..........!^^^!^^^## + ####!##]#!!##!..!##!#.....######..##..####]#..########..####{........}#..!..###[####..!..#####............####!##!..#### ######]############## + ##############..#####.....!###[#XX#!..######..########..####!.@....@.!#..#....|..|....#..#####.............<######..}### #################### + ##############..}####...............X.######..########..}####........##..#....|..|....#..###>......]!.....########..#### #################### + #############!..!####..X..............#####>............#######!..!####..{....|..|....#..#####....!##!.....<#####!..!### #################### +##############..########..#####...!######!..##..######..##..#############!..!####..#....|..|....}..###>.....!##!....###########################!..!### +##############..########..#####...########..##..######..##..#####v#######[..6..#{..#....|..|....#..#####.....![......<###>......w############..6..}### +##############..###vv###..#####...#######{..##..######..##........########..6..##..#....|..|....#..###>.............########..###############..6..#### +####!##[#!#[##..###..###..##[##...########..##..####[#..########..##[####!..!55##..!..####]###..!..#####............#####[#!..!###![######[!!55!..!### +###!..............................########..##........................####.............|XXXX|............................XXXXXXXX.................#### +####..............................}#######..#{........................####.............|XXXX|............................XXXXXXXX.................#### +####..!##!####..###..###..#####...!######!..##..###########]#############!..!55!#..!##[##########..#####..###..###..#######!..!#]#!##!..!##!!55!..!### +####..########..###..###..#####..X............X.##########################..6..##......|XXXX|...#..#####..###^^###..########..########..#####..6..#### +###{..######>..............<###.................##########################..6..##......|XXXX|...#^^#####..########..######p.....<#####..}####..6..}### +###!..!#######..#]#..###..#################]#############################!..!#######..########..########..########..#################!..!######!..!### +###!..!#######..###..###..#################!..!### #vv#..########..#######!..!######!..!######!..!######!..!############# ###### +###{..######>..............<###############{..#### #--#..########..#######{..########..}######{..#####vv#..#vv########### +####..########..###..#]#..##################..##### #.....########..########..########..########..#####..#..#..############ +####..!##!####..###..###..#####[#!##!###!##!..!##!# #..###########..#[######..!##!!##!..!###!##!..!#[!!..!..!..!![######[!# +####..............................................# #..###########......####............####..............................# +###!...................X..........................# #..##...............###!............####..............................# +####!##]#!#]##..###..###..##]####!##!#]#!##!##!]#!# #...#.....#######--#####!##]#!!]#!..!###!]#!..!##!!..!..!..!!##!..!##!# +##############..###^^###..######################### ##..##........XX....###############..########..#####........#####..##### +##############..########..######################## #{..##.....##[####..###############..}#######..}#####]#^^#]######..}### +##############..########..######################## ##..##..............##############!..!######!..!################!..!### + ###!..!######!..!######!..!### #...#.....#######--##############!..!######!..!############# ###### + ###[..6..####[..#######{..#### #..##.........###..############vv#..#vv#####..############## + ####..6..#####..########..##### #..##.....##..###..############..#..#..####{..############## + ###!..!55####!..!#{#!##!..!#[!# #..##.....##..###--#![###!####!..!..!..!!##!55!##!![###!#### + ####......####................# #...#.....##....|.........!###.............6XX6.........!### + ####......####................# #..#####..##....|.........####.............6XX6.........#### + ###!..!55!###!..!##!!]#!..!##!# #..#[###..#######..#!##!..####!..!..!..!!##!55!##!!##!..#### + ####..6..#####..########..##### #.........|<##.....#####..}####........#####..}#######..}### + ####..6..#####..]#######..}### #.........|<##.....#####..######]#^^#]######..########..#### + ###!..!######!..!######!..!### ####..########..#]#####!..!################!..!######!..!### +#############!..#######!..!### ###### ####..#######!..!######!..!######!..!######!..!### ###### +############....}####..6..}### ###{..#######{..#####vv#..#vv####{..#######{..#### +###!#[#!####2...#####..6..#### ####..########..#####..#..#..#####..########..##### +#o.......!#......![#!55!..!### ###!..!###!##!..####!..!..!..!!##!..!#[!!##!..!##!# +#o........................#### ####..####......####..............................# +#o........................#### ####..####......!###..............................# +#o.......!#]!......#!55!..!### ###!..!###!##]#!####!..!..!..!!]#!..!##!!##!##!]#!# +###!#]#!######...!###..6..#### ####..###############........#####..############### +#############{...####..6..}### ####..}###############]#^^#]######..}############# +##############..!######!..!### ###### ####..###########################!..!############# + ##### ###### ###!..!######!..!### ###### + ###{..########..}### + #####..########..#### + #!##!..!#[!!##!..!### + #................#### + #................#### + #!]#!..!##!!]#!..!### + #####..########..#### + ####..}#######..}### + ###!..!######!..!### + ###### ###!..!### + ###{..#### + #####..##### + #!##!..!#[!# + #..........# + #..........# + #!]#!..!##!# + #####..##### + ####..}### + ########## \ No newline at end of file diff --git a/maps/demo/1717736334625.maze b/maps/demo/1717736334625.maze new file mode 100644 index 00000000..a0c6b297 --- /dev/null +++ b/maps/demo/1717736334625.maze @@ -0,0 +1,160 @@ + #################### + #################### + #################### + ######[############## + #..........!vvv!vvv## + #..........!...!...## + ####.......!...!...## + ##{...............## + ###...............}# + ###!..............!# + #####################T.|..............*# + #######################!..............!# + #######################{...............}# + #![######[!![######[!###.......!...!...## + #..............................!...!...## + #..............................!^^^!^^^## + #!##!..!##!!##!..!##!#####]############## + #####..########..######################## + ####..}#######..}####################### + ###!..!######!..!####################### + ##############..########..#######!..!### + #############{..#######{..#####..6..}### + ###.3.########..########..#####..6..#### + ##....![#!###!..!######!..!###!55!..!### + ##2.......####...<######...<##......#### + ##........##T...######T...####......#### + ###!..!##!###!..!######!..!###!55!..!### + ####..########..########..#####..6..#### + ###{..########..}#######..}####..6..}### + ###!..!#######..########..#######!..!### #### + ##################################..########..########..#######!..!#######..#### + ###############################..|..|..#####..########..}######{..#####..|..|..# + ################################..|..|..####{..########..########..#####..|..|..# + #![######[!![###!####![###!#####--+--+--####!..!######!..!###!##!..!#[!#--+--+--## + #................!###......!###...|]!|....................................|]!|...# + #................####......####...|![|....................................|![|...# + #!##!..!##!!##!..####!##!..#####--+--+--####!..!##XX#]!..!###!]#!..!##!#--+--+--## + #####..########..}#######..}####..|..|..#####..###XX###..########..#####..|..|..# + ####..}#######..########..#####..|..|..#####..##w..w##..########..}####..|..|..# + ###!..!######!..!######!..!#######..########..#>....<#..#######!..!#######..#### ###### + ####..########..#######!..!#################..#>....<#..########..########..#######!..!### + ###{..#######{..#######[..6..###############..##w..w##..#####vv#...|XXXX|......}###{..#### + ####..########..########..6..###############..###XX###..#####..#...|XXXX|......#####..#### + ###!..!######!..!######!..!55#![###!#####[#!55!##XX##!..!####..![#########..!..#!##!..#### + ####...<######..########............!###...........................|XXXX|.............#### + ##T...########..########............####...........................|XXXX|.............!### + ###!..!######!..!######!..!55!!##!..#######!55!##]###!..!####..!..###[####..!..#!##]#!#### + ####..########..########..6..#####..}#######..########..}####..#....|..|....#..########### + ####..}#######..}#######..6..#####..#######{..########..#####..#....|..|....#..########### + ####..########..#######!..!######!..!#######..########..#####..{....|..|....#..########### + ###!..!######!..!#######..########..#######!..!##############..#....|..|....}..####!..!### + ###{..########..########..########..#######{..##############{..#....|..|....#..#####..#### + ####..#######{..########..###vv###..########..###############..#....|..|....#..####{..#### + ####..!##!!##!55!##!####..###..###..####!##!..!##!![######[!#..!..####]###..!..#!##!55!### + ####.........6XX6..................................................|XXXX|..........6XX6..# + ###!.........6XX6..................................................|XXXX|..........6XX6..# + ####!##]#!!##!55!##!####............####!##!##!]#!!##!..!##!#..!##[##########..#!##!55!### + ##############..}#######.............<################..#####......|XXXX|...#..#####..}### + ##############..######>......]!.....##################..}####......|XXXX|...#^^#####..#### + ###### #############!..!#######....!##!.....<###############!..!#######..########..#######!..!### + ###!..!### ####..#######!..!#####>.....!##!....#################!..!######!..!######!..!### ###### + ###{..#### ###{..#######[..########.....![......<###############{..#####..6..}#######..#### + #####..##### ####..########..######>.............##################..#####..6..#######{..##### + #!##!..!#[!# ###!..!######!..!#{#####............#####[#!##!###!##!..!##!!55!..!###!##!55!##!# + #..........# ####...<######....................................................####...6XX6...# + #..........# ##T...########....................................................####...6XX6...# + #!]#!..!##!# ###!..!######!..!##!####..###..###..#######!##!#]#!##!##!]#!!55!..!###!##!55!##!# + #####..##### ####..########..########..###^^###..#########################..6..########..}#### + ####..}### ####..}#######..]#######..########..#########################..6..}#######..#### + ###### ###!..!### ####..#######!..!#######..########..###########################!..!######!..!### + ###!..!######!..#################!..!#######..########..########..########..#######!..!######!..!############# + ###[..######....}#######vv#######{..########..########..#######{..########..}#######..}####..6..}####p......p# + ####..######2...########..########..########..########..########..########..########..#####..6..#######!..!### + ###!..!#{##......![#![##..##[!!##!..!##!####..##[##[##..########..######[#..####!##!..!###!55!..!######!..!#### + ####............................................................X.....................####......####..........# + ####..................................................................................####......####..........# + ###!..!##!#]!......#!##!..!##!!##!##!]#!##....!######!....####]#..########..####!]#!..!###!55!..!######!..!#### + ####..########...!######..################....#......#....######..########..########..#####..6..#######!..!### + ####..]######{...#######..}###############....#.@..@.#....######..########..}#######..}####..6..}####s......p# + ###### ###!..!#######..!######!..!###############....{......}....#####>............#######!..!######!..!#######..#### + ###!..!################!..!##############[##########....#......#....######..##..##############..########..#######!..!### + ###[..#################[..6..#####..............####....#.@..@.#....######..##..#####v########..########..########..}### + ####..##################..6..#####............X.####....#......#....######..##........########..########..########..#### + ###!..!#{#![######[!###!..!55##[##..!####]###!..####....!##..##!....####[#..########..##[###[#..########..####!##!..!### + ####................####............######vv##...................................................X..................#### + ####................####............##w.....##........................................................X.............#### + ###!..!##!!##!..!##!###!..!55!#.....######..##..######..##]##]##..#############]##############..###..###..####!]#!..!### + ####..########..########..6..##.....!###[#XX#!..######..########..############################..###.X##{.X########..#### + ####..]#######..}#######..6..##...............X.######..########..############################X.###..###..########..}### + #### #### ###!..!######!..!######!..!####..X..............######..########..############################..###..###..#######!..!### +####..########..#######!..!######!..########..!####...!######!..##..#####!..!######!..!######!..!######!..!#######..###..###..#######!..!############# +####..########..#####vv#..#vv#####............}####...########..##..###vv#..#vv####{..#######{..#######{..########..}##X.###..#######{..############## +####..########..#####..#..#..####{............#####...#######{..##..###..#..#..#####..########..########..########.X###..###..########..############## +####..########..####!..!..!..!!##!..!......!..!##!#...########..##..##!..!..!..!!##!..!##!!##!..!##!!##!..########..###..###X.####!##!..!##!![######[# +#...X....##...........................................########..##........................................####.......X...............................# +#........##...X.......................................}#######..#{........................................!###.............X.........................# +####..X..##.....####!..!..!..!!.....55555555.....!#...!######!..##..##!..!..!..!!##!##!]#!!##!##!]#!!##]#!########..########..#]##!##!##!]#!!##!..!### +###{.....##.X...}####........#{.....6..4.!.6.....##..X............X.###........###################################..########..##################..#### +####.X....X.....######]#^^#]###.....6!!!!!!6...!.##.................####]#^^#]####################################..########..##################..}### +#######......##################.!...6!!!!>.6.....##############]##################################################..########..#################!..!### +#######....X.##F..p############.....6.......w##.!...6!!!!!!6.....##>......w########### ###[..6..#####..}### +###{.....##.....}#######..#####.....6.!.3..6.....}####..############### ####..6..#####..#### +####..X..##X....#####[#!..!###!.....55555555.....!#[#!..!###![######[!# ###!..!55#!##!..!### +#........##..........XXXXXXXX......................XXXXXXXX...........# ####............#### +#........##...X......XXXXXXXX......................XXXXXXXX...........# ####............#### +####..########..#######!..!#]####!..!......!..!######!..!#]#!##!..!##!# ###!..!55!!]#!..!### +####..########..########..#######{............########..########..##### ####..6..#####..#### +####..########..######p.....<#####............}#####p.....<#####..}### ####..6..#####..}### +####..########..#################!..!######!..!################!..!### ###!..!######!..!### + #### #### ####..########..########..#### ###### ###!..!######!..!### + ###{..########..########..#### ###[..#######{..#### + ####..#######...########..}### ####..########..##### + ###!..!#####...#########..#### ###!..!#{#!##!..!##!# + ####..####{...##########...### ####................# + ####..#####...###########...## ####................# + ###!..!#####...###########...# ###!..!##!!##!##!]#!# + ####..#######...##########...# ####..############### + ####..}#######...#########...# ####..]############# + #### ####..#########...#######...## ###!..!############# + ####..########..##########...!.3.!...### ###### + ####..########..###########.........#### + ####..###vv###..###########........##### + ##[##..###..###..##[#######........###### + #....................#####...!]!...###### + #....................####...####...###### + #####..###..###..########..######...##### + ####..###..###..#######{..#######...}### + ##>..............<######..########..#### + ####..#]#..###..########..########..#### + ####..###..###..########..#######!..!### + ##>..............<#####{..#######{..#### + ####..###..#]#..########..########..##### + #####..###..###..#######!..!###!##!..!#[!# + #....................####..####..........# + #.............X......####..####..........# + ##]##..###..###..##]####!..!###!]#!..!##!# + ####..###^^###..########..########..##### + ####..########..########..}#######..}### + ####..########..########..#######!..!### + ####..########..#### #### ###### + ####.....!!.....#### + ####!..........!#### + ######!......!###### + #######{....}####### + #######!....!####### + ########XXXX######## + #######!....!####### + #######{....}####### + #######!----!####### + ########XXXX######## + #######!----!####### + #######{....}####### + #######!....!####### + ########XXXX######## + #######!....!####### + #######{....}####### + #######!....!####### + ########oooo######## + #################### \ No newline at end of file diff --git a/maps/demo/1717736336652.maze b/maps/demo/1717736336652.maze new file mode 100644 index 00000000..59729294 --- /dev/null +++ b/maps/demo/1717736336652.maze @@ -0,0 +1,120 @@ + ########## ########## + ####vv#### ###[..#### + #####..##### ####..#### + #![##..##[!# ###!..!#{## + #..........# ####......# + #..........# ####......# + #!##!..!##!# ###!..!##!# + #####..##### ####..##### + ####..}### ####..]### + ###### #### ###!..!### ###!..!### ###### + ###!..!#######..#######!..!################!..!######!..!############# #################### + ###[..6..##..|..|..####{..#####>......w####{..#######{..########vv#### #################### + ####..6..##..|..|..#####..########..########..########..########..#### #################### + ###!..!55##--+--+--#!##!..!#[!#[#!..!###!##!..!#[!!##!..####![##..#### ##################### + ####.........|]!|..............XXXXXXXX.................####......!### #...!################ + ####.........|![|..............XXXXXXXX.................!###......#### #....!############### + ###!..!55!#--+--+--#!]#!..!##!###!..!#]#!]#!..!##!!##]#!####!##!..#### ##....####vv###vv#### + ####..6..##..|..|..#####..########..########..##################..}### ##...!##!..!#!..!### + ####..6..##..|..|..#####..}#####p.....<#####..}#################..#### ##!........X|X....o# + ###### ###!..!#######..#######!..!################!..!################!..!### ###........X|X....o# + ########## ###!..!######!..!#################..########..#######!..!################!..#################........X|X....o# + ########## ###{..########..}####p......p####{..#######{..#######[..#####X.F..F.p###....}#######vv######!........X|X....o# + ########### ####..########..#######!..!#######..########..########..#######!..!#####2...########..######...!##!..!#!..!### + #![###!#### ####..!##!!##!..!######!..!######!..!######!..!######!..!#{####!..!####......![#![##..##[!#....####^^###^^#### + #......!### ####............####..........####...<######...<######........................................!############### + #......#### ###!............####..........##T...######T...########.......................................!################ + #!##!..#### ####!##]#!!]#!..!######!..!######!..!######!..!######!..!##!###!..!####]!......#!##!..!##!#################### + #####..}### ##############..#######!..!#######..########..########..#######!..!#######...!######..######################## + ####..#### ##############..}####s......p#####..}#######..}#######..]####w.F..F.s####{...#######..}####################### + ###!..!### ###### #############!..!#######..########..########..#######!..!#######..########..!######!..!####################### + ###!..!######!..!######!..!#######..#######!..!#######..#######!..!######!..########..!################!..!############# + ####..#######{..#######{..#####..|..|..####{..#######{..########..########............}#######vv#######{..############## + ####{..########..########..#####..|..|..#####..########..#######{..#######{............########..########..#########..T## + #!##!55!##!!##!..!#[!!##!..!#[!#--+--+--#!##!..!#[!###!..!###!##!55!##!!##!..!......!..!##!![##..##[!!##!..!##!#[##!...## + #...6XX6..........................|]!|.............####...<##...6XX6...................................................## + #...6XX6..........................|![|.............##T...####...6XX6.................................................!### + #!##!55!##!!]#!..!##!!]#!..!##!#--+--+--#!]#!..!##!###!..!###!##!55!##!!.....55555555.....!!##!..!##!!##!##!]#!####..}### + #####..}#######..########..#####..|..|..#####..########..########..}###{.....6..4.!.6.....#####..##################..#### + ####..########..}#######..}####..|..|..#####..}#######..}#######..#####.....6!!!!!!6...!.#####..}#################..#### + ###### ###!..!######!..!######!..!#######..#######!..!#######..#######!..!####.!...6!!!!>.6.....####!..!#################..#### +###!..!#######..########..#################!..!#################..###############.....6...............<###..6..}#######..}### +###!..!#######..########..########..#####################......###########..#]#..###..#######!..!#######..#### + ###### ###!..!#######..########..#####################....X.##F..p#####..###..###..#######!..!######!..#### + ###{..#######{..#######{..########vv########...X.....X..######>..............<###..6..}#####....}### + #####..########..########..########..#######{.....##.....}#######..###..#]#..#####..6..######2...#### + #!##!..!##!###!..!######!..!###![##..##[!####..X..##X....########..###..###..####!55!..!####......![## + #..........####..########...<##...................##...................................####..........# + #..........####..######T...####...................##...X..................X............####..........# + #!##!##!]#!###!..!######!..!###!##!..!##!####..########..#####]##..###..###..##]#!55!..!####]!......## + ###############..########..########..########..########..########..###^^###..#####..6..########...!## + ##############..}#######..}#######..}#######..########..########..########..#####..6..}######{...### + ##############..########..#######!..!#######..########..########..########..#######!..!#######..!### ###### + #############!..!#######..########..#######!..!######!..!######!..!######!..!######!..!################!..!### + #############[..#######{..########..}####........##..6..}####..6..}#######..#####vv#..#vv#####vv#######{..#### + ##############..########..########..####!.@....@.!#..6..#####..6..#######{..#####..#..#..#####..########..##### + ####!##[#!###!..!#{#####..######[#..####{........}!55!..!###!55!..!###!##!55!##!!..!..!..!![##..##[!!##!..!#[!# + ###!......####..........X...................!!..........####......####...6XX6.................................# + ####......####..............................!!..........####......####...6XX6.................................# + ####..!##!###!..!##!##]#..########..####{........}!55!..!###!55!..!###!##!55!##!!..!..!..!!##!..!##!!]#!..!##!# + ####..########..########..########..####!.@....@.!#..6..#####..6..########..}####........#####..########..##### + ###{..########..]#######..########..}####........##..6..}####..6..}#######..######]#^^#]######..}#######..}### + ###!..!######!..!######>............#######!..!######!..!######!..!######!..!################!..!######!..!### + ###!..!#################..##..#############!..!######!..!#######..########..#######!..!######!..!### ###### + ###[..6..###############..##..#####v#######{..#####vv#..#vv##vv#...|XXXX|......}####..}######{..#### + ####..6..###############..##........########..#####..#..#..##..#...|XXXX|......#####..########..#### + ###!..!55##[#!##!#####[#..########..##[#####..!##!!..!..!..!#..![#########..!..#!##!..!###!##!..#### + ####....................................####.......................|XXXX|.............####......#### + ####....................................###!.......................|XXXX|.............####......!### + ###!..!55!###!##!#]##########]##############!##]#!!..!..!..!#..!..###[####..!..#!]#!..!###!##]#!#### + ####..6..##########################################........##..#....|..|....#..#####..############## + ####..6..###########################################]#^^#]###..#....|..|....#..#####..}############# + ###!..!######################################################..{....|..|....#..####!..!############# +#############!..!############# ###!..!######!..!####..#....|..|....}..####!..!### +#############{..############## ###{..#######{..####{..#....|..|....#..#####..}### +##############..############## #####..########..#####..#....|..|....#..#####..#### +####!##[#!!##!..!##!#[#!##!#### #!##!..!#[!!##!..!##!#..!..####]###..!..#!##!..!### +###!..........................# #...........................|XXXX|.............#### +####..........................# #...........................|XXXX|.............#### +####..!##!!##!##!]#!###!##!#]## #!]#!..!##!!##!##!]#!#..!##[##########..#!]#!..!### +####..######################## #####..###############......|XXXX|...#..#####..#### +###{..######################## ####..}##############......|XXXX|...#^^#####..}### +###!..!####################### ###!..!#################..########..#######!..!### +####..########..#### ###### ###!..!######!..!#######..############## +###{..}######{..}### ###{..########..}####..|..|..#####vv#### +####..########..#### #####..########..#####..|..|..#####..#### +##................## #!##!..!##!!##!..!####--+--+--#![##..#### +##..!.!!!.].!.!.!.## #................####...|]!|.........!### +#{..p.....!...!...<# #................####...|![|.........#### +##..!.![!.!.!.!.!.## #!##!##!]#!!]#!..!####--+--+--#!##!..#### +##....!.......!...## ###############..#####..|..|..#####..}### +##..!.!.!.!.!]!.!.## ##############..}####..|..|..#####..#### +#>....!.s.!.......## #############!..!#######..#######!..!### +##..!.!.!.!.!.!.!.}# ###!..!######!..#### ###### +#{..........!.!.w.## ####..}#####....}### +##!!![!.!!!.[.!.!.## #####..######2...#### +##..*!...!........<# #!##!..!####......![## +##.!!!!.!!!.!.}.!.## #......####..........# +#{..........!...p.## #......####..........# +#################### #!]#!..!####]!......## +#################### #####..########...!## +#################### ####..}######{...### +#################### #################### \ No newline at end of file diff --git a/maps/demo/1717736337459.maze b/maps/demo/1717736337459.maze new file mode 100644 index 00000000..22f86935 --- /dev/null +++ b/maps/demo/1717736337459.maze @@ -0,0 +1,130 @@ + #################### + ####..########..#### + ####..###vv###..#### + ##[##..###..###..##[## + #....................# + #....................# + #####..###..###..##### + ####..###..###..#### + ##>..............<## + ###### ####..#]#..###..#### + ###!..!############# ##################################..###..###..#### + ###[..############## ################################>..............<## + ####..#########..T## ##################################..###..#]#..#### + ###!..!#{##[##!...## ####!##[#!![######[!![###!########..###..###..##### + ####..............## ###!......................!###....................# + ####............!### ####......................####.............X......# + ###!..!##!####..}### ####..!##!!##!..!##!!##!..#####]##..###..###..##]## + ####..########..#### ####..########..########..}#######..###^^###..#### + ####..]#######..#### ###{..########..}#######..########..########..#### + ###!..!#######..#### ###!..!######!..!######!..!#######..########..#### ##### ##### + #################################!..!################!..!######!..!################!..!######!..!######!..########..!####################### + #################################{..###############..6..}######[..6..##############[..########..}#######............}####################### + ##################################..###############..6..########..6..###############..########..#######{............######################## + ##[#!##!####[#!##!###![######[!####..!##!![######[!!55!..!######!..!55#![###!#######!..!#{#!##!..!###!##!..!......!..!##!#####[############## + #..............................####......................########............!#######............####..............................!vvv!vvv## + #..............................###!......................########............########............####..............................!...!...## + ####!##!#]####!##!#]#!##!..!##!####!##]#!!##!..!##!!55!..!######!..!55!!##!..#######!..!##!!]#!..!###!.....55555555.....!###.......!...!...## + ########################..##################..#####..6..########..6..#####..}#######..########..####{.....6..4.!.6.....###{...............## + ########################..}#################..}####..6..}#######..6..#####..########..]#######..}####.....6!!!!!!6...!.####...............}# + ###### #######################!..!################!..!######!..!######!..!######!..!######!..!######!..!####.!...6!!!!>.6.....####!..............!# + ###!..!################!..!#######..########..########..########..#######!..!######!..!#######..###############.....6.....<#..#################!..!######!..!### + ###!..!### ##############..#>....<#..##################..#### ###### + ###{..#### ##############..##w..w##..#################{..#### + ####..##### ##############..###XX###..#########..T######..#### + ####..!##!# ####!##[#!#[#!55!##XX##!..!####[##!...#####!..!### + ####......# ###!..................................######...<## + ###!......# ####................................!#####T...#### + ####!##]#!# ####..!##!###!55!##]###!..!#######..}######!..!### + ########### ####..########..########..}#######..########..#### + ########## ###{..#######{..########..########..########..}### + ########## ###!..!#######..########..########..########..#### + ###!..!######!..!#######..#######!..!####################### + ###[..#######{..#####..|..|..##vv#..#vv###############vv#### + ####..########..#####..|..|..##..#..#..###############..#### + ###!..!#{#####..!##!#--+--+--#!..!..!..!![###!####![##..#### + ####......####.........|]!|...................!###......!### + ####......###!.........|![|...................####......#### + ###!..!##!####!##]#!#--+--+--#!..!..!..!!##!..####!##!..#### + ####..###############..|..|..##........#####..}#######..}### + ####..]##############..|..|..###]#^^#]######..########..#### + ###!..!#################..#################!..!######!..!### + #vv#..########..#######!..!######!..!#######..########..#### + #--#..########..#######{..#######{..#######{..########..}### + #.....########..########..########..########..########..#### + #..###########..#[##!##!..!##!!##!..########..######[#..##### + #..###########......................####....X...............# + #..##...............................!###....................# + #...#.....#######--#!##!##!]#!!##]#!######]#..########..##### + ##..##........XX....#########################..########..#### + #{..##.....##[####..#########################..########..}### + ##..##..............########################>............#### + #...#.....#######--########### ####..##..########## + #..##.........###..#####vv#### ####..##..#####v#### + #..##.....##..###..#####..#### ####..##........#### + #..##.....##..###--#![##..#### ###[#..########..##[## + #...#.....##....|.........!### #....................# + #..#####..##....|.........#### #....................# + #..#[###..#######..#!##!..#### ##########]########### + #.........|<##.....#####..}### #################### + #.........|<##.....#####..#### #################### + ####..########..#]#####!..!### #################### + ####..#######!..!######!..!### + ###{..#######{..########..#### + ####..########..#######{..##### + ###!..!###!##!..####!##!55!##!# + ####...<##......####...6XX6...# + ##T...####......!###...6XX6...# + ###!..!###!##]#!####!##!55!##!# + ####..##################..}#### + ####..}#################..#### + ####..#################!..!### + ###!..!### ###### + ###{..#### + #####..#### + #!##!..#### + #......#### + #......!### + #!##]#!#### + ########### + ########## + ########## \ No newline at end of file diff --git a/maps/demo/1717736338134.maze b/maps/demo/1717736338134.maze new file mode 100644 index 00000000..58349bb9 --- /dev/null +++ b/maps/demo/1717736338134.maze @@ -0,0 +1,180 @@ + #################### + #################### + ###[############]### + ##................## + ##..!.!!!.!.!.!.!.## + ##........!...!...<# + ##..!.!*!.!.}.!.!.## + ##....!...........## + ##..!.!!!.!.!!!.!.## + #>........!.......}# + ##..!.!.{.!.!.!.!.## + ##............!...## + ##..!!!.!.!.!.!.!.## + #{................<# + ##..!.!.!.!.!.!.!.## + ##..........!.....## + ####..########..#### + ###{..}######{..}### + ####..########..#### + ###### ####..########..#### + ###!..!#######..#### #### + #..6..}######{..#### + ##..6..########..#### + #!55!..!######!..!### + #......########..#### + #......########..#### + #!55!..!######!..!### + ##..6..########..#### + #..6..}#######..}### + #### ###!..!#######..#### ###### + ####..########..########..#######!..!### + #..|..|..#####..########..########..}### + #..|..|..#####..###vv###..########..#### + ##--+--+--##[##..###..###..##[#!##!..!### + #...|]!|.............................#### + #...|![|.............................#### + ##--+--+--#####..###..###..####!]#!..!### + #..|..|..#####..###..###..########..#### + #..|..|..###>..............<######..}### + ####..########..#]#..###..#######!..!### + ####..########..###..###..############## + #..|..|..###>..............<############ + #..|..|..#####..###..#]#..############## + ##--+--+--#####..###..###..####![###!#### + #...|]!|.............................!### + #...|![|................X............#### + ##--+--+--##]##..###..###..##]#!##!..#### + #..|..|..#####..###^^###..########..}### + #..|..|..#####..########..########..#### + ###### ####..########..########..#######!..!### #### + ###!..!################!..!######!..!######!..!### ####..#### + ###[..#################[..6..##vv#..#vv#####..}### #..|..|..# + ####..##################..6..##..#..#..#####..#### #..|..|..# + ###!..!#{#####!##[#!###!..!55#!..!..!..!!##!..!### ##--+--+--## + ####......###!......####......................#### #...|]!|...# + ####......####......####......................#### #...|![|...# + ###!..!##!####..!##!###!..!55!!..!..!..!!]#!..!### ##--+--+--## + ####..########..########..6..##........#####..#### #..|..|..# + ####..]######{..########..6..###]#^^#]######..}### #..|..|..# + ###!..!######!..!######!..!################!..!### ####..#### + #############!..!######!..!######!..#################!..!##########################!..!### + ##############..#######{..######....}####p......p####[..##################vv#####..6..}### + #############{..########..######2...#######!..!#######..#########..T######..#####..6..#### + ####!##[#!!##!55!##!!##!..!#[!#......![####!..!######!..!#{##[##!...##![##..##[!!55!..!### + ###!.........6XX6.................................####..............##................#### + ####.........6XX6.................................####............!###................#### + ####..!##!!##!55!##!!]#!..!##!#]!......####!..!######!..!##!####..}###!##!..!##!!55!..!### + ####..########..}#######..########...!#####!..!#######..########..########..#####..6..#### + ###{..########..########..}######{...####s......p#####..]#######..########..}####..6..}### + ###!..!######!..!######!..!#######..!#######..#######!..!#######..#######!..!######!..!### + ###### ###!..!######!..!#######..########..########..########..########..################################## + ###{..#######{..#######{..########..########..########..########..#####>......w###############vv#### + #####..########..########..#######...########..}#######..########..########..##################..#### + #!##!..!##!!##!..!#[!###!..!#####...#########..########..########..#####[#!..!####[#!##!###![##..##[# + #....................####...<##{...##########...###....X....##..........XXXXXXXX....................# + #....................##T...#####...###########...##.........##...X......XXXXXXXX....................# + #!##!##!]#!!]#!..!##!###!..!#####...###########...#####..X..##.....#######!..!#]####!##!#]#!##!..!### + ###############..########..#######...##########...####{.....##.X...}#######..##################..#### + ##############..}#######..}#######...#########...#####.X....X.....######p.....<###############..}### + #############!..!#######..#########...#######...#########......##############################!..!### + ###!..!#######..##########...!.3.!...##########....X.##F..p##############!..#######!..!### + ###[..6..####{..###########.........########...X.....X..#####>......w###....}######{..#### + ####..6..#####..###########........########{.....##.....}#######..######2...########..#### + ###!..!55####!..!#########........##########..X..##X....#####[#!..!####......![#!##!..!### + ####......####...<#######...!]!...######.........##..........XXXXXXXX....................# + ####......##T...########...####...######.........##...X......XXXXXXXX....................# + ###!..!55!###!..!#######..######...#########..########..#######!..!#]##]!......#!##!##!]## + ####..6..#####..#######{..#######...}#######..########..########..########...!############ + ####..6..#####..}#######..########..########..########..######p.....<####{...############# + ###### ###!..!#######..########..########..########..########..##################..!############# + ###!..!### ###### ###!..!#######..########..#######!..!######!..!##########################!..!### + #..6..}### ###[..6..#####..########..#######{..#######{..############################..}### + ##..6..#### ####..6..#####..########..########..########..############################..#### + #!55!..!### ###!..!55#####..##[##[##..####!##!..!##!!##!..!#[!![######[!#[#!##!###!##!..!### + #......#### ####........................................................................#### + #......#### ####........................................................................#### + #!55!..!### ###!..!55!##....!######!....##!##!##!]#!!]#!..!##!!##!..!##!###!##!#]#!]#!..!### + ##..6..#### ####..6..###....#......#....################..########..##################..#### + #..6..}### ####..6..###....#.@..@.#....################..}#######..}#################..}### + ###!..!### ###!..!#####....{......}....###############!..!######!..!################!..!### + ###!..!################!..!#####....#......#....#####!..!######!..!######!..!### ###### + ###[..#################[..######....#.@..@.#....######..}####vv#..#vv#####..}### + ####..##################..######....#......#....######..#####..#..#..#####..#### + ###!..!#{#####!##[#!###!..!#{###....!##..##!....##!##!..!###!..!..!..!!##!..!### + ####......###!......####................................####................#### + ####......####......####................................####................#### + ###!..!##!####..!##!###!..!##!####..##]##]##..####!]#!..!###!..!..!..!!]#!..!### + ####..########..########..########..########..########..#####........#####..#### + ####..]######{..########..]#######..########..########..}#####]#^^#]######..}### + ###!..!######!..!######!..!#######..########..#######!..!################!..!### ###### + #############!..!######!..!######!..!######!..!######!..!######!..!#######..#######!..!######!..!### + #############{..#######{..#####vv#..#vv####[..########..#######{..#####..|..|..##vv#..#vv#####..#### + ###############..########..#####..#..#..#####..#######{..########..#####..|..|..##..#..#..####{..##### + #![######[!!##!..!##!!##!..!#[!!..!..!..!###!..!#{#!##!55!##!!##!..!##!#--+--+--#!..!..!..!!##!55!##!# + #........................................####.........6XX6................|]!|................6XX6...# + #........................................####.........6XX6................|![|................6XX6...# + #!##!..!##!!##!##!]#!!]#!..!##!!..!..!..!###!..!##!!##!55!##!!##!##!]#!#--+--+--#!..!..!..!!##!55!##!# + #####..##################..#####........#####..########..}##############..|..|..##........#####..}#### + ####..}#################..}#####]#^^#]######..]#######..###############..|..|..###]#^^#]######..#### + ###!..!################!..!################!..!######!..!#################..#################!..!### + ###### ###!..!#######..########..#######!..!#######..#######!..!######!..!############# ###### + ###[..6..#####..##[##[##..########..#######{..########..########..############## + ####..6..#####.....##.....#######{..########..#######{..#######{..#########..T## + ###!..!55#######...##...######!##!55!##!###!..!###!##!55!##!!##!55!##!#[##!...## + ####...............##T...........6XX6...####..####...6XX6......6XX6...........## + ####..........T....##...T........6XX6...####..####...6XX6......6XX6.........!### + ###!..!55!##....T..##.T.....##!##!55!##!###!..!###!##!55!##!!##!55!##!####..}### + ####..6..###.......!!........!####..}#######..########..}#######..}#######..#### + ####..6..##{.T....T...T...T.1#####..########..}#######..########..########..#### + #### #### ###!..!#####....T...T...T....!###!..!#######..#######!..!######!..!#######..#### +####..########..#######!..!###!..T..............#####!..!#######..#### ###### ####..#### #### +####..########..#######{..#####2....T....T..T...}####[..#######{..#### #..|..|..# +####..########..########..####!........!!....T..######..########..#### #..|..|..# +##[#..########..####!##!..!#[!##.T..T..##......T#####!..!#{####!..!### ##--+--+--## +#......X...............................##..T......####......####..#### #...|]!|...# +#...........X.........................T##.........####......####..#### #...|![|...# +####..###..###..####!]#!..!##!######...##...#########!..!##!###!..!### ##--+--+--## +####..###.X##{.X########..########.....##.....########..########..#### #..|..|..# +####X.###..###..########..}#######..##]##]##..########..]#######..}### #..|..|..# +####..###..###..#######!..!#######..########..#######!..!#######..#### #### ####..#### +####..###..###..#######!..!#######..########..#######!..!######!..!#######..#### #### +####..}##X.###..#######{..########..########..}######{..#######{..#####..|..|..# +####.X###..###..########..#######{..########..########..########..#####..|..|..# +####..###..###X.####!##!..#######!..!######!55!###!##!..!#[!####..!##!#--+--+--## +#......X..................####...6..6.......................####.........|]!|...# +#............X............!###...6..6.......................###!.........|![|...# +####..########..#]##!##]#!#######!..!#####]!55!###!]#!..!##!####!##]#!#--+--+--## +####..########..##################..########..########..###############..|..|..# +####..########..##################..##p..p##..########..}##############..|..|..# +####..########..##################..#!....!#..#######!..!#################..#### + #### #### ###!..!#######..#!....!#..#######!..!### #### + ###{..########..##s..p##..#####vv#..#vv# + #####..########..###FF###..#####..#..#..## + #!##!..!#[!#[#!55!##FF##!..!###!..!..!..!# + #.......................6..6.............# + #.......................6..6.............# + #!]#!..!##!###!55!##]###!..!###!..!..!..!# + #####..########..########..}####........## + ####..}######{..########..######]#^^#]## + ###!..!#######..########..############## + ###### ###!..!######!..!### + ###{..#######[..6..# + #####..########..6..# + #!##!..!#[!###!..!55## + #..........####......# + #..........####......# + #!]#!..!##!###!..!55!# + #####..########..6..## + ####..}#######..6..# + ###!..!######!..!### + ####..#### ###### + ###!..!### + ##......## + #!......!# + #{......}# + #!......!# + ##......## + ##!....!## + ###oooo### + ########## \ No newline at end of file diff --git a/maps/demo/1717736338869.maze b/maps/demo/1717736338869.maze new file mode 100644 index 00000000..a4e0f3f3 --- /dev/null +++ b/maps/demo/1717736338869.maze @@ -0,0 +1,170 @@ + #################### + #################### + #####v#####v######## + ##w...............## + ##!!!.!!!.!.!.!.!.## + ##.p......!.s.!...## + ##.!!.!.![!.!.[.!.## + ##....!...........## + ##!]!.!.!.!.!!.!!.## + ##..!.....!...*...## + ##..!.!.!.!.!!.!!.## + ##............!...## + ##.!![!.!.!.!.!.!.## + ##................## + ##..!.!.!.!.!.].!.## + ##....!.!...!....p## + ####..#^######..#### + ###{..}######{..}### + ####..########..#### + ####..########..#### + #############!..!### #### + #############[..#### + ##############..#### + ####!##[#!###!..!#{## + ###!......####......# + ####......####......# + ####..!##!###!..!##!# + ####..########..##### + ###{..########..]### + ###### ###!..!######!..!### ###### + ###!..!#######..########..#######!..!### + ###[..########..########..#####..6..}### + ####..########..########..#####..6..#### + ###!..!#{###[#..########..####!55!..!### + ####.............X..................#### + ####..................X.............#### + ###!..!##!####..###..###..####!55!..!### + ####..########..###.X##{.X#####..6..#### + ####..]#######X.###..###..#####..6..}### + ###!..!#######..###..###..#######!..!### + ##############..###..###..#######!..!############# + ##############..}##X.###..########..}############# + ##############.X###..###..########..############### + ##[#!##!#######..###..###X.####!##!..!#######!##[#!# + #.................X.................p#######!......# + #.......................X............########......# + ####!##!#]#####..########..#]##!]#!..!#######..!##!# + ##############..########..########..########..##### + ##############..########..########..}######{..#### + ##############..########..#######!..!######!..!### ###### + ###!..!#######..########..########..#######!..!######!..!### + ###[..#####..|..|..##vv#...|XXXX|......}###{..#######{..#### + ####..#####..|..|..##..#...|XXXX|......#####..########..#### + ###!..!#{##--+--+--##..![#########..!..#!##!..!##!!##!..#### + ####.........|]!|..........|XXXX|.......................#### + ####.........|![|..........|XXXX|.......................!### + ###!..!##!#--+--+--##..!..###[####..!..#!##!##!]#!!##]#!#### + ####..#####..|..|..##..#....|..|....#..##################### + ####..]####..|..|..##..#....|..|....#..##################### + ###### ###!..!#######..#####..{....|..|....#..##################### + ###!..!##########################!..!######!..!####..#....|..|....}..##################### + #vv#..#vv###############vv#######{..#######[..####{..#....|..|....#..##################### + ##..#..#..###############..########..########..#####..#....|..|....#..##################### + #!..!..!..!#[#!##!###![##..##[!!##!..#######!..!#{##..!..####]###..!..##[#!##!####[#!##!#### + #....................................########.............|XXXX|...........................# + #....................................!#######.............|XXXX|...........................# + #!..!..!..!###!##!#]#!##!..!##!!##]#!#######!..!##!#..!##[##########..####!##!#]####!##!#]## + ##........###############..##################..#####......|XXXX|...#..##################### + ##]#^^#]################..}#################..]####......|XXXX|...#^^##################### + #### #######################!..!################!..!#######..########..######################## + ####..########..#### ###!..!################!..!#######..#######!..!#######..#######!..!### + ###{..#######{..#### ###{..###############..6..}######{..#####..6..}######{..########..#### + ####..########..#### ####..###############..6..########..#####..6..########..#######{..##### + ###!..!######!..!### ####..!##!![###!####!55!..!######!..!###!55!..!######!..!###!##!55!##!# + ####..########..#### ####............!###......########...<##......########...<##...6XX6...# + ####..########..#### ###!............####......######T...####......######T...####...6XX6...# + ###!..!######!..!### ####!##]#!!##!..####!55!..!######!..!###!55!..!######!..!###!##!55!##!# + ####..########..#### ##############..}####..6..########..#####..6..########..########..}#### + ####..}#######..}### ##############..#####..6..}#######..}####..6..}#######..}#######..#### + ###### ####..########..#### #############!..!######!..!#######..#######!..!#######..#######!..!### +###!..!######!..!######!..!################!..!#######..########..#######!..!#######..########..#######!..!### +###[..#######{..#######{..#################{..########..########..#######{..########..##[##[##..#####..6..}### +####..########..########..#########..T######..########..########..########..########.....##.....#####..6..#### +###!..!#{#!##!..!#[!!##!..!#[!#[##!...######..!##!####..##[##[##..####!##!..!##!######...##...######!55!..!### +####..................................######.............................................##T..............#### +####................................!######!........................................T....##...T...........#### +###!..!##!!]#!..!##!!]#!..!##!####..}#######!##]#!##....!######!....##!##!##!]#!##....T..##.T.....##!55!..!### +####..########..########..########..################....#......#....##############.......!!........!#..6..#### +####..]#######..}#######..}#######..################....#.@..@.#....#############{.T....T...T...T.1##..6..}### +###!..!######!..!######!..!#######..################....{......}....##############....T...T...T....!###!..!### + ###### ##############..########..#######!..!#####....#......#....############!..T..............###################### + ##############..########..########..######....#.@..@.#....#############2....T....T..T...}###############vv#### + ##############..###vv###..#######{..######....#......#....############!........!!....T..################..#### + ####!##[#!####..###..###..####!##!55!##!##....!##..##!....###[#!##!#####.T..T..##......T##![######[!![##..##[# + ###!.............................6XX6..........................................##..T.........................# + ####.............................6XX6.........................................T##............................# + ####..!##!####............####!##!55!##!####..##]##]##..#######!##!#]#######...##...######!##!..!##!!##!..!### + ####..########.............<######..}#######..########..##################.....##.....########..########..#### + ###{..######>......]!.....########..########..########..##################..##]##]##..########..}#######..}### + ###!..!#######....!##!.....<#####!..!#######..########..##################..########..#######!..!######!..!### + ############>.....!##!....########..#######!..!######!..!######!..!######!..!######!..!######!..!### ###### + ##############.....![......<#####{..########..}######{..########..}######[..#######{..########..#### + ############>.............########..########..########..########..########..########..#######{..##### + ####!##[#!####............#######!..!###!##!..!###!##!..!#[!!##!..!######!..!#{#!##!..####!##!55!##!# + ###!..........................####...<##......####................########............####...6XX6...# + ####..........................##T...####......####................########............!###...6XX6...# + ####..!##!####..###..###..#######!..!###!]#!..!###!]#!..!##!!]#!..!######!..!##!!##]#!####!##!55!##!# + ####..########..###^^###..########..########..########..########..########..##################..}#### + ###{..########..########..########..}#######..}#######..}#######..}#######..]#################..#### + ###### ###!..!#######..########..########..#######!..!######!..!######!..!######!..!################!..!### +###!..!######!..!#######..#######!..!######!..!######!..!######!..!#################..#### ###### +###[..#######{..#######{..#######[..#######{..#######[..#######[..6..##############{..#### +####..########..########..########..########..########..########..6..###############..#### +###!..!#{#!##!..!#[!###!..!######!..!#{#!##!..#######!..!#{####!..!55#![###!#######!..!### +####................####..########............########......####............!#######...<## +####................####..########............!#######p.....####............######T...#### +###!..!##!!]#!..!##!###!..!######!..!##!!##]#!#######!..!##!###!..!55!!##!..#######!..!### +####..########..########..########..##################..########..6..#####..}#######..#### +####..]#######..}#######..}#######..]#################..]#######..6..#####..########..}### +###!..!######!..!#######..#######!..!################!..!######!..!######!..!#######..#### + ###### ####..#######!..########..!######!..!#######..########..########..#######!..!### + ###{..########............}####vv#..#vv####{..########..}####..|..|..#####..}### + ####..#######{............#####..#..#..#####..########..#####..|..|..#####..#### + ###!..!###!##!..!......!..!##!!..!..!..!####..######[#..#####--+--+--#!##!..!### + ####...<##..................................X..................|]!|.........#### + ##T...####.....................................................|![|.........#### + ###!..!###!.....55555555.....!!..!..!..!##]#..########..#####--+--+--#!]#!..!### + ####..####{.....6..4.!.6.....##........#####..########..#####..|..|..#####..#### + ####..}####.....6!!!!!!6...!.###]#^^#]######..########..}####..|..|..#####..}### + ####..#####.!...6!!!!>.6.....##############>............########..#######!..!### + ###########.....6.......w##.!...6!!!!!!6.....#####vv########..##..#####v########..########..############## + ####..#####.....6.!.3..6.....}####..########..##........########..###vv###..############## + ##[#!..!###!.....55555555.....!![##..##[!##[#..########..##[##[##..###..###..##[#![###!#### + #.XXXXXXXX.............................................................................!### + #.XXXXXXXX.............................................................................#### + ####!..!#]####!..!......!..!###!##!..!##!#########]##############..###..###..####!##!..#### + ####..#######{............########..############################..###..###..########..}### + ##p.....<#####............}#######..}#########################>..............<######..#### + #############!..!######!..!######!..!###########################..#]#..###..#######!..!### + ###!..!######!..!######!..!### ###!..!#######..###..###..#### ###### + ###[..6..####{..#######[..#### ###{..######>..............<## + ####..6..#####..########..#### #####..########..###..#]#..#### + ###!..!55#!##!..!#[!###!..!#{## #!##!..!##!####..###..###..##### + ####................####......# #..............................# + ####................####......# #.......................X......# + ###!..!55!!]#!..!##!###!..!##!# #!##!##!]#!#]##..###..###..##]## + ####..6..#####..########..##### ###############..###^^###..#### + ####..6..#####..}#######..]### ##############..########..#### + ###!..!######!..!######!..!### ##############..########..#### + ###### ###### ###### ###!..!######!..!### + ###[..########..}### + ####..########..#### + ###!..!#{#!##!..!### + ####............#### + ####............#### + ###!..!##!!]#!..!### + ####..########..#### + ####..]#######..}### + ###!..!######!..!### + ###### ####..#### + ###!..!### + ##......## + #!......!# + #{......}# + #!......!# + ##......## + ##!....!## + ###oooo### + ########## \ No newline at end of file diff --git a/maps/demo/1717736339610.maze b/maps/demo/1717736339610.maze new file mode 100644 index 00000000..44aced35 --- /dev/null +++ b/maps/demo/1717736339610.maze @@ -0,0 +1,150 @@ + #################### ########## + #################### ########## + #################### ############ + ##############[###### #![######[!# + ##vvv!vvv!..........# #..........# + ##...!...!..........# #..........# + ##...!...!.......#### #!##!..!##!# + #{...............}## #####..##### + #!..............!### ####..}### + #*..............|.T# ###!..!### + #!..............!###########################..#### + #{...............####>......w##############{..#### + ##...............}######..#########..T######..#### + ##...!...!.......####[#!..!####[##!...#####!..!### + ##...!...!...........XXXXXXXX.........######...<## + ##^^^!^^^!...........XXXXXXXX.......!#####T...#### + ##############]########!..!#]#####..}######!..!### + ########################..########..########..#### + ######################p.....<#####..########..}### + ##################################..########..#### + #############!..!#######..########..############## + #############{..########..########..############## + ##############..########..###vv###..############### + ##[#!##!###!##!..!##!#[##..###..###..##[#![######[!# + #..................................................# + #..................................................# + ####!##!#]#!##!##!]#!####..###..###..####!##!..!##!# + ########################..###..###..########..##### + ######################>..............<######..}### + ########################..#]#..###..#######!..!### + #############!..!#######..###..###..#######!..!### + #############{..######>..............<######..}### + ###############..########..###..#]#..########..#### + #![######[!!##!..!#[!####..###..###..####!##!..!### + #..............................................#### + #.................................X............#### + #!##!..!##!!]#!..!##!#]##..###..###..##]#!]#!..!### + #####..########..########..###^^###..########..#### + ####..}#######..}#######..########..########..}### + ###!..!######!..!#######..########..#######!..!### ###### + ###!..!### ###### ####..#######!..!######!..#######!..!### + #..6..}### ###{..#######[..6..###....}####vv#..#vv# + ##..6..#### ####..########..6..###2...#####..#..#..## + #!55!..!### ###!..!######!..!55##......![#!..!..!..!# + #......#### ####..########..........................# + #......#### ####..########..........................# + #!55!..!### ###!..!######!..!55!#]!......#!..!..!..!# + ##..6..#### ####..########..6..#####...!###........## + #..6..}### ####..}#######..6..####{...#####]#^^#]## + ###!..!### ####..#######!..!#######..!################ + ###!..!################!..########..!######!..!##############[################## + ###{..##################............}######{..########..............############ + ####..#################{............########..########............X.############# + ####..!##!#[#!##!###!##!..!......!..!##!!##!..!#[!#[##..!####]###!..##![######[!# + ####....................................................######vv##..............# + ###!....................................................##w.....##..............# + ####!##]#!###!##!#]#!.....55555555.....!!]#!..!##!#.....######..##..##!##!..!##!# + ####################{.....6..4.!.6.....#####..#####.....!###[#XX#!..######..##### + #####################.....6!!!!!!6...!.#####..}####...............X.######..}### + ###### #####################.!...6!!!!>.6.....####!..!####..X..............#####!..!### + ###!..!######!..#######!..!####.....6.......w########### + ####..#######{..########..###########........########{..########..###FF###..########..######!#[#!### + ###!..!###!##!55!##!####..!##!######........######!##!55!##!#[#!55!##FF##!..!####[#!..!###!.......o# + ####...<##...6XX6...####......#####...!]!...######...6XX6................6..6....XXXXXXXX.........o# + ##T...####...6XX6...###!......####...####...######...6XX6................6..6....XXXXXXXX.........o# + ###!..!###!##!55!##!####!##]#!####..######...#####!##!55!##!###!55!##]###!..!######!..!#]#!.......o# + ####..########..}################{..#######...}#######..}#######..########..}#######..######!#]#!### + ####..}#######..##################..########..########..#######{..########..######p.....<########### + ####..#######!..!#################..########..#######!..!#######..########..######################## + ###!..!######!..!################!..!######!..!######!..!################!..!### + ###{..#######[..#################{..#######{..#######{..##################..}### + #####..########..##################..########..########..##################..#### + #!##!..!#[!###!..!#{#![######[!!##!..########..!##!!##!..!##!![######[!!##!..!### + #..........####......................########................................#### + #..........####......................!######!................................#### + #!]#!..!##!###!..!##!!##!..!##!!##]#!########!##]#!!##!##!]#!!##!..!##!!]#!..!### + #####..########..########..######################################..########..#### + ####..}#######..]#######..}#####################################..}#######..}### + ###!..!######!..!######!..!####################################!..!######!..!### + ###### ###!..!######!..!### ###### ###### + ###[..6..####{..#### + ####..6..#####..##### + ###!..!55#!##!..!#[!# + ####................# + ####................# + ###!..!55!!]#!..!##!# + ####..6..#####..##### + ####..6..#####..}### + #################### \ No newline at end of file diff --git a/maps/demo/1717736340299.maze b/maps/demo/1717736340299.maze new file mode 100644 index 00000000..dd158e7a --- /dev/null +++ b/maps/demo/1717736340299.maze @@ -0,0 +1,190 @@ + #################### + #################### + ###[############]### + ##................## + ##..!.!!!.!.!.!.!.## + ##........!...!...<# + ##..!.!*!.!.}.!.!.## + ##....!...........## + ##..!.!!!.!.!!!.!.## + #>........!.......}# + ##..!.!.{.!.!.!.!.## + ##............!...## + ##..!!!.!.!.!.!.!.## + #{................<# + ##..!.!.!.!.!.!.!.## + ##..........!.....## + ####..########..#### + ###{..}######{..}### + ####..########..#### + ####..########..#### + ###!..!### #### + ###[..6..# + ####..6..# + ###!..!55## + ####......# + ####......# + ###!..!55!# + ####..6..## + ####..6..# + ###!..!### ###### + #############!..!################!..!### + #############{..#################{..#### + ##############..##################..##### + ##[#!##!###!##!..!#[!#[#!##!###!##!..!#[!# + #........................................# + #........................................# + ####!##!#]#!]#!..!##!###!##!#]#!]#!..!##!# + ##############..##################..##### + ##############..}#################..}### + #############!..!################!..!### + #############!..!################!..!############# + #############{..########vv#######[..############## + ###.3.########..########..########..############## + ##....![#!!##!..!#[!![##..##[!###!..!#{#![###!#### + ##2...........................####............!### + ##............................####............#### + ###!..!##!!]#!..!##!!##!..!##!###!..!##!!##!..#### + ####..########..########..########..########..}### + ###{..########..}#######..}#######..]#######..#### + ###!..!######!..!######!..!######!..!######!..!### ###### + #############!..!################!..!################!..!### + ##############..##################..##################..}### + #############{..#################{..##################..#### + ####!##[#!!##!55!##!![###!####!##!55!##!![######[!!##!..!### + ###!.........6XX6.........!###...6XX6...................#### + ####.........6XX6.........####...6XX6...................#### + ####..!##!!##!55!##!!##!..####!##!55!##!!##!..!##!!]#!..!### + ####..########..}#######..}#######..}#######..########..#### + ###{..########..########..########..########..}#######..}### + ###!..!######!..!######!..!######!..!######!..!######!..!### + ###!..!######!..!#################..########..#### ###### + ###[..#######{..#################{..#######{..#### + ####..########..##################..########..#### + ###!..!#{#!##!..!#[!![######[!###!..!######!..!### + ####..........................####..########..#### + ####..........................####..########..#### + ###!..!##!!]#!..!##!!##!..!##!###!..!######!..!### + ####..########..########..########..########..#### + ####..]#######..}#######..}#######..}#######..}### + #### ###!..!######!..!######!..!#######..########..#### + ####..###########################!..!#######..########..######################## + ###{..##################vv#######[..########..########..}####################### + ####..##################..########..#######{..########..######################## + ###!..!#######!##[#!![##..##[!###!..!#{####!..!######!..!###![######[!![###!#### + ####..#######!................####..........................................!### + ####..########................####..........................................#### + ###!..!#######..!##!!##!..!##!###!..!##!###!..!##XX#]!..!###!##!..!##!!##!..#### + ####..########..########..########..########..###XX###..########..########..}### + ####..}######{..########..}#######..]#######..##w..w##..########..}#######..#### + #### ####..#######!..!######!..!######!..!#######..#>....<#..#######!..!######!..!### +####..#######!..!######!..!######!..!######!..!#######..#>....<#..#######!..################################## +#..|..|..##vv#..#vv####{..#######{..######......######..##w..w##..######....}################################# +#..|..|..##..#..#..#####..########..####!........!####..###XX###..######2...################################## +#--+--+--#!..!..!..!!##!..!#[!####..!##!{..@..@..}#[#!55!##XX##!..!####......![#![######[!![###!####![###!#### +#..|]!|.......................####..............................................................!###......!### +#..|![|.......................###!..............................................................####......#### +#--+--+--#!..!..!..!!]#!..!##!####!##]#!{..@..@..}###!55!##]###!..!####]!......#!##!..!##!!##!..####!##!..#### +#..|..|..##........#####..##############!........!####..########..}#######...!######..########..}#######..}### +#..|..|..###]#^^#]######..}###############......#####{..########..#######{...#######..}#######..########..#### +####..#################!..!################!..!#######..########..########..!######!..!######!..!######!..!### ###### + #### ###!..!######!..!#######..########..#######!..!###########################..########..#######!..!### + ###[..6..####[..#######{..########..}######{..############################..########..########..#### + ####..6..#####..########..########..########..############################..###vv###..#######{..#### + ###!..!55####!..!#{#####..######[#..####!##!..!##!![######[!#[#!##!#######..###..###..####!##!55!### + ####......####..........X....................................................................6XX6..# + ####......####...............................................................................6XX6..# + ###!..!55!###!..!##!##]#..########..####!##!##!]#!!##!..!##!###!##!#]#####............####!##!55!### + ####..6..#####..########..########..##################..##################.............<######..}### + ####..6..#####..]#######..########..}#################..}###############>......]!.....########..#### + ###!..!######!..!######>............#################!..!#################....!##!.....<#####!..!### + #############!..!#################..##..#######################!..!###############>.....!##!....#######!..!### + ##############..}#################..##..#####v#################{..##################.....![......<#####{..#### + ##############..##################..##........#########..T######..################>.............########..#### + ####!##[#!!##!..!#######!##[#!##[#..########..##[##[##!...##!##!..!#[!#[#!##!#######............####!##!..!#[# + ###!...........p#######!..................................##.................................................# + ####............########................................!###.................................................# + ####..!##!!]#!..!#######..!##!#########]##############..}###!]#!..!##!###!##!#]#####..###..###..####!]#!..!### + ####..########..########..############################..########..##################..###^^###..########..#### + ###{..########..}######{..############################..########..}#################..########..########..}### + ###!..!######!..!######!..!###########################..#######!..!#################..########..#######!..!### +#############!..!#################..########..########..########..########..#######!..!######!..!######!..!### ###### +#############{..##################..########..#####..|..|..#####..##[##[##..########..#######{..########..#### +##############..##################..###vv###..#####..|..|..#####.....##.....#######{..########..#######{..##### +#[######[!!##!..!##!![######[!#[##..###..###..##[##--+--+--#######...##...######!##!55!##!!##!..!##!!##!55!##!# +#....................................................|]!|............##T...........6XX6................6XX6...# +#....................................................|![|.......T....##...T........6XX6................6XX6...# +###!..!##!!##!##!]#!!##!..!##!####..###..###..#####--+--+--###....T..##.T.....##!##!55!##!!##!##!]#!!##!55!##!# +####..##################..########..###..###..#####..|..|..###.......!!........!####..}#################..}#### +####..}#################..}#####>..............<###..|..|..##{.T....T...T...T.1#####..##################..#### +###!..!################!..!#######..#]#..###..########..######....T...T...T....!###!..!################!..!### + ###### ###!..!#################..###..###..#######!..!###!..T..............#####!..!######!..!### ###### + ###[..#####>......w###>..............<###vv#..#vv##2....T....T..T...}#####..#######{..#### + ####..########..########..###..#]#..#####..#..#..#!........!!....T..#####{..########..##### + ###!..!#{##[#!..!#######..###..###..####!..!..!..!##.T..T..##......T##!##!55!##!!##!..!##!# + ####.......XXXXXXXX........................................##..T.........6XX6.............# + ####.......XXXXXXXX..............X........................T##............6XX6.............# + ###!..!##!###!..!#]##]##..###..###..##]#!..!..!..!######...##...######!##!55!##!!##!##!]#!# + ####..########..########..###^^###..#####........#####.....##.....########..}############## + ####..]#####p.....<#####..########..######]#^^#]######..##]##]##..########..############## + ###!..!#################..########..##################..########..#######!..!############# + ###!..!### ###!..!######!..!### ####..#######!..!######!..!### + #vv#..#vv# ###[..########..}### ###{..#######{..########..#### + ##..#..#..## ####..########..#### ####..########..#######{..##### + #!..!..!..!# ###!..!#{#!##!..!### ###!..!#######..!##!!##!55!##!# + #..........# ####............#### ####..########.........6XX6...# + #..........# ####p...........#### ####..#######!.........6XX6...# + #!..!..!..!# ###!..!##!!]#!..!### ###!..!#######!##]#!!##!55!##!# + ##........## ####..########..#### ####..##################..}#### + ##]#^^#]## ####..]#######..}### ####..}#################..#### + ########## ###### ###!..!######!..!### ####..#################!..!### + ###!..!#######..########..##################..#### ###### + #vv#..#vv##vv#...|XXXX|......}###########..|..|..# + ##..#..#..##..#...|XXXX|......############..|..|..# + #!..!..!..!#..![#########..!..##[#!##!####--+--+--## + #.................|XXXX|....................|]!|...# + #.................|XXXX|....................|![|...# + #!..!..!..!#..!..###[####..!..####!##!#]##--+--+--## + ##........##..#....|..|....#..############..|..|..# + ##]#^^#]###..#....|..|....#..############..|..|..# + ###########..{....|..|....#..###############..#### + ###!..!####..#....|..|....}..####!..!### #### + ####..####{..#....|..|....#..####{..#### + ####{..#####..#....|..|....#..#####..##### + #!##!55!##!#..!..####]###..!..#!##!..!##!# + #...6XX6..........|XXXX|.................# + #...6XX6..........|XXXX|.................# + #!##!55!##!#..!##[##########..#!##!##!]#!# + #####..}####......|XXXX|...#..############ + ####..#####......|XXXX|...#^^########### + ###!..!#######..########..############## + ###### ###!..!######!..!### + ###[..#######{..#### + ####..########..##### + ###!..!#{#!##!..!#[!# + ####................# + ####................# + ###!..!##!!]#!..!##!# + ####..########..##### + ####..]#######..}### + ###!..!######!..!### + ####..########..#### + ####.....!!.....#### + ####!..........!#### + ######!......!###### + #######{....}####### + #######!....!####### + ########XXXX######## + #######!....!####### + #######{....}####### + #######!----!####### + ########XXXX######## + #######!----!####### + #######{....}####### + #######!....!####### + ########XXXX######## + #######!....!####### + #######{....}####### + #######!....!####### + ########oooo######## + #################### \ No newline at end of file diff --git a/maps/demo/1717736340991.maze b/maps/demo/1717736340991.maze new file mode 100644 index 00000000..8f14dec1 --- /dev/null +++ b/maps/demo/1717736340991.maze @@ -0,0 +1,170 @@ + ########## + ###oooo### + ##......## + #!......!# + #{......}# + #!......!# + ##......## + ###!..!### + ####..#### + #### ####..#### + ####..#######!..!### + ###{..#######[..6..# + ####..########..6..# + ###!..!######!..!55## + ####...<######......# + ##T...########......# + ###!..!######!..!55!# + ####..########..6..## + ####..}#######..6..# + #### ####..#######!..!### ##### + ####..########..########..#######!..#### + #..|..|..#####..########..######....}### + #..|..|..#####..###vv###..######2...#### + ##--+--+--#####..###..###..#####......![## + #...|]!|.................................# + #...|![|.................................# + ##--+--+--#####............#####]!......## + #..|..|..#####.............<######...!## + #..|..|..###>......]!.....#######{...### + ###### ####..########....!##!.....<######..!### + ###!..!######!..!#####>.....!##!....#######!..!### + ####..}#######..########.....![......<#####{..#### + #####..#######{..######>.............########..##### + #!##!..!###!##!55!##!####............####!##!..!##!# + #......####...6XX6.................................# + #......####...6XX6.................................# + #!]#!..!###!##!55!##!####..###..###..####!##!##!]#!# + #####..########..}#######..###^^###..############### + ####..}#######..########..########..############## + ###!..!######!..!#######..########..############## + #############!..!################!..!######!..!### + #############{..#################[..6..####{..#### + ##############..##################..6..#####..#### + ##[#!##!###!##!..!#[!![######[!###!..!55#!##!..#### + #..............................####............#### + #..............................####............!### + ####!##!#]#!]#!..!##!!##!..!##!###!..!55!!##]#!#### + ##############..########..########..6..########### + ##############..}#######..}#######..6..########### + #############!..!######!..!######!..!############# ###### ### + ###!..!#######..########..########..#################!..!##############[######## + ###[..6..##..|..|..#####..########..}##############..6..}#######..............## + ####..6..##..|..|..####{..########..###############..6..########............X.## + ###!..!55##--+--+--####!..!######!..!###![###!####!55!..!####[##..!####]###!..### + ####.........|]!|.............................!###......####......######vv##....# + ####.........|![|.............................####......####......##w.....##....# + ###!..!55!#--+--+--####!..!##XX#]!..!###!##!..####!55!..!####.....######..##..### + ####..6..##..|..|..#####..###XX###..########..}####..6..#####.....!###[#XX#!..## + ####..6..##..|..|..#####..##w..w##..########..#####..6..}####...............X.## + ###!..!#######..########..#>....<#..#######!..!######!..!####..X..............## + #############!..!######!..!#######..#>....<#..#################!..!####...!######!..##..## + ###########..6..}######{..########..##w..w##..########vv#######[..6..##...########..##..## + ############..6..########..########..###XX###..########..########..6..##...#######{..##..## + #![######[!!55!..!#######..!##!#[#!55!##XX##!..!###![##..#######!..!55##...########..##..### + #................########................................!#######..........########..##....# + #................#######!................................########..........}#######..#{....# + #!##!..!##!!55!..!#######!##]#!###!55!##]###!..!###!##!..#######!..!55!#...!######!..##..### + #####..#####..6..##################..########..}#######..}#######..6..##..X............X.## + ####..}####..6..}################{..########..########..########..6..##.................## + ###!..!######!..!#################..########..#######!..!######!..!################]###### + #######################!..!######!..!#######..########..########..########..#######!..!############# + #####################vv#..#vv####{..########..########..########..########..#######{..############## + ######################..#..#..#####..########..########..########..########..########..#########..T## + #![######[!####!##[#!!..!..!..!!##!..!#[!####..########..######[#..########..####!##!..!#[!#[##!...## + #..........###!..............................X....##................X..............................## + #..........####...................................##...X.................X.......................!### + #!##!..!##!####..!##!!..!..!..!!]#!..!##!####..X..##.....########..###..###..####!]#!..!##!####..}### + #####..########..#####........#####..#######{.....##.X...}#######..###.X##{.X########..########..#### + ####..}######{..######]#^^#]######..}#######.X....X.....########X.###..###..########..}#######..#### + ###### ###!..!######!..!################!..!##########......###########..###..###..#######!..!#######..#### +###!..!######!..!#######..########..#####################....X.##F..p#####..###..###..#######!..!### #### +###[..6..####{..########..########..##################...X.....X..########..}##X.###..#####..6..}### +####..6..#####..########..###vv###..#################{.....##.....}#######.X###..###..#####..6..#### +###!..!55#!##!..!##!#[##..###..###..##[#![######[!####..X..##X....########..###..###X.####!55!..!### +####.......................................................##................X..................#### +####.......................................................##...X..................X............#### +###!..!55!!##!##!]#!####..###..###..####!##!..!##!####..########..########..########..#]##!55!..!### +####..6..###############..###..###..########..########..########..########..########..#####..6..#### +####..6..#############>..............<######..}#######..########..########..########..#####..6..}### +###!..!#################..#]#..###..#######!..!#######..########..########..########..#######!..!### + ###### ###!..!#######..###..###..#######!..!#######..########..########..########..######################## + ###[..6..###>..............<#####{..########..########..########..##[##[##..######################## + ####..6..#####..###..#]#..########..########..########..########.....##.....######################## + ###!..!55#####..###..###..########..!##!####..##[##[##..##########...##...######![######[!![###!#### + ####..........................####...................................##T........................!### + ####...................X......###!..............................T....##...T.....................#### + ###!..!55!#]##..###..###..##]#####!##]#!##....!######!....####....T..##.T.....##!##!..!##!!##!..#### + ####..6..#####..###^^###..################....#......#....####.......!!........!####..########..}### + ####..6..#####..########..################....#.@..@.#....###{.T....T...T...T.1#####..}#######..#### + ###!..!#######..########..################....{......}....####....T...T...T....!###!..!######!..!### + ###### ####..#######!..!######!..!#####....#......#....##!..T..............###############!..!### + ###{..#######[..6..####{..######....#.@..@.#....###2....T....T..T...}##############{..#### + ####..########..6..#####..######....#......#....##!........!!....T..################..#### + ###!..!######!..!55#!##!..!##!##....!##..##!....####.T..T..##......T###[#!##!###!##!..#### + ####...<######.............................................##..T......................#### + ##T...########............................................T##.........................!### + ###!..!######!..!55!!##!##!]#!####..##]##]##..##########...##...#########!##!#]#!##]#!#### + ####..########..6..###############..########..########.....##.....######################## + ####..}#######..6..###############..########..########..##]##]##..######################## + #### ####..#######!..!#################..########..########..########..######################## + ####..########..########..#######!..!######!..!######!..!######!..!######!..!#######..########..############## + #..|..|..####{..########..}######{..#####vv#..#vv####[..6..#####..}####vv#..#vv#####..########..}############# + #..|..|..#####..########..########..#####..#..#..#####..6..#####..#####..#..#..####{..########..#########..T## + ##--+--+--#####..######[#..########..!##!!..!..!..!###!..!55#!##!..!###!..!..!..!###!..!######!55!####[##!...## + #...|]!|.......X...............####................####............####.............6..6.....................## + #...|![|.......................###!................####............####.............6..6...................!### + ##--+--+--###]#..########..########!##]#!!..!..!..!###!..!55!!]#!..!###!..!..!..!###!..!#####]!55!#######..}### + #..|..|..#####..########..###############........#####..6..#####..#####........#####..########..########..#### + #..|..|..#####..########..}###############]#^^#]######..6..#####..}#####]#^^#]######..##p..p##..########..#### + ####..#######>............###########################!..!######!..!#################..#!....!#..########..#### + ###!..!#######..##..#################### ####..#######!..########..!#######..#!....!#..########..#### + #vv#..#vv#####..##..#####v############## ###{..########............}#######..##s..p##..#####..|..|..# + ##..#..#..#####..##........############## ####..#######{............########..###FF###..#####..|..|..# + #!..!..!..!##[#..########..##[#![###!#### ###!..!###!##!..!......!..!##!#[#!55!##FF##!..!####--+--+--# + #....................................!### ####...<##.................................6..6......|]!|..# + #....................................#### ##T...####.................................6..6......|![|..# + #!..!..!..!#########]##########!##!..#### ###!..!###!.....55555555.....!###!55!##]###!..!####--+--+--# + ##........#########################..}### ####..####{.....6..4.!.6.....#####..########..}####..|..|..# + ##]#^^#]##########################..#### ####..}####.....6!!!!!!6...!.####{..########..#####..|..|..# + #################################!..!### ###### ####..#####.!...6!!!!>.6.....#####..########..########..#### + ###### ###!..!######!..!####.....6.........!.......}# + ##..!.!.!!!.!.!.!.## + ##............!.w.## + ##..![!.!]!.!.!.!.## + ##{.....!*........<# + ##..!.!.!.!.!.}.!.## + ##..p.......!.....## + #################### + #################### + #################### + #################### \ No newline at end of file diff --git a/maps/demo/1717736341736.maze b/maps/demo/1717736341736.maze new file mode 100644 index 00000000..baa75c83 --- /dev/null +++ b/maps/demo/1717736341736.maze @@ -0,0 +1,130 @@ + ########## ############################## + ########## ####vv########..########..#### + ###.3.##### #####..########..########..#### + ##....![#!# #![##..######[#..########..##### + ##2.......# #......!###.......X............# + ##........# #......####............X.......# + ###!..!##!# #!##!..########..###..###..##### + ####..##### #####..}#######..###.X##{.X#### + ###{..#### ####..########X.###..###..#### + ###!..!### ###### ###!..!#######..###..###..#### + ########## ####..#######!..!######!..!#######..###..###..#### + ########## ###{..#######{..#######{..########..}##X.###..#### + ############ ####..########..########..########.X###..###..#### + #![######[!# ###!..!#######..!##!!##!..!#[!####..###..###X.##### + #..........# ####...<######.......................X............# + #..........# ##T...#######!.............................X......# + #!##!..!##!# ###!..!#######!##]#!!]#!..!##!####..########..#]### + #####..##### ####..##################..########..########..#### + ####..}### ####..}#################..}#######..########..#### + ###### ###!..!### ###### ####..#################!..!#######..########..#### + ###!..!######!..!######!..!################!..!################!..!######!..!############# + ###{..#######{..#######{..##################..########vv#######[..#####..6..}############# + #####..########..########..#################{..########..########..#####..6..#########..T## + #!##!..!##!!##!..!#[!!##!..!#[!####!##[#!!##!55!##!![##..#######!..!#{#!55!..!####[##!...## + #..............................###!.........6XX6.........!#######............####........## + #..............................####.........6XX6.........########............####......!### + #!##!##!]#!!]#!..!##!!]#!..!##!####..!##!!##!55!##!!##!..#######!..!##!!55!..!#######..}### + ###############..########..########..########..}#######..}#######..#####..6..########..#### + ##############..}#######..}######{..########..########..########..]####..6..}#######..#### + #############!..!######!..!######!..!######!..!######!..!######!..!######!..!#######..#### + ###!..!#################..#################!..!######!..!#######..########..############## + ###{..########vv#######{..#################[..#######{..########..########..############## + ####..########..########..##################..########..########..###vv###..#########..T## + ####..!##!![##..#######!..!#######!##[#!###!..!#{#!##!..!##!####..###..###..#####[##!...## + ####............!#######..#######!......####............................................## + ###!............########..########......####..........................................!### + ####!##]#!!##!..#######!..!#######..!##!###!..!##!!##!##!]#!####............########..}### + ##############..}#######..########..########..##################.............<######..#### + ##############..########..}######{..########..]###############>......]!.....########..#### + #############!..!#######..#######!..!######!..!#################....!##!.....<######..#### ###### + ###!..!#######..########..########..########..#######!..!#####>.....!##!....#######!..!######!..!### + ###{..########..########..########..########..########..}#######.....![......<#####{..########..}### + ####..########..###vv###..########..########..########..######>.............########..########..#### + ####..!##!#[##..###..###..##[#####..##[##[##..####!##!..!#######............####!##!..!#[!!##!..!### + ####....................................................####....................................#### + ###!....................................................####....................................#### + ####!##]#!####..###..###..######....!######!....##!]#!..!#######..###..###..####!]#!..!##!!]#!..!### + ##############..###..###..######....#......#....######..########..###^^###..########..########..#### + ############>..............<####....#.@..@.#....######..}#######..########..########..}#######..}### + ##############..#]#..###..######....{......}....#####!..!#######..########..#######!..!######!..!### ###### + ##############..###..###..######....#......#....###############!..#######!..!#######..########..#######!..!### + ############>..............<####....#.@..@.#....##############....}######{..########..########..}####........# + ##############..###..#]#..######....#......#....##############2...########..#######{..########..####{..####..# + ####!##[#!####..###..###..######....!##..##!....###[#!##!####......![#!##!..!##!###!..!######!55!###!..![[!..# + ###!...............................................................................6..6.................*....# + ####...................X...........................................................6..6......................# + ####..!##!#]##..###..###..##]#####..##]##]##..#######!##!#]##]!......#!##!##!]#!###!..!#####]!55!###!..!]]!..# + ####..########..###^^###..########..########..##################...!################..########..####{..####..# + ###{..########..########..########..########..#################{...#################..##p..p##..#####........# + ###### ### ###!..!#######..########..########..########..##################..!#################..#!....!#..#######!..!### + ###!..!##############[###########!..!######!..!######!..!######!..!####vv#..########..#################!..!#######..#!....!#..#### ###### + ###{..########..............#####{..#######[..6..####{..#######{..#####--#..########..##################..}#######..##s..p##..#### + #####..########............X.######..########..6..#####..########..#####.....########..##################..########..###FF###..#### + #!##!..!#[!#[##..!####]###!..##!##!..#######!..!55#!##!..########..!##!#..###########..#[##![######[!!##!..!####[#!55!##FF##!..!#### + #................######vv##..........########............########......#..###########......................####.............6..6...# + #................##w.....##..........!#######............!######!......#..##...............................####.............6..6...# + #!]#!..!##!#.....######..##..##!##]#!#######!..!55!!##]#!########!##]#!#...#.....#######--#!##!..!##!!]#!..!######!55!##]###!..!#### + #####..#####.....!###[#XX#!..################..6..######################..##........XX....#####..########..########..########..}### + ####..}####...............X.################..6..#####################{..##.....##[####..#####..}#######..}######{..########..#### + #### ###!..!####..X..............###############!..!########################..##..............####!..!######!..!#######..########..#### + ####..########..#####...!######!..##..#####!..!######!..!########################...#.....#######--##############!..!### #### #### + ####..##[##[##..#####...########..##..#####{..#######[..6..######################..##.........###..##p......p#####..}### + ####.....##.....#####...#######{..##..######..########..6..######################..##.....##..###..####!..!#######..#### + #######...##...#######...########..##..##!##!..!#[!###!..!55#![######[!![###!#####..##.....##..###--####!..!###!##!..!### + #.........##T............########..##..............####......................!####...#.....##....|...................#### + #....T....##...T.........}#######..#{..............####......................#####..#####..##....|...................#### + ###....T..##.T.....###...!######!..##..##!]#!..!##!###!..!55!!##!..!##!!##!..#####..#[###..#######..####!..!###!]#!..!### + ##.......!!........!#..X............X.######..########..6..#####..########..}####.........|<##.....####!..!#######..#### + #{.T....T...T...T.1##.................######..}#######..6..#####..}#######..#####.........|<##.....##s......p#####..}### + ###....T...T...T....!#############]#########!..!######!..!######!..!######!..!#######..########..#]######..#######!..!### +####################!..T..............### ### ###### ###!..!######!..!######!..!#######..########..#######!..!######!..!### +#####################2....T....T..T...}# ###{..#######[..#######[..#######{..########..}######{..#######[..#### +####################!........!!....T..## ####..########..########..########..########..########..########..#### +######################.T..T..##......T### ####..!##!###!..!#{####!..!#{#####..######[#..####!##!..#######!..!#{## +################!............##..T......# ####......####......####..........X.....................########......# +###############!............T##.........# ###!......####......####................................!#######......# +####vv###vv#####...#######...##...####### ####!##]#!###!..!##!###!..!##!##]#..########..####!##]#!#######!..!##!# +###!..!#!..!##!...######.....##.....#### ##############..########..########..########..##################..##### +#o....X|X........!######..##]##]##..#### ##############..]#######..]#######..########..}#################..]### +#o....X|X........#######..########..#### ###### #############!..!######!..!######>............#################!..!### +#o....X|X........### #### #### ###!..!################!..########..!#######..##..#############!..!############# +#o....X|X........!## ###[..6..###############............}#######..##..#####v########..############## +###!..!#!..!##!...## ####..6..##############{............########..##........#######{..############## +####^^###^^####....## ###!..!55#![######[!!##!..!......!..!##!##[#..########..##[#!##!55!##!#[#!##!#### +###############!....# ####...........................................................6XX6.............# +################!...# ####...........................................................6XX6.............# +##################### ###!..!55!!##!..!##!!.....55555555.....!#########]##########!##!55!##!###!##!#]## +#################### ####..6..#####..####{.....6..4.!.6.....#########################..}############# +#################### ####..6..#####..}####.....6!!!!!!6...!.#########################..############## +#################### ###!..!######!..!####.!...6!!!!>.6.....########################!..!############# + ###### ###!..!####.....6.......w####{..#######{..#### + ####..#####.....6.!.3..6.....}####..########..########..#### + ####..!##!!.....55555555.....!#[#!..!###!##!..!##!###!..!### + ####...........................XXXXXXXX...........####..#### + ###!...........................XXXXXXXX...........####..#### + ####!##]#!###!..!......!..!######!..!#]#!##!##!]#!###!..!### + #############{............########..##################..#### + ##############............}#####p.....<###############..}### + #############!..!######!..!###########################..#### + ####..#######!..!######!..!######!..!### #### + #..|..|..#####..}######{..#######{..#### + #..|..|..#####..########..########..#### + ##--+--+--#!##!..!#######..!##!!##!..#### + #...|]!|.........########............#### + #...|![|.........#######!............!### + ##--+--+--#!]#!..!#######!##]#!!##]#!#### + #..|..|..#####..######################## + #..|..|..#####..}####################### + ####..#######!..!####################### + #### ###!..!### + ###{..#### + ####..##### + ####..!##!# + ####......# + ###!......# + ####!##]#!# + ########### + ########## + ########## \ No newline at end of file diff --git a/maps/demo/1717736342386.maze b/maps/demo/1717736342386.maze new file mode 100644 index 00000000..f04e25c4 --- /dev/null +++ b/maps/demo/1717736342386.maze @@ -0,0 +1,140 @@ + #################### + #################### + ##################### + ####!##[#!####!##[#!# + ###!......###!......# + ####......####......# + ####..!##!####..!##!# + ####..########..##### + ###{..#######{..#### + ###!..!######!..!### ###### ###### + #############!..########..!######!..!##########################!..!### + ##############............}######{..##################vv#######{..#### + ###.3.#######{............########..##################..########..#### + ##....![#!!##!..!......!..!##!!##!..!##!![######[!![##..##[!!##!..#### + ##2...............................................................#### + ##................................................................!### + ###!..!##!!.....55555555.....!!##!##!]#!!##!..!##!!##!..!##!!##]#!#### + ####..####{.....6..4.!.6.....###############..########..############## + ###{..#####.....6!!!!!!6...!.###############..}#######..}############# + ###!..!####.!...6!!!!>.6.....##############!..!######!..!############# ###### + ###########.....6.......]!.....###############..6..}### + ###### ###!..!######!..!######!..!######!..!#######....!##!.....<###############!..!### + ###!..!#################..########..#######!..!#####>.....!##!....######################## + ###[..6..##X.F..F.p####{..########..}####..6..}#######.....![......<#############X.F..F.p# + ####..6..####!..!#######..########..#####..6..######>.............#################!..!### + ###!..!55####!..!#######..######[#..####!55!..!#######............####![######[!###!..!#### + ####....................X.....................####........................................# + ####..........................................####........................................# + ###!..!55!###!..!#####]#..########..####!55!..!#######..###..###..####!##!..!##!###!..!#### + ####..6..####!..!#######..########..#####..6..########..###^^###..########..#######!..!### + ####..6..##w.F..F.s#####..########..}####..6..}#######..########..########..}####w.F..F.s# + ###!..!#######..#######>............#######!..!#######..########..#######!..!#######..#### + ###### ###!..!#######..##..#######################!..!######!..!#######..########..#### + ###[..6..#####..##..#####v#################[..#####..6..}#######..##[##[##..#### + ####..6..#####..##........#########..T######..#####..6..########.....##.....#### + ###!..!55###[#..########..##[##[##!...#####!..!#{#!55!..!#########...##...####### + ####..................................######............####.........##T........# + ####................................!#######............####....T....##...T.....# + ###!..!55!#########]##############..}######!..!##!!55!..!#####....T..##.T.....### + ####..6..#########################..########..#####..6..######.......!!........!# + ####..6..#########################..########..]####..6..}####{.T....T...T...T.1## + ###!..!###########################..#######!..!######!..!#####....T...T...T....!# + ####..#################!..!#######..########..#######!..!###!..T..............### + ###{..#################[..########..########..#####..6..}####2....T....T..T...}# + ####..##################..########..########..#####..6..####!........!!....T..## + ###!..!#######!##[#!###!..!#{#####..########..####!55!..!#####.T..T..##......T### + ####...<#####!......####..........X....##...............####.........##..T......# + ##T...########......####...............##...X...........####........T##.........# + ###!..!#######..!##!###!..!##!####..X..##.....####!55!..!#########...##...####### + ####..########..########..#######{.....##.X...}####..6..########.....##.....#### + ####..}######{..########..]#######.X....X.....#####..6..}#######..##]##]##..#### + #### ####..#######!..!######!..!##########......##########!..!#######..########..#### + ####..#################!..!######!..!##########....X.##F..p############[###################################### + #..|..|..##############[..#######{..########...X.....X..########..............################################ + #..|..|..######..T######..########..#######{.....##.....}#######............X.################################# + ##--+--+--##[##!...#####!..!#{#!##!..!#[!####..X..##X....#####[##..!####]###!..##![######[!#[#!##!###![######[!# + #...|]!|...........######.........................##...............######vv##..................................# + #...|![|.........!#######.........................##...X...........##w.....##..................................# + ##--+--+--#####..}######!..!##!!]#!..!##!####..########..#####.....######..##..##!##!..!##!###!##!#]#!##!..!##!# + #..|..|..#####..########..########..########..########..#####.....!###[#XX#!..######..##################..##### + #..|..|..#####..########..]#######..}#######..########..#####...............X.######..}#################..}### + ###### ####..########..#######!..!######!..!#######..########..#####..X..............#####!..!################!..!### +###!..!##########################!..!######!..!################!..!######!..!####...!######!..##..######..########..#######!..!### +#........########################{..#######{..#####X.F..F.p###......#####{..#####...########..##..###vv#...|XXXX|......}###{..#### +#..####..}########################..########..#######!..!###!........!####..#####...#######{..##..###..#...|XXXX|......#####..#### +#..![[!..!#[#!##!####[#!##!###!##!..!##!!##!..!##!###!..!###{..@..@..}!##!..!#[!#...########..##..###..![#########..!..#!##!..#### +#...*...............................................................................########..##...........|XXXX|.............#### +#...................................................................................}#######..#{...........|XXXX|.............!### +#..!]]!..!###!##!#]####!##!#]#!##!##!]#!!##!##!]#!###!..!###{..@..@..}!]#!..!##!#...!######!..##..###..!..###[####..!..#!##]#!#### +#..####..}###########################################!..!###!........!####..#####..X............X.###..#....|..|....#..########### +#........##########################################w.F..F.s###......######..}####.................###..#....|..|....#..########### +###!..!###############################################..#######!..!######!..!################]#######..{....|..|....#..########### + ###### #vv#..########..########..#######!..!######!..!##############..#....|..|....}..####!..!####################### + #--#..########..#######{..########..}######{..##############{..#....|..|....#..##vv#..#vv##################### + #.....########..########..########..########..###############..#....|..|....#..##..#..#..#############!#[#!### + #..###########..#[#####!..!###!##!..!###!##!..!##!![######[!#..!..####]###..!..#!..!..!..!![######[!!.......o# + #..###########......####..####......####...........................|XXXX|...................................o# + #..##...............####..####......####...........................|XXXX|...................................o# + #...#.....#######--####!..!###!]#!..!###!##!##!]#!!##!..!##!#..!##[##########..#!..!..!..!!##!..!##!!.......o# + ##..##........XX....#####..########..##################..#####......|XXXX|...#..##........#####..######!#]#!### + #{..##.....##[####..#####..}#######..}#################..}####......|XXXX|...#^^###]#^^#]######..}############# + ##..##..............#####..#######!..!################!..!#######..########..#################!..!############# + #...#.....#######--####!..!################!..!#######..#######!..!#######..#######!..!### ###### + #..##.........###..##vv#..#vv##############{..#######{..#######{..#####..|..|..##..6..}### + #..##.....##..###..##..#..#..###############..########..########..#####..|..|..##..6..#### + #..##.....##..###--#!..!..!..!![######[!!##!..#######!..!#######..!##!#--+--+--#!55!..!### + #...#.....##....|.............................########..########.........|]!|.........#### + #..#####..##....|.............................!#######..#######!.........|![|.........#### + #..#[###..#######..#!..!..!..!!##!..!##!!##]#!#######!..!#######!##]#!#--+--+--#!55!..!### + #.........|<##.....##........#####..##################..###############..|..|..##..6..#### + #.........|<##.....###]#^^#]######..}#################..}##############..|..|..##..6..}### + ####..########..#]###############!..!#################..##################..#######!..!### + ####..#######!..!######!..!#######..#### #### ####..#### ###### + ###{..#####..6..}######{..#######{..#### ###{..#### + ####..#####..6..########..########..#### ####..#### + ###!..!###!55!..!###!##!..!#[!###!..!### ###!..!### + ####..####......####..........####...<## ####...<## + ####..####......####..........##T...#### ##T...#### + ###!..!###!55!..!###!]#!..!##!###!..!### ###!..!### + ####..#####..6..########..########..#### ####..#### + ####..}####..6..}#######..}#######..}### ####..}### + ####..#######!..!######!..!#######..#### ####..#### + #############!..!#######..########..#### #### #### + ###########vv#..#vv#####..########..#### + ############..#..#..#####..########..#### + #![######[!!..!..!..!##[#..########..##### + #...........................X............# + #................................X.......# + #!##!..!##!!..!..!..!####..###..###..##### + #####..#####........#####..###.X##{.X#### + ####..}#####]#^^#]######X.###..###..#### + ###!..!#################..###..###..#### + ###### ###!..!#######..###..###..#### + ###{..########..}##X.###..#### + ####..########.X###..###..#### + ####..!##!####..###..###X.##### + ####.............X............# + ###!...................X......# + ####!##]#!####..########..#]### + ##############..########..#### + ##############..########..#### + ##############..########..#### + ###!..!######!..!### + ###[..########..}### + ####..########..#### + ###!..!#{#!##!..!### + ####............#### + ####............#### + ###!..!##!!]#!..!### + ####..########..#### + ####..]#######..}### + #################### \ No newline at end of file diff --git a/maps/demo/1717736343728.maze b/maps/demo/1717736343728.maze new file mode 100644 index 00000000..5437d2bd --- /dev/null +++ b/maps/demo/1717736343728.maze @@ -0,0 +1,170 @@ + #################### + ########oooo######## + #######!....!####### + ########XXXX######## + #######!....!####### + #######{....}####### + #######!5555!####### + ########XXXX######## + #######!5555!####### + #######{....}####### + #######!....!####### + ########XXXX######## + #######!....!####### + #######{....}####### + #######!....!####### + #######!....!####### + ######!......!###### + #####..........##### + ####.....!!.....#### + ####..########..#### + ###!..#### #### + ##....}### + ##2...#### + ##......![## + #..........# + #..........# + ##]!......## + ####...!## + ###{...### + ####..!### ##### + ####..#################!..#### + ###{..########vv######....}### + ####..########..######2...#### + ###!..!###![##..##[!#......![## + ####..####....................# + ####..####....................# + ###!..!###!##!..!##!#]!......## + ####..########..########...!## + ####..}#######..}######{...### + #### #### ####..#######!..!#######..!### + ####..########..#################!..!######!..!### ##### + ####..########..#################[..########..}### + ####..########..#########..T######..########..#### + #####..########..#####[##!...#####!..!#{#!##!..!### + #....X....##.................######............#### + #.........##...X...........!#######............#### + #####..X..##.....########..}######!..!##!!]#!..!### + ###{.....##.X...}#######..########..########..#### + ####.X....X.....########..########..]#######..}### + #######......###########..#######!..!######!..!### + #######....X.##F..p####!..!#######..########..######################## + ####...X.....X..#######{..########..##[##[##..###############>......w# + ###{.....##.....}#######..########.....##.....##################..#### + #####..X..##X....########..!##!######...##...######![######[!#[#!..!#### + #.........##.........####...............##T...................XXXXXXXX.# + #.........##...X.....###!..........T....##...T................XXXXXXXX.# + #####..########..########!##]#!##....T..##.T.....##!##!..!##!###!..!#]## + ####..########..################.......!!........!####..########..#### + ####..########..###############{.T....T...T...T.1#####..}#####p.....<# + ####..########..################....T...T...T....!###!..!############# + #### ###!..!######!..!###!..T..............#####!..!### + ####..#######{..#####2....T....T..T...}#####..}### + ####{..########..####!........!!....T..######..#### + #!##!55!##!!##!..!##!##.T..T..##......T##!##!..!### + #...6XX6......................##..T............#### + #...6XX6.....................T##...............#### + #!##!55!##!!##!##!]#!######...##...######!]#!..!### + #####..}#################.....##.....########..#### + ####..##################..##]##]##..########..}### + ###!..!#################..########..#######!..!### + #############!..!#######..#######!..!#######..#### ########## + #############{..#####..|..|..#####..}####..|..|..# ####vv#### + ##############..#####..|..|..#####..#####..|..|..# #####..#### + ####!##[#!!##!..!#[!#--+--+--#!##!..!####--+--+--## #![##..#### + ###!...................|]!|.........####...|]!|...# #......!### + ####...................|![|.........####...|![|...# #......#### + ####..!##!!]#!..!##!#--+--+--#!]#!..!####--+--+--## #!##!..#### + ####..########..#####..|..|..#####..#####..|..|..# #####..}### + ###{..########..}####..|..|..#####..}####..|..|..# ####..#### + #### #### ###!..!######!..!#######..#######!..!#######..#### ###!..!### #### #### +####..########..#################!..!#######..########..###########################!..!#################..########..#### +#..|..|..##..|..|..###############..########..########..########vv#####X.F..F.p####{..##################..########..}### +#..|..|..##..|..|..##############{..#######...########..}#######..#######!..!#######..#########..T#####{..########..#### +#--+--+--##--+--+--#![######[!!##!55!##!##...#########..####![##..##[!###!..!###!##!..!##!#[##!...#####!..!######!..!#### +#..|]!|......|]!|................6XX6...{...##########...###......................................##....................# +#..|![|......|![|................6XX6...#...###########...##....................................!###....................# +#--+--+--##--+--+--#!##!..!##!!##!55!##!##...###########...#!##!..!##!###!..!###!##!##!]#!####..}######!..!##XX#]!..!#### +#..|..|..##..|..|..#####..########..}######...##########...#####..#######!..!#################..########..###XX###..#### +#..|..|..##..|..|..#####..}#######..########...#########...#####..}####w.F..F.s###############..########..##w..w##..#### +####..########..#######!..!######!..!########...#######...#####!..!#######..##################..########..#>....<#..#### +###!..!##########################!..!#########...!.3.!...#######..#######!..!######!..!#################..#>....<#..#### +###[..6..########################[..6..########.........#######{..#######[..#####vv#..#vv###############..##w..w##..#### +####..6..#########################..6..########........#########..########..#####..#..#..###############..###XX###..#### +###!..!55##[#!##!###![######[!###!..!55#######........#########!..!######!..!#{#!..!..!..!#[#!##!####[#!55!##XX##!..!#### +####..........................####......#####...!]!...##########..########..............................................# +####..........................####......####...####...##########..########..............................................# +###!..!55!###!##!#]#!##!..!##!###!..!55!####..######...########!..!######!..!##!!..!..!..!###!##!#]####!55!##]###!..!#### +####..6..###############..########..6..####{..#######...}#######..########..#####........###############..########..}### +####..6..###############..}#######..6..#####..########..########..}#######..]#####]#^^#]###############{..########..#### +###!..!################!..!######!..!#######..########..########..#######!..!###########################..########..#### +####..#### #vv#..########..########..########..########..########..#######!..!######!..!######!..!### #### +#..|..|..# #--#..########..########..########..########..########..}######{..#######{..#####..6..}### +#..|..|..# #.....########..########..########..#######{..########..########..########..#####..6..#### +#--+--+--## #..###########..#[######..##[##[##..#######!..!######!55!###!##!..!##!!##!..!##!!55!..!### +#..|]!|...# #..###########.............................6..6.......................................#### +#..|![|...# #..##......................................6..6.......................................#### +#--+--+--## #...#.....#######--###....!######!....#####!..!#####]!55!###!##!##!]#!!##!##!]#!!55!..!### +#..|..|..# ##..##........XX....###....#......#....######..########..#########################..6..#### +#..|..|..# #{..##.....##[####..###....#.@..@.#....######..##p..p##..#########################..6..}### +####..#### ##..##..............###....{......}....######..#!....!#..###########################!..!### + #### #...#.....#######--###....#......#....######..#!....!#..#################!..########..!############# + #..##.........###..###....#.@..@.#....######..##s..p##..##################............}############# + #..##.....##..###..###....#......#....######..###FF###..#################{............############## + #..##.....##..###--###....!##..##!....###[#!55!##FF##!..!###![######[!!##!..!......!..!##!![###!#### + #...#.....##....|....................................6..6.......................................!### + #..#####..##....|....................................6..6.......................................#### + #..#[###..#######..#####..##]##]##..#######!55!##]###!..!###!##!..!##!!.....55555555.....!!##!..#### + #.........|<##.....#####..########..########..########..}#######..####{.....6..4.!.6.....#####..}### + #.........|<##.....#####..########..#######{..########..########..}####.....6!!!!!!6...!.#####..#### + ####..########..#]######..########..########..########..#######!..!####.!...6!!!!>.6.....####!..!### + #############!..!######!..!######!..!#######..########..#######!..!######!..!####.....6.............#######!..!################!..!######!..!################!..!### + ###!..!######!..!######!..!#######..########..##..#############!..!#######..#######!..#######!..!### ###### + ###{..#####vv#..#vv#####..#######{..########..##..#####v#######{..#####..|..|..###....}####..6..}### + ####..#####..#..#..####{..########..########..##........########..#####..|..|..###2...#####..6..#### + ####..!##!!..!..!..!!##!55!##!###!..!#####[#..########..##[#!##!..#####--+--+--##......![#!55!..!### + ####...................6XX6...####...<##..........................####...|]!|...................#### + ###!...................6XX6...##T...####..........................!###...|![|...................#### + ####!##]#!!..!..!..!!##!55!##!###!..!############]##########!##]#!#####--+--+--##]!......#!55!..!### + ###########........#####..}#######..###################################..|..|..#####...!###..6..#### + ############]#^^#]######..########..}##################################..|..|..####{...####..6..}### + #######################!..!#######..######################################..########..!######!..!### + ###!..!######!..!######!..!### #### ###!..!#######..#### + ###[..#######[..6..#####..}### ####..}######{..#### + ####..########..6..#####..#### #####..########..#### + ###!..!#{####!..!55#!##!..!### #!##!..!######!..!### + ####......####............#### #......########..#### + ####......####............#### #......########..#### + ###!..!##!###!..!55!!]#!..!### #!]#!..!######!..!### + ####..########..6..#####..#### #####..########..#### + ####..]#######..6..#####..}### ####..}#######..}### + ###!..!######!..!######!..!### ###!..!#######..#### #### + ####..#######!..!######!..!### ###### ####..########..#### + ###{..#####..6..}######{..#### ###{..}######{..}### + ####..#####..6..########..##### ####..########..#### + ###!..!###!55!..!###!##!..!#[!# ##................## + ####..####......####..........# ##..!.!!!.].!.!.!.## + ####..####......####..........# #{..p.....!...!...<# + ###!..!###!55!..!###!]#!..!##!# ##..!.![!.!.!.!.!.## + ####..#####..6..########..##### ##....!.......!...## + ####..}####..6..}#######..}### ##..!.!.!.!.!]!.!.## + ####..#######!..!######!..!### #>....!.s.!.......## + #### ###!..!### ###### ##..!.!.!.!.!.!.!.}# + ###{..#### #{..........!.!.w.## + #####..#### ##!!![!.!!!.[.!.!.## + #!##!..#### ##..*!...!........<# + #......#### ##.!!!!.!!!.!.}.!.## + #......!### #{..........!...p.## + #!##]#!#### #################### + ########### #################### + ########## #################### + ########## #################### \ No newline at end of file diff --git a/maps/demo/1717736344950.maze b/maps/demo/1717736344950.maze new file mode 100644 index 00000000..7ac7e62e --- /dev/null +++ b/maps/demo/1717736344950.maze @@ -0,0 +1,180 @@ + #################### + #################### + ###[############]### + ##................## + ##..!.!!!.!.!.!.!.## + ##........!...!...<# + ##..!.!*!.!.}.!.!.## + ##....!...........## + ##..!.!!!.!.!!!.!.## + #>........!.......}# + ##..!.!.{.!.!.!.!.## + ##............!...## + ##..!!!.!.!.!.!.!.## + #{................<# + ##..!.!.!.!.!.!.!.## + ##..........!.....## + ####..########..#### + ###{..}######{..}### + ####..########..#### + ####..########..#### + ###!..!#######..#### + ####..}######{..#### + #####..########..#### + #!##!..!######!..!### + #......########...<## + #......######T...#### + #!]#!..!######!..!### + #####..########..#### + ####..}#######..}### + ###!..!#######..#### + #############!..!#######..#### + #############[..6..####{..#### + ##############..6..#####..#### + ####!##[#!###!..!55####!..!### + ###!......####......####...<## + ####......####......##T...#### + ####..!##!###!..!55!###!..!### + ####..########..6..#####..#### + ###{..########..6..#####..}### + #### ###### ###!..!######!..!#######..#### ###### + ####..#######!..!######!..!#######..########..#################!..!### + ###{..#######{..#######{..########..########..###############..6..}### + ####..########..########..########..########..###############..6..#### + ###!..!#######..!##!!##!..!#[!##[#..########..####![######[!!55!..!### + ####..########.......................X............................#### + ####..#######!............................X.......................#### + ###!..!#######!##]#!!]#!..!##!####..###..###..####!##!..!##!!55!..!### + ####..##################..########..###.X##{.X########..#####..6..#### + ####..}#################..}#######X.###..###..########..}####..6..}### + ####..#################!..!#######..###..###..#######!..!######!..!### ###### + ###!..!### ##############..###..###..########..#######!..!######!..!### + ####..}### ##############..}##X.###..#####..|..|..##..6..}####..6..}### + #####..#### ###.3.########.X###..###..#####..|..|..##..6..#####..6..#### + #!##!..!### ##....![#!####..###..###X.#####--+--+--#!55!..!###!55!..!### + #......#### ##2..............X...............|]!|.........####......#### + #......#### ##.....................X.........|![|.........####......#### + #!]#!..!### ###!..!##!####..########..#]###--+--+--#!55!..!###!55!..!### + #####..#### ####..########..########..#####..|..|..##..6..#####..6..#### + ####..}### ###{..########..########..#####..|..|..##..6..}####..6..}### + ###!..!### ###!..!#######..########..########..#######!..!######!..!### + #############!..!################!..!#######..########..#######!..!#######..########..############## + #############[..6..#####vv#######{..########..########..}######{..########..########..############## + ###.3.########..6..#####..########..#######{..########..########..########..###vv###..############### + ##....![#!###!..!55#![##..##[!!##!..!#[!###!..!######!55!###!##!..!#[!####..###..###..####![######[!# + ##2.......####.............................6..6.....................................................# + ##........####.............................6..6.....................................................# + ###!..!##!###!..!55!!##!..!##!!]#!..!##!###!..!#####]!55!###!]#!..!##!####............####!##!..!##!# + ####..########..6..#####..########..########..########..########..########.............<######..##### + ###{..########..6..#####..}#######..}#######..##p..p##..########..}#####>......]!.....########..}### + ###### ###!..!######!..!######!..!######!..!#######..#!....!#..#######!..!#######....!##!.....<#####!..!### +###!..!######!..!#######..########..#######!..!#######..#!....!#..#######!..!#####>.....!##!....############## +###{..#######{..########..##[##[##..#######{..########..##s..p##..#####..6..}#######.....![......<############ +####..########..########.....##.....########..########..###FF###..#####..6..######>.............############## +###!..!#[!!##!..!#[!######...##...######!##!..!#[!#[#!55!##FF##!..!###!55!..!#######............#####[#!##!#### +#............................##T...............................6..6.........####..............................# +#.......................T....##...T............................6..6.........####..............................# +#]#!..!##!!]#!..!##!##....T..##.T.....##!]#!..!##!###!55!##]###!..!###!55!..!#######..###..###..#######!##!#]## +####..########..######.......!!........!####..########..########..}####..6..########..###^^###..############## +####..}#######..}####{.T....T...T...T.1#####..}######{..########..#####..6..}#######..########..############## +###!..!######!..!#####....T...T...T....!###!..!#######..########..#######!..!#######..########..############## +###!..!######!..!###!..T..............#####!..!######!..!######!..!################!..!######!..!### +###{..#######{..#####2....T....T..T...}####[..########..#######{..########vv#######{..#####..6..}### +####..########..####!........!!....T..######..#######{..########..########..########..#####..6..#### +####..!##!!##!..!##!##.T..T..##......T#####!..!#{#!##!55!##!!##!..!##!![##..##[!!##!..!##!!55!..!### +####.........................##..T......####.........6XX6.......................................#### +###!........................T##.........####.........6XX6.......................................#### +####!##]#!!##!##!]#!######...##...#########!..!##!!##!55!##!!##!##!]#!!##!..!##!!##!##!]#!!55!..!### +########################.....##.....########..########..}#################..###############..6..#### +########################..##]##]##..########..]#######..##################..}##############..6..}### +########################..########..#######!..!######!..!################!..!################!..!### +#################################!..!#######..########..#######!..!################!..!### ###### +#>......w########################{..########..########..#######{..########vv########..}### +####..#########..T################..########..########..########..########..########..#### +#[#!..!####[##!...######!##[#!####..!##!####..##[##[##..####!##!..!#[!![##..##[!!##!..!### +#XXXXXXXX.........#####!......####....................................................#### +#XXXXXXXX.......!#######......###!....................................................#### +###!..!#]#####..}#######..!##!####!##]#!##....!######!....##!]#!..!##!!##!..!##!!]#!..!### +####..########..########..################....#......#....######..########..########..#### +##p.....<#####..#######{..################....#.@..@.#....######..}#######..}#######..}### +##############..#######!..!###############....{......}....#####!..!######!..!######!..!### + ###!..!#######..########..######....#......#....#####!..!######!..!######!..!### + ###[..#######{..########..}#####....#.@..@.#....######..#######{..#####..6..}### + ####..########..########..######....#......#....#####{..########..#####..6..#### + ###!..!#{#####..######[#..######....!##..##!....##!##!55!##!!##!..!##!!55!..!### + ####..........X......................................6XX6...................#### + ####.................................................6XX6...................#### + ###!..!##!##]#..########..########..##]##]##..####!##!55!##!!##!##!]#!!55!..!### + ####..########..########..########..########..########..}##############..6..#### + ####..]#######..########..}#######..########..########..###############..6..}### + ###!..!######>............########..########..#######!..!################!..!### ###### + ###!..!#######..##..#############!..!#######..########..########..#######!..!######!..!### + ###[..########..##..#####v#######{..#######{..#####vv#...|XXXX|......}####..}#######..}### + ####..########..##........########..########..#####..#...|XXXX|......#####..########..#### + ###!..!#{###[#..########..##[#!##!..!##!###!..!####..![#########..!..#!##!..!###!##!..!### + ####....................................####..####.......|XXXX|.............####......#### + ####....................................####..####.......|XXXX|.............####......#### + ###!..!##!#########]##########!##!##!]#!###!..!####..!..###[####..!..#!]#!..!###!]#!..!### + ####..######################################..#####..#....|..|....#..#####..########..#### + ####..]#####################################..}####..#....|..|....#..#####..}#######..}### + ###!..!#####################################..#####..{....|..|....#..####!..!######!..!### +#############!..!### ###!..!####..#....|..|....}..#####..########..############## +##############..}### ###[..6..#{..#....|..|....#..#####..########..}############# +##############..#### ####..6..##..#....|..|....#..####{..########..############## +#[######[!!##!..!### ###!..!55##..!..####]###..!..####!..!######!..!###![###!#### +#...............#### ####.............|XXXX|.................................!### +#...............#### ####.............|XXXX|.................................#### +###!..!##!!]#!..!### ###!..!55!#..!##[##########..####!..!##XX#]!..!###!##!..#### +####..########..#### ####..6..##......|XXXX|...#..#####..###XX###..########..}### +####..}#######..}### ####..6..##......|XXXX|...#^^#####..##w..w##..########..#### +###!..!######!..!### #### ###!..!#######..########..########..#>....<#..#######!..!### + ###### #vv#..########..#### ###!..!######!..!#######..########..#>....<#..############## + #--#..########..#### ###{..#######[..#####..|..|..#####..##w..w##..############## + #.....########..#### ####..########..#####..|..|..#####..###XX###..############### + #..###########..#[### ####..!##!###!..!#{##--+--+--##[#!55!##XX##!..!###![######[!# + #..###########......# ####......####.........|]!|.................................# + #..##...............# ###!......####.........|![|.................................# + #...#.....#######--## ####!##]#!###!..!##!#--+--+--####!55!##]###!..!###!##!..!##!# + ##..##........XX....# ##############..#####..|..|..#####..########..}#######..##### + #{..##.....##[####..# ##############..]####..|..|..####{..########..########..}### + ##..##..............# #############!..!#######..########..########..#######!..!### + #...#.....#######--# ###!..!#######..#######!..!#######..########..#### + #..##.........###..# ###{..#######{..########..########..########..#### + #..##.....##..###..# #####..########..#######{..#######...########..}### + #..##.....##..###--## #!##!..!##!###!..!###!##!55!##!##...#########..#### + #...#.....##....|...# #..........####...<##...6XX6...{...##########...### + #..#####..##....|...# #..........##T...####...6XX6...#...###########...## + #..#[###..#######..## #!##!##!]#!###!..!###!##!55!##!##...###########...# + #.........|<##.....# ###############..########..}######...##########...# + #.........|<##.....# ##############..}#######..########...#########...# + ####..########..#]## ##############..#######!..!########...#######...## + #### ###### #### ###### ######...!.3.!...### + #######.........#### + #######........##### + ######........###### + #####...!]!...###### + ####...####...###### + ####..######...##### + ###{..#######...}### + ####..########..#### + ####..########..#### #### + #### ####..########..#### + ####.....!!.....#### + ####!..........!#### + ######!......!###### + #######{....}####### + #######!....!####### + ########XXXX######## + #######!....!####### + #######{....}####### + #######!----!####### + ########XXXX######## + #######!----!####### + #######{....}####### + #######!....!####### + ########XXXX######## + #######!....!####### + #######{....}####### + #######!....!####### + ########oooo######## + #################### \ No newline at end of file diff --git a/maps/demo/1717736345637.maze b/maps/demo/1717736345637.maze new file mode 100644 index 00000000..f227cd2a --- /dev/null +++ b/maps/demo/1717736345637.maze @@ -0,0 +1,120 @@ + ########## + ###[..#### + ####..#### + ###!..!#{## + ####......# + ####p.....# + ###!..!##!# + ####..##### + ####..]### + #### ###### ###!..!### ###### + ####..#######!..!##########################!..!######!..!### ########## + ###{..#######{..###########################{..#######{..#### ####vv#### + ####..########..############################..########..##### #####..#### + ###!..!#######..!##!#[#!##!###![######[!!##!..!#[!!##!..!##!# #![##..#### + ####..########..............................................# #......!### + ####..#######!..............................................# #......#### + ###!..!#######!##]#!###!##!#]#!##!..!##!!]#!..!##!!##!##!]#!# #!##!..#### + ####..############################..########..############### #####..}### + ####..}###########################..}#######..}############# ####..#### + ####..###########################!..!######!..!############# ###!..!### + #############!..!###########################..#######!..!######!..!#################..#### + #############{..#########################..|..|..#####..}######[..6..##############{..#### + ###.3.########..#########################..|..|..#####..########..6..###############..#### + ##....![#!!##!..!#[!####!##[#!#[#!##!####--+--+--#!##!..!######!..!55#![######[!###!..!### + ##2.................###!...................|]!|.........########................####..#### + ##..................####...................|![|.........########................####..#### + ###!..!##!!]#!..!##!####..!##!###!##!#]##--+--+--#!]#!..!######!..!55!!##!..!##!###!..!### + ####..########..########..###############..|..|..#####..########..6..#####..########..#### + ###{..########..}######{..###############..|..|..#####..}#######..6..#####..}#######..}### + ###!..!######!..!######!..!#################..#######!..!######!..!######!..!#######..#### ###### ###### + #######################!..########..!######!..!################!..!##########################!..!######!..!######!..!######!..!### + ##############vv########............}######{..#####X.F..F.p####[..6..###############vv#####..6..}####..6..}#######..}#######..#### + ##############..#######{............########..#######!..!#######..6..###############..#####..6..#####..6..########..#######{..##### + ##[#!##!###![##..##[!!##!..!......!..!##!!##!..!#[!###!..!######!..!55#![######[!![##..##[!!55!..!###!55!..!###!##!..!###!##!55!##!# + #............................................................####................................####......####......####...6XX6...# + #............................................................####................................####......####......####...6XX6...# + ####!##!#]#!##!..!##!!.....55555555.....!!]#!..!##!###!..!######!..!55!!##!..!##!!##!..!##!!55!..!###!55!..!###!]#!..!###!##!55!##!# + ##############..####{.....6..4.!.6.....#####..#######!..!#######..6..#####..########..#####..6..#####..6..########..########..}#### + ##############..}####.....6!!!!!!6...!.#####..}####w.F..F.s#####..6..#####..}#######..}####..6..}####..6..}#######..}#######..#### + #############!..!####.!...6!!!!>.6.....####!..!#######..#######!..!######!..!######!..!######!..!######!..!######!..!######!..!### ###### + ###!..!####.....6.......]!.....########..}#####!........X|X....o# + ###!..!######!..!######!..!######!..!#####....{......}....####....T...T...T....!####..###..###..########....!##!.....<#####!..!######........X|X....o# + ###!..!######!..!######!..!######!..!#####....#......#....##!..T..............######..###..###..######>.....!##!....#################........X|X....o# + ###[..#######[..#######[..#######{..######....#.@..@.#....###2....T....T..T...}#####..}##X.###..########.....![......<##############!........X|X....o# + ####..########..########..########..######....#......#....##!........!!....T..######.X###..###..######>.............################...!##!..!#!..!### + ###!..!#{####!..!#{####!..!#{#!##!..!#[!##....!##..##!....####.T..T..##......T######..###..###X.########............####![###!#####....####^^###^^#### + ####......####......####.............................................##..T.............X......................................!###....!############### + ####......####......####............................................T##......................X................................####...!################ + ###!..!##!###!..!##!###!..!##!!]#!..!##!####..##]##]##..##########...##...##########..########..#]######..###..###..####!##!..######################## + ####..########..########..########..########..########..########.....##.....########..########..########..###^^###..########..}####################### + ####..]#######..]#######..]#######..}#######..########..########..##]##]##..########..########..########..########..########..######################## + ###### ###!..!######!..!######!..!######!..!#######..########..########..########..########..########..########..########..#######!..!####################### + ###!..!################!..!#######..########..#######!..!######!..!######!..!#######..#######!..!######!..!######!..#######!..!### ###### + ###{..#################{..#######{..########..}######{..#######{..#######{..#######{..#######[..#######{..######....}#######..#### + ####..#########..T######..########..########..########..########..########..########..########..########..######2...#######{..##### + ####..!##!#[##!...######..!##!####..######[#..####!##!..!##!!##!..!##!!##!..!##!###!..!######!..!#{#!##!..!##!#......![#!##!55!##!# + ####..............######..........X.............................................####...<######.............................6XX6...# + ###!............!######!........................................................##T...########.............................6XX6...# + ####!##]#!####..}#######!##]#!##]#..########..####!##!##!]#!!##!##!]#!!##!##!]#!###!..!######!..!##!!##!##!]#!#]!......#!##!55!##!# + ##############..##################..########..######################################..########..##################...!######..}#### + ##############..##################..########..}#####################################..}#######..]################{...#######..#### + ###### ##############..#################>............######################################..#######!..!#################..!######!..!### +###!..!#################..########..########..##..#############!..!### ###!..!#######..########..#######!..!### ###### +###[..6..##>......w#####..########..}#######..##..#####v########..}### ###[..6..#####..########..#######{..#### +####..6..#####..#######{..########..########..##........########..#### ####..6..#####..###vv###..########..##### +###!..!55##[#!..!######!..!######!55!#####[#..########..##[#!##!..!### ###!..!55##[##..###..###..##[#!##!..!##!# +####.......XXXXXXXX....6..6.......................................#### ####....................................# +####.......XXXXXXXX....6..6.......................................#### ####....................................# +###!..!55!###!..!#]####!..!#####]!55!############]##########!]#!..!### ###!..!55!####..###..###..####!##!##!]#!# +####..6..#####..########..########..############################..#### ####..6..#####..###..###..############### +####..6..###p.....<#####..##p..p##..############################..}### ####..6..###>..............<############ +###!..!#################..#!....!#..###########################!..!### ###### ###### ###!..!#######..#]#..###..############## +###!..!#################..#!....!#..#######!..!#################..#######!..!######!..!######!..!#######..###..###..############## +###{..#####>......w#####..##s..p##..#######{..###############..|..|..#####..}######{..#######{..######>..............<######vv#### +####..########..########..###FF###..########..###############..|..|..#####..########..########..########..###..#]#..########..#### +####..!##!#[#!..!####[#!55!##FF##!..!###!##!..####![######[!#--+--+--#!##!..!###!##!..!#[!!##!..########..###..###..####![##..#### +####.......XXXXXXXX..............6..6.........####.............|]!|.........####................####..........................!### +###!.......XXXXXXXX..............6..6.........!###.............|![|.........####................!###.............X............#### +####!##]#!###!..!#]####!55!##]###!..!###!##]#!####!##!..!##!#--+--+--#!]#!..!###!]#!..!##!!##]#!#####]##..###..###..##]#!##!..#### +##############..########..########..}#################..#####..|..|..#####..########..##################..###^^###..########..}### +############p.....<####{..########..##################..}####..|..|..#####..}#######..}#################..########..########..#### +########################..########..#################!..!#######..#######!..!######!..!#################..########..#######!..!### + #############!..!#######..#### ###### ###!..!### ###### ###### ###!..#######!..#### ###### + ##############..#######{..#### ###{..#### ##....}#####....}### + #############{..########..#### #####..#### ##2...######2...#### + ####!##[#!!##!55!##!###!..!### #!##!..#### ##......![##......![## + ###!.........6XX6...####..#### #......#### #....................# + ####.........6XX6...####..#### #......!### #....................# + ####..!##!!##!55!##!###!..!### #!##]#!#### ##]!......##]!......## + ####..########..}#######..#### ########### ####...!######...!## + ###{..########..########..}### ########## ###{...######{...### + #### ###!..!######!..!#######..#### ########## ####..!#######..!### +####..########..#######!..!######!..!### ##### ##### +###{..}######{..}######[..6..####{..#### +####..########..########..6..#####..##### +##................#####!..!55#!##!..!#[!# +##..!.!!!.].!.!.!.######................# +#{..p.....!...!...<#####................# +##..!.![!.!.!.!.!.#####!..!55!!]#!..!##!# +##....!.......!...######..6..#####..##### +##..!.!.!.!.!]!.!.######..6..#####..}### +#>....!.s.!.......#####!..!######!..!### +##..!.!.!.!.!.!.!.}# ###### ###### +#{..........!.!.w.## +##!!![!.!!!.[.!.!.## +##..*!...!........<# +##.!!!!.!!!.!.}.!.## +#{..........!...p.## +#################### +#################### +#################### +#################### \ No newline at end of file diff --git a/maps/demo/candidate1.maze b/maps/demo/candidate1.maze new file mode 100644 index 00000000..4d6eff1d --- /dev/null +++ b/maps/demo/candidate1.maze @@ -0,0 +1,190 @@ + #################### + ########oooo######## + #######!....!####### + ########XXXX######## + #######!....!####### + #######{....}####### + #######!5555!####### + ########XXXX######## + #######!5555!####### + #######{....}####### + #######!....!####### + ########XXXX######## + #######!....!####### + #######{....}####### + #######!....!####### + #######!....!####### + ######!......!###### + #####..........##### + ####.....!!.....#### + #### ####..########..#### + ####..#######!..!### #### + ###{..########..#### + ####..#######{..##### + ###!..!###!##!55!##!# + ####...<##...6XX6...# + ##T...####...6XX6...# + ###!..!###!##!55!##!# + ####..########..}#### + ####..}#######..#### + ###### ####..#######!..!### + ###!..!#######..########..############## + #vv#..#vv#####..##[##[##..############## + ##..#..#..#####.....##.....############## + #!..!..!..!######...##...#######[#!##!#### + #...................##T..................# + #..............T....##...T...............# + #!..!..!..!##....T..##.T.....#####!##!#]## + ##........###.......!!........!########## + ##]#^^#]###{.T....T...T...T.1########### + ############....T...T...T....!########## + ####################!..T..............############ + ##############vv#####2....T....T..T...}#####vv#### + ##############..####!........!!....T..######..##### + ####!##[#!![##..######.T..T..##......T##![##..##[!# + ###!............!###.........##..T................# + ####............####........T##...................# + ####..!##!!##!..##########...##...######!##!..!##!# + ####..########..}#######.....##.....########..##### + ###{..########..########..##]##]##..########..}### + ###### ###!..!######!..!#######..########..#######!..!### + ###!..!#################..#######!..!######!..!### ###### + ###{..#################{..########..########..}### + #####..##################..#######{..########..#### + #!##!..!#[!![######[!###!..!###!##!55!##!!##!..!### + #....................####...<##...6XX6.........#### + #....................##T...####...6XX6.........#### + #!]#!..!##!!##!..!##!###!..!###!##!55!##!!]#!..!### + #####..########..########..########..}#######..#### + ####..}#######..}#######..}#######..########..}### + ###### ###!..!######!..!#######..#######!..!######!..!### ###### + ###!..!#######..########..########..########..#################!..!### + ###{..########..########..########..########..#####>......w#####..}### + ####..########..###vv###..########..########..########..########..#### + ####..!##!####..###..###..########..########..#####[#!..!###!##!..!### + ####..............................X....##..........XXXXXXXX.......#### + ###!...................................##...X......XXXXXXXX.......#### + ####!##]#!####............########..X..##.....#######!..!#]#!]#!..!### + ##############.............<#####{.....##.X...}#######..########..#### + ############>......]!.....########.X....X.....######p.....<#####..}### + ##############....!##!.....<#########......####################!..!### + ###!..!#####>.....!##!....###########....X.##F..p####!..#######!..!### + ###[..6..#####.....![......<######...X.....X..######....}#######..}### + ####..6..###>.............#######{.....##.....}#####2...########..#### + ###!..!55#####............########..X..##X....#####......![#!##!..!### + ####...................................##.........................#### + ####...................................##...X.....................#### + ###!..!55!####..###..###..########..########..#####]!......#!]#!..!### + ####..6..#####..###^^###..########..########..########...!######..#### + ####..6..#####..########..########..########..#######{...#######..}### + ###!..!#######..########..########..########..########..!######!..!### + ###!..!######!..!#################..########..#######!..!### ###### + ###{..########..###############vv#...|XXXX|......}###{..#### + ####..#######{..###############..#...|XXXX|......#####..#### + ####..!##!!##!55!##!####!##[#!#..![#########..!..#!##!..#### + ####.........6XX6...###!.............|XXXX|.............#### + ###!.........6XX6...####.............|XXXX|.............!### + ####!##]#!!##!55!##!####..!##!#..!..###[####..!..#!##]#!#### + ##############..}#######..#####..#....|..|....#..########### + ##############..#######{..#####..#....|..|....#..########### + #### #############!..!######!..!####..{....|..|....#..########### + ####..#### ###!..!##############..#....|..|....}..####!..!### + ###{..#### ###[..##############{..#....|..|....#..##..6..}### + ####..#### ####..###############..#....|..|....#..##..6..#### + ###!..!### ###!..!#{#![###!#####..!..####]###..!..#!55!..!### + ####..#### ####............!###.......|XXXX|.............#### + ####..#### ####............####.......|XXXX|.............#### + ###!..!### ###!..!##!!##!..#####..!##[##########..#!55!..!### + ####..#### ####..########..}####......|XXXX|...#..##..6..#### + ####..}### ####..]#######..#####......|XXXX|...#^^##..6..}### + ####..#### ###!..!######!..!#######..########..#######!..!### +#############!..!##########################!..!######!..!#######..#######!..!#######..#### +#X.F..F.p#####..########vv#####>......w#####..#######{..#######{..#######{..#######{..#### +###!..!######{..########..########..#######{..########..########..########..########..#### +###!..!###!##!55!##!![##..##[!#[#!..!###!##!55!##!!##!..!#[!###!..!#######..!##!###!..!### +#............6XX6..............XXXXXXXX....6XX6.............####...<######......####...<## +#............6XX6..............XXXXXXXX....6XX6.............##T...#######!......##T...#### +###!..!###!##!55!##!!##!..!##!###!..!#]#!##!55!##!!]#!..!##!###!..!#######!##]#!###!..!### +###!..!#######..}#######..########..########..}#######..########..##################..#### +#w.F..F.s#####..########..}#####p.....<#####..########..}#######..}#################..}### +####..#######!..!######!..!################!..!######!..!#######..##################..#### ###### +###!..!###############################################..########..##################..#### #############!..!### +###[..#####p......p######.3.##########################..########..########vv#######{..#### ###########..6..}### +####..#######!..!######!.....#########################..########..########..########..#### ###########..6..#### +###!..!#{####!..!#####........#[#########[#!##!#######..##[##[##..####![##..#######!..!### ####!##[#!!55!..!### +####....................!###................................................!#######..#### ###!............#### +####...................######...............................................########..#### ####............#### +###!..!##!###!..!####]#######!..!####]#####!##!#]###....!######!....##!##!..#######!..!### ####..!##!!55!..!### +####..#######!..!#############..####################....#......#....######..}#######..#### ####..#####..6..#### +####..]####s......p###########..####################....#.@..@.#....######..########..}### ###{..#####..6..}### +###!..!#######..##############..####################....{......}....#####!..!#######..#### ###!..!######!..!### ###### +####..####################[###..###########!..!#####....#......#....######..########..#################!..!################!..!### +###{..###################...##..###########[..######....#.@..@.#....#####{..########..}################{..##################..}### +####..##################.....!..############..######....#......#....######..########..##################..##################..#### +###!..!#######!##[#!##..........!##[#######!..!#{###....!##..##!....######..######[#..#####[#!##!###!##!..!#[!![######[!!##!..!### +####..#######!...........#!#............####..............................X...................................................#### +####..########..........#####...........####..................................................................................#### +###!..!#######..!##!#############.....#####!..!##!####..##]##]##..######]#..########..#######!##!#]#!]#!..!##!!##!..!##!!]#!..!### +####..########..##################.4.#######..########..########..########..########..##################..########..########..#### +####..}######{..############################..]#######..########..########..########..}#################..}#######..}#######..}### +####..#######!..!##########################!..!#######..########..#######>............#################!..!######!..!######!..!### +###!..!######!..!######!..!#######..########..#######!..!######!..!#######..##..#############!..!######!..!######!..!### ###### +###{..########..}######{..########..########..#######{..#######{..########..##..#####v#######{..#######{..#######{..#### +####..########..########..########..########..########..########..########..##........########..########..########..#### +###!..!##!!##!..!#######..!##!##[#..########..########..!##!!##!..######[#..########..##[#!##!..####!##!..!##!!##!..#### +#...............########.............X............####............####..........................####................#### +#...............#######!..................X.......###!............!###..........................!###................!### +###!##!]#!!]#!..!#######!##]#!####..###..###..########!##]#!!##]#!#############]##########!##]#!####!##!##!]#!!##]#!#### +##############..##################..###.X##{.X########################################################################## +##############..}#################X.###..###..########################################################################## +#############!..!#################..###..###..########################################################################## + ####..##################..###..###..#######!..!### + ###{..##################..}##X.###..########..}### + ####..##################.X###..###..########..#### + ###!..!#######!##[#!####..###..###X.####!##!..!### + ####..#######!.............X..................#### + ####..########...................X............#### + ###!..!#######..!##!####..########..#]##!]#!..!### + ####..########..########..########..########..#### + ####..}######{..########..########..########..}### + ####..#######!..!#######..########..#######!..!### + #### ###!..!######!..!######!..!############# + ###[..6..####[..#######{..############## + ####..6..#####..########..############## + ###!..!55####!..!#{#####..!##!![###!#### + ####......####......####............!### + ####......####......###!............#### + ###!..!55!###!..!##!####!##]#!!##!..#### + ####..6..#####..##################..}### + ####..6..#####..]#################..#### + ###!..!######!..!################!..!### + ###!..!######!..!################!..!### + ###{..#######[..6..##############{..#### + ####..########..6..###############..##### + ####..!##!###!..!55#![###!########..!##!# + ####......####............!#######......# + ###!......####............#######!......# + ####!##]#!###!..!55!!##!..########!##]#!# + ##############..6..#####..}############## + ##############..6..#####..############## + #############!..!######!..!############# + ###!..!### ###### + ####..}### + #####..#### + #!##!..!### + #......#### + #......#### + #!]#!..!### + #####..#### + ####..}### + ###!..!### + ###!..!### + ##........## + #{..####..}# + #!..![[!..!# + #....*.....# + #..........# + #!..!]]!..!# + #{..####..}# + ##........## + ########## \ No newline at end of file diff --git a/maps/demo/candidate2.maze b/maps/demo/candidate2.maze new file mode 100644 index 00000000..a88d066d --- /dev/null +++ b/maps/demo/candidate2.maze @@ -0,0 +1,130 @@ +######################################## +##################################vv#### +#########################..T######..##### +##############[######[##!...##![##..##[!# +##vvv!vvv!..................##..........# +##...!...!................!###..........# +##...!...!.......#######..}###!##!..!##!# +#{...............}######..########..##### +#!..............!#######..########..}### +#*..............|.T#####..#######!..!### ###### +#!..............!######!..!######!..!######!..!### ########## +#{...............######[..6..####[..#####..6..}### ####vv#### +##...............}######..6..#####..#####..6..#### #####..##### +##...!...!.......######!..!55####!..!#{#!55!..!### #![##..##[!# +##...!...!..........####......####............#### #..........# +##^^^!^^^!..........####......####............#### #..........# +##############]########!..!55!###!..!##!!55!..!### #!##!..!##!# +########################..6..#####..#####..6..#### #####..##### +########################..6..#####..]####..6..}### ####..}### +#######################!..!######!..!######!..!### ###!..!### ###### #### #### + ##############..########..############## ###!..!######!..!#######..########..#### + #############{..########..}############# ###[..6..####{..########..########..}### + ##############..########..############## ####..6..#####..#######{..########..#### + ####!##[#!####..######[#..####![###!#### ###!..!55#!##!..!#[!###!..!######!55!#### + ###!..........X.....................!### ####...................6..6.............# + ####................................#### ####...................6..6.............# + ####..!##!##]#..########..####!##!..#### ###!..!55!!]#!..!##!###!..!#####]!55!#### + ####..########..########..########..}### ####..6..#####..########..########..#### + ###{..########..########..}#######..#### ####..6..#####..}#######..##p..p##..#### + ###### ###!..!######>............#######!..!### ###!..!######!..!#######..#!....!#..#### +###!..!######!..!#######..##..#############!..!################!..!######!..!#######..#!....!#..#### +###[..#######{..########..##..#####v########..}#################..#######{..########..##s..p##..#### +####..########..########..##........########..#################{..########..########..###FF###..#### +###!..!#{#!##!..!#[!##[#..########..##[#!##!..!#######!##[#!!##!55!##!!##!..!##!#[#!55!##FF##!..!#### +####..........................................#######!.........6XX6..........................6..6...# +####..........................................########.........6XX6..........................6..6...# +###!..!##!!]#!..!##!#########]##########!]#!..!#######..!##!!##!55!##!!##!##!]#!###!55!##]###!..!#### +####..########..############################..########..########..}#################..########..}### +####..]#######..}###########################..}######{..########..#################{..########..#### +###!..!######!..!##########################!..!######!..!######!..!#################..########..#### + ###### ###!..!######!..!######!..!######!..!#######..########..########..########..############## + ###[..#######[..#######{..#######[..#######{..#######{..########..########..############## + ####..########..########..########..########..########..########..###vv###..############## + ###!..!#{####!..!#{#!##!..!#[!###!..!#{####!..!######!..!#######..###..###..####![###!#### + ####......####................####......####..########..####..........................!### + ####......####................####......####..########..####..........................#### + ###!..!##!###!..!##!!]#!..!##!###!..!##!###!..!######!..!#######............####!##!..#### + ####..########..########..########..########..########..########.............<######..}### + ####..]#######..]#######..}#######..]#######..}#######..}#####>......]!.....########..#### + ###!..!######!..!######!..!######!..!#######..########..########....!##!.....<#####!..!### + ###### #############!..!#######..########..################>.....!##!....############## + #############{..########..########..##################.....![......<###>......w# + ##############..########..########..################>.............########..#### + ####!##[#!!##!..!#[!####..##[##[##..####![###!########............#####[#!..!#### + ###!..........................................!###.....................XXXXXXXX.# + ####..........................................####.....................XXXXXXXX.# + ####..!##!!]#!..!##!##....!######!....##!##!..########..###..###..#######!..!#]## + ####..########..######....#......#....######..}#######..###^^###..########..#### + ###{..########..}#####....#.@..@.#....######..########..########..######p.....<# + ###!..!######!..!#####....{......}....#####!..!#######..########..############## +#######################!..!###############....#......#....###############!..!################!..!############# +##############vv#######{..################....#.@..@.#....################..}#######vv#######{..############## +##############..########..################....#......#....################..########..########..############### +####!##[#!![##..##[!!##!..!#[!![######[!##....!##..##!....##![######[!!##!..!###![##..####!##!..!#[!####!##[#!# +###!.......................................................................p####......!###..........###!......# +####........................................................................####......####..........####......# +####..!##!!##!..!##!!]#!..!##!!##!..!##!####..##]##]##..####!##!..!##!!]#!..!###!##!..####!]#!..!##!####..!##!# +####..########..########..########..########..########..########..########..########..}#######..########..##### +###{..########..}#######..}#######..}#######..########..########..}#######..}#######..########..}######{..#### +###!..!######!..!######!..!######!..!#######..########..#######!..!######!..!######!..!######!..!######!..!### #### #### +###!..!######!..!####vv#..########..#######!..!#######..########..#######!..########..!#######..########..########..########..#### +###{..#######[..6..##--#..########..########..########..########..########............}#######..########..########..##[##[##..#### +####..########..6..##.....########..#######{..########..###vv###..#######{............########..########..########.....##.....#### +####..!##!###!..!55##..###########..#[##!##!55!##!#[##..###..###..##[#!##!..!......!..!##!##[#..########..##########...##...####### +####......####......#..###########.........6XX6..................................................X.....................##T........# +###!......####......#..##..................6XX6.......................................................X...........T....##...T.....# +####!##]#!###!..!55!#...#.....#######--#!##!55!##!####..###..###..####!.....55555555.....!####..###..###..######....T..##.T.....### +##############..6..##..##........XX....#####..}#######..###..###..####{.....6..4.!.6.....#####..###.X##{.X######.......!!........!# +##############..6..#{..##.....##[####..#####..######>..............<###.....6!!!!!!6...!.#####X.###..###..#####{.T....T...T...T.1## +#############!..!####..##..............####!..!#######..#]#..###..#####.!...6!!!!>.6.....#####..###..###..######....T...T...T....!# + ###### #...#.....#######--####!..!#######..###..###..#####.....6...............<###.!...6!!!!!!6.....#####..}##X.###..#####2....T....T..T...}########### + #..##.....##..###..#####..########..###..#]#..#####.....6.!.3..6.....}####.X###..###..####!........!!....T..####!#[#!### + #..##.....##..###--#!##!..########..###..###..####!.....55555555.....!####..###..###X.######.T..T..##......T##!.......o# + #...#.....##....|.........####...............................................X.....................##..T..............o# + #..#####..##....|.........!###.............X.......................................X..............T##.................o# + #..#[###..#######..#!##]#!#####]##..###..###..##]####!..!......!..!#######..########..#]########...##...######!.......o# + #.........|<##.....###############..###^^###..#######{............########..########..########.....##.....######!#]#!### + #.........|<##.....###############..########..########............}#######..########..########..##]##]##..############## + ####..########..#]################..########..#######!..!######!..!#######..########..########..########..############## + ###!..!######!..!### ####..########..########..#######!..!######!..!#######..########..##### #### + ###[..6..####{..#### ###{..########..########..#######{..########..#####vv#...|XXXX|......}# + ####..6..#####..#### ####..########..########..########..#######{..#####..#...|XXXX|......## + ###!..!55#!##!..#### ###!..!#######..########..####!##!..!##!!##!55!##!#..![#########..!..## + ####............#### ####...<##....X....##......................6XX6..........|XXXX|.......# + ####............!### ##T...####.........##...X..................6XX6..........|XXXX|.......# + ###!..!55!!##]#!#### ###!..!#######..X..##.....####!##!##!]#!!##!55!##!#..!..###[####..!..## + ####..6..########### ####..#######{.....##.X...}#################..}####..#....|..|....#..# + ####..6..########### ####..}#######.X....X.....##################..#####..#....|..|....#..# + ###!..!############# ####..###########......####################!..!####..{....|..|....#..# + ###!..!### ####..###########....X.##F..p####!..!######!..!####..#....|..|....}..# + ###{..#### ###{..########...X.....X..#######{..########..####{..#....|..|....#..# + #####..##### ####..#######{.....##.....}#######..#######{..#####..#....|..|....#..# + #!##!..!#[!# ###!..!#######..X..##X....####!##!..!#[!!##!55!##!#..!..####]###..!..## + #..........# ####...<##.........##......................6XX6..........|XXXX|.......# + #..........# ##T...####.........##...X..................6XX6..........|XXXX|.......# + #!]#!..!##!# ###!..!#######..########..####!]#!..!##!!##!55!##!#..!##[##########..## + #####..##### ####..########..########..########..########..}####......|XXXX|...#..# + ####..}### ####..}#######..########..########..}#######..#####......|XXXX|...#^^# + ###!..!### #### ####..########..########..#######!..!######!..!#######..########..#### + ###### ####..########..#######!..!######!..!############# ###### #### #### + ####..########..#####..6..}######{..#####>......w# + ###...########..}####..6..########..########..#### + ###...#########..####!55!..!#######..!##!#[#!..!#### + #{...##########...###......########.......XXXXXXXX.# + ##...###########...##......#######!.......XXXXXXXX.# + ##...###########...#!55!..!#######!##]#!###!..!#]## + ###...##########...##..6..##################..#### + ####...#########...##..6..}###############p.....<# + #####...#######...#####!..!####################### + ######...!.3.!...######!..!### + #######.........########..#### + #######........########{..##### + ######........######!##!55!##!# + #####...!]!...######...6XX6...# + ####...####...######...6XX6...# + ####..######...#####!##!55!##!# + ###{..#######...}#######..}#### + ####..########..########..#### + ############################## \ No newline at end of file diff --git a/maps/demo/candidate3.maze b/maps/demo/candidate3.maze new file mode 100644 index 00000000..cc66d331 --- /dev/null +++ b/maps/demo/candidate3.maze @@ -0,0 +1,140 @@ + #################### #################### + #################### #############[..6..# + ###[############]### ##############..6..# + ##................## ####!##[#!###!..!55## + ##..!.!!!.!.!.!.!.## ###!......####......# + ##........!...!...<# ####......####......# + ##..!.!*!.!.}.!.!.## ####..!##!###!..!55!# + ##....!...........## ####..########..6..## + ##..!.!!!.!.!!!.!.## ###{..########..6..# + #>........!.......}# ###!..!######!..!### + ##..!.!.{.!.!.!.!.## ###!..!#######..############## + ##............!...## ###[..#####..|..|..########### + ##..!!!.!.!.!.!.!.## ####..#####..|..|..########### + #{................<# ###!..!#{##--+--+--##[#!##!#### + ##..!.!.!.!.!.!.!.## ####.........|]!|.............# + ##..........!.....## ####.........|![|.............# + ####..########..#### ###!..!##!#--+--+--####!##!#]## + ###{..}######{..}### ####..#####..|..|..########### + ####..########..#### ####..]####..|..|..########### + ####..########..#### ###!..!#######..############## + ###!..!############# ####..########..#### + ###[..6..########### ####..########..#### + ####..6..######..T## ###...########..}### + ###!..!55##[##!...## ###...#########..#### + ####..............## #{...##########...### + ####............!### ##...###########...## + ###!..!55!####..}### ##...###########...# + ####..6..#####..#### ###...##########...# + ####..6..#####..#### ####...#########...# + ###!..!#######..#### #####...#######...## + ##############..########..####################...!.3.!...############# + ####vv########..########..}####################.........############## + #####..#######{..########..#####################........################ + #![##..##[!###!..!######!55!###![######[!######........##########!##[#!# + #.............6..6.......................#####...!]!...#########!......# + #.............6..6.......................####...####...##########......# + #!##!..!##!###!..!#####]!55!###!##!..!##!####..######...#########..!##!# + #####..########..########..########..#######{..#######...}#######..##### + ####..}#######..##p..p##..########..}#######..########..#######{..#### + ###!..!#######..#!....!#..#######!..!#######..########..#######!..!### + ###!..!#######..#!....!#..#################!..!######!..!#######..#### + ###{..########..##s..p##..#################{..########..}######{..#### + #####..########..###FF###..##################..########..########..#### + #!##!..!##!#[#!55!##FF##!..!####[#!##!###!##!..!#[!!##!..!######!..!### + #.......................6..6.............................########..#### + #.......................6..6.............................########..#### + #!##!##!]#!###!55!##]###!..!######!##!#]#!]#!..!##!!]#!..!######!..!### + ###############..########..}#################..########..########..#### + #############{..########..##################..}#######..}#######..}### + ##############..########..#################!..!######!..!#######..#### + ###!..!#################..#######!..!######!..!#######..#######!..!### + ###[..###############..|..|..####[..#######[..6..##..|..|..##..6..}### + ####..###############..|..|..#####..########..6..##..|..|..##..6..#### + ###!..!#{#![######[!#--+--+--####!..!#{####!..!55##--+--+--#!55!..!### + ####...................|]!|...####......####.........|]!|.........#### + ####...................|![|...####p.....####.........|![|.........#### + ###!..!##!!##!..!##!#--+--+--####!..!##!###!..!55!#--+--+--#!55!..!### + ####..########..#####..|..|..#####..########..6..##..|..|..##..6..#### + ####..]#######..}####..|..|..#####..]#######..6..##..|..|..##..6..}### + ###!..!######!..!#######..#######!..!######!..!#######..#######!..!### + ###### ####..#######!..!######!..!######!..!######!..!######!..!####################### + ###{..#######[..6..####{..######......#####{..#######{..######################## + ####..########..6..#####..####!........!####..########..######################## + ###!..!######!..!55#!##!..!#[!{..@..@..}!##!..!#[!!##!..!#[!![######[!#[#!##!#### + ####...<######..................................................................# + ##T...########..................................................................# + ###!..!######!..!55!!]#!..!##!{..@..@..}!]#!..!##!!]#!..!##!!##!..!##!###!##!#]## + ####..########..6..#####..####!........!####..########..########..############## + ####..}#######..6..#####..}#####......######..}#######..}#######..}############# + ####..#######!..!######!..!######!..!######!..!######!..!######!..!############# + ##############..########..#######!..########..!################!..!### + #############{..#######{..########............}#################..}### + ###############..########..#######{............##################..#### + #![######[!###!..!######!..!###!##!..!......!..!##!![######[!!##!..!### + #..........####...<######..####....................................#### + #..........##T...########..####....................................#### + #!##!..!##!###!..!######!..!###!.....55555555.....!!##!..!##!!]#!..!### + #####..########..########..####{.....6..4.!.6.....#####..########..#### + ####..}#######..}#######..}####.....6!!!!!!6...!.#####..}#######..}### + ###!..!#######..########..#####.!...6!!!!>.6.....####!..!######!..!### + ###!..!################!..!####.....6.......]!.....########..}#######..}### + ###### #######################!..!######!..!######!..!#######....!##!.....<#####!..!######!..!### + ###!..!###########################..########..#######!..!######!..!#####>.....!##!....########..########..############## + ###{..#########.3.#############vv#...|XXXX|......}#vv#..#vv####[..########.....![......<######..########..############## + #####..#######!.....############..#...|XXXX|......##..#..#..#####..######>.............########..########..#########..T## + #!##!..!#[!##........#[#########..![#########..!..#!..!..!..!###!..!#{#####............######[#..########..#####[##!...## + #..............!###...................|XXXX|.................####.................................X....................## + #.............######..................|XXXX|.................####......................................X.............!### + #!]#!..!##!#]#######!..!####]###..!..###[####..!..#!..!..!..!###!..!##!####..###..###..########..###..###..########..}### + #####..##############..#########..#....|..|....#..##........#####..########..###^^###..########..###.X##{.X########..#### + ####..}#############..#########..#....|..|....#..###]#^^#]######..]#######..########..########X.###..###..########..#### + #### ###!..!#############..#########..{....|..|....#..##############!..!#######..########..########..###..###..########..#### +####..########..##########[###..#########..#....|..|....}..##############!..!######!..!######!..!#######..###..###..########..#### +####..########..#########...##..########{..#....|..|....#..###############..#######[..6..#####..}#######..}##X.###..#####..|..|..# +####..########..########.....!..#########..#....|..|....#..##############{..########..6..#####..########.X###..###..#####..|..|..# +####..########..######..........!##[#####..!..####]###..!..##[#!##!###!##!55!##!###!..!55#!##!..!#######..###..###X.#####--+--+--# +#...X....##..............#!#...................|XXXX|....................6XX6...####............####.......X...............|]!|..# +#........##...X.........#####..................|XXXX|....................6XX6...####............####.............X.........|![|..# +####..X..##.....#################.....###..!##[##########..####!##!#]#!##!55!##!###!..!55!!]#!..!#######..########..#]###--+--+--# +###{.....##.X...}#################.4.####......|XXXX|...#..###############..}#######..6..#####..########..########..#####..|..|..# +####.X....X.....#########################......|XXXX|...#^^###############..########..6..#####..}#######..########..#####..|..|..# +#######......###############################..########..#################!..!######!..!######!..!#######..########..########..#### +#######....X.##F..p# ###!..!#######..########..#################!..!#######..########..#######!..!####vv#..########..#### +####...X.....X..#### #vv#..#vv####{..########..}#######vv#######{..########..##[##[##..#######[..6..##--#..########..#### +###{.....##.....}### ##..#..#..#####..########..########..########..########.....##.....########..6..##.....########..#### +####..X..##X....##### #!..!..!..!####..######[#..####![##..##[!!##!..##########...##...#########!..!55##..###########..#[## +#........##.........# #..............X...............................####.........##T........####......#..###########.....# +#........##...X.....# #..............................................!###....T....##...T.....####......#..##..............# +####..########..##### #!..!..!..!##]#..########..####!##!..!##!!##]#!######....T..##.T.....#####!..!55!#...#.....#######--# +####..########..#### ##........#####..########..########..################.......!!........!####..6..##..##........XX....# +####..########..#### ##]#^^#]######..########..}#######..}##############{.T....T...T...T.1#####..6..#{..##.....##[####..# +####..########..#### #############>............#######!..!###############....T...T...T....!###!..!####..##..............# + #### #### ##############..##..########## ###### #!..T..............### ###### #...#.....#######--# + ####vv########..##..#####v#### ##2....T....T..T...}# #..##.........###..# + #####..########..##........#### #!........!!....T..## #..##.....##..###..# + #![##..##[!##[#..########..##[## ###.T..T..##......T### #..##.....##..###--# + #..............................# #.........##..T......# #...#.....##....|..# + #..............................# #........T##.........# #..#####..##....|..# + #!##!..!##!#########]########### #######...##...####### #..#[###..#######..# + #####..######################## ####.....##.....#### #.........|<##.....# + ####..}####################### ####..##]##]##..#### #.........|<##.....# + ###!..!####################### ####..########..#### ####..########..#]## + ###### #### #### #### ####..#### + ###!..!### + ##......## + #!......!# + #{......}# + #!......!# + ##......## + ##!....!## + ###oooo### + ########## \ No newline at end of file diff --git a/maps/demo/candidate5.maze b/maps/demo/candidate5.maze new file mode 100644 index 00000000..2676e34f --- /dev/null +++ b/maps/demo/candidate5.maze @@ -0,0 +1,150 @@ + #################### + ####..}######{..#### + #####..########..##### + #!##!..!###!##!..!#[!# + #......####..........# + #......####..........# + #!]#!..!###!]#!..!##!# + #####..########..##### + ####..}#######..}### + ###### ###!..!######!..!### + ###!..!#######..########..############## + ###{..########..########..############## + #####..########..###vv###..############## + #!##!..!#[!####..###..###..#####[#!##!#### + #........................................# + #........................................# + #!]#!..!##!####............#######!##!#]## + #####..########.............<############ + ####..}#####>......]!.....############## + ###!..!#######....!##!.....<############ + ###!..!#####>.....!##!....############## + ###{..########.....![......<############ + #####..######>.............############## + #!##!..!#[!####............####![###!#### + #....................................!### + #....................................#### + #!]#!..!##!####..###..###..####!##!..#### + #####..########..###^^###..########..}### + ####..}#######..########..########..#### + ###!..!#######..########..#######!..!### + ###!..!######!..!######!..!######!..!############# ############################## + #..6..}######[..#######{..#####..6..}############# ############################## + ##..6..########..########..#####..6..############## ############################### + #!55!..!######!..!#{#!##!..!##!!55!..!###![###!#### #![######[!#####[############## + #......########......................####......!### #....................!vvv!vvv## + #......########......................####......#### #....................!...!...## + #!55!..!######!..!##!!##!##!]#!!55!..!###!##!..#### #!##!..!##!###.......!...!...## + ##..6..########..###############..6..########..}### #####..######{...............## + #..6..}#######..]##############..6..}#######..#### ####..}######...............}# + ###!..!######!..!################!..!######!..!### ###!..!######!..............!# + #############!..!############# ####..#######!..!#################..#####T.|..............*# + #############[..6..########### ###{..########..}################{..#######!..............!# + ##############..6..########### ####..########..##################..######{...............}# + ####!##[#!###!..!55#![###!#### ###!..!###!##!..!###![###!#######!..!######.......!...!...## + ###!......####............!### ####..####......####......!#######..####..........!...!...## + ####......####............#### ####..####......####......########..####..........!^^^!^^^## + ####..!##!###!..!55!!##!..#### ###!..!###!]#!..!###!##!..#######!..!########]############## + ####..########..6..#####..}### ####..########..########..}#######..######################## + ###{..########..6..#####..#### ####..}#######..}#######..########..}####################### + ###!..!######!..!######!..!### ####..#######!..!######!..!#######..######################## + ###!..!######!..!######!..!#################..########..#######!..!#######..############## + ###[..#######{..#######{..##################..########..#####..6..}####..|..|..########### + ####..########..########..##################..###vv###..#####..6..#####..|..|..########### + ###!..!#{#!##!..!##!!##!..!#[!![######[!#[##..###..###..##[#!55!..!####--+--+--#![###!#### + ####..............................................................####...|]!|.........!### + ####p.............................................................####...|![|.........#### + ###!..!##!!##!##!]#!!]#!..!##!!##!..!##!####..###..###..####!55!..!####--+--+--#!##!..#### + ####..##################..########..########..###..###..#####..6..#####..|..|..#####..}### + ####..]#################..}#######..}#####>..............<###..6..}####..|..|..#####..#### + ###!..!################!..!######!..!#######..#]#..###..#######!..!#######..#######!..!### + ##################################..##################..###..###..#######!..!######!..!### ###### + #####################>......w##..|..|..#############>..............<#####{..########..}### + ########################..#####..|..|..###############..###..#]#..########..########..#### + ##[#!##!###![######[!#[#!..!####--+--+--#####!##[#!####..###..###..####!##!..!#[!!##!..!### + #.....................XXXXXXXX....|]!|...###!..........................................#### + #.....................XXXXXXXX....|![|...####...................X......................#### + ####!##!#]#!##!..!##!###!..!#]##--+--+--#####..!##!#]##..###..###..##]#!]#!..!##!!]#!..!### + ##############..########..#####..|..|..#####..########..###^^###..########..########..#### + ##############..}#####p.....<##..|..|..####{..########..########..########..}#######..}### + ###### #############!..!#################..#######!..!#######..########..#######!..!######!..!### + ###!..!##########################!..!#################..########..#######!..!################!..!######!..!############# + ####..}#######vv#################{..###############vv#...|XXXX|......}##......################..}######[..############## + #####..########..##################..###############..#...|XXXX|......#!........!##############..########..############## + #!##!..!###![##..##[!####!##[#!!##!..!##!![######[!#..![#########..!..#{..@..@..}#[#!##!###!##!..!######!..!#{##[#!##!#### + #......####..........###!.................................|XXXX|................................p########................# + #......####..........####.................................|XXXX|.................................########................# + #!]#!..!###!##!..!##!####..!##!!##!##!]#!!##!..!##!#..!..###[####..!..#{..@..@..}###!##!#]#!]#!..!######!..!##!###!##!#]## + #####..########..########..##################..#####..#....|..|....#..#!........!##############..########..############## + ####..}#######..}######{..##################..}####..#....|..|....#..###......################..}#######..]############# + ###!..!######!..!######!..!################!..!####..{....|..|....#..####!..!################!..!######!..!############# + #vv#..########..########..########..########..#####..#....|..|....}..#####..#######!..!######!..!################!..!### + #--#..########..########..########..#####..|..|..#{..#....|..|....#..##..|..|..##..6..}######[..6..#####vv#######{..#### + #.....########..########..########..#####..|..|..##..#....|..|....#..##..|..|..##..6..########..6..#####..########..##### + #..###########..#[######..########..#####--+--+--##..!..####]###..!..##--+--+--#!55!..!######!..!55#![##..##[!!##!..!##!# + #..###########..........X....##............|]!|..........|XXXX|..........|]!|.........########..........................# + #..##........................##...X........|![|..........|XXXX|..........|![|.........########..........................# + #...#.....#######--#####..X..##.....#####--+--+--##..!##[##########..##--+--+--#!55!..!######!..!55!!##!..!##!!##!##!]#!# + ##..##........XX....####{.....##.X...}####..|..|..##......|XXXX|...#..##..|..|..##..6..########..6..#####..############### + #{..##.....##[####..#####.X....X.....#####..|..|..##......|XXXX|...#^^##..|..|..##..6..}#######..6..#####..}############# + ##..##..............########......###########..########..########..########..#######!..!######!..!######!..!############# + #...#.....#######--########....X.##F..p####!..!#######..########..#######!..#######!..!######!..!############# + #..##.........###..#####...X.....X..#######{..########..########..######....}######{..#####vv#..#vv########### + #..##.....##..###..####{.....##.....}#######..#######...########..}#####2...########..#####..#..#..########### + #..##.....##..###--#####..X..##X....####!##!..!##!##...#########..#####......![#!##!..####!..!..!..!#[#!##!#### + #...#.....##....|............##...................{...##########...###................####....................# + #..#####..##....|............##...X...............#...###########...##................!###....................# + #..#[###..#######..#####..########..####!##!##!]#!##...###########...##]!......#!##]#!####!..!..!..!###!##!#]## + #.........|<##.....#####..########..#################...##########...#####...!#############........########### + #.........|<##.....#####..########..##################...#########...####{...###############]#^^#]############ + ####..########..#]######..########..###################...#######...######..!################################# +#############!..!######!..!######!..!#######..#######!..!#########...!.3.!...######!..!### +##############..#####vv#..#vv#####..#####..|..|..####{..###########.........#####..6..}### +###!#[#!#####{..#####..#..#..####{..#####..|..|..#####..###########........######..6..#### +#o.......!!##!55!##!!..!..!..!!##!55!##!#--+--+--#!##!..!##!######........######!55!..!### +#o...........6XX6................6XX6......|]!|.............#####...!]!...######......#### +#o...........6XX6................6XX6......|![|.............####...####...######......#### +#o.......!!##!55!##!!..!..!..!!##!55!##!#--+--+--#!##!##!]#!####..######...#####!55!..!### +###!#]#!######..}####........#####..}####..|..|..##############{..#######...}####..6..#### +##############..######]#^^#]######..#####..|..|..###############..########..#####..6..}### +#############!..!################!..!#######..##################..########..#######!..!### + ###### ###!..!######!..!######!..!######!..!######!..!######!..!############# + ###{..#######[..6..##vv#..#vv####{..#######{..#####vv#..#vv########### + #####..########..6..##..#..#..#####..########..#####..#..#..######..T## + #!##!..!##!###!..!55#!..!..!..!!##!..!#[!!##!..####!..!..!..!#[##!...## + #..........####................................####..................## + #..........####................................!###................!### + #!##!##!]#!###!..!55!!..!..!..!!]#!..!##!!##]#!####!..!..!..!####..}### + ###############..6..##........#####..###############........#####..#### + ##############..6..###]#^^#]######..}###############]#^^#]######..#### + #############!..!################!..!###########################..#### + ###### ##############..#######!..#### #### + ###########..|..|..###....}### + ###########..|..|..###2...#### + ##[#!##!####--+--+--##......![## + #.............|]!|.............# + #.............|![|.............# + ####!##!#]##--+--+--##]!......## + ###########..|..|..#####...!## + ###########..|..|..####{...### + ##############..########..!### + ###!..########..!### + ####............}### + ####{............##### + #!##!..!......!..!##!# + #....................# + #....................# + #!.....55555555.....!# + #{.....6..4.!.6.....## + ##.....6!!!!!!6...!.# + #.!...6!!!!>.6.....# + #.....6.......w##..6..}######[..#######{..#######{..#######{..#### + #####..########..########..########..########..#####..6..########..########..########..########..#### + #!##!..!##!![##..##[!!##!..!###![##..##[!#[#!..!###!55!..!######!..!#{####!..!###!##!..!#[!!##!..#### + #.........................p####...........XXXXXXXX.......########......####..####................#### + #..........................####...........XXXXXXXX.......########......####..####................!### + #!##!##!]#!!##!..!##!!]#!..!###!##!..!##!###!..!#]#!55!..!######!..!##!###!..!###!]#!..!##!!##]#!#### + ###############..########..########..########..#####..6..########..########..########..############## + ##############..}#######..}#######..}#####p.....<##..6..}#######..]#######..}#######..}############# + #############!..!######!..!######!..!################!..!######!..!#######..#######!..!############# + ###!..!######!..!#######..########..###########################!..!######!..########..!######!..!### + ###{..#######{..########..########..########vv#################{..########............}######{..#### + ####..########..########..###vv###..########..##################..#######{............########..#### + ####..!##!!##!..!##!####..###..###..####![##..##[!![###!####!##!..!##!!##!..!......!..!##!!##!..#### + ####....................................................!###....................................#### + ###!....................................................####....................................!### + ####!##]#!!##!##!]#!####............####!##!..!##!!##!..####!##!##!]#!!.....55555555.....!!##]#!#### + ########################.............<######..########..}#############{.....6..4.!.6.....########### + ######################>......]!.....########..}#######..###############.....6!!!!!!6...!.########### + ###### ########################....!##!.....<#####!..!######!..!##############.!...6!!!!>.6.....########### ###### +###!..!######!..!######!..!#####>.....!##!....#######!..!######!..!##############.....6..............########..####!.@....@.!###########.....6.!.3..6.....}####..#####..#..#..# +###!..!#[!!##!..!#[!!##!..!##!####............########..!##!{........}![###!####!.....55555555.....!!##!..!#[!!..!..!..# +#.................................................####..........!!..........!###.......................................# +#.................................................###!..........!!..........####.......................................# +#]#!..!##!!]#!..!##!!##!##!]#!####..###..###..########!##]#!{........}!##!..#######!..!......!..!###!]#!..!##!!..!..!..# +####..########..##################..###^^###..##############!.@....@.!####..}######{............########..#####........# +####..}#######..}#################..########..###############........#####..########............}#######..}#####]#^^#]## +###!..!######!..!#################..########..#################!..!######!..!######!..!######!..!######!..!############# + ###### ####..#######!..!######!..!#######..#### ####..########..########..##################..########..#### + #..|..|..####{..########..#######{..#### ###{..#######{..########..}#################..########..}### + #..|..|..#####..#######{..########..#### ####..########..########..#################{..########..#### + ##--+--+--#####..!##!!##!55!##!###!..!### ###!..!#######..######[#..####![###!#######!..!######!..!### + #...|]!|...####.........6XX6...####..#### ####...<##....X.....................!###...................# + #...|![|...###!.........6XX6...####..#### ##T...####..........................####...................# + ##--+--+--#####!##]#!!##!55!##!###!..!### ###!..!#####]#..########..####!##!..#######!..!##XX#]!..!### + #..|..|..###############..}#######..#### ####..########..########..########..}#######..###XX###..#### + #..|..|..###############..########..}### ####..}#######..########..}#######..########..##w..w##..#### + ####..#################!..!#######..#### ####..#######>............#######!..!#######..#>....<#..#### + #### #############!..!######!..#################!..!#######..##..#############!..!#######..#>....<#..#### + ####vv#####vv#..#vv###....}##############..6..}#######..##..#####v#####..6..}#######..##w..w##..#### + #####..#####..#..#..###2...###############..6..########..##........#####..6..########..###XX###..#### + #![##..##[!!..!..!..!#......![#####!##[#!!55!..!#####[#..########..##[#!55!..!####[#!55!##XX##!..!### + #..............................###!............####..........................####...................# + #..............................####............####..........................####...................# + #!##!..!##!!..!..!..!#]!......#####..!##!!55!..!############]##########!55!..!######!55!##]###!..!### + #####..#####........#####...!######..#####..6..#########################..6..########..########..}### + ####..}#####]#^^#]#####{...######{..#####..6..}########################..6..}######{..########..#### + ###!..!#################..!######!..!######!..!##########################!..!#######..########..#### + ###### ###!..!######!..!#######..########..#################!..!### #### #### + ###[..#######{..########..########..##################..}### + ####..########..########..########..##################..#### + ###!..!#{#!##!..######[#..########..#####[#!##!###!##!..!### + ####............####.......X............................#### + ####............!###............X.......................#### + ###!..!##!!##]#!########..###..###..#######!##!#]#!]#!..!### + ####..##################..###.X##{.X##################..#### + ####..]#################X.###..###..##################..}### + ###!..!#################..###..###..#################!..!### + #############!..!#################..###..###..#################!..!### + #>......w####{..##################..}##X.###..###############..6..}### + ####..########..##################.X###..###..###############..6..#### + ##[#!..!###!##!..!##!![######[!####..###..###X.####![######[!!55!..!### + #.XXXXXXXX............................X............................#### + #.XXXXXXXX..................................X......................#### + ####!..!#]#!##!##!]#!!##!..!##!####..########..#]##!##!..!##!!55!..!### + ####..##################..########..########..########..#####..6..#### + ##p.....<###############..}#######..########..########..}####..6..}### + #######################!..!#######..########..#######!..!######!..!### ###### + ##############..########..########..#######!..!#######..########..#######!..!### + ##############..##[##[##..#######{..#####vv#..#vv##vv#...|XXXX|......}#vv#..#vv# + ###############.....##.....########..#####..#..#..##..#...|XXXX|......##..#..#..## + #![######[!######...##...#########!..!###!..!..!..!#..![#########..!..#!..!..!..!# + #...................##T........####...<##.................|XXXX|.................# + #..............T....##...T.....##T...####.................|XXXX|.................# + #!##!..!##!##....T..##.T.....#####!..!###!..!..!..!#..!..###[####..!..#!..!..!..!# + #####..######.......!!........!####..#####........##..#....|..|....#..##........## + ####..}####{.T....T...T...T.1#####..}#####]#^^#]###..#....|..|....#..###]#^^#]## + ###!..!#####....T...T...T....!####..###############..{....|..|....#..########### + ###!..!###!..T..............######..########..#####..#....|..|....}..########### + ###{..#####2....T....T..T...}#####..########..####{..#....|..|....#..##>......w# + ####..####!........!!....T..######..########..#####..#....|..|....#..#####..#### + ####..!##!##.T..T..##......T######..########..#####..!..####]###..!..##[#!..!#### + ####...............##..T..........X....##................|XXXX|........XXXXXXXX.# + ###!..............T##..................##...X............|XXXX|........XXXXXXXX.# + ####!##]#!######...##...##########..X..##.....#####..!##[##########..####!..!#]## + ##############.....##.....#######{.....##.X...}####......|XXXX|...#..#####..#### + ##############..##]##]##..########.X....X.....#####......|XXXX|...#^^###p.....<# + ##############..########..###########......###########..########..############## + ###!..!######!..!##########....X.##F..p############vv#..########..#### + #..6..}#######..}#######...X.....X..########vv#####--#..########..#### + ##..6..########..#######{.....##.....}#######..#####.....########..#### + #!55!..!###!##!..!#######..X..##X....####![##..#####..###########..#[### + #......####......####.........##...............!####..###########......# + #......####......####.........##...X...........#####..##...............# + #!55!..!###!]#!..!#######..########..####!##!..#####...#.....#######--## + ##..6..########..########..########..########..}####..##........XX....# + #..6..}#######..}#######..########..########..####{..##.....##[####..# + ###!..!######!..!#######..########..#######!..!####..##..............# + ###### ###### ###!..!######!..!### ###### #...#.....#######--# + ###[..########..#### #..##.........###..# + ####..#######{..##### #..##.....##..###..# + ###!..!#{#!##!55!##!# #..##.....##..###--## + ####.........6XX6...# #...#.....##....|...# + ####.........6XX6...# #..#####..##....|...# + ###!..!##!!##!55!##!# #..#[###..#######..## + ####..########..}#### #.........|<##.....# + ####..]#######..#### #.........|<##.....# + ###!..!######!..!### ####..########..#]## + ###### ###### #### ####..#### + ###!..!### + ##......## + #!......!# + #{......}# + #!......!# + ##......## + ##!....!## + ###oooo### + ########## \ No newline at end of file diff --git a/maps/demo/game1_player_pov.maze b/maps/demo/game1_player_pov.maze new file mode 100644 index 00000000..8e03cb19 --- /dev/null +++ b/maps/demo/game1_player_pov.maze @@ -0,0 +1,200 @@ + #################### + ########oooo######## + #######!....!####### + ########XXXX######## + #######!....!####### + #######{....}####### + #######!5555!####### + ########XXXX######## + #######!5555!####### + #######{....}####### + #######!....!####### + ########XXXX######## + #######!....!####### + #######{....}####### + #######!....!####### + #######!....!####### + ######!......!###### + #####..........##### + ####.....!!.....#### + ####..########..#### + ###!..!############# + #..6..}############# + ##..6..############### + #!55!..!#######!##[#!# + #......#######!......# + #......########......# + #!55!..!#######..!##!# + ##..6..########..##### + #..6..}######{..#### + ###### ###!..!######!..!### + ###!..!#######..########..############## + ###[..########..########..}############# + ####..#######{..########..############## + ###!..!#{####!..!######!..!###![###!#### + ####................................!### + ####................................#### + ###!..!##!###!..!##XX#]!..!###!##!..#### + ####..########..###XX###..########..}### + ####..]#######..##w..w##..########..#### + ###### ###!..!#######..#>....<#..#######!..!### + ###!..!###########################..#>....<#..#######!..!### + ###[..##################vv########..##w..w##..#######{..#### + ####..##################..########..###XX###..########..#### + ###!..!#{#![######[!![##..#####[#!55!##XX##!..!###!##!..#### + ####......................!###..........................#### + ####......................####..........................!### + ###!..!##!!##!..!##!!##!..#######!55!##]###!..!###!##]#!#### + ####..########..########..}#######..########..}############# + ####..]#######..}#######..#######{..########..############## + ###### ###!..!######!..!######!..!#######..########..############## + ###!..!################!..!######!..!#######..########..#######!..!### + ###{..###############..6..}######{..#####vv#...|XXXX|......}#..6..}### + ####..#########..T###..6..########..#####..#...|XXXX|......##..6..#### + ####..!##!#[##!...##!55!..!###!##!..!#[!#..![#########..!..#!55!..!### + ####..............##......####.................|XXXX|.............#### + ###!............!###......####.................|XXXX|.............#### + ####!##]#!####..}###!55!..!###!]#!..!##!#..!..###[####..!..#!55!..!### + ##############..#####..6..########..#####..#....|..|....#..##..6..#### + ##############..#####..6..}#######..}####..#....|..|....#..##..6..}### + ##############..#######!..!######!..!####..{....|..|....#..####!..!### + ####..########..###############..#....|..|....}..########### + ####..########..##############{..#....|..|....#..########### + ###...########..}########..T###..#....|..|....#..########### + ###...#########..#####[##!...###..!..####]###..!..#![###!#### + #{...##########...###........##.......|XXXX|.............!### + ##...###########...##......!###.......|XXXX|.............#### + ##...###########...#####..}####..!##[##########..#!##!..#### + ###...##########...#####..#####......|XXXX|...#..#####..}### + ####...#########...#####..#####......|XXXX|...#^^#####..#### + #####...#######...######..########..########..#######!..!### + ######...!.3.!...#######..########..#######!..!######!..!### + #######.........########..########..#####..6..}######{..#### + #######........#########..###vv###..#####..6..########..##### + ######........#######[##..###..###..##[#!55!..!###!##!..!#[!# + #####...!]!...######..........................####..........# + ####...####...######..........................####..........# + ####..######...#########..###..###..####!55!..!###!]#!..!##!# + ###{..#######...}#######..###..###..#####..6..########..##### + ####..########..######>..............<###..6..}#######..}### + ###### ####..########..########..#]#..###..#######!..!######!..!### + ###!..!######!..!#######..########..###..###..#################!..!### + ###{..#######{..#######{..######>..............<######vv#####..6..}### + ####..########..########..########..###..#]#..########..#####..6..#### + ####..!##!!##!..!#[!###!..!#######..###..###..####![##..##[!!55!..!### + ####................####..####....................................#### + ###!................####..####.............X......................#### + ####!##]#!!]#!..!##!###!..!####]##..###..###..##]#!##!..!##!!55!..!### + ##############..########..########..###^^###..########..#####..6..#### + ##############..}#######..}#######..########..########..}####..6..}### + #############!..!#######..########..########..#######!..!######!..!### + ###########################################!..########..!#################..#######!..!### + ############################################............}################{..#####vv#..#vv# + #########################..T###############{............##################..#####..#..#..## + ####!##[#!![######[!#[##!...######!##[#!!##!..!......!..!##!![###!#######!..!###!..!..!..!# + ###!........................#####!................................!#######..####..........# + ####......................!#######................................########..####..........# + ####..!##!!##!..!##!####..}#######..!##!!.....55555555.....!!##!..#######!..!###!..!..!..!# + ####..########..########..########..####{.....6..4.!.6.....#####..}#######..#####........## + ###{..########..}#######..#######{..#####.....6!!!!!!6...!.#####..########..}#####]#^^#]## + ###!..!######!..!#######..#######!..!####.!...6!!!!>.6.....####!..!#######..############## + #############!..!######!..!#################..#####.....6.........!.......}# + ##..!.!.!!!.!.!.!.## + ##............!.w.## + ##..![!.!]!.!.!.!.## + ##{.....!*........<# + ##..!.!.!.!.!.}.!.## + ##..p.......!.....## + #################### + #################### + #################### + #################### \ No newline at end of file diff --git a/maps/demo/game2_zeus_pov.maze b/maps/demo/game2_zeus_pov.maze new file mode 100644 index 00000000..0d714c28 --- /dev/null +++ b/maps/demo/game2_zeus_pov.maze @@ -0,0 +1,160 @@ + #################### + #################### + #####v#####v######## + ##w...............## + ##!!!.!!!.!.!.!.!.## + ##.p......!.s.!...## + ##.!!.!.![!.!.[.!.## + ##....!...........## + ##!]!.!.!.!.!!.!!.## + ##..!.....!...*...## + ##..!.!.!.!.!!.!!.## #################### + ##............!...## ####vv############## + ##.!![!.!.!.!.!.!.## #####..#########..T## + ##................## #![##..#####[##!...## + ##..!.!.!.!.!.].!.## #......!###........## + ##....!.!...!....p## #......####......!### + ####..#^######..#### #!##!..########..}### + ###{..}######{..}### #####..}#######..#### + ####..########..#### ####..########..#### + ####..########..#### ###### ###!..!#######..#### + ###!..!############# ###!..!#######..########..############## + #..6..}#######vv#### #vv#..#vv#####..########..############## + ##..6..########..##### ##..#..#..#####..###vv###..#########..T## + #!55!..!###![##..##[!# #!..!..!..!####..###..###..#####[##!...## + #......####..........# #......................................## + #......####..........# #....................................!### + #!55!..!###!##!..!##!# #!..!..!..!####............########..}### + ##..6..########..##### ##........#####.............<######..#### + #..6..}#######..}### ##]#^^#]####>......]!.....########..#### + ###### ###!..!######!..!### ##############....!##!.....<######..#### + ###!..!#######..########..#################!..!#####>.....!##!....############## + #vv#..#vv#####..########..########vv#######{..########.....![......<######vv#### + ##..#..#..#####..########..########..########..######>.............########..##### + #!..!..!..!##[#..########..####![##..##[!!##!..!##!####............####![##..##[!# + #.................X..............................................................# + #......................X.........................................................# + #!..!..!..!####..###..###..####!##!..!##!!##!##!]#!####..###..###..####!##!..!##!# + ##........#####..###.X##{.X########..##################..###^^###..########..##### + ##]#^^#]######X.###..###..########..}#################..########..########..}### + ##############..###..###..#######!..!#################..########..#######!..!### + ####..########..###..###..#######!..!#################..#################!..!############# + #..|..|..#####..}##X.###..#######{..###############..|..|..###############..}############# + #..|..|..#####.X###..###..########..###############..|..|..###############..############### + ##--+--+--#####..###..###X.####!##!..!#[!![###!#####--+--+--##[#!##!###!##!..!###![######[!# + #...|]!|..........X............................!###...|]!|...................####..........# + #...|![|................X......................####...|![|...................####..........# + ##--+--+--#####..########..#]##!]#!..!##!!##!..#####--+--+--####!##!#]#!]#!..!###!##!..!##!# + #..|..|..#####..########..########..########..}####..|..|..###############..########..##### + #..|..|..#####..########..########..}#######..#####..|..|..###############..}#######..}### + ####..########..########..#######!..!######!..!#######..#################!..!######!..!### + #### #############!..!#######..#######!..!######!..!######!..!######!..!######!..!### + #############{..#####..|..|..####{..#######{..#######{..#######{..########..}### + ##############..#####..|..|..#####..########..########..########..########..#### + ####!##[#!!##!..!##!#--+--+--#!##!..!#[!!##!..####!##!..!#[!!##!..!#[!!##!..!### + ###!...................|]!|...................####..........................#### + ####...................|![|...................!###..........................#### + ####..!##!!##!##!]#!#--+--+--#!]#!..!##!!##]#!####!]#!..!##!!]#!..!##!!]#!..!### + ####..###############..|..|..#####..##################..########..########..#### + ###{..###############..|..|..#####..}#################..}#######..}#######..}### + ##### ###!..!#################..#######!..!################!..!######!..!######!..!### + ###!..#################!..!######!..!######!..!##########################!..!######!..!### + ##....}################{..#######[..########..}##########################{..#####..6..}### + ##2...##################..########..########..############################..#####..6..#### + ##......![#![######[!!##!..!#[!###!..!#{#!##!..!###![######[!![######[!!##!..!#[!!55!..!### + #..............................####............####....................................#### + #..............................####p...........####....................................#### + ##]!......#!##!..!##!!]#!..!##!###!..!##!!]#!..!###!##!..!##!!##!..!##!!]#!..!##!!55!..!### + ####...!######..########..########..########..########..########..########..#####..6..#### + ###{...#######..}#######..}#######..]#######..}#######..}#######..}#######..}####..6..}### + ####..!######!..!######!..!######!..!######!..!######!..!######!..!######!..!######!..!### +#############!..!######!..!#######..#######!..!######!..!#######..########..#######!..!######!..!### +#############{..########..#####..|..|..####[..6..###......######..########..#######{..#######[..#### +##############..#######{..#####..|..|..#####..6..#!........!####..########..########..########..#### +####!##[#!!##!..!#[!!##!55!##!#--+--+--####!..!55#{..@..@..}####..########..####!##!..#######!..!#{## +###!...................6XX6......|]!|...####....................X....##...............########......# +####...................6XX6......|![|...####.........................##...X...........!#######......# +####..!##!!]#!..!##!!##!55!##!#--+--+--####!..!55!{..@..@..}####..X..##.....####!##]#!#######!..!##!# +####..########..########..}####..|..|..#####..6..#!........!###{.....##.X...}#################..##### +###{..########..}#######..#####..|..|..#####..6..###......######.X....X.....##################..]### +###!..!######!..!######!..!#######..#######!..!######!..!##########......####################!..!### + ###### ####..#####vv#..########..#######!..!######!..!##########....X.##F..p############################### + ###{..#####--#..########..#####vv#..#vv##vv#..#vv#####...X.....X..################################## + ####..#####.....########..#####..#..#..##..#..#..####{.....##.....}##################..T############# + ###!..!####..###########..#[##!..!..!..!!..!..!..!####..X..##X....####![###!#####[##!...######!##[#!# + ####..#####..###########...................................##...............!###........#####!......# + ####..#####..##............................................##...X...........####......!#######......# + ###!..!####...#.....#######--#!..!..!..!!..!..!..!####..########..####!##!..########..}#######..!##!# + ####..#####..##........XX....##........##........#####..########..########..}#######..########..##### + ####..}###{..##.....##[####..###]#^^#]####]#^^#]######..########..########..########..#######{..#### + ####..#####..##..............#########################..########..#######!..!#######..#######!..!### + #### #...#.....#######--##############!..!######!..!#######..########..#######!..!######!..!############# + #..##.........###..##############{..#####vv#..#vv##vv#...|XXXX|......}###{..########..############## + #..##.....##..###..###############..#####..#..#..##..#...|XXXX|......#####..#######{..############## + #..##.....##..###--##[#!##!###!##!..!##!!..!..!..!#..![#########..!..#!##!..!##!!##!55!##!#[#!##!### + #...#.....##....|........................................|XXXX|....................6XX6............# + #..#####..##....|........................................|XXXX|....................6XX6............# + #..#[###..#######..####!##!#]#!##!##!]#!!..!..!..!#..!..###[####..!..#!##!##!]#!!##!55!##!###!##!#]# + #.........|<##.....######################........##..#....|..|....#..###############..}############# + #.........|<##.....#######################]#^^#]###..#....|..|....#..###############..############## + ####..########..#]#################################..{....|..|....#..##############!..!############# +#######################!..!######!..#######!..!################!..!####..#....|..|....}..########################!..!### +###########>......w#####..######....}####vv#..#vv##X.F..F.p####{..####{..#....|..|....#..###############vv#####..6..}### +##############..#######{..######2...#####..#..#..####!..!#######..#####..#....|..|....#..###############..#####..6..#### +#[######[!#[#!..!###!##!55!##!#......![#!..!..!..!###!..!###!##!..!#[!#..!..####]###..!..##[#!##!###![##..##[!!55!..!### +#..........XXXXXXXX....6XX6..................................................|XXXX|.................................#### +#..........XXXXXXXX....6XX6..................................................|XXXX|.................................#### +###!..!##!###!..!#]#!##!55!##!#]!......#!..!..!..!###!..!###!]#!..!##!#..!##[##########..####!##!#]#!##!..!##!!55!..!### +####..########..########..}#######...!###........####!..!#######..#####......|XXXX|...#..###############..#####..6..#### +####..}#####p.....<#####..#######{...#####]#^^#]###w.F..F.s#####..}####......|XXXX|...#^^###############..}####..6..}### +###!..!################!..!#######..!#################..#######!..!#######..########..#################!..!######!..!### + ###### ###!..!######!..!#######..#### ###!..!#######..#######!..!#######..#### ###!..!### ###### + ###{..#####vv#..#vv####{..#### ###{..#####..|..|..####[..#######{..#### ###[..6..# + #####..#####..#..#..#####..#### #####..#####..|..|..#####..########..#### ####..6..# + #!##!..!##!!..!..!..!###!..!### #!##!..#####--+--+--####!..!#{####!..!### ###!..!55## + #....................####..#### #......####...|]!|...####......####...<## ####......# + #....................####..#### #......!###...|![|...####......##T...#### ####......# + #!##!##!]#!!..!..!..!###!..!### #!##]#!#####--+--+--####!..!##!###!..!### ###!..!55!# + ############........#####..#### ############..|..|..#####..########..#### ####..6..## + ############]#^^#]######..}### ###########..|..|..#####..]#######..}### ####..6..# + ########################..#### ##############..#######!..!#######..#### ###!..!### + ###!..!### ###!..!######!..!######!..!### ###### + #..6..}### ###{..#######[..########..}### + ##..6..#### #####..########..########..#### + #!55!..!### #!##!..#######!..!#{#!##!..!### + #......#### #......########............#### + #......#### #......!#######............#### + #!55!..!### #!##]#!#######!..!##!!]#!..!### + ##..6..#### ###############..########..#### + #..6..}### ##############..]#######..}### + ###!..!### #############!..!######!..!### + ###### ###!..!######!..!### + ###[..6..#####..}### + ####..6..#####..#### + ###!..!55#!##!..!### + ####............#### + ####............#### + ###!..!55!!]#!..!### + ####..6..#####..#### + ####..6..#####..}### + ###!..!######!..!### #### + ###### ####..########..#### + ####.....!!.....#### + ####!..........!#### + ######!......!###### + #######{....}####### + #######!....!####### + ########XXXX######## + #######!....!####### + #######{....}####### + #######!----!####### + ########XXXX######## + #######!----!####### + #######{....}####### + #######!....!####### + ########XXXX######## + #######!....!####### + #######{....}####### + #######!....!####### + ########oooo######## + #################### \ No newline at end of file diff --git a/maps/demo/notes.txt b/maps/demo/notes.txt new file mode 100644 index 00000000..28183367 --- /dev/null +++ b/maps/demo/notes.txt @@ -0,0 +1,5 @@ +1: leaning towards zeus / player won with lucky tp +2: better than 1 +3: NOPE +4. good? +5. bad \ No newline at end of file diff --git a/maps/rooms/10x10/alt.spawn b/maps/rooms/10x10/alt.spawn new file mode 100644 index 00000000..63c0e84c --- /dev/null +++ b/maps/rooms/10x10/alt.spawn @@ -0,0 +1,10 @@ +###!..!### +#........# +!.@....@.! +{........} +....!!.... +....!!.... +{........} +!.@....@.! +#........# +###!..!### \ No newline at end of file diff --git a/maps/rooms/10x10/basic.orb b/maps/rooms/10x10/basic.orb index dcd78629..e2e09f33 100644 --- a/maps/rooms/10x10/basic.orb +++ b/maps/rooms/10x10/basic.orb @@ -1,10 +1,10 @@ -####..#### -#........# -{........} +###!..!### #........# +{..####..} +!..![[!..! ....*..... .......... -#.....w..# -{........} +!..!]]!..! +{..####..} #........# -####..#### \ No newline at end of file +###!..!### \ No newline at end of file diff --git a/maps/rooms/10x10/basic1.exit b/maps/rooms/10x10/basic1.exit index 57def1c3..52cb9297 100644 --- a/maps/rooms/10x10/basic1.exit +++ b/maps/rooms/10x10/basic1.exit @@ -1,10 +1,10 @@ ########## ########## -####[##### -#.......## +##!#[#!### +!.......o# ........o# ........o# -#.......## -####]##### +!.......o# +##!#]#!### ########## ########## \ No newline at end of file diff --git a/maps/rooms/10x10/basic2.exit b/maps/rooms/10x10/basic2.exit index 3e1f92bf..adeb37d9 100644 --- a/maps/rooms/10x10/basic2.exit +++ b/maps/rooms/10x10/basic2.exit @@ -1,10 +1,10 @@ ########## ########## -####[##### -##......## +###!#[#!## +#o.......! #o........ #o........ -##......## -####]##### +#o.......! +###!#]#!## ########## ########## \ No newline at end of file diff --git a/maps/rooms/10x10/basic3.exit b/maps/rooms/10x10/basic3.exit index 50524600..bb666c33 100644 --- a/maps/rooms/10x10/basic3.exit +++ b/maps/rooms/10x10/basic3.exit @@ -1,10 +1,10 @@ ########## -####oo#### -##......## +###oooo### ##......## +#!......!# #{......}# +#!......!# ##......## -##......## -####..#### +###!..!### ####..#### ####..#### \ No newline at end of file diff --git a/maps/rooms/10x10/basic4.exit b/maps/rooms/10x10/basic4.exit index 1dc087b4..1d321824 100644 --- a/maps/rooms/10x10/basic4.exit +++ b/maps/rooms/10x10/basic4.exit @@ -1,10 +1,10 @@ ####..#### -####..#### -##......## +###!..!### ##......## +#!......!# #{......}# +#!......!# ##......## -##......## -####..#### -####oo#### +##!....!## +###oooo### ########## \ No newline at end of file diff --git a/maps/rooms/10x10/crush_cross.medium b/maps/rooms/10x10/crush_cross.medium index cad98cd3..82d92ff1 100644 --- a/maps/rooms/10x10/crush_cross.medium +++ b/maps/rooms/10x10/crush_cross.medium @@ -1,10 +1,10 @@ -####..#### +###!..!### ####..#### ###{..#### -####--#[## -...|XX|... -...|XX|... -##]#--#### +!##!55!##! +...6XX6... +...6XX6... +!##!55!##! ####..}### ####..#### -####..#### \ No newline at end of file +###!..!### \ No newline at end of file diff --git a/maps/rooms/10x10/cubby.loot b/maps/rooms/10x10/cubby.loot index 33b5db5d..41bf8d91 100644 --- a/maps/rooms/10x10/cubby.loot +++ b/maps/rooms/10x10/cubby.loot @@ -1,10 +1,10 @@ ########## #p......p# -####..#### -####..#### +###!..!### +###!..!### .......... .......... -####..#### -####..#### +###!..!### +###!..!### #s......p# ####..#### \ No newline at end of file diff --git a/maps/rooms/10x10/firepit.hard b/maps/rooms/10x10/firepit.hard new file mode 100644 index 00000000..9fa2fb46 --- /dev/null +++ b/maps/rooms/10x10/firepit.hard @@ -0,0 +1,10 @@ +###!..#### +##....}### +##2...#### +#......![# +.......... +.......... +#]!......# +####...!## +###{...### +####..!### \ No newline at end of file diff --git a/maps/rooms/10x10/firepit.medium b/maps/rooms/10x10/firepit.medium deleted file mode 100644 index 23123086..00000000 --- a/maps/rooms/10x10/firepit.medium +++ /dev/null @@ -1,10 +0,0 @@ -####..#### -###...}### -##&...#### -#......#[# -.......... -.......... -#]#......# -####...&## -###{...### -####..#### \ No newline at end of file diff --git a/maps/rooms/10x10/hall_b.easy b/maps/rooms/10x10/hall_b.easy deleted file mode 100644 index 3cb96e3a..00000000 --- a/maps/rooms/10x10/hall_b.easy +++ /dev/null @@ -1,10 +0,0 @@ -########## -####..#### -###{..#### -####..#### -####..#### -####..#### -####..#### -###{..#### -####..#### -####..#### \ No newline at end of file diff --git a/maps/rooms/10x10/hall_bl.easy b/maps/rooms/10x10/hall_bl.easy index 2d740b1c..d9375d18 100644 --- a/maps/rooms/10x10/hall_bl.easy +++ b/maps/rooms/10x10/hall_bl.easy @@ -1,10 +1,10 @@ ########## ########## ########## -#[######## +![###!#### +......!### ......#### -......#### -####..#### +!##!..#### ####..}### ####..#### -####..#### \ No newline at end of file +###!..!### \ No newline at end of file diff --git a/maps/rooms/10x10/hall_bl_arrow.medium b/maps/rooms/10x10/hall_bl_arrow.medium new file mode 100644 index 00000000..8370c51a --- /dev/null +++ b/maps/rooms/10x10/hall_bl_arrow.medium @@ -0,0 +1,10 @@ +########## +####vv#### +####..#### +![##..#### +......!### +......#### +!##!..#### +####..}### +####..#### +###!..!### \ No newline at end of file diff --git a/maps/rooms/10x10/hall_blr.easy b/maps/rooms/10x10/hall_blr.easy index e738ce0b..93bb37ca 100644 --- a/maps/rooms/10x10/hall_blr.easy +++ b/maps/rooms/10x10/hall_blr.easy @@ -1,10 +1,10 @@ ########## ########## ########## -#[######[# +![######[! .......... .......... -####..#### +!##!..!##! ####..#### ####..}### -####..#### \ No newline at end of file +###!..!### \ No newline at end of file diff --git a/maps/rooms/10x10/hall_blr_arrow.medium b/maps/rooms/10x10/hall_blr_arrow.medium new file mode 100644 index 00000000..e5fcea0e --- /dev/null +++ b/maps/rooms/10x10/hall_blr_arrow.medium @@ -0,0 +1,10 @@ +########## +####vv#### +####..#### +![##..##[! +.......... +.......... +!##!..!##! +####..#### +####..}### +###!..!### \ No newline at end of file diff --git a/maps/rooms/10x10/hall_br.easy b/maps/rooms/10x10/hall_br.easy index 1638db67..58e08f7e 100644 --- a/maps/rooms/10x10/hall_br.easy +++ b/maps/rooms/10x10/hall_br.easy @@ -1,10 +1,10 @@ ########## ########## ########## -#######[## +####!##[#! +###!...... ####...... -####...... -####..#### +####..!##! ####..#### ###{..#### -####..#### \ No newline at end of file +###!..!### \ No newline at end of file diff --git a/maps/rooms/10x10/hall_br_fire.hard b/maps/rooms/10x10/hall_br_fire.hard new file mode 100644 index 00000000..423dd212 --- /dev/null +++ b/maps/rooms/10x10/hall_br_fire.hard @@ -0,0 +1,10 @@ +########## +########## +###.3.#### +##....![#! +##2....... +##........ +###!..!##! +####..#### +###{..#### +###!..!### \ No newline at end of file diff --git a/maps/rooms/10x10/hall_l.easy b/maps/rooms/10x10/hall_l.easy deleted file mode 100644 index a3a85f23..00000000 --- a/maps/rooms/10x10/hall_l.easy +++ /dev/null @@ -1,10 +0,0 @@ -########## -########## -########## -########## -.........# -.........# -########## -########## -########## -########## \ No newline at end of file diff --git a/maps/rooms/10x10/hall_lr.easy b/maps/rooms/10x10/hall_lr.easy index 610461ed..48a39802 100644 --- a/maps/rooms/10x10/hall_lr.easy +++ b/maps/rooms/10x10/hall_lr.easy @@ -1,10 +1,10 @@ ########## ########## ########## -#[######## +#[#!##!### .......... .......... -########]# +###!##!#]# ########## ########## ########## \ No newline at end of file diff --git a/maps/rooms/10x10/hall_lr_crush.medium b/maps/rooms/10x10/hall_lr_crush.medium new file mode 100644 index 00000000..9e59dd80 --- /dev/null +++ b/maps/rooms/10x10/hall_lr_crush.medium @@ -0,0 +1,10 @@ +########## +#>......w# +####..#### +#[#!..!### +.XXXXXXXX. +.XXXXXXXX. +###!..!#]# +####..#### +##p.....<# +########## \ No newline at end of file diff --git a/maps/rooms/10x10/hall_r.easy b/maps/rooms/10x10/hall_r.easy deleted file mode 100644 index 18855e0f..00000000 --- a/maps/rooms/10x10/hall_r.easy +++ /dev/null @@ -1,10 +0,0 @@ -########## -########## -########## -########## -#......... -#......... -########## -########## -########## -########## \ No newline at end of file diff --git a/maps/rooms/10x10/hall_t.easy b/maps/rooms/10x10/hall_t.easy deleted file mode 100644 index a56256bb..00000000 --- a/maps/rooms/10x10/hall_t.easy +++ /dev/null @@ -1,10 +0,0 @@ -####..#### -####..#### -###{..#### -####..#### -####..#### -####..#### -####..#### -####..#### -####..#### -########## \ No newline at end of file diff --git a/maps/rooms/10x10/hall_tb.easy b/maps/rooms/10x10/hall_tb.easy index 853d360d..47299edb 100644 --- a/maps/rooms/10x10/hall_tb.easy +++ b/maps/rooms/10x10/hall_tb.easy @@ -1,10 +1,10 @@ ####..#### ###{..#### ####..#### +###!..!### ####..#### ####..#### -####..#### -####..#### +###!..!### ####..#### ####..}### ####..#### \ No newline at end of file diff --git a/maps/rooms/10x10/hall_tb_teleporter.easy b/maps/rooms/10x10/hall_tb_teleporter.easy new file mode 100644 index 00000000..f40374bb --- /dev/null +++ b/maps/rooms/10x10/hall_tb_teleporter.easy @@ -0,0 +1,10 @@ +####..#### +###{..#### +####..#### +###!..!### +####...<## +##T...#### +###!..!### +####..#### +####..}### +####..#### \ No newline at end of file diff --git a/maps/rooms/10x10/hall_tbl.easy b/maps/rooms/10x10/hall_tbl.easy index db5ef386..bd78cb73 100644 --- a/maps/rooms/10x10/hall_tbl.easy +++ b/maps/rooms/10x10/hall_tbl.easy @@ -1,10 +1,10 @@ -####..#### +###!..!### ####..}### ####..#### -####..#### +!##!..!### ......#### ......#### -#]##..#### +!]#!..!### ####..#### ####..}### -####..#### \ No newline at end of file +###!..!### \ No newline at end of file diff --git a/maps/rooms/10x10/hall_tbl.loot b/maps/rooms/10x10/hall_tbl.loot new file mode 100644 index 00000000..c8dc0325 --- /dev/null +++ b/maps/rooms/10x10/hall_tbl.loot @@ -0,0 +1,10 @@ +###!..!### +####..}### +####..#### +!##!..!### +.....p#### +......#### +!]#!..!### +####..#### +####..}### +###!..!### \ No newline at end of file diff --git a/maps/rooms/10x10/hall_tbl_alt.easy b/maps/rooms/10x10/hall_tbl_alt.easy new file mode 100644 index 00000000..2baebad1 --- /dev/null +++ b/maps/rooms/10x10/hall_tbl_alt.easy @@ -0,0 +1,10 @@ +###!..!### +#..6..}### +#..6..#### +!55!..!### +......#### +......#### +!55!..!### +#..6..#### +#..6..}### +###!..!### \ No newline at end of file diff --git a/maps/rooms/10x10/hall_tblr.easy b/maps/rooms/10x10/hall_tblr.easy index 41e8ceae..c4d34b0f 100644 --- a/maps/rooms/10x10/hall_tblr.easy +++ b/maps/rooms/10x10/hall_tblr.easy @@ -1,10 +1,10 @@ -####..#### +###!..!### ###{..#### ####..#### -####..##[# +!##!..!#[! .......... .......... -#]##..#### +!]#!..!##! ####..#### ####..}### -####..#### \ No newline at end of file +###!..!### \ No newline at end of file diff --git a/maps/rooms/10x10/hall_tbr.easy b/maps/rooms/10x10/hall_tbr.easy index dc6ec628..01fbe7ea 100644 --- a/maps/rooms/10x10/hall_tbr.easy +++ b/maps/rooms/10x10/hall_tbr.easy @@ -1,10 +1,10 @@ -####..#### +###!..!### ###[..#### ####..#### -####..##{# +###!..!#{# ####...... ####...... -####..#### +###!..!##! ####..#### ####..]### -####..#### \ No newline at end of file +###!..!### \ No newline at end of file diff --git a/maps/rooms/10x10/hall_tbr.loot b/maps/rooms/10x10/hall_tbr.loot new file mode 100644 index 00000000..24ef979f --- /dev/null +++ b/maps/rooms/10x10/hall_tbr.loot @@ -0,0 +1,10 @@ +###!..!### +###[..#### +####..#### +###!..!#{# +####...... +####p..... +###!..!##! +####..#### +####..]### +###!..!### \ No newline at end of file diff --git a/maps/rooms/10x10/hall_tbr_alt.easy b/maps/rooms/10x10/hall_tbr_alt.easy new file mode 100644 index 00000000..3f4a1547 --- /dev/null +++ b/maps/rooms/10x10/hall_tbr_alt.easy @@ -0,0 +1,10 @@ +###!..!### +###[..6..# +####..6..# +###!..!55# +####...... +####...... +###!..!55! +####..6..# +####..6..# +###!..!### \ No newline at end of file diff --git a/maps/rooms/10x10/hall_tl.easy b/maps/rooms/10x10/hall_tl.easy index 8c8ac34a..c0b46ea1 100644 --- a/maps/rooms/10x10/hall_tl.easy +++ b/maps/rooms/10x10/hall_tl.easy @@ -1,10 +1,10 @@ -####..#### +###!..!### ###{..#### ####..#### -####..#### -......#### +!##!..#### ......#### -###]###### +......!### +!##]#!#### ########## ########## ########## \ No newline at end of file diff --git a/maps/rooms/10x10/hall_tlr.easy b/maps/rooms/10x10/hall_tlr.easy index 50aa147d..a5dff4a7 100644 --- a/maps/rooms/10x10/hall_tlr.easy +++ b/maps/rooms/10x10/hall_tlr.easy @@ -1,10 +1,10 @@ -####..#### +###!..!### ###{..#### ####..#### -####..#### +!##!..!##! .......... .......... -#######]## +!##!##!]#! ########## ########## ########## \ No newline at end of file diff --git a/maps/rooms/10x10/hall_tlr_arrow.medium b/maps/rooms/10x10/hall_tlr_arrow.medium new file mode 100644 index 00000000..aa5a9b79 --- /dev/null +++ b/maps/rooms/10x10/hall_tlr_arrow.medium @@ -0,0 +1,10 @@ +###!..!### +#vv#..#vv# +#..#..#..# +!..!..!..! +.......... +.......... +!..!..!..! +#........# +##]#^^#]## +########## \ No newline at end of file diff --git a/maps/rooms/10x10/hall_tr.easy b/maps/rooms/10x10/hall_tr.easy index 805a6684..9752f29a 100644 --- a/maps/rooms/10x10/hall_tr.easy +++ b/maps/rooms/10x10/hall_tr.easy @@ -1,10 +1,10 @@ -####..#### +###!..!### ###{..#### ####..#### -####..#### -####...... +####..!##! ####...... -#######]## +###!...... +####!##]#! ########## ########## ########## \ No newline at end of file diff --git a/maps/rooms/10x10/spike_square.medium b/maps/rooms/10x10/spike_square.medium index 9bc4b0c6..efb9caad 100644 --- a/maps/rooms/10x10/spike_square.medium +++ b/maps/rooms/10x10/spike_square.medium @@ -1,10 +1,10 @@ -##[#..#### +####..#### +#..|..|..# #..|..|..# -#..|..|..} #--+--+--# -...|..|... -...|..|... +...|]!|... +...|![|... #--+--+--# -{..|..|..# #..|..|..# -####..#]## \ No newline at end of file +#..|..|..# +####..#### \ No newline at end of file diff --git a/maps/rooms/10x10/teleporter.medium b/maps/rooms/10x10/teleporter.medium index 174f7f2c..099d60d3 100644 --- a/maps/rooms/10x10/teleporter.medium +++ b/maps/rooms/10x10/teleporter.medium @@ -1,9 +1,9 @@ ########## ########## #####..T## -#[###...## +#[##!...## ........## -......#### +......!### ####..}### ####..#### ####..#### diff --git a/maps/rooms/10x10/troll_cubbies.loot b/maps/rooms/10x10/troll_cubbies.loot new file mode 100644 index 00000000..5ae6ddf9 --- /dev/null +++ b/maps/rooms/10x10/troll_cubbies.loot @@ -0,0 +1,10 @@ +########## +#X.F..F.p# +###!..!### +###!..!### +.......... +.......... +###!..!### +###!..!### +#w.F..F.s# +####..#### \ No newline at end of file diff --git a/maps/rooms/20x20/armory.loot b/maps/rooms/20x20/armory.loot new file mode 100644 index 00000000..eb3556f2 --- /dev/null +++ b/maps/rooms/20x20/armory.loot @@ -0,0 +1,20 @@ +####..########..#### +####..########..}### +###{..########..#### +###!..!######!..!### +.................... +.................... +###!..!##XX#]!..!### +####..###XX###..#### +####..##w..w##..#### +####..#>....<#..#### +####..#>....<#..#### +####..##w..w##..#### +####..###XX###..#### +#[#!55!##XX##!..!### +.................... +.................... +###!55!##]###!..!### +####..########..}### +###{..########..#### +####..########..#### \ No newline at end of file diff --git a/maps/rooms/20x20/central_cache.loot b/maps/rooms/20x20/central_cache.loot index 0d56761f..4bdcef36 100644 --- a/maps/rooms/20x20/central_cache.loot +++ b/maps/rooms/20x20/central_cache.loot @@ -1,20 +1,20 @@ ####..########..#### ####..########..}### ###{..########..#### -####..########--#### -...|..|............. -...|..|............. -####..######]#--#### -####..###[####..#### +###!..!######!55!### +...6..6............. +...6..6............. +###!..!#####]!55!### +####..########..#### ####..##p..p##..#### -####..##....##..#### -####..##....##..#### +####..#!....!#..#### +####..#!....!#..#### ####..##s..p##..#### ####..###FF###..#### -#[##--###FF###..#### -.............|..|... -.............|..|... -####--###]####..#### +#[#!55!##FF##!..!### +.............6..6... +.............6..6... +###!55!##]###!..!### ####..########..}### ###{..########..#### ####..########..#### \ No newline at end of file diff --git a/maps/rooms/20x20/crush_axis.hard b/maps/rooms/20x20/crush_axis.hard new file mode 100644 index 00000000..ef4cad6f --- /dev/null +++ b/maps/rooms/20x20/crush_axis.hard @@ -0,0 +1,20 @@ +####..########..#### +#vv#...|XXXX|......} +#..#...|XXXX|......# +#..![#########..!..# +.......|XXXX|....... +.......|XXXX|....... +#..!..###[####..!..# +#..#....|..|....#..# +#..#....|..|....#..# +#..{....|..|....#..# +#..#....|..|....}..# +{..#....|..|....#..# +#..#....|..|....#..# +#..!..####]###..!..# +.......|XXXX|....... +.......|XXXX|....... +#..!##[##########..# +#......|XXXX|...#..# +#......|XXXX|...#^^# +####..########..#### \ No newline at end of file diff --git a/maps/rooms/20x20/crush_spiral.hard b/maps/rooms/20x20/crush_spiral.hard deleted file mode 100644 index 1ec2bb95..00000000 --- a/maps/rooms/20x20/crush_spiral.hard +++ /dev/null @@ -1,20 +0,0 @@ -####..########..#### -#vv#...|XXXX|......} -#..#...|XXXX|......# -#..#[#########FF#..# -.......|XXXX|...#... -.......|XXXX|...}... -###[##########..#..# -#...|XXXX|...#..#..# -#...|XXXX|...#..#..# -#..########..#..#..# -#..#......#..#..#..# -{..#..##..#..#..#..# -#..#..##p....#..#..# -#..#..######]#..#..# -...#...|XXXX|...#... -...#...|XXXX|...#... -#..###[##########..# -#......|XXXX|......# -#......|XXXX|......# -####..########..#### \ No newline at end of file diff --git a/maps/rooms/20x20/dark_halls_1.hard b/maps/rooms/20x20/dark_halls_1.hard new file mode 100644 index 00000000..58a05c34 --- /dev/null +++ b/maps/rooms/20x20/dark_halls_1.hard @@ -0,0 +1,20 @@ +####..########..#### +####..########..#### +####..########..#### +##[#..########..#### +.......X............ +............X....... +####..###..###..#### +####..###.X##{.X#### +####X.###..###..#### +####..###..###..#### +####..###..###..#### +####..}##X.###..#### +####.X###..###..#### +####..###..###X.#### +.......X............ +.............X...... +####..########..#]## +####..########..#### +####..########..#### +####..########..#### \ No newline at end of file diff --git a/maps/rooms/20x20/dark_halls_2.hard b/maps/rooms/20x20/dark_halls_2.hard new file mode 100644 index 00000000..c952bad6 --- /dev/null +++ b/maps/rooms/20x20/dark_halls_2.hard @@ -0,0 +1,20 @@ +####..########..#### +####..########..#### +####..###vv###..#### +#[##..###..###..##[# +.................... +.................... +####..###..###..#### +####..###..###..#### +##>..............<## +####..#]#..###..#### +####..###..###..#### +##>..............<## +####..###..#]#..#### +####..###..###..#### +.................... +.............X...... +#]##..###..###..##]# +####..###^^###..#### +####..########..#### +####..########..#### \ No newline at end of file diff --git a/maps/rooms/20x20/dark_room_1.hard b/maps/rooms/20x20/dark_room_1.hard new file mode 100644 index 00000000..ad3ef7e4 --- /dev/null +++ b/maps/rooms/20x20/dark_room_1.hard @@ -0,0 +1,20 @@ +####..########..#### +####..########..#### +####..###vv###..#### +####..###..###..#### +.................... +.................... +####............#### +####.............<## +##>......]!.....#### +####....!##!.....<## +##>.....!##!....#### +####.....![......<## +##>.............#### +####............#### +.................... +.................... +####..###..###..#### +####..###^^###..#### +####..########..#### +####..########..#### \ No newline at end of file diff --git a/maps/rooms/20x20/dark_room_2.hard b/maps/rooms/20x20/dark_room_2.hard new file mode 100644 index 00000000..549d7b34 --- /dev/null +++ b/maps/rooms/20x20/dark_room_2.hard @@ -0,0 +1,20 @@ +####..########..#### +####..########..#### +####..########..#### +####..########..#### +....X....##......... +.........##...X..... +####..X..##.....#### +###{.....##.X...}### +####.X....X.....#### +#######......####### +#######....X.##F..p# +####...X.....X..#### +###{.....##.....}### +####..X..##X....#### +.........##......... +.........##...X..... +####..########..#### +####..########..#### +####..########..#### +####..########..#### \ No newline at end of file diff --git a/maps/rooms/20x20/dual_halls_lr.medium b/maps/rooms/20x20/dual_halls_lr.medium new file mode 100644 index 00000000..b0b6367d --- /dev/null +++ b/maps/rooms/20x20/dual_halls_lr.medium @@ -0,0 +1,20 @@ +#################### +#####.3.############ +###!.....########### +##........#[######## +....!###............ +...######........... +#]#######!..!####]## +##########..######## +##########..######## +##########..######## +######[###..######## +#####...##..######## +####.....!..######## +##..........!##[#### +.....#!#............ +....#####........... +#############.....## +##############.4.### +#################### +#################### \ No newline at end of file diff --git a/maps/rooms/20x20/dual_halls_tb.medium b/maps/rooms/20x20/dual_halls_tb.medium new file mode 100644 index 00000000..df058079 --- /dev/null +++ b/maps/rooms/20x20/dual_halls_tb.medium @@ -0,0 +1,20 @@ +####..########..#### +####..########..#### +###...########..}### +##...#########..#### +{...##########...### +#...###########...## +##...###########...# +###...##########...# +####...#########...# +#####...#######...## +######...!.3.!...### +#######.........#### +#######........##### +######........###### +#####...!]!...###### +####...####...###### +####..######...##### +###{..#######...}### +####..########..#### +####..########..#### \ No newline at end of file diff --git a/maps/rooms/20x20/fake_wall_maze.hard b/maps/rooms/20x20/fake_wall_maze.hard deleted file mode 100644 index 66629de3..00000000 --- a/maps/rooms/20x20/fake_wall_maze.hard +++ /dev/null @@ -1,20 +0,0 @@ -####..########..#### -##v#............}### -##FF............#### -##F######..#######[# -...##.p##..#>....... -...##..##..#>....... -#]##..###..##..##..# -###..####..##..##..# -##..#####--##..}#..# -#.......F..F...##XX# -#..######..######..# -{..######..######..# -#..##...F..F.......# -#..##..##--######..# -...##..##..##v###... -...##......##.###... -#..#####]####-###..# -#..................# -#..................# -####..######[#..#### \ No newline at end of file diff --git a/maps/rooms/20x20/fireball_home.hard b/maps/rooms/20x20/fireball_home.hard index 82fd1980..f97c31a9 100644 --- a/maps/rooms/20x20/fireball_home.hard +++ b/maps/rooms/20x20/fireball_home.hard @@ -1,20 +1,20 @@ -####..########..#### -####.....<>.....}### -###{....p<>.....#### -####..########..#### -......|......|...... -......|......|...... -#-----+------+-----# -{.....|......|.....# -#.....|.&..&.|.....# -#.....|......|.....# -#.....|......|.....# -#.....|.&..&.|.....# -#.....|......|.....} -#-----+------+-----# -......|......|...... -......|......|...... -####..########..#### -###{.....<>p....#### -####.....<>.....}### -####..########..#### \ No newline at end of file +###!..########..!### +####............}### +###{............#### +!##!..!......!..!##! +.................... +.................... +!.....55555555.....! +{.....6..4.!.6.....# +#.....6!!!!!!6...!.# +#.!...6!!!!>.6.....# +#.....6.........!.......}# +##..!.!.{.!.!.!.!.## +##............!...## +##..!!!.!.!.!.!.!.## +#{................<# +##..!.!.!.!.!.!.!.## +##..........!.....## +####..########..#### +###{..}######{..}### +####..########..#### +####..########..#### \ No newline at end of file diff --git a/maps/rooms/20x20/pillar_maze_bottom_2.orb b/maps/rooms/20x20/pillar_maze_bottom_2.orb new file mode 100644 index 00000000..6290969c --- /dev/null +++ b/maps/rooms/20x20/pillar_maze_bottom_2.orb @@ -0,0 +1,20 @@ +#################### +#################### +#####v#####v######## +##w...............## +##!!!.!!!.!.!.!.!.## +##.p......!.s.!...## +##.!!.!.![!.!.[.!.## +##....!...........## +##!]!.!.!.!.!!.!!.## +##..!.....!...*...## +##..!.!.!.!.!!.!!.## +##............!...## +##.!![!.!.!.!.!.!.## +##................## +##..!.!.!.!.!.].!.## +##....!.!...!....p## +####..#^######..#### +###{..}######{..}### +####..########..#### +####..########..#### \ No newline at end of file diff --git a/maps/rooms/20x20/pillar_maze_top_1.orb b/maps/rooms/20x20/pillar_maze_top_1.orb new file mode 100644 index 00000000..398eca2b --- /dev/null +++ b/maps/rooms/20x20/pillar_maze_top_1.orb @@ -0,0 +1,20 @@ +####..########..#### +###{..}######{..}### +####..########..#### +##................## +##..!.!!!.!.!.!.!.## +#{........!.s.!...<# +##..!.!.[.!.!.!.!.## +##..p.!...........## +##..!.!.!.!.!]!.!.## +#>........!.......}# +##..!.!.!!!.!.!.!.## +##............!.w.## +##..![!.!]!.!.!.!.## +##{.....!*........<# +##..!.!.!.!.!.}.!.## +##..p.......!.....## +#################### +#################### +#################### +#################### \ No newline at end of file diff --git a/maps/rooms/20x20/pillar_maze_top_2.orb b/maps/rooms/20x20/pillar_maze_top_2.orb new file mode 100644 index 00000000..f4bb2627 --- /dev/null +++ b/maps/rooms/20x20/pillar_maze_top_2.orb @@ -0,0 +1,20 @@ +####..########..#### +###{..}######{..}### +####..########..#### +##................## +##..!.!!!.].!.!.!.## +#{..p.....!...!...<# +##..!.![!.!.!.!.!.## +##....!.......!...## +##..!.!.!.!.!]!.!.## +#>....!.s.!.......## +##..!.!.!.!.!.!.!.}# +#{..........!.!.w.## +##!!![!.!!!.[.!.!.## +##..*!...!........<# +##.!!!!.!!!.!.}.!.## +#{..........!...p.## +#################### +#################### +#################### +#################### \ No newline at end of file diff --git a/maps/rooms/20x20/potion_caches.loot b/maps/rooms/20x20/potion_caches.loot deleted file mode 100644 index 9eeeb4d1..00000000 --- a/maps/rooms/20x20/potion_caches.loot +++ /dev/null @@ -1,20 +0,0 @@ -#################### -#{..|.|.|.|.|.|.p### -##p.|.|.|.|.|.|..}## -#########.########## -.....####.######.... -.....}###.#####..... -##...###{.####..##]# -##...####.###..##v## -##...####.##..###.p# -##...####.#..####.## -##...####F#.#####F## -##...####.........}# -##...###..#.######## -##...##..##.######## -........###.#####... -.......####......... -##...]############## -##...##..|.|.|.|.p## -#>.......|.|.|.|.p## -#################### \ No newline at end of file diff --git a/maps/rooms/20x20/teleporter_hall.hard b/maps/rooms/20x20/teleporter_hall.hard new file mode 100644 index 00000000..a96b4e33 --- /dev/null +++ b/maps/rooms/20x20/teleporter_hall.hard @@ -0,0 +1,20 @@ +####..########..#### +####..##[##[##..#### +####.....##.....#### +######...##...###### +.........##T........ +....T....##...T..... +##....T..##.T.....## +##.......!!........! +#{.T....T...T...T.1# +##....T...T...T....! +!..T..............## +#2....T....T..T...}# +!........!!....T..## +##.T..T..##......T## +.........##..T...... +........T##......... +######...##...###### +####.....##.....#### +####..##]##]##..#### +####..########..#### \ No newline at end of file diff --git a/maps/rooms/40x40/complex_maze.hard b/maps/rooms/40x40/complex_maze.hard deleted file mode 100644 index e00f1c03..00000000 --- a/maps/rooms/40x40/complex_maze.hard +++ /dev/null @@ -1,40 +0,0 @@ -##############[######################### -###########............<################ -###########s##>.........F....########### -####vv###[#######F##F######..#####vv#### -..................##....###..#####...... -..................##....###..#####...... -####..################..###..#####..#]## -####..################..}##..#####..#### -####.......####.........###..#####..#### -######]##XX####-#####...###..####{..#### -####...............##...###..#####..#### -####..###..#######.##...###...XXX...#### -####..###..#######.##...###...XXX...#### -####..###..####....#{...###..########[## -......###..####.#.###...###............. -......###.......#.......###............. -####..###XX#########..#####..########### -####..##>..####.......####{..######[#### -####..###XX####.####..#####..###......## -#########.......####..#####..###......## -#########..####^####..#####..###..###### -#########..#########--##v##..###..###### -########{..###........##.##..###..###### -#########..###..##....##.h#..###..###### -......###..###..##....##-##..###..###... -......###..###..##....|...|.......###... -##]#--###XX###..##--##########]######..# -####..###..###..##..##.............X#..# -####--###..###..}#..##....##........#..# -####..###.......##..##....##........#..# -####..########..##..##....##.&......#..# -####--###.......##........##...........# -###{..###..###..########..############## -####--###..###..##[#####FF##########[### -...........}##.......................... -...........###.......................... -####..########..########..##]#####..#### -###{..########..########..########..#### -####..########..########..########..}### -####..########..########..########..#### \ No newline at end of file diff --git a/maps/test/anim_test.maze b/maps/test/anim_test.maze new file mode 100644 index 00000000..a222b0dd --- /dev/null +++ b/maps/test/anim_test.maze @@ -0,0 +1,9 @@ +####### +#.....# +#..3..# +#.....# +{.....} +#.....# +#.@...# +#.....# +####### \ No newline at end of file diff --git a/maps/test/big.maze b/maps/test/big.maze new file mode 100644 index 00000000..ae7c5c4b --- /dev/null +++ b/maps/test/big.maze @@ -0,0 +1,31 @@ +#####[########################################## +#...-.-.1...1..-...............................# +#..............................................# +#...@..*.....T.................................# +#..............................................# +{..............................................# +#...........{..............{.......{...........# +#..............................................# +#..............................................# +#..............................................# +#..............................................# +#...........{..................................# +#..........................{...................# +#..{...............................{...........# +#..............................................# +#..............................................# +#..............................................# +#..............................................# +#..............................................# +#..............................................# +#..............................................# +#..............................................# +#..............................................# +#..............................................# +#..............................................# +#..............................................# +#..............................................# +#..............................................# +#..............................................# +#..............................................# +################################################ \ No newline at end of file diff --git a/maps/test/hallway.maze b/maps/test/hallway.maze new file mode 100644 index 00000000..c8017ef8 --- /dev/null +++ b/maps/test/hallway.maze @@ -0,0 +1,50 @@ +####..#### +###{.o#### +####..#### +###!..!### +####..#### +####..#### +###!..!### +####..#### +####..}### +####..#### +####..#### +###{..#### +####..#### +###!..!### +####..#### +####..#### +###!..!### +####..#### +####..}### +####..#### +####..#### +###{..#### +####..#### +###!..!### +####..#### +####..#### +###!..!### +####..#### +####..}### +####*.#### +####..#### +###{..#### +####..#### +###!..!### +####..#### +####..#### +###!..!### +####..#### +####..}### +####..#### +####..#### +###{..#### +####..#### +###!..!### +####..#### +####..#### +###!..!### +####..#### +####@.}### +####..#### \ No newline at end of file diff --git a/maps/test/itemRoom.maze b/maps/test/itemRoom.maze index 6a4393d7..78c8c4f2 100644 --- a/maps/test/itemRoom.maze +++ b/maps/test/itemRoom.maze @@ -12,7 +12,7 @@ {................} #...@...@....@...# #................# -{................} -#...............o# -#>...............# +{......MM......XX} +#T.............Xo# +#>.............XX# ################## \ No newline at end of file diff --git a/maps/test/maze6.maze b/maps/test/maze6.maze index bd73811a..d4a0e592 100644 --- a/maps/test/maze6.maze +++ b/maps/test/maze6.maze @@ -1,5 +1,5 @@ -############# +###[######### #...........# -#..@..*...o.# +{..@........} #...........# -############# \ No newline at end of file +######]###### \ No newline at end of file diff --git a/maps/test/mirrorRoom.maze b/maps/test/mirrorRoom.maze new file mode 100644 index 00000000..9a376116 --- /dev/null +++ b/maps/test/mirrorRoom.maze @@ -0,0 +1,9 @@ +#######[############## +#...........@........# +#.M..................} +{...........*........# +#.........n..........# +{....................} +#........n...........# +#.............o......} +###################### \ No newline at end of file diff --git a/maps/test/orb_drop.maze b/maps/test/orb_drop.maze new file mode 100644 index 00000000..8a1e87df --- /dev/null +++ b/maps/test/orb_drop.maze @@ -0,0 +1,5 @@ +############### +#|####........# +#@*.|#........# +#....}........# +############### \ No newline at end of file diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index 742402f0..b96b8351 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -17,14 +17,16 @@ set(FILES audio/audiomanager.cpp camera.cpp client.cpp - cube.cpp util.cpp lobbyfinder.cpp shader.cpp model.cpp - lightsource.cpp renderable.cpp + + animation.cpp + animationmanager.cpp + bone.cpp ) # OpenGL diff --git a/src/client/animation.cpp b/src/client/animation.cpp new file mode 100644 index 00000000..bc5d5d21 --- /dev/null +++ b/src/client/animation.cpp @@ -0,0 +1,81 @@ +#include "client/animation.hpp" + +Animation::Animation(const std::string& animationPath, Model* model) { + Assimp::Importer importer; + const aiScene* scene = importer.ReadFile(animationPath, + aiProcess_Triangulate); + assert(scene && scene->mRootNode); + auto animation = scene->mAnimations[0]; + m_duration = animation->mDuration; + m_ticksPerSecond = animation->mTicksPerSecond; + aiMatrix4x4 globalTransformation = scene->mRootNode->mTransformation; + globalTransformation = globalTransformation.Inverse(); + readHierarchyData(m_rootNode, scene->mRootNode); + readMissingBones(animation, *model); +} + +Animation::Animation(const std::string& animationDirPath, const std::string& animName, int frames): + m_rootNode({}) +{ + // not used by this constructor + m_duration = 0; + m_ticksPerSecond = 0; + + for (int i = 1; i <= frames; i++) { + auto frame_model_path = animationDirPath + "/" + animName + std::to_string(i) + ".obj"; + auto frame_model = new Model(frame_model_path, true); + model_keyframes.push_back(frame_model); + } +} + +Bone* Animation::findBone(const std::string& name) { + auto iter = std::find_if(m_bones.begin(), m_bones.end(), + [&](const Bone& Bone) + { + return Bone.getBoneName() == name; + } + ); + if (iter == m_bones.end()) return nullptr; + else return &(*iter); +} + + +void Animation::readMissingBones(const aiAnimation* animation, Model& model) { + int size = animation->mNumChannels; + + auto& boneInfoMap = model.getBoneInfoMap(); //getting m_BoneInfoMap from Model class + int& boneCount = model.getBoneCount(); //getting the m_BoneCounter from Model class + + //reading channels(bones engaged in an animation and their keyframes) + for (int i = 0; i < size; i++) + { + auto channel = animation->mChannels[i]; + std::string boneName = channel->mNodeName.data; + + if (boneInfoMap.find(boneName) == boneInfoMap.end()) + { + boneInfoMap[boneName].id = boneCount; + boneCount++; + } + m_bones.push_back(Bone(channel->mNodeName.data, + boneInfoMap[channel->mNodeName.data].id, channel)); + } + + m_boneInfoMap = boneInfoMap; +} + +void Animation::readHierarchyData(AssimpNodeData& dest, const aiNode* src) { + assert(src); + + dest.name = src->mName.data; + dest.transformation = matrixToGLM(src->mTransformation); + dest.numChildren = src->mNumChildren; + + for (int i = 0; i < src->mNumChildren; i++) + { + AssimpNodeData newData; + readHierarchyData(newData, src->mChildren[i]); + dest.children.push_back(newData); + } +} + diff --git a/src/client/animationmanager.cpp b/src/client/animationmanager.cpp new file mode 100644 index 00000000..a3e37793 --- /dev/null +++ b/src/client/animationmanager.cpp @@ -0,0 +1,107 @@ +#include "client/animationmanager.hpp" + +AnimationManager::AnimationManager() { + currEntity = 0; + m_currentTime = 0.0; + m_currentAnimation = nullptr; + + m_deltaTime = 0; // Tyler: is this still used? linter complaining about it not being initialized + + m_finalBoneMatrices.reserve(100); + + for (int i = 0; i < 100; i++) + m_finalBoneMatrices.push_back(glm::mat4(1.0f)); + + currFrame = 0; + lastFrameTime = 0.0; +} + +void AnimationManager::updateAnimation(float dt) { + m_deltaTime = dt; + if (entityAnimMap[currEntity].second) { + m_currentAnimation = entityAnimMap[currEntity].second; + m_currentTime = entityAnimMap[currEntity].first; + m_currentTime += m_currentAnimation->getTicksPerSecond() * dt; + m_currentTime = fmod(m_currentTime, m_currentAnimation->getDuration()); + entityAnimMap[currEntity].first = m_currentTime; + calculateBoneTransform(&m_currentAnimation->getRootNode(), glm::mat4(1.0f)); + } +} + +Model* AnimationManager::updateFrameAnimation(float dt) { + if (entityAnimFrameMap[currEntity].second) { + m_currentAnimation = entityAnimFrameMap[currEntity].second; + m_currentTime = entityAnimFrameMap[currEntity].first; + m_currentTime += dt; + int currFrame = static_cast (m_currentTime / MAX_FRAME_TIME); + entityAnimFrameMap[currEntity].first = m_currentTime; + return m_currentAnimation->getFrame(currFrame); + } else { + return nullptr; + } +} + + +void AnimationManager::playAnimation(Animation* pAnimation) { + m_currentAnimation = pAnimation; + m_currentTime = 0.0f; +} + +void AnimationManager::calculateBoneTransform(const AssimpNodeData* node, glm::mat4 parentTransform) { + if (m_currentAnimation == nullptr) { + return; // note from tyler: added this check because no longer setting currentAnimation in constructor + } + + std::string nodeName = node->name; + glm::mat4 nodeTransform = node->transformation; + + Bone* bone = m_currentAnimation->findBone(nodeName); + + if (bone) + { + bone->update(m_currentTime); + nodeTransform = bone->getLocalTransform(); + } + + glm::mat4 globalTransformation = parentTransform * nodeTransform; + + auto boneInfoMap = m_currentAnimation->getBoneIDMap(); + glm::mat4 rootTransform = m_currentAnimation->getRootNode().transformation; + + if (boneInfoMap.find(nodeName) != boneInfoMap.end()) + { + int index = boneInfoMap[nodeName].id; + glm::mat4 offset = boneInfoMap[nodeName].offset; + m_finalBoneMatrices[index] = globalTransformation * offset; + } + + for (int i = 0; i < node->numChildren; i++) + calculateBoneTransform(&node->children[i], globalTransformation); +} + +void AnimationManager::setAnimation(EntityID id, ModelType modelType, AnimState animState) { + if (entityAnimMap.find(id) == entityAnimMap.end() || entityAnimMap[id].second != objAnimMap[modelType][animState]) { + entityAnimMap[id] = std::make_pair(0.0f, objAnimMap[modelType][animState]); + } + currEntity = id; +} + +void AnimationManager::setFrameAnimation(EntityID id, ModelType modelType, AnimState animState) { + if (entityAnimFrameMap.find(id) == entityAnimFrameMap.end() || entityAnimFrameMap[id].second != objAnimMap[modelType][animState]) { + static std::random_device dev; + static std::mt19937 rng(dev()); + static std::uniform_int_distribution dist(0,51); + entityAnimFrameMap[id] = std::make_pair(dist(rng) * MAX_FRAME_TIME, objAnimMap[modelType][animState]); + } + currEntity = id; +} + +void AnimationManager::addAnimation(Animation* anim, ModelType modelType, AnimState animState) { + if (objAnimMap.find(modelType) == objAnimMap.end()) { + std::unordered_map animMap; + objAnimMap[modelType] = animMap; + } + + this->objAnimMap[modelType][animState] = anim; +} + diff --git a/src/client/audio/audiomanager.cpp b/src/client/audio/audiomanager.cpp index b5322890..87371a45 100644 --- a/src/client/audio/audiomanager.cpp +++ b/src/client/audio/audiomanager.cpp @@ -103,7 +103,9 @@ void AudioManager::doTick(glm::vec3 player_pos, auto source = light_sources.at(i); if (source.has_value() && source->type == ObjectType::Torchlight) { this->serverLightSFXs.at(i)->setPosition(source->physics.corner.x, source->physics.corner.y, source->physics.corner.z); - this->serverLightSFXs.at(i)->play(); + if (this->serverLightSFXs.at(i)->getStatus() != sf::SoundSource::Status::Playing) { + this->serverLightSFXs.at(i)->play(); + } } else { this->serverLightSFXs.at(i)->stop(); } diff --git a/src/client/bone.cpp b/src/client/bone.cpp new file mode 100644 index 00000000..c84c94bf --- /dev/null +++ b/src/client/bone.cpp @@ -0,0 +1,125 @@ +#include "client/bone.hpp" + +Bone::Bone(const std::string& name, int ID, const aiNodeAnim* channel) : m_name(name), m_ID(ID), m_localTransform(1.0f) { + + m_numPositions = channel->mNumPositionKeys; + + for (int positionIndex = 0; positionIndex < m_numPositions; ++positionIndex) + { + aiVector3D aiPosition = channel->mPositionKeys[positionIndex].mValue; + float timeStamp = channel->mPositionKeys[positionIndex].mTime; + keyPosition data; + data.position = getGLMVec(aiPosition); + data.timeStamp = timeStamp; + m_positions.push_back(data); + } + + m_numRotations = channel->mNumRotationKeys; + for (int rotationIndex = 0; rotationIndex < m_numRotations; ++rotationIndex) + { + aiQuaternion aiOrientation = channel->mRotationKeys[rotationIndex].mValue; + float timeStamp = channel->mRotationKeys[rotationIndex].mTime; + keyRotation data; + data.orientation = getGLMQuat(aiOrientation); + data.timeStamp = timeStamp; + m_rotations.push_back(data); + } + + m_numScalings = channel->mNumScalingKeys; + for (int keyIndex = 0; keyIndex < m_numScalings; ++keyIndex) + { + aiVector3D scale = channel->mScalingKeys[keyIndex].mValue; + float timeStamp = channel->mScalingKeys[keyIndex].mTime; + keyScale data; + data.scale = getGLMVec(scale); + data.timeStamp = timeStamp; + m_scales.push_back(data); + } +} + +void Bone::update(float animationTime) { + glm::mat4 translation = interpolatePosition(animationTime); + glm::mat4 rotation = interpolateRotation(animationTime); + glm::mat4 scale = interpolateScaling(animationTime); + m_localTransform = translation * rotation * scale; +} + +int Bone::getPositionIndex(float animationTime) { + for (int index = 0; index < m_numPositions - 1; ++index) + { + if (animationTime < m_positions[index + 1].timeStamp) + return index; + } + assert(0); +} + +int Bone::getRotationIndex(float animationTime) { + for (int index = 0; index < m_numRotations - 1; ++index) + { + if (animationTime < m_rotations[index + 1].timeStamp) + return index; + } + assert(0); +} + +int Bone::getScaleIndex(float animationTime) { + for (int index = 0; index < m_numScalings - 1; ++index) + { + if (animationTime < m_scales[index + 1].timeStamp) + return index; + } + assert(0); +} + +float Bone::getScaleFactor(float lastTimeStamp, float nextTimeStamp, float animationTime) { + float scaleFactor = 0.0f; + float midWayLength = animationTime - lastTimeStamp; + float framesDiff = nextTimeStamp - lastTimeStamp; + scaleFactor = midWayLength / framesDiff; + return scaleFactor; +} + + +glm::mat4 Bone::interpolatePosition(float animationTime) { + if (1 == m_numPositions) + return glm::translate(glm::mat4(1.0f), m_positions[0].position); + + int p0Index = getPositionIndex(animationTime); + int p1Index = p0Index + 1; + float scaleFactor = getScaleFactor(m_positions[p0Index].timeStamp, + m_positions[p1Index].timeStamp, animationTime); + glm::vec3 finalPosition = glm::mix(m_positions[p0Index].position, m_positions[p1Index].position + , scaleFactor); + return glm::translate(glm::mat4(1.0f), finalPosition); +} + +glm::mat4 Bone::interpolateRotation(float animationTime) { + if (1 == m_numRotations) + { + auto rotation = glm::normalize(m_rotations[0].orientation); + return glm::toMat4(rotation); + } + + int p0Index = getRotationIndex(animationTime); + int p1Index = p0Index + 1; + float scaleFactor = getScaleFactor(m_rotations[p0Index].timeStamp, + m_rotations[p1Index].timeStamp, animationTime); + glm::quat finalRotation = glm::slerp(m_rotations[p0Index].orientation, m_rotations[p1Index].orientation + , scaleFactor); + finalRotation = glm::normalize(finalRotation); + return glm::toMat4(finalRotation); + +} + +glm::mat4 Bone::interpolateScaling(float animationTime) { + if (1 == m_numScalings) + return glm::scale(glm::mat4(1.0f), m_scales[0].scale); + + int p0Index = getScaleIndex(animationTime); + int p1Index = p0Index + 1; + float scaleFactor = getScaleFactor(m_scales[p0Index].timeStamp, + m_scales[p1Index].timeStamp, animationTime); + glm::vec3 finalScale = glm::mix(m_scales[p0Index].scale, m_scales[p1Index].scale + , scaleFactor); + return glm::scale(glm::mat4(1.0f), finalScale); +} \ No newline at end of file diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 7a8b33a3..1eefe444 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -67,9 +67,9 @@ void Camera::update(float xpos, float ypos) { pitch = -70.0f; glm::vec3 front; - front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch)); + front.x = cos(glm::radians(yaw + 90.0f)) * cos(glm::radians(pitch)); front.y = sin(glm::radians(pitch)); - front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch)); + front.z = sin(glm::radians(yaw + 90.0f)) * cos(glm::radians(pitch)); cameraFront = glm::normalize(front); glm::vec3 oldCamUp = cameraUp; @@ -97,6 +97,10 @@ void Camera::updatePos(glm::vec3 pos) { cameraPos = pos; } +void Camera::setPitch(float pitch) { + this->pitch = pitch; +} + DungeonMasterCamera::DungeonMasterCamera() : Camera() { pitch = -89.0f; this->farClip = 500.0f; @@ -148,4 +152,5 @@ void DungeonMasterCamera::update(float xpos, float ypos) { this->view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp); viewProjMat = this->projection * this->view; -} \ No newline at end of file +} + diff --git a/src/client/client.cpp b/src/client/client.cpp index d1ee5a85..4de23bec 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -19,13 +20,15 @@ #include "client/constants.hpp" #include -#include "client/lightsource.hpp" #include "client/shader.hpp" #include "client/model.hpp" #include "glm/fwd.hpp" #include "server/game/object.hpp" #include "server/game/solidsurface.hpp" +#include "shared/game/constants.hpp" +#include "shared/game/dir_light.hpp" #include "shared/game/event.hpp" +#include "shared/game/sharedmodel.hpp" #include "shared/game/sharedobject.hpp" #include "shared/network/constants.hpp" #include "shared/utilities/rng.hpp" @@ -37,6 +40,7 @@ #include "shared/game/celltype.hpp" #include "shared/utilities/timer.hpp" #include "shared/utilities/typedefs.hpp" +#include "shared/network/session.hpp" #define GLM_ENABLE_EXPERIMENTAL #include "glm/ext/matrix_clip_space.hpp" @@ -46,10 +50,13 @@ #include #include +#define M_PI 3.14159265358979323846 using namespace boost::asio::ip; using namespace std::chrono_literals; +bool firstPos = true; + // Checker for events sent / later can be made in an array glm::vec3 sentCamMovement = glm::vec3(-1.0f); @@ -66,26 +73,35 @@ Client::Client(boost::asio::io_context& io_context, GameConfig config): config(config), gameState(GamePhase::TITLE_SCREEN, config), session(nullptr), - gui(this), + gui(this, config), gui_state(gui::GUIState::INITIAL_LOAD), lobby_finder(io_context, config), cam(new Camera()) { + + + // Initialize Client's GUIState::Lobby related state + // Initial lobby player state is set to connected (this assumes that whenever + // GUIState is set to GUIState::Lobby, the client is connected to a lobby) + this->lobbyPlayerState = LobbyPlayerState::Connected; + + // Initial GUIState::Lobby player status table role selection radio button + // state (none of the radio buttons are selected) + this->roleSelection = RadioButtonState::NoneSelected; audioManager = new AudioManager(); - Client::window_width = config.client.window_width; - Client::window_height = static_cast((config.client.window_width * 2.0f) / 3.0f); - if (config.client.lobby_discovery) { lobby_finder.startSearching(); } + + phase_change = false; } AudioManager* Client::getAudioManager() { return this->audioManager; } -bool Client::connectAndListen(std::string ip_addr) { +bool Client::connect(std::string ip_addr) { this->endpoints = resolver.resolve(ip_addr, std::to_string(config.network.server_port)); this->session = std::make_shared(std::move(this->socket), SessionInfo(this->config.client.default_name, {}, {})); @@ -102,9 +118,8 @@ bool Client::connectAndListen(std::string ip_addr) { auto packet = PackagedPacket::make_shared(PacketType::ClientDeclareInfo, ClientDeclareInfoPacket { .player_name = name }); - this->session->sendPacketAsync(packet); + this->session->sendPacket(packet); - this->session->startListen(); return true; } @@ -120,12 +135,23 @@ bool Client::init() { return false; } - /* Create a windowed mode window and its OpenGL context */ + GLFWmonitor* monitor; + if (config.client.fullscreen) { + monitor = glfwGetPrimaryMonitor(); + const GLFWvidmode * mode = glfwGetVideoMode(monitor); + Client::window_width = mode->width; + Client::window_height = mode->height; + } else { // windowed + monitor = NULL; + Client::window_width = UNIT_WINDOW_WIDTH; + Client::window_height = UNIT_WINDOW_HEIGHT; + } + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - window = glfwCreateWindow(Client::window_width, Client::window_height, "Arcana", NULL, NULL); - if (!window) - { + window = glfwCreateWindow(Client::window_width, Client::window_height, "Wrath of Zeus", monitor, NULL); + if (!window) { + std::cerr << "could not create window" << std::endl; glfwTerminate(); return false; } @@ -151,61 +177,150 @@ bool Client::init() { return false; } + // display first frame of the loading screen this->displayCallback(); auto shaders_dir = getRepoRoot() / "src/client/shaders"; auto graphics_assets_dir = getRepoRoot() / "assets/graphics"; - auto cube_vert_path = shaders_dir / "cube.vert"; - auto cube_frag_path = shaders_dir / "cube.frag"; - this->cube_shader = std::make_shared(cube_vert_path.string(), cube_frag_path.string()); - + auto player_models_dir = graphics_assets_dir / "player_models"; + auto item_models_dir = graphics_assets_dir / "item_models"; + auto env_models_dir = graphics_assets_dir / "env_models"; + auto entity_models_dir = graphics_assets_dir / "entity_models"; + + auto deferred_geometry_vert_path = shaders_dir / "deferred_geometry.vert"; + auto deferred_geometry_frag_path = shaders_dir / "deferred_geometry.frag"; + this->deferred_geometry_shader = std::make_shared(deferred_geometry_vert_path.string(), deferred_geometry_frag_path.string()); + + auto deferred_lighting_vert_path = shaders_dir / "deferred_lighting.vert"; + auto deferred_lighting_frag_path = shaders_dir / "deferred_lighting.frag"; + auto dm_deferred_lighting_frag_path = shaders_dir / "dm_deferred_lighting.frag"; + this->deferred_lighting_shader = std::make_shared(deferred_lighting_vert_path.string(), deferred_lighting_frag_path.string()); + this->dm_deferred_lighting_shader = std::make_shared(deferred_lighting_vert_path.string(), dm_deferred_lighting_frag_path.string()); + + auto deferred_light_box_vert_path = shaders_dir / "deferred_light_box.vert"; + auto deferred_light_box_frag_path = shaders_dir / "deferred_light_box.frag"; + this->deferred_light_box_shader = std::make_shared(deferred_light_box_vert_path.string(), deferred_light_box_frag_path.string()); + auto floor_model_path = env_models_dir / "floor-normal.obj"; + this->floor_model = std::make_unique(floor_model_path.string(), true); - auto model_vert_path = shaders_dir / "model.vert"; - auto model_frag_path = shaders_dir / "model.frag"; - this->model_shader = std::make_shared(model_vert_path.string(), model_frag_path.string()); + auto wall_model_path = env_models_dir / "wall-tops.obj"; + this->wall_model = std::make_unique(wall_model_path.string(), true); - auto cube_model_path = graphics_assets_dir / "cube.obj"; - this->cube_model = std::make_unique(cube_model_path.string()); + auto pillar_model_path = env_models_dir / "pillar-tops.obj"; + this->pillar_model = std::make_unique(pillar_model_path.string(), true); - auto bear_model_path = graphics_assets_dir / "bear-sp22.obj"; - this->bear_model = std::make_unique(bear_model_path.string()); - // this->bear_model->scaleAbsolute(0.25); + auto torchlight_model_path = env_models_dir / "exit.obj"; + this->torchlight_model = std::make_unique(torchlight_model_path.string(), true); - auto player_model_path = graphics_assets_dir / "Fire-testing.obj"; - this->player_model = std::make_unique(player_model_path.string()); - this->player_model->scaleAbsolute(0.25); + auto torchpost_model_path = env_models_dir / "Torch" / "Torch.obj"; + this->torchpost_model = std::make_unique(torchpost_model_path.string(), false); - this->light_source = std::make_unique(); + auto slime_model_path = entity_models_dir / "slime.obj"; + this->slime_model = std::make_unique(slime_model_path.string(), true); - auto lightVertFilepath = shaders_dir / "lightsource.vert"; - auto lightFragFilepath = shaders_dir / "lightsource.frag"; - this->light_source_shader = std::make_shared(lightVertFilepath.string(), lightFragFilepath.string()); + auto bear_model_path = entity_models_dir / "bear-sp22.obj"; + this->bear_model = std::make_unique(bear_model_path.string(), true); - auto torchlight_model_path = graphics_assets_dir / "cube.obj"; - this->torchlight_model = std::make_unique(torchlight_model_path.string()); + auto sungod_model_path = entity_models_dir / "sungod.obj"; + this->sungod_model = std::make_unique(sungod_model_path.string(), true); - auto solid_surface_vert_path = shaders_dir / "solidsurface.vert"; - auto solid_surface_frag_path = shaders_dir / "solidsurface.frag"; - this->solid_surface_shader = std::make_shared(solid_surface_vert_path.string(), solid_surface_frag_path.string()); + auto python_model_path = entity_models_dir / "flying-python.obj"; + this->python_model = std::make_unique(python_model_path.string(), true); - auto wall_model_path = graphics_assets_dir / "wall.obj"; - this->wall_model = std::make_unique(wall_model_path.string()); - auto wall_vert_path = shaders_dir / "wall.vert"; - auto wall_frag_path = shaders_dir / "wall.frag"; - this->wall_shader = std::make_shared(wall_vert_path.string(), wall_frag_path.string()); + auto item_model_path = item_models_dir / "item.obj"; + this->item_model = std::make_unique(item_model_path.string(), true); - auto dm_cube_frag_path = shaders_dir / "dm_cube.frag"; - this->dm_cube_shader = std::make_shared(wall_vert_path.string(), dm_cube_frag_path.string()); + auto spike_trap_model_path = env_models_dir / "Spike_trap" / "Trap.obj"; + this->spike_trap_model = std::make_unique(spike_trap_model_path.string(), false); - auto pillar_model_path = graphics_assets_dir / "pillar.obj"; - this->pillar_model = std::make_unique(pillar_model_path.string()); + auto lava_cross_model_path = env_models_dir / "lava" / "lava_cross.obj"; + this->lava_cross_model = std::make_unique(lava_cross_model_path.string(), false); - this->gui_state = GUIState::TITLE_SCREEN; + auto chest_model_path = item_models_dir / "Chest1" / "Chest1 1.obj"; + this->chest_model = std::make_unique(chest_model_path.string(), false); + + auto lava_row_model_path = env_models_dir / "lava" / "lava_row.obj"; + this->lava_horizontal_model = std::make_unique(lava_row_model_path.string(), false); + // thank you openai! + this->lava_horizontal_model->rotateAbsolute(rotate90DegreesAroundYAxis(glm::vec3(1.0f, 0.0f, 0.0f))); + this->lava_vertical_model = std::make_unique(lava_row_model_path.string(), false); + + auto arrow_model_path = entity_models_dir / "Arrow_red" / "Arrow.obj"; + this->arrow_model = std::make_unique(arrow_model_path.string(), false); + + auto arrow_trap_model_path = env_models_dir / "Arrow trap" / "levelout_21.obj"; + this->arrow_trap_model = std::make_unique(arrow_trap_model_path.string(), false); + + auto orb_model_path = item_models_dir / "orb_final/Orb 1.obj"; + this->orb_model = std::make_unique(orb_model_path.string(), false); + + auto teleport_model_path = env_models_dir / "teleport" / "teleport.obj"; + this->teleport_model = std::make_unique(teleport_model_path.string(), true); + + auto mirror_model_path = item_models_dir / "Mirror" / "mirror_rotated.obj"; + this->mirror_model = std::make_unique(mirror_model_path.string(), false); + + auto exit_model_path = env_models_dir / "exit.obj"; + this->exit_model = std::make_unique(exit_model_path.string(), true); + + auto lightning_model_path = env_models_dir / "Lightning1" / "Lightning1_translated.obj"; + this->lightning_model = std::make_unique(lightning_model_path.string(), true); + + auto player_walk_path = graphics_assets_dir / "animations/walk.fbx"; + auto player_jump_path = graphics_assets_dir / "animations/jump.fbx"; + auto player_idle_path = graphics_assets_dir / "animations/idle.fbx"; + auto player_run_path = graphics_assets_dir / "animations/run.fbx"; + auto player_atk_path = graphics_assets_dir / "animations/slash.fbx"; + auto player_use_potion_path = graphics_assets_dir / "animations/drink.fbx"; + auto torchlight_anim_path = graphics_assets_dir / "animations/fire-fix"; + auto fireball_anim_path = graphics_assets_dir / "animations/fireball"; + + auto fire_player_model_path = player_models_dir / "char_1_rename/char1.fbx"; + auto lightning_player_model_path = player_models_dir / "char_2_rename/char2.fbx"; + auto water_player_model_path = player_models_dir / "char_3/model_char_3.fbx"; + + this->fire_player_model = std::make_unique(fire_player_model_path.string(), false); + this->lightning_player_model = std::make_unique(lightning_player_model_path.string(), false); + this->water_player_model = std::make_unique(water_player_model_path.string(), false); + + const int NUM_PLAYER_MODELS = 3; + std::array, NUM_PLAYER_MODELS> player_models = { + // for some reason the first one only works if it is std::make_pair but the other two are fine with the + // curly brace syntax... WTF, just making them all use std::make_pair so they line up + std::make_pair(ModelType::PlayerFire, this->fire_player_model.get()), + std::make_pair(ModelType::PlayerLightning, this->lightning_player_model.get()), + std::make_pair(ModelType::PlayerWater, this->water_player_model.get()) + }; + + animManager = new AnimationManager(); + Animation* torchlight_animation = new Animation(torchlight_anim_path.string(), "fire-fix", 45); + animManager->addAnimation(torchlight_animation, ModelType::Torchlight, AnimState::IdleAnim); + Animation* fireball_animation = new Animation(fireball_anim_path.string(), "fireball", 60); + animManager->addAnimation(fireball_animation, ModelType::Fireball, AnimState::IdleAnim); + animManager->addAnimation(fireball_animation, ModelType::SpellOrb, AnimState::IdleAnim); + for (int i = 0; i < NUM_PLAYER_MODELS; i++) { + Animation* player_walk = new Animation(player_walk_path.string(), player_models.at(i).second); + Animation* player_jump = new Animation(player_jump_path.string(), player_models.at(i).second); + Animation* player_idle = new Animation(player_idle_path.string(), player_models.at(i).second); + Animation* player_run = new Animation(player_run_path.string(), player_models.at(i).second); + Animation* player_atk = new Animation(player_atk_path.string(), player_models.at(i).second); + Animation* player_use_potion = new Animation(player_use_potion_path.string(), player_models.at(i).second); + + animManager->addAnimation(player_walk, player_models.at(i).first, AnimState::WalkAnim); + animManager->addAnimation(player_jump, player_models.at(i).first, AnimState::JumpAnim); + animManager->addAnimation(player_idle, player_models.at(i).first, AnimState::IdleAnim); + animManager->addAnimation(player_run, player_models.at(i).first, AnimState::SprintAnim); + animManager->addAnimation(player_atk, player_models.at(i).first, AnimState::AttackAnim); + animManager->addAnimation(player_use_potion, player_models.at(i).first, AnimState::DrinkPotionAnim); + } + + this->configureGBuffer(); this->audioManager->init(); - this->audioManager->playMusic(ClientMusic::TitleTheme); + + this->displayCallback(); return true; } @@ -230,15 +345,18 @@ void Client::displayCallback() { this->gui.beginFrame(); if (this->gameState.phase == GamePhase::TITLE_SCREEN) { - glClearColor(0.8f, 0.8f, 0.8f, 1.0f); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); } else if (this->gameState.phase == GamePhase::LOBBY) { - glClearColor(0.8f, 0.8f, 0.8f, 1.0f); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + } else if (this->gameState.phase == GamePhase::INTRO_CUTSCENE) { + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + this->draw(); } else if (this->gameState.phase == GamePhase::GAME) { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); this->draw(); } else if (this->gameState.phase == GamePhase::RESULTS) { - if (this->gui_state == GUIState::GAME_HUD) + if (this->gui_state == GUIState::GAME_HUD || this->gui_state == GUIState::DEAD_SCREEN) this->gui_state = GUIState::RESULTS_SCREEN; this->draw(); } @@ -246,7 +364,7 @@ void Client::displayCallback() { this->setWorldPos(); this->gui.layoutFrame(this->gui_state); - this->gui.handleInputs(mouse_xpos, mouse_ypos, is_left_mouse_down); + this->gui.handleInputs(mouse_xpos, mouse_ypos, is_left_mouse_down, is_right_mouse_down); this->gui.renderFrame(); /* Poll for and process events */ @@ -254,8 +372,62 @@ void Client::displayCallback() { glfwSwapBuffers(window); } + +void Client::sendTrapEvent(bool hover, bool place, ModelType trapType) { + auto eid = this->session->getInfo().client_eid.value(); + + switch (trapType) { + case ModelType::FloorSpikeFull: + this->session->sendEvent(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::FloorSpikeFull, hover, place))); + break; + case ModelType::FloorSpikeVertical: + this->session->sendEvent(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::FloorSpikeVertical, hover, place))); + break; + case ModelType::FloorSpikeHorizontal: + this->session->sendEvent(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::FloorSpikeHorizontal, hover, place))); + break; + case ModelType::FireballTrapUp: + this->session->sendEvent(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::FireballTrapUp, hover, place))); + break; + case ModelType::FireballTrapDown: + this->session->sendEvent(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::FireballTrapDown, hover, place))); + break; + case ModelType::FireballTrapLeft: + this->session->sendEvent(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::FireballTrapLeft, hover, place))); + break; + case ModelType::FireballTrapRight: + this->session->sendEvent(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::FireballTrapRight, hover, place))); + break; + case ModelType::ArrowTrapUp: + this->session->sendEvent(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::ArrowTrapUp, hover, place))); + break; + case ModelType::ArrowTrapDown: + this->session->sendEvent(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::ArrowTrapDown, hover, place))); + break; + case ModelType::ArrowTrapLeft: + this->session->sendEvent(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::ArrowTrapLeft, hover, place))); + break; + case ModelType::ArrowTrapRight: + this->session->sendEvent(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::ArrowTrapRight, hover, place))); + break; + case ModelType::SpikeTrap: + this->session->sendEvent(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::SpikeTrap, hover, place))); + break; + case ModelType::TeleporterTrap: + this->session->sendEvent(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::TeleporterTrap, hover, place))); + break; + case ModelType::Lightning: + this->session->sendEvent(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::Lightning, hover, place))); + break; + case ModelType::LightCut: + this->session->sendEvent(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::LightCut, hover, place))); + break; + } + +} + // Handle any updates -void Client::idleCallback(boost::asio::io_context& context) { +void Client::idleCallback() { // have to do before processing server input because if the phase changes // in this call to process server input, we should just skip this first // inbetween phase and don't get any input because we might get a serialization @@ -263,7 +435,10 @@ void Client::idleCallback(boost::asio::io_context& context) { GamePhase rendered_phase = this->gameState.phase; if (this->session != nullptr) { - processServerInput(context); + // only defer packets to next frame if we've already loaded the entire initial game, + // which is signified by having received our EID from the server + bool allow_defer = this->session->getInfo().client_eid.has_value(); + processServerInput(allow_defer); } // If we aren't in the middle of the game then we shouldn't capture any movement info @@ -272,17 +447,38 @@ void Client::idleCallback(boost::asio::io_context& context) { if (this->gui_state != GUIState::GAME_HUD) { return; } - glm::vec3 cam_movement = glm::vec3(0.0f); + glm::vec3 move_dir(0.0f); + bool moved = false; // Sets a direction vector - if(is_held_right) - cam_movement += cam->move(true, 1.0f); - if(is_held_left) - cam_movement += cam->move(true, -1.0f); - if (is_held_up) - cam_movement += cam->move(false, 1.0f); - if (is_held_down) - cam_movement += cam->move(false, -1.0f); + if(is_held_right) { + move_dir.x += 1.0f; + moved = true; + } + if(is_held_left) { + move_dir.x -= 1.0f; + moved = true; + } + if (is_held_up) { + move_dir.z += 1.0f; + moved = true; + } + if (is_held_down) { + move_dir.z -= 1.0f; + moved = true; + } + + glm::vec3 cam_movement(0.0f); + + if (moved) { + move_dir = glm::normalize(move_dir); + cam_movement += cam->move(true, move_dir.x); + cam_movement += cam->move(false, move_dir.z); + } + + if (std::isnan(cam_movement.x) || std::isnan(cam_movement.y) || std::isnan(cam_movement.z)) { + cam_movement = glm::vec3(0.0f); + } // Update camera facing direction cam->update(mouse_xpos, mouse_ypos); @@ -291,89 +487,200 @@ void Client::idleCallback(boost::asio::io_context& context) { if (this->session != nullptr && this->session->getInfo().client_eid.has_value()) { auto eid = this->session->getInfo().client_eid.value(); - this->session->sendEventAsync(Event(eid, EventType::ChangeFacing, ChangeFacingEvent(eid, cam->getFacing()))); + this->session->sendEvent(Event(eid, EventType::ChangeFacing, ChangeFacingEvent(eid, cam->getFacing()))); // Send jump action if (is_held_space) { - this->session->sendEventAsync(Event(eid, EventType::StartAction, StartActionEvent(eid, glm::vec3(0.0f, 1.0f, 0.0f), ActionType::Jump))); + this->session->sendEvent(Event(eid, EventType::StartAction, StartActionEvent(eid, glm::vec3(0.0f, 1.0f, 0.0f), ActionType::Jump))); } - if (this->session->getInfo().is_dungeon_master.value()) { + // DM not placing + if (this->session->getInfo().is_dungeon_master.value() && !is_left_mouse_down) { + // zoom in if (is_held_i) { - this->session->sendEventAsync(Event(eid, EventType::StartAction, StartActionEvent(eid, glm::vec3(0.0f, -1.0f, 0.0f), ActionType::Zoom))); + this->session->sendEvent(Event(eid, EventType::StartAction, StartActionEvent(eid, glm::vec3(0.0f, -1.0f, 0.0f), ActionType::Zoom))); } + + // zoom out if (is_held_o) { - this->session->sendEventAsync(Event(eid, EventType::StartAction, StartActionEvent(eid, glm::vec3(0.0f, 1.0f, 0.0f), ActionType::Zoom))); + this->session->sendEvent(Event(eid, EventType::StartAction, StartActionEvent(eid, glm::vec3(0.0f, 1.0f, 0.0f), ActionType::Zoom))); + } + + auto obj = this->gameState.objects.at(eid); + + auto model = obj->trapInventoryInfo->inventory[obj->trapInventoryInfo->selected - 1]; + + // udpate orientation if right clicked + if (is_right_mouse_down) { + this->orientation = (this->orientation + 1) % 4; // 4 possible directions + } + + if (model == ModelType::SunGod) { + const ModelType sunGodCellType[] = { ModelType::FireballTrapUp, ModelType::FireballTrapRight, ModelType::FireballTrapDown, ModelType::FireballTrapLeft }; + + model = sunGodCellType[this->orientation]; } - // send one event - if ((is_held_down || is_held_i || is_held_left || is_held_right || is_held_up || is_held_o) && is_pressed_p) - this->session->sendEventAsync(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::FloorSpikeFull, true, false))); + if (model == ModelType::ArrowTrap) { + const ModelType arrowTrapCellType[] = { ModelType::ArrowTrapUp, ModelType::ArrowTrapRight, ModelType::ArrowTrapDown, ModelType::ArrowTrapLeft }; + + model = arrowTrapCellType[this->orientation]; + } + + // send a trap event regardless if DM + sendTrapEvent(true, false, model); } - if (this->session->getInfo().is_dungeon_master.value() && is_pressed_p && is_left_mouse_down) { - auto self = this->gameState.objects.at(eid); + // DM actually placing now + if (this->session->getInfo().is_dungeon_master.value() && is_left_mouse_down) { + auto obj = this->gameState.objects.at(eid); - auto selectedTrap = self->trapInventoryInfo->inventory[self->trapInventoryInfo->selected - 1]; + auto model = obj->trapInventoryInfo->inventory[obj->trapInventoryInfo->selected - 1]; - switch (selectedTrap) { - case ModelType::FloorSpikeFull: - this->session->sendEventAsync(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::FloorSpikeFull, false, true))); - break; - case ModelType::FloorSpikeVertical: - this->session->sendEventAsync(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::FloorSpikeVertical, false, true))); - break; - case ModelType::FloorSpikeHorizontal: - this->session->sendEventAsync(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::FloorSpikeHorizontal, false, true))); - break; - case ModelType::FireballTrap: - this->session->sendEventAsync(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::FireballTrap, false, true))); - break; - case ModelType::SpikeTrap: - this->session->sendEventAsync(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::SpikeTrap, false, true))); - break; + if (model == ModelType::SunGod) { + const ModelType sunGodCellType[] = { ModelType::FireballTrapUp, ModelType::FireballTrapRight, ModelType::FireballTrapDown, ModelType::FireballTrapLeft }; + + model = sunGodCellType[this->orientation]; + } + + if (model == ModelType::ArrowTrap) { + const ModelType arrowTrapCellType[] = { ModelType::ArrowTrapUp, ModelType::ArrowTrapRight, ModelType::ArrowTrapDown, ModelType::ArrowTrapLeft }; + + model = arrowTrapCellType[this->orientation]; } + + sendTrapEvent(false, true, model); } // If movement 0, send stopevent if ((sentCamMovement != cam_movement) && cam_movement == glm::vec3(0.0f)) { - this->session->sendEventAsync(Event(eid, EventType::StopAction, StopActionEvent(eid, cam_movement, ActionType::MoveCam))); + this->session->sendEvent(Event(eid, EventType::StopAction, StopActionEvent(eid, cam_movement, ActionType::MoveCam))); sentCamMovement = cam_movement; } // If movement detected, different from previous, send start event else if (sentCamMovement != cam_movement) { - this->session->sendEventAsync(Event(eid, EventType::StartAction, StartActionEvent(eid, cam_movement, ActionType::MoveCam))); + this->session->sendEvent(Event(eid, EventType::StartAction, StartActionEvent(eid, cam_movement, ActionType::MoveCam))); sentCamMovement = cam_movement; } } } -void Client::processServerInput(boost::asio::io_context& context) { - context.run_for(5ms); +void Client::processServerInput(bool allow_defer) { // probably want to put rendering logic inside of client, so that this main function // mimics the server one where all of the important logic is done inside of a run command // But this is a demo of how you could use the client session to get information from // the game state - for (Event event : this->session->getEvents()) { + const static std::chrono::milliseconds MIN_DEFER_TIME = 5ms; + static std::chrono::milliseconds defer_time = MIN_DEFER_TIME; + bool had_to_defer = false; + + auto start = std::chrono::system_clock::now(); + + auto events = this->session->handleAllReceivedPackets(); + + for (const auto& event : events) { + this->events_received.push_back(event); + } + + while (!this->events_received.empty()) { + const Event& event = this->events_received.front(); + if (event.type == EventType::LoadGameState) { GamePhase old_phase = this->gameState.phase; this->gameState.update(boost::get(event.data).state); - // Change the UI to the game hud UI whenever we change into the GAME game phase - if (old_phase != GamePhase::GAME && this->gameState.phase == GamePhase::GAME) { - // set to Dungeon Master POV if DM - if (this->session->getInfo().is_dungeon_master.has_value() && this->session->getInfo().is_dungeon_master.value()) { - this->cam = std::make_unique(); - // TODO: fix race condition where this doesn't get received in time when reconnecting because the server is doing way more stuff and is delayed + if (!this->session->getInfo().is_dungeon_master.has_value()) { + if (old_phase != GamePhase::GAME && this->gameState.phase == GamePhase::GAME) { + phase_change = true; } + } + else { + if (phase_change || (old_phase != GamePhase::GAME && this->gameState.phase == GamePhase::GAME)) { + std::cout << "game phase change!" << std::endl; + + // Stop play menu theme music + audioManager->stopMusic(ClientMusic::MenuTheme); + + // set to Dungeon Master POV if DM + if (this->session->getInfo().is_dungeon_master.value()) { + this->cam = std::make_unique(); + + // Play Maze Exploration theme (DM) + audioManager->playMusic(ClientMusic::MazeExplorationDMTheme); - this->gui_state = GUIState::GAME_HUD; + // TODO: fix race condition where this doesn't get received in time when reconnecting because the server is doing way more stuff and is delayed + } + else { + // If player, play Maze Exploration theme (players) + audioManager->playMusic(ClientMusic::MazeExplorationPlayersTheme); + } - audioManager->stopMusic(ClientMusic::TitleTheme); - audioManager->playMusic(ClientMusic::GameTheme); + this->gui_state = GUIState::GAME_HUD; + + phase_change = false; + } + else if (this->gameState.phase == GamePhase::GAME || this->gameState.phase == GamePhase::RESULTS) { + // TODO: Keep track of the match phase and who won the match to play the correct client + // music + + if (this->gameState.phase == GamePhase::GAME) { + // Keep track of the match phase from the previous tick (though initialize to current tick + // for initialization) + static MatchPhase previousMatchPhase = this->gameState.matchPhase; + + if (previousMatchPhase != this->gameState.matchPhase) { + std::cout << "Match phase change!" << std::endl; + + previousMatchPhase = this->gameState.matchPhase; + + // Play relay race theme + if (this->session->getInfo().is_dungeon_master.value()) { + std::cout << "Relay Race - DM!" << std::endl; + + // Play DM relay race theme + audioManager->stopMusic(ClientMusic::MazeExplorationDMTheme); + + audioManager->playMusic(ClientMusic::RelayRaceDMTheme); + + } + else { + // Player in Relay Race + std::cout << "Relay Race - Player!" << std::endl; + + // Play Player relay race theme + audioManager->stopMusic(ClientMusic::MazeExplorationPlayersTheme); + + audioManager->playMusic(ClientMusic::RelayRacePlayersTheme); + } + } + } + else { + // Game has ended - GamePhase::Results + std::cout << "Game has ended! Playing victory music." << std::endl; + + // Play victory theme of the team that won + if (this->session->getInfo().is_dungeon_master.value()) { + // Stop relay race music of the DM + audioManager->stopMusic(ClientMusic::RelayRaceDMTheme); + } + else { + // Stop relay race music of the Players + audioManager->stopMusic(ClientMusic::RelayRacePlayersTheme); + } + + // Play victory theme for team that won + if (this->gameState.playerVictory) { + audioManager->playSFX(ClientSFX::VictoryThemePlayers); + } + else { + audioManager->playSFX(ClientSFX::VictoryThemeDM); + } + } + + } } } else if (event.type == EventType::LoadSoundCommands) { auto self_eid = this->session->getInfo().client_eid; @@ -392,24 +699,140 @@ void Client::processServerInput(boost::asio::io_context& context) { } EntityID light_id = updated_light_source.lightSources[i].value().eid; this->closest_light_sources[i] = this->gameState.objects[light_id]; + if (!this->closest_light_sources[i].has_value()) { + continue; + } // update intensity with incoming intensity this->closest_light_sources[i]->pointLightInfo->intensity = updated_light_source.lightSources[i]->intensity; + this->closest_light_sources[i]->pointLightInfo->is_cut = updated_light_source.lightSources[i]->is_cut; + this->gameState.objects.at(light_id)->pointLightInfo->is_cut = updated_light_source.lightSources[i]->is_cut; } + } else if (event.type == EventType::LoadIntroCutscene) { + const auto& data = boost::get(event.data); + this->intro_cutscene = data; + this->gui_state = GUIState::INTRO_CUTSCENE; + this->audioManager->stopMusic(ClientMusic::MenuTheme); + } + + this->events_received.pop_front(); + + auto now = std::chrono::system_clock::now(); + if (allow_defer && now - start > defer_time) { + defer_time = defer_time * 2; + had_to_defer = true; + break; + } + } + if (allow_defer && !had_to_defer) { + defer_time -= 1ms; + if (defer_time < MIN_DEFER_TIME) { + defer_time = MIN_DEFER_TIME; } } } +void Client::configureGBuffer() { + glGenFramebuffers(1, &gBuffer); + glBindFramebuffer(GL_FRAMEBUFFER, gBuffer); + + // position color buffer + glGenTextures(1, &gPosition); + glBindTexture(GL_TEXTURE_2D, gPosition); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, WINDOW_WIDTH, WINDOW_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gPosition, 0); + // normal color buffer + glGenTextures(1, &gNormal); + glBindTexture(GL_TEXTURE_2D, gNormal); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, WINDOW_WIDTH, WINDOW_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, gNormal, 0); + // color + specular color buffer + glGenTextures(1, &gAlbedoSpec); + glBindTexture(GL_TEXTURE_2D, gAlbedoSpec); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, WINDOW_WIDTH, WINDOW_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, gAlbedoSpec, 0); + + // tell OpenGL which color attachments we'll use (of this framebuffer) for rendering + unsigned int attachments[3] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 }; + glDrawBuffers(3, attachments); + + // create and attach depth buffer (renderbuffer) + unsigned int rboDepth; + glGenRenderbuffers(1, &rboDepth); + glBindRenderbuffer(GL_RENDERBUFFER, rboDepth); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, WINDOW_WIDTH, WINDOW_HEIGHT); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepth); + // finally check if framebuffer is complete + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + std::cout << "Framebuffer not complete!" << std::endl; + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + this->deferred_lighting_shader->use(); + this->deferred_lighting_shader->setInt("gPosition", 0); + this->deferred_lighting_shader->setInt("gNormal", 1); + this->deferred_lighting_shader->setInt("gAlbedoSpec", 2); + + this->dm_deferred_lighting_shader->use(); + this->dm_deferred_lighting_shader->setInt("gPosition", 0); + this->dm_deferred_lighting_shader->setInt("gNormal", 1); + this->dm_deferred_lighting_shader->setInt("gAlbedoSpec", 2); +} + void Client::draw() { - // auto start = std::chrono::system_clock::now(); + if (!this->session->getInfo().client_eid.has_value()) { + return; + } + this->geometryPass(); + this->lightingPass(); +} + +void Client::geometryPass() { + glBindFramebuffer(GL_FRAMEBUFFER, gBuffer); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + this->deferred_geometry_shader->use(); + auto viewProj = this->cam->getViewProj(); + this->deferred_geometry_shader->setMat4("viewProj", viewProj); + // this->deferred_geometry_shader->setMat4("finalBonesMatrices[0]", transforms[i]); if (!this->session->getInfo().client_eid.has_value()) { + std::cerr << "WARNING: trying to do geometry loop without knowing self eid\n"; return; } - auto eid = this->session->getInfo().client_eid.value(); + + EntityID self_eid = this->session->getInfo().client_eid.value(); bool is_dm = this->session->getInfo().is_dungeon_master.value(); - glm::vec3 my_pos = this->gameState.objects[eid]->physics.corner; - for (auto& [id, sharedObject] : this->gameState.objects) { + std::unordered_map>* objects = &this->gameState.objects; + if (this->gameState.phase == GamePhase::INTRO_CUTSCENE) { + if (!this->intro_cutscene.has_value()) { + return; // haven't received cutscene packet yet + } + // use different values for the intro cutscene + if (is_dm) { + self_eid = this->intro_cutscene->dm_eid; + // look down + this->cam->update(WINDOW_WIDTH / 2, WINDOW_HEIGHT - 5); + } else { + self_eid = this->intro_cutscene->pov_eid; + // look straight ahead + this->cam->update(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2); + } + objects = &this->intro_cutscene->state.objects; + } + + glm::vec3 my_pos = (*objects)[self_eid]->physics.corner; + + double currentTime = glfwGetTime(); + double timeElapsed = currentTime - lastTime; + lastTime = currentTime; + + // draw all objects to g-buffer + for (auto& [id, sharedObject] : *objects) { if (!sharedObject.has_value()) { continue; } @@ -422,19 +845,46 @@ void Client::draw() { auto dist = glm::distance(sharedObject->physics.corner, my_pos); - if (!is_floor) { - if (!is_dm && !is_ceiling && dist > RENDER_DISTANCE) { + if (!is_dm && !is_ceiling && + (sharedObject->type == ObjectType::FloorSpike || + sharedObject->type == ObjectType::Lava || + sharedObject->type == ObjectType::ArrowTrap || + sharedObject->type == ObjectType::SpikeTrap || + sharedObject->type == ObjectType::TeleporterTrap || + sharedObject->type == ObjectType::Projectile || + sharedObject->modelType == ModelType::Chest) + && dist > this->config.client.render / 2) { continue; } + else if (!is_dm && !is_ceiling && dist > this->config.client.render) { + continue; + } + } + + if (is_dm && + (sharedObject->type == ObjectType::FloorSpike || + sharedObject->type == ObjectType::Lava || + sharedObject->type == ObjectType::ArrowTrap || + sharedObject->type == ObjectType::SpikeTrap || + sharedObject->type == ObjectType::Projectile || + sharedObject->type == ObjectType::Torchlight || // light post + sharedObject->modelType == ModelType::Chest) + && dist > this->config.client.render) { + continue; } switch (sharedObject->type) { case ObjectType::Player: { + // search all player inventories to see who has the orb and update it's position (while it's held by a player) + if (sharedObject->inventoryInfo->hasOrb) { + player_has_orb_global_id = sharedObject->globalID; + } + // don't render yourself - if (this->session->getInfo().client_eid.has_value() && sharedObject->globalID == this->session->getInfo().client_eid.value()) { - // TODO: Update the player eye level to an acceptable level + if (sharedObject->globalID == self_eid) { glm::vec3 pos = sharedObject->physics.getCenterPosition(); + pos.y -= sharedObject->physics.dimensions.y / 2.0f; pos.y += PLAYER_EYE_LEVEL; cam->updatePos(pos); @@ -453,54 +903,126 @@ void Client::draw() { } break; } + if (!sharedObject->playerInfo->is_alive) { + break; // don't render dead players + } + + animManager->setAnimation(sharedObject->globalID, sharedObject->modelType, sharedObject->animState); + + /* Update model animation */ + animManager->updateAnimation(timeElapsed); + auto transforms = animManager->getFinalBoneMatrices(); + + for (int i = 0; i < (transforms.size() < 100 ? transforms.size() : 100); ++i) { + deferred_geometry_shader->setMat4("finalBonesMatrices[" + std::to_string(i) + "]", transforms[i]); + } if (!sharedObject->playerInfo->render) { break; } // dont render while invisible - auto lightPos = glm::vec3(0.0f, 10.0f, 0.0f); auto player_pos = sharedObject->physics.getCenterPosition(); + player_pos.y -= (sharedObject->physics.dimensions.y / 2.0f); + + auto player_dir = sharedObject->physics.facing; + + if (player_dir == glm::vec3(0.0f)) { + player_dir = glm::vec3(0.0f, 0.0f, 1.0f); + } + player_dir.y = 0.0f; + + Model* curr_player_model; + switch (sharedObject->modelType) { + case ModelType::PlayerFire: + curr_player_model = this->fire_player_model.get(); + break; + case ModelType::PlayerLightning: + curr_player_model = this->lightning_player_model.get(); + break; + case ModelType::PlayerWater: + curr_player_model = this->water_player_model.get(); + break; + default: + std::cerr << "WARNING: player with non valid model type detected, defaulting to fire..."; + curr_player_model = this->fire_player_model.get(); + break; + } - this->player_model->translateAbsolute(player_pos); - this->player_model->draw( - this->model_shader.get(), - this->cam->getViewProj(), + curr_player_model->rotateAbsolute(glm::normalize(player_dir), true); + curr_player_model->translateAbsolute(player_pos); + curr_player_model->scaleAbsolute(PLAYER_MODEL_SCALE); + curr_player_model->draw( + this->deferred_geometry_shader.get(), this->cam->getPos(), - {}, true); + + // draw mirror above head if holding + for (auto& [_, pair] : sharedObject->inventoryInfo->usedItems) { + ModelType model = pair.first; + if (model == ModelType::Mirror) { + // im so FUCKING good at programming + mirror_model->setDimensions(glm::vec3(1.438951, 0.052374, 0.955402)); + mirror_model->translateAbsolute(player_pos); + mirror_model->translateRelative(glm::vec3(0.0f, 4.5f, 0.0f)); + mirror_model->draw( + this->deferred_geometry_shader.get(), + this->cam->getPos(), + true); + } + } + break; } // CHANGE THIS case ObjectType::DungeonMaster: { - // don't render yourself - if (this->session->getInfo().client_eid.has_value() && sharedObject->globalID == this->session->getInfo().client_eid.value()) { + // shouldn't render DM at all? + + if (sharedObject->globalID == self_eid) { // TODO: Update the player eye level to an acceptable level glm::vec3 pos = sharedObject->physics.getCenterPosition(); pos.y += PLAYER_EYE_LEVEL; cam->updatePos(pos); - break; } - auto lightPos = glm::vec3(0.0f, 10.0f, 0.0f); - auto player_pos = sharedObject->physics.corner; - - this->player_model->setDimensions(sharedObject->physics.dimensions); - this->player_model->translateAbsolute(player_pos); - this->player_model->draw( - this->model_shader.get(), - this->cam->getViewProj(), + // auto player_pos = sharedObject->physics.corner; + // auto player_dir = sharedObject->physics.facing; + + // // this->player_model->setDimensions(sharedObject->physics.dimensions); + // this->player_model->translateAbsolute(player_pos); + // if (player_dir == glm::vec3(0.0f)) { + // player_dir = glm::vec3(0.0f, 0.0f, 1.0f); + // } + // player_dir.y = 0.0f; + // this->player_model->rotateAbsolute(glm::normalize(player_dir), true); + // this->player_model->draw( + // this->deferred_geometry_shader.get(), + // this->cam->getPos(), + // true); + break; + } + case ObjectType::Slime: { + this->slime_model->setDimensions(sharedObject->physics.dimensions); + this->slime_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + this->slime_model->draw(this->deferred_geometry_shader.get(), this->cam->getPos(), - {}, true); - this->drawBbox(sharedObject); break; } - case ObjectType::Slime: { - auto cube = std::make_unique(glm::vec3(0.0, 1.0f, 0.0f)); - cube->scaleAbsolute(sharedObject->physics.dimensions); - cube->translateAbsolute(sharedObject->physics.getCenterPosition()); - cube->draw(this->cube_shader.get(), - this->cam->getViewProj(), + case ObjectType::Minotaur: { + this->bear_model->setDimensions(sharedObject->physics.dimensions); + this->bear_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + this->bear_model->translateRelative(glm::vec3(0, -8.5f, 0)); + this->bear_model->rotateAbsolute(rotate90DegreesAroundYAxis(sharedObject->physics.facing)); + this->bear_model->draw(this->deferred_geometry_shader.get(), + this->cam->getPos(), + true); + break; + } + case ObjectType::Python: { + this->python_model->setDimensions(sharedObject->physics.dimensions); + this->python_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + this->python_model->rotateAbsolute(sharedObject->physics.facing); + + this->python_model->draw(this->deferred_geometry_shader.get(), this->cam->getPos(), - {}, true); break; } @@ -509,272 +1031,490 @@ void Client::draw() { // don't render ceiling as DM break; } + if (!is_dm && sharedObject->solidSurface->is_internal) { // dont render internal walls as non DM break; } Model* model = this->wall_model.get(); - Shader* shader = this->wall_shader.get(); + Shader* shader = this->deferred_geometry_shader.get(); + switch (sharedObject->solidSurface->surfaceType) { case SurfaceType::Wall: model = this->wall_model.get(); - shader = this->wall_shader.get(); break; case SurfaceType::Pillar: model = this->pillar_model.get(); - shader = this->wall_shader.get(); break; case SurfaceType::Ceiling: - model = this->cube_model.get(); - shader = this->solid_surface_shader.get(); + model = this->floor_model.get(); break; case SurfaceType::Floor: - model = this->cube_model.get(); - shader = this->solid_surface_shader.get(); + model = this->floor_model.get(); break; } - if (is_dm) { - // if the DM, override - if (sharedObject->solidSurface->surfaceType != SurfaceType::Floor) { - shader = this->dm_cube_shader.get(); - } - else { - shader = this->solid_surface_shader.get(); - } - - if (sharedObject->solidSurface->dm_highlight) { - model->overrideSolidColor(glm::vec3(1.0f, 0.0f, 0.0f)); - } else { - model->overrideSolidColor({}); - } - } - model->setDimensions(sharedObject->physics.dimensions); model->translateAbsolute(sharedObject->physics.getCenterPosition()); - if (is_dm) { // - model->draw(shader, - this->cam->getViewProj(), - this->cam->getPos(), - {}, - true); - } else { - model->draw(shader, - this->cam->getViewProj(), - this->cam->getPos(), - this->closest_light_sources, - true); - } + model->draw(shader, + this->cam->getPos(), + true); break; } case ObjectType::FakeWall: { glm::vec3 color; if (sharedObject->trapInfo->triggered) { - color = glm::vec3(0.4f, 0.5f, 0.7f); + this->wall_model->setDimensions(sharedObject->physics.dimensions); + this->wall_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + this->wall_model->draw(this->deferred_geometry_shader.get(), + this->cam->getPos(), + true); } else { - // off-color if not currently "visible" - // TODO: change to translucent - color = glm::vec3(0.5f, 0.6f, 0.8f); } - auto cube = std::make_unique(color); - cube->scaleAbsolute(sharedObject->physics.dimensions); - cube->translateAbsolute(sharedObject->physics.getCenterPosition()); - cube->draw(this->cube_shader.get(), - this->cam->getViewProj(), - this->cam->getPos(), - {}, - true); break; } case ObjectType::SpikeTrap: { - auto cube = std::make_unique(glm::vec3(1.0f, 0.1f, 0.1f)); - cube->scaleAbsolute( sharedObject->physics.dimensions); - cube->translateAbsolute(sharedObject->physics.getCenterPosition()); - cube->draw(this->cube_shader.get(), - this->cam->getViewProj(), + // if not DM and this is a ghost trap, break + if (!is_dm && sharedObject->trapInfo->dm_hover) { + break; + } + + this->spike_trap_model->rotateAbsolute(M_PI, glm::vec3(0.0f, 0.0f, 1.0f)); + this->spike_trap_model->setDimensions(sharedObject->physics.dimensions); + this->spike_trap_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + this->spike_trap_model->draw(this->deferred_geometry_shader.get(), this->cam->getPos(), - {}, true); + this->spike_trap_model->rotateAbsolute(0.0f, glm::vec3(0.0f, 0.0f, 1.0f)); break; } - case ObjectType::Torchlight: { - // do not render torches if dungeon master - if (!this->session->getInfo().is_dungeon_master.has_value()) { - break; // just in case this message wasn't received, don't crash + case ObjectType::FireballTrap: { + // if not DM and this is a ghost trap, break + if (!is_dm && sharedObject->trapInfo->dm_hover) { + break; } - if (!this->session->getInfo().is_dungeon_master.value()) { - this->torchlight_model->translateAbsolute(sharedObject->physics.getCenterPosition()); - this->torchlight_model->draw( - this->light_source_shader.get(), - this->cam->getViewProj(), - this->cam->getPos(), - {}, - true); - } - break; - } - case ObjectType::FireballTrap: { - auto cube = std::make_unique(glm::vec3(0.0f, 0.5f, 0.5f)); - cube->scaleAbsolute( sharedObject->physics.dimensions); - cube->translateAbsolute(sharedObject->physics.getCenterPosition()); - cube->draw(this->cube_shader.get(), - this->cam->getViewProj(), + this->sungod_model->setDimensions(sharedObject->physics.dimensions); + this->sungod_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + this->sungod_model->rotateAbsolute(sharedObject->physics.facing); + this->sungod_model->draw(this->deferred_geometry_shader.get(), this->cam->getPos(), - {}, true); break; } case ObjectType::ArrowTrap: { - auto cube = std::make_unique(glm::vec3(0.5f, 0.3f, 0.2f)); - cube->scaleAbsolute( sharedObject->physics.dimensions); - cube->translateAbsolute(sharedObject->physics.getCenterPosition()); - cube->draw(this->cube_shader.get(), - this->cam->getViewProj(), + // if not DM and this is a ghost trap, break + if (!is_dm && sharedObject->trapInfo->dm_hover) { + break; + } + + this->arrow_trap_model->rotateAbsolute(sharedObject->physics.facing, true); + this->arrow_trap_model->setDimensions(sharedObject->physics.dimensions); + this->arrow_trap_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + this->arrow_trap_model->draw(this->deferred_geometry_shader.get(), this->cam->getPos(), - {}, true); break; } - case ObjectType::Projectile: { - // TODO use model - auto cube = std::make_unique(glm::vec3(1.0f, 0.1f, 0.1f)); - cube->scaleAbsolute( sharedObject->physics.dimensions); - cube->translateAbsolute(sharedObject->physics.getCenterPosition()); - cube->draw(this->cube_shader.get(), - this->cam->getViewProj(), - this->cam->getPos(), - {}, - true); + case ObjectType::Projectile: { + switch (sharedObject->modelType) { + case ModelType::Arrow: { + auto dim = ARROW_DIMENSIONS * 0.005f; + this->arrow_model->setDimensions(dim); + this->arrow_model->rotateAbsolute(sharedObject->physics.facing); + this->arrow_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + this->arrow_model->draw(this->deferred_geometry_shader.get(), + this->cam->getPos(), true); + break; + } + // case ModelType::Fireball: { + // break; + // } + default: + // this->spike_trap_model->setDimensions(sharedObject->physics.dimensions); + // this->spike_trap_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + // this->spike_trap_model->draw(this->deferred_geometry_shader.get(), + // this->cam->getPos(), true); + break; + } break; } case ObjectType::FloorSpike: { - auto cube = std::make_unique(glm::vec3(0.0f, 1.0f, 0.0f)); - cube->scaleAbsolute( sharedObject->physics.dimensions); - cube->translateAbsolute(sharedObject->physics.getCenterPosition()); - cube->draw(this->cube_shader.get(), - this->cam->getViewProj(), - this->cam->getPos(), - {}, - true); + // if not DM and this is a ghost trap, break + if (!is_dm && sharedObject->trapInfo->dm_hover) { + break; + } + + this->spike_trap_model->setDimensions(sharedObject->physics.dimensions); + this->spike_trap_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + this->spike_trap_model->draw(this->deferred_geometry_shader.get(), + this->cam->getPos(), true); + break; + } + case ObjectType::Lava: { + Model* model; + switch (sharedObject->modelType) { + case ModelType::LavaCross: { + model = this->lava_cross_model.get(); + break; + } + case ModelType::LavaVertical: { + model = this->lava_vertical_model.get(); + break; + } + case ModelType::LavaHorizontal: { + model = this->lava_horizontal_model.get(); + break; + } + default: { + model = this->lava_cross_model.get(); + break; + } + } + model->setDimensions(sharedObject->physics.dimensions); + model->translateAbsolute(sharedObject->physics.getCenterPosition()); + model->draw(this->deferred_geometry_shader.get(), + this->cam->getPos(), true); break; } case ObjectType::Orb: { if (!sharedObject->iteminfo->held && !sharedObject->iteminfo->used) { - glm::vec3 color = glm::vec3(0.0f, 0.7f, 1.0f); - auto cube = std::make_unique(color); - cube->scaleAbsolute(sharedObject->physics.dimensions); - cube->translateAbsolute(sharedObject->physics.getCenterPosition()); - cube->draw(this->cube_shader.get(), - this->cam->getViewProj(), + this->orb_model->setDimensions(sharedObject->physics.dimensions); + this->orb_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + this->orb_model->draw(this->deferred_geometry_shader.get(), this->cam->getPos(), - {}, true); } break; } case ObjectType::Potion: { if (!sharedObject->iteminfo->held && !sharedObject->iteminfo->used) { - glm::vec3 color; - if (sharedObject->modelType == ModelType::HealthPotion) { - color = glm::vec3(1.0f, 0.0f, 0.0f); - } else if (sharedObject->modelType == ModelType::NauseaPotion || sharedObject->modelType == ModelType::InvincibilityPotion) { - color = glm::vec3(1.0f, 0.5f, 0.0f); - } else if (sharedObject->modelType == ModelType::InvisibilityPotion) { - color = glm::vec3(0.2f, 0.2f, 0.2f); - } + Model* model = this->chest_model.get(); - auto cube = std::make_unique(color); - cube->scaleAbsolute(sharedObject->physics.dimensions); - cube->translateAbsolute(sharedObject->physics.getCenterPosition()); - cube->draw(this->cube_shader.get(), - this->cam->getViewProj(), + model->setDimensions(sharedObject->physics.dimensions); + model->translateAbsolute(sharedObject->physics.getCenterPosition()); + model->draw(this->deferred_geometry_shader.get(), this->cam->getPos(), - {}, true); } break; } case ObjectType::Spell: { if (!sharedObject->iteminfo->held && !sharedObject->iteminfo->used) { - glm::vec3 color; - if (sharedObject->modelType == ModelType::FireSpell) { - color = glm::vec3(0.9f, 0.1f, 0.0f); - } - else if (sharedObject->modelType == ModelType::HealSpell) { - color = glm::vec3(1.0f, 1.0f, 0.0f); - } - else { - color = glm::vec3(0.8f, 0.7f, 0.6f); - } - - auto cube = std::make_unique(color); - cube->scaleAbsolute(sharedObject->physics.dimensions); - cube->translateAbsolute(sharedObject->physics.getCenterPosition()); - cube->draw(this->cube_shader.get(), - this->cam->getViewProj(), + this->chest_model->setDimensions(sharedObject->physics.dimensions); + this->chest_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + this->chest_model->draw(this->deferred_geometry_shader.get(), this->cam->getPos(), - {}, true); } break; } case ObjectType::TeleporterTrap: { - auto cube = std::make_unique(glm::vec3(0.0f, 1.0f, 1.0f)); - cube->scaleAbsolute( sharedObject->physics.dimensions); - cube->translateAbsolute(sharedObject->physics.getCenterPosition()); - cube->draw(this->cube_shader.get(), - this->cam->getViewProj(), + // if not DM and this is a ghost trap, break + if (!is_dm && sharedObject->trapInfo->dm_hover) { + break; + } + this->teleport_model->setDimensions(sharedObject->physics.dimensions); + this->teleport_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + this->teleport_model->draw(this->deferred_geometry_shader.get(), this->cam->getPos(), - {}, true); break; } case ObjectType::Exit: { - auto cube = std::make_unique(glm::vec3(0.0f, 0.0f, 0.0f)); - cube->scaleAbsolute( sharedObject->physics.dimensions); - cube->translateAbsolute(sharedObject->physics.getCenterPosition()); - cube->draw(this->cube_shader.get(), - this->cam->getViewProj(), + this->exit_model->setDimensions( sharedObject->physics.dimensions); + this->exit_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + this->exit_model->draw(this->deferred_geometry_shader.get(), this->cam->getPos(), - {}, true); break; } case ObjectType::Weapon: { if (!sharedObject->iteminfo->held && !sharedObject->iteminfo->used) { - auto cube = std::make_unique(glm::vec3(0.5f)); - cube->scaleAbsolute(sharedObject->physics.dimensions); - cube->translateAbsolute(sharedObject->physics.getCenterPosition()); - cube->draw(this->cube_shader.get(), - this->cam->getViewProj(), + this->chest_model->setDimensions(sharedObject->physics.dimensions); + this->chest_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + this->chest_model->draw(this->deferred_geometry_shader.get(), this->cam->getPos(), - {}, true); } break; } case ObjectType::WeaponCollider: { - if (sharedObject->weaponInfo->attacked) { - auto cube = std::make_unique(glm::vec3(1.0f)); - cube->scaleAbsolute(sharedObject->physics.dimensions); - cube->translateAbsolute(sharedObject->physics.getCenterPosition()); - cube->draw(this->cube_shader.get(), - this->cam->getViewProj(), + if (sharedObject->weaponInfo->lightning) { + if (!sharedObject->weaponInfo->attacked) { + this->exit_model->setDimensions(glm::vec3(3.0f, 0.01f, 3.0f)); + glm::vec3 preview_pos = sharedObject->physics.getCenterPosition(); + preview_pos.y = 0.0f; + this->exit_model->translateAbsolute(preview_pos); + this->exit_model->draw(this->deferred_geometry_shader.get(), + this->cam->getPos(), + true); + } else { + this->lightning_model->setDimensions(glm::vec3(1.314906, 2.238910 * 2.0f, 0.019732) * 10.0f); + this->lightning_model->translateAbsolute(sharedObject->physics.corner); + glm::vec3 facing_curr_player = rotate90DegreesAroundYAxis(glm::normalize( + sharedObject->physics.getCenterPosition() - cam->getPos() + )); + this->lightning_model->rotateAbsolute(facing_curr_player); + this->lightning_model->draw(this->deferred_geometry_shader.get(), + this->cam->getPos(), + true); + } + } + // dont render weapon colliders + break; + } + case ObjectType::Mirror: { + if (!sharedObject->iteminfo->held && !sharedObject->iteminfo->used) { + this->chest_model->setDimensions(sharedObject->physics.dimensions); + this->chest_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + this->chest_model->draw(this->deferred_geometry_shader.get(), this->cam->getPos(), - {}, - false); + true); } break; } + case ObjectType::Torchlight: { + // render the post in the geometry pass, and the light in the lighting pass + // i fucked with the y value to make it the right height but the x and z are from the + // model coords + this->torchpost_model->setDimensions(glm::vec3(0.860699, 5.2f, 0.827778)); + this->torchpost_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + this->torchpost_model->translateRelative(glm::vec3(0, -0.2f, 0)); + this->torchpost_model->draw(this->deferred_geometry_shader.get(), + this->cam->getPos(), + true); + } default: break; } } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glUseProgram(0); +} + +void Client::lightingPass() { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, gPosition); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, gNormal); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, gAlbedoSpec); + + auto* closest_lights = &this->closest_light_sources; + auto* gamestate_objects = &this->gameState.objects; + auto eid = this->session->getInfo().client_eid.value(); + bool is_dm = this->session->getInfo().is_dungeon_master.value(); + if (this->gameState.phase == GamePhase::INTRO_CUTSCENE) { + if (!this->intro_cutscene.has_value()) { + return; // don't have cutscene info yet + } + + // replace with intro cutscene state + closest_lights = &this->intro_cutscene->lights; + gamestate_objects = &this->intro_cutscene->state.objects; + if (is_dm) { + eid = this->intro_cutscene->dm_eid; + } else { + eid = this->intro_cutscene->pov_eid; + } + } + + glm::vec3 my_pos = gamestate_objects->at(eid)->physics.corner; + + std::shared_ptr lighting_shader = is_dm ? dm_deferred_lighting_shader: deferred_lighting_shader; + + lighting_shader->use(); + auto camPos = this->cam->getPos(); + lighting_shader->setVec3("viewPos", camPos); + + if (is_dm) { + auto ambient = glm::vec3(0.1, 0.1, 0.1); + auto diffuse = glm::vec3(0.1, 0.1, 0.1); + auto specular = glm::vec3(0.1, 0.1, 0.1); + std::array dirLights = { + DirLight{glm::vec3(1.0f, 0.0f, 0.0f), ambient, diffuse, specular}, + DirLight{glm::vec3(-1.0f, 1.0f, 0.0f), ambient, diffuse, specular}, + DirLight{glm::vec3(0.0f, 1.0f, 1.0f), ambient, diffuse, specular}, + DirLight{glm::vec3(0.0f, 1.0f, -1.0f), ambient, diffuse, specular}, + }; + + for (int i = 0; i < dirLights.size(); i++) { + std::string i_s = std::to_string(i); + lighting_shader->setVec3("dirLights[" + i_s + "].direction", dirLights[i].direction); + lighting_shader->setVec3("dirLights[" + i_s + "].ambient_color", ambient); + lighting_shader->setVec3("dirLights[" + i_s + "].diffuse_color", diffuse); + lighting_shader->setVec3("dirLights[" + i_s + "].specular_color", specular); + } + } + + for (int i = 0; i < closest_lights->size(); i++) { + boost::optional& curr_source = closest_lights->at(i); + if (!curr_source.has_value()) { + // put the light in africa so it isn't shown + // important for intro cutscene since other lights wont replace entries that are removed! + glm::vec3 FAR_AWAY(10000, 10000, 10000); + lighting_shader->setVec3("pointLights[" + std::to_string(i) + "].position", FAR_AWAY); + continue; + } else if (!this->gameState.objects.contains(curr_source->globalID)) { + glm::vec3 FAR_AWAY(10000, 10000, 10000); + lighting_shader->setVec3("pointLights[" + std::to_string(i) + "].position", FAR_AWAY); + continue; + } else if (!this->gameState.objects.at(curr_source->globalID).has_value()) { + glm::vec3 FAR_AWAY(10000, 10000, 10000); + lighting_shader->setVec3("pointLights[" + std::to_string(i) + "].position", FAR_AWAY); + continue; + } + + SharedPointLightInfo& properties = curr_source->pointLightInfo.value(); + + glm::vec3 pos = curr_source->physics.getCenterPosition(); + if (curr_source->type == ObjectType::Orb && curr_source->iteminfo->held) { + pos = this->gameState.objects.at(player_has_orb_global_id)->physics.getCenterPosition(); + } + + if (curr_source->modelType == ModelType::Lightning) { + pos.y = 0.0f; + } + + lighting_shader->setFloat("pointLights[" + std::to_string(i) + "].intensity", properties.intensity); + + lighting_shader->setVec3("pointLights[" + std::to_string(i) + "].position", pos); + + lighting_shader->setVec3("pointLights[" + std::to_string(i) + "].ambient_color", properties.ambient_color); + lighting_shader->setVec3("pointLights[" + std::to_string(i) + "].diffuse_color", properties.diffuse_color); + lighting_shader->setVec3("pointLights[" + std::to_string(i) + "].specular_color", properties.specular_color); + + lighting_shader->setFloat("pointLights[" + std::to_string(i) + "].attn_linear", properties.attenuation_linear); + lighting_shader->setFloat("pointLights[" + std::to_string(i) + "].attn_quadratic", properties.attenuation_quadratic); + } + + if (quadVAO == 0) { + float quadVertices[] = { + // positions // texture Coords + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, + }; + // setup plane VAO + glGenVertexArrays(1, &quadVAO); + glGenBuffers(1, &quadVBO); + glBindVertexArray(quadVAO); + glBindBuffer(GL_ARRAY_BUFFER, quadVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); + } + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glBindVertexArray(0); + + // copy content of geometry's depth buffer to default framebuffer's depth buffer + glBindFramebuffer(GL_READ_FRAMEBUFFER, gBuffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // write to default framebuffer + // blit to default framebuffer. Note that this may or may not work as the internal formats of both the FBO and default framebuffer have to match. + // the internal formats are implementation defined. This works on all of my systems, but if it doesn't on yours you'll likely have to write to the + // depth buffer in another shader stage (or somehow see to match the default framebuffer's internal format with the FBO's internal format). + glBlitFramebuffer(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + double currentTime = glfwGetTime(); + double timeElapsed = currentTime - lastFrameTime; + lastFrameTime = currentTime; + + // render torch lights on top of scene + this->deferred_light_box_shader->use(); + glm::mat4 viewProj = this->cam->getViewProj(); + this->deferred_light_box_shader->setMat4("viewProj", viewProj); + for (auto& [id, sharedObject] : *gamestate_objects) { + if (!sharedObject.has_value()) { + continue; + } + + auto dist = glm::distance(sharedObject->physics.corner, my_pos); + if (!is_dm && dist > this->config.client.render) { + continue; + } + + if (!sharedObject->pointLightInfo.has_value()) { + continue; + } + + SharedPointLightInfo& properties = sharedObject->pointLightInfo.value(); + + this->deferred_light_box_shader->use(); + + switch (sharedObject->type) { + case ObjectType::Torchlight: { + + glm::vec3 v(1.0f); + + animManager->setFrameAnimation(sharedObject->globalID, sharedObject->modelType, sharedObject->animState); + Model* torch_frame_model = animManager->updateFrameAnimation(timeElapsed); + glm::vec3 dims = torch_frame_model->getDimensions(); + + glm::vec3 flame_color; + + if (sharedObject->pointLightInfo->is_cut) { + glm::vec3 black(0.1f, 0.1f, 0.1f); + flame_color = black; + } else { + flame_color = properties.diffuse_color; + } + + this->deferred_light_box_shader->setVec3("lightColor", flame_color); + torch_frame_model->setDimensions(2.0f * sharedObject->physics.dimensions); + torch_frame_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + torch_frame_model->draw(this->deferred_light_box_shader.get(), this->cam->getPos(), true, flame_color); + break; + } + case ObjectType::Projectile: { + if (sharedObject->modelType == ModelType::Fireball) { + glm::vec3 flame_color = properties.diffuse_color; + + animManager->setFrameAnimation(sharedObject->globalID, sharedObject->modelType, sharedObject->animState); + Model* fireball_frame_model = animManager->updateFrameAnimation(timeElapsed); + if (fireball_frame_model == nullptr) { + continue; // THIS IS BREAKING WHEN SUN GODS FIREBALLS ARE DESTROYED WHWEN LIGHTS ARE CUT + } + + this->deferred_light_box_shader->setVec3("lightColor", flame_color); + + fireball_frame_model->setDimensions(sharedObject->physics.dimensions); + fireball_frame_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + fireball_frame_model->draw(this->deferred_light_box_shader.get(), + this->cam->getPos(), true); + break; + } else if (sharedObject->modelType == ModelType::SpellOrb) { + glm::vec3 flame_color = properties.diffuse_color; + + animManager->setFrameAnimation(sharedObject->globalID, sharedObject->modelType, sharedObject->animState); + Model* fireball_frame_model = animManager->updateFrameAnimation(timeElapsed); + if (fireball_frame_model == nullptr) { + continue; // THIS IS BREAKING WHEN SUN GODS FIREBALLS ARE DESTROYED WHWEN LIGHTS ARE CUT + } + + this->deferred_light_box_shader->setVec3("lightColor", flame_color); + + fireball_frame_model->setDimensions(sharedObject->physics.dimensions); + fireball_frame_model->translateAbsolute(sharedObject->physics.getCenterPosition()); + fireball_frame_model->draw(this->deferred_light_box_shader.get(), + this->cam->getPos(), true); + break; + } + break; + } + default: + continue; + } + + } - // auto stop = std::chrono::system_clock::now(); - // std::cout << std::chrono::duration_cast(stop - start).count() << "\n"; } void Client::drawBbox(boost::optional object) { @@ -785,19 +1525,23 @@ void Client::drawBbox(boost::optional object) { // it was off on the x axis. bbox_pos.y += object->physics.dimensions.y / 2.0f; - auto object_bbox = std::make_unique(glm::vec3(0.0f, 1.0f, 1.0f)); - object_bbox->scaleAbsolute(object->physics.dimensions); - object_bbox->translateAbsolute(bbox_pos); - object_bbox->draw(this->cube_shader.get(), - this->cam->getViewProj(), + item_model->setDimensions(object->physics.dimensions); + item_model->translateAbsolute(bbox_pos); + item_model->rotateAbsolute(glm::normalize(object->physics.facing), true); + item_model->draw(this->deferred_geometry_shader.get(), this->cam->getPos(), - {}, - false); + true); } } // callbacks - for Interaction void Client::keyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) { + if (this->gui_state == GUIState::INITIAL_LOAD) { + this->audioManager->playMusic(ClientMusic::MenuTheme); + this->gui_state = GUIState::TITLE_SCREEN; + return; + } + // Check for a key press. /* Store player EID for use in certain key handling */ std::optional eid; @@ -818,6 +1562,10 @@ void Client::keyCallback(GLFWwindow *window, int key, int scancode, int action, if (this->gameState.phase == GamePhase::GAME) { if (this->gui_state == GUIState::GAME_ESC_MENU) { this->gui_state = GUIState::GAME_HUD; + + if (is_dm.has_value() && is_dm.value()) { + this->cam->setPitch(-89.0); + } } else if (this->gui_state == GUIState::GAME_HUD) { this->gui_state = GUIState::GAME_ESC_MENU; @@ -846,7 +1594,7 @@ void Client::keyCallback(GLFWwindow *window, int key, int scancode, int action, case GLFW_KEY_E: if (eid.has_value()) { if (is_dm.has_value() && !is_dm.value()) { - this->session->sendEventAsync(Event(eid.value(), EventType::UseItem, UseItemEvent(eid.value()))); + this->session->sendEvent(Event(eid.value(), EventType::UseItem, UseItemEvent(eid.value()))); } } break; @@ -854,53 +1602,28 @@ void Client::keyCallback(GLFWwindow *window, int key, int scancode, int action, case GLFW_KEY_Q: if (eid.has_value()) { if (is_dm.has_value() && !is_dm.value()) { - this->session->sendEventAsync(Event(eid.value(), EventType::DropItem, DropItemEvent(eid.value()))); + this->session->sendEvent(Event(eid.value(), EventType::DropItem, DropItemEvent(eid.value()))); } } break; case GLFW_KEY_1: - if (eid.has_value()) { - if (is_dm.has_value() && !is_dm.value()) { - this->session->sendEventAsync(Event(eid.value(), EventType::SelectItem, SelectItemEvent(eid.value(), 1))); - } - } - break; - case GLFW_KEY_2: - if (eid.has_value()) { - if (is_dm.has_value() && !is_dm.value()) { - this->session->sendEventAsync(Event(eid.value(), EventType::SelectItem, SelectItemEvent(eid.value(), 2))); - } - } - break; - case GLFW_KEY_3: - if (eid.has_value()) { - if (is_dm.has_value() && !is_dm.value()) { - this->session->sendEventAsync(Event(eid.value(), EventType::SelectItem, SelectItemEvent(eid.value(), 3))); - } - } - break; - case GLFW_KEY_4: - if (eid.has_value()) { - if (is_dm.has_value() && !is_dm.value()) { - this->session->sendEventAsync(Event(eid.value(), EventType::SelectItem, SelectItemEvent(eid.value(), 4))); - } - } break; + case GLFW_KEY_RIGHT: if (eid.has_value()) { if (is_dm.has_value() && is_dm.value()) { - this->session->sendEventAsync(Event(eid.value(), EventType::SelectItem, SelectItemEvent(eid.value(), 1))); + this->session->sendEvent(Event(eid.value(), EventType::SelectItem, SelectItemEvent(eid.value(), 1))); } } break; case GLFW_KEY_LEFT: if (eid.has_value()) { if (is_dm.has_value() && is_dm.value()) { - this->session->sendEventAsync(Event(eid.value(), EventType::SelectItem, SelectItemEvent(eid.value(), -1))); + this->session->sendEvent(Event(eid.value(), EventType::SelectItem, SelectItemEvent(eid.value(), -1))); } } break; @@ -923,6 +1646,10 @@ void Client::keyCallback(GLFWwindow *window, int key, int scancode, int action, is_held_right = true; break; + case GLFW_KEY_H: + gui.displayControl(); + break; + /* Space also uses a flag to constantly send events when key is held */ case GLFW_KEY_SPACE: is_held_space = true; @@ -935,24 +1662,14 @@ void Client::keyCallback(GLFWwindow *window, int key, int scancode, int action, case GLFW_KEY_O: // zoom out is_held_o = true; break; - case GLFW_KEY_P: // to place or not to place - is_pressed_p = !is_pressed_p; - if (is_pressed_p) { - // unhighlight hover - if (eid.has_value()) { - // nothing being placed, so the CellType we pass shouldn't matter! - this->session->sendEventAsync(Event(eid.value(), EventType::TrapPlacement, TrapPlacementEvent(eid.value(), this->world_pos, CellType::ArrowTrapUp, true, false))); - } - } - else { - this->session->sendEventAsync(Event(eid.value(), EventType::TrapPlacement, TrapPlacementEvent(eid.value(), this->world_pos, CellType::ArrowTrapUp, false, false))); - } - break; /* Send an event to start 'shift' movement (i.e. sprint) */ case GLFW_KEY_LEFT_SHIFT: - if (eid.has_value() && !this->session->getInfo().is_dungeon_master.value()) { - this->session->sendEventAsync(Event(eid.value(), EventType::StartAction, StartActionEvent(eid.value(), glm::vec3(0.0f), ActionType::Sprint))); + if (eid.has_value()) { + this->session->sendEvent(Event(eid.value(), EventType::StartAction, StartActionEvent(eid.value(), glm::vec3(0.0f), ActionType::Sprint))); } + + break; + case GLFW_KEY_LEFT_CONTROL: is_held_i = true; break; @@ -965,29 +1682,29 @@ void Client::keyCallback(GLFWwindow *window, int key, int scancode, int action, switch (key) { case GLFW_KEY_S: is_held_down = false; - if (eid.has_value() && this->session->getInfo().is_dungeon_master.value() && is_pressed_p) { - this->session->sendEventAsync(Event(eid.value(), EventType::TrapPlacement, TrapPlacementEvent(eid.value(), this->world_pos, CellType::ArrowTrapUp, true, false))); + if (eid.has_value() && this->session->getInfo().is_dungeon_master.value()) { + //sendTrapEvent(true, false, (this->gameState.objects.at(eid.value()))->trapInventoryInfo->inventory[(this->gameState.objects.at(eid.value()))->trapInventoryInfo->selected-1]); } break; case GLFW_KEY_W: is_held_up = false; - if (eid.has_value() && this->session->getInfo().is_dungeon_master.value() && is_pressed_p) { - this->session->sendEventAsync(Event(eid.value(), EventType::TrapPlacement, TrapPlacementEvent(eid.value(), this->world_pos, CellType::ArrowTrapUp, true, false))); + if (eid.has_value() && this->session->getInfo().is_dungeon_master.value()) { + //sendTrapEvent(true, false, (this->gameState.objects.at(eid.value()))->trapInventoryInfo->inventory[(this->gameState.objects.at(eid.value()))->trapInventoryInfo->selected-1]); } break; case GLFW_KEY_A: is_held_left = false; - if (eid.has_value() && this->session->getInfo().is_dungeon_master.value() && is_pressed_p) { - this->session->sendEventAsync(Event(eid.value(), EventType::TrapPlacement, TrapPlacementEvent(eid.value(), this->world_pos, CellType::ArrowTrapUp, true, false))); + if (eid.has_value() && this->session->getInfo().is_dungeon_master.value()) { + //sendTrapEvent(true, false, (this->gameState.objects.at(eid.value()))->trapInventoryInfo->inventory[(this->gameState.objects.at(eid.value()))->trapInventoryInfo->selected-1]); } break; case GLFW_KEY_D: is_held_right = false; - if (eid.has_value() && this->session->getInfo().is_dungeon_master.value() && is_pressed_p) { - this->session->sendEventAsync(Event(eid.value(), EventType::TrapPlacement, TrapPlacementEvent(eid.value(), this->world_pos, CellType::ArrowTrapUp, true, false))); + if (eid.has_value() && this->session->getInfo().is_dungeon_master.value()) { + //sendTrapEvent(true, false, (this->gameState.objects.at(eid.value()))->trapInventoryInfo->inventory[(this->gameState.objects.at(eid.value()))->trapInventoryInfo->selected-1]); } break; @@ -997,22 +1714,21 @@ void Client::keyCallback(GLFWwindow *window, int key, int scancode, int action, break; case GLFW_KEY_LEFT_SHIFT: - if (eid.has_value() && !this->session->getInfo().is_dungeon_master.value()) { - this->session->sendEventAsync(Event(eid.value(), EventType::StopAction, StopActionEvent(eid.value(), glm::vec3(0.0f), ActionType::Sprint))); + if (eid.has_value()) { + this->session->sendEvent(Event(eid.value(), EventType::StopAction, StopActionEvent(eid.value(), glm::vec3(0.0f), ActionType::Sprint))); } - is_held_i = false; break; case GLFW_KEY_O: // zoom out is_held_o = false; - if (eid.has_value() && this->session->getInfo().is_dungeon_master.value() && is_pressed_p) { - this->session->sendEventAsync(Event(eid.value(), EventType::TrapPlacement, TrapPlacementEvent(eid.value(), this->world_pos, CellType::ArrowTrapUp, true, false))); + if (eid.has_value() && this->session->getInfo().is_dungeon_master.value()) { + //sendTrapEvent(true, false, (this->gameState.objects.at(eid.value()))->trapInventoryInfo->inventory[(this->gameState.objects.at(eid.value()))->trapInventoryInfo->selected-1]); } break; case GLFW_KEY_I: // zoom out - if (eid.has_value() && this->session->getInfo().is_dungeon_master.value() && is_pressed_p) { - this->session->sendEventAsync(Event(eid.value(), EventType::TrapPlacement, TrapPlacementEvent(eid.value(), this->world_pos, CellType::ArrowTrapUp, true, false))); - } + is_held_i = false; + break; + case GLFW_KEY_LEFT_CONTROL: is_held_i = false; break; default: @@ -1022,18 +1738,6 @@ void Client::keyCallback(GLFWwindow *window, int key, int scancode, int action, if (action == GLFW_REPEAT) { switch (key) { - /* case GLFW_KEY_S: - this->session->sendEventAsync(Event(eid.value(), EventType::TrapPlacement, TrapPlacementEvent(eid.value(), this->world_pos, CellType::FloorSpikeFull, true, false))); - break; - case GLFW_KEY_W: - this->session->sendEventAsync(Event(eid.value(), EventType::TrapPlacement, TrapPlacementEvent(eid.value(), this->world_pos, CellType::FloorSpikeFull, true, false))); - break; - case GLFW_KEY_A: - this->session->sendEventAsync(Event(eid.value(), EventType::TrapPlacement, TrapPlacementEvent(eid.value(), this->world_pos, CellType::FloorSpikeFull, true, false))); - break; - case GLFW_KEY_D: - this->session->sendEventAsync(Event(eid.value(), EventType::TrapPlacement, TrapPlacementEvent(eid.value(), this->world_pos, CellType::FloorSpikeFull, true, false))); - break;*/ case GLFW_KEY_BACKSPACE: auto ms_since_epoch = getMsSinceEpoch(); if (Client::time_of_last_keystroke + 100 < ms_since_epoch) { @@ -1044,6 +1748,54 @@ void Client::keyCallback(GLFWwindow *window, int key, int scancode, int action, } } +void Client::scrollCallback(GLFWwindow* window, double xoffset, double yoffset) { + std::optional eid; + std::optional is_dm; + + if (this->session != nullptr) { + eid = this->session->getInfo().client_eid; + is_dm = this->session->getInfo().is_dungeon_master; + } + + if (!eid.has_value()) + return; + + auto self = this->gameState.objects.at(eid.value()); + + if (yoffset >= 1) { + this->session->sendEvent(Event(eid.value(), EventType::SelectItem, SelectItemEvent(eid.value(), -1))); + + if (is_dm.has_value() && is_dm.value()) { + // optimistic update on scroll, otherwise might lag + int idx = self->trapInventoryInfo->selected; + + + if (idx - 1 == 0) + idx = TRAP_INVENTORY_SIZE; + + idx -= 1; + + sendTrapEvent(true, false, self->trapInventoryInfo->inventory[idx - 1]); + } + } + + if (yoffset <= -1) { + this->session->sendEvent(Event(eid.value(), EventType::SelectItem, SelectItemEvent(eid.value(), 1))); + + if (is_dm.has_value() && is_dm.value()) { + // optimistic update on scroll, otherwise might lag + int idx = self->trapInventoryInfo->selected; + + if (idx + 1 > TRAP_INVENTORY_SIZE) + idx = 1; + + idx += 1; + + sendTrapEvent(true, false, self->trapInventoryInfo->inventory[idx - 1]); + } + } +} + void Client::mouseCallback(GLFWwindow* window, double xposIn, double yposIn) { // cppcheck-suppress constParameterPointer auto new_mouse_xpos = static_cast(xposIn); auto new_mouse_ypos = static_cast(yposIn); @@ -1053,13 +1805,6 @@ void Client::mouseCallback(GLFWwindow* window, double xposIn, double yposIn) { / mouse_xpos = new_mouse_xpos; mouse_ypos = new_mouse_ypos; - - if (is_pressed_p) { - auto eid = this->session->getInfo().client_eid.value(); - - // the actual trap doesn't matter, this is just for highlighting purposes - this->session->sendEventAsync(Event(eid, EventType::TrapPlacement, TrapPlacementEvent(eid, this->world_pos, CellType::FloorSpikeFull, true, false))); - } } void Client::setWorldPos() { @@ -1089,6 +1834,15 @@ void Client::mouseButtonCallback(GLFWwindow* window, int button, int action, int } } + if (button == GLFW_MOUSE_BUTTON_RIGHT) { + if (action == GLFW_PRESS) { + is_right_mouse_down = true; + } + else { + is_right_mouse_down = false; // always set to false + } + } + std::optional eid; if (this->session != nullptr && this->session->getInfo().client_eid.has_value()) { @@ -1105,7 +1859,7 @@ void Client::mouseButtonCallback(GLFWwindow* window, int button, int action, int case GLFW_MOUSE_BUTTON_LEFT: if (eid.has_value() && this->session->getInfo().is_dungeon_master.has_value() && !this->session->getInfo().is_dungeon_master.value()) { - this->session->sendEventAsync(Event(eid.value(), EventType::UseItem, UseItemEvent(eid.value()))); + this->session->sendEvent(Event(eid.value(), EventType::UseItem, UseItemEvent(eid.value()))); } break; } diff --git a/src/client/cube.cpp b/src/client/cube.cpp deleted file mode 100644 index a26781f2..00000000 --- a/src/client/cube.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include "client/cube.hpp" -#include "shared/game/sharedobject.hpp" - -Cube::Cube(glm::vec3 newColor) { - // create a vertex buffer for positions and normals - // insert the data into these buffers - // initialize model matrix - glm::vec3 cubeMin = glm::vec3(-0.5f, -0.5f, -0.5f); - glm::vec3 cubeMax = glm::vec3(0.5f, 0.5f, 0.5f); - - - // The color of the cube. Try setting it to something else! - color = newColor; - - // Specify vertex positions - positions = { - // Front - glm::vec3(cubeMin.x, cubeMin.y, cubeMax.z), - glm::vec3(cubeMax.x, cubeMin.y, cubeMax.z), - glm::vec3(cubeMax.x, cubeMax.y, cubeMax.z), - glm::vec3(cubeMin.x, cubeMax.y, cubeMax.z), - - // Back - glm::vec3(cubeMax.x, cubeMin.y, cubeMin.z), - glm::vec3(cubeMin.x, cubeMin.y, cubeMin.z), - glm::vec3(cubeMin.x, cubeMax.y, cubeMin.z), - glm::vec3(cubeMax.x, cubeMax.y, cubeMin.z), - - // Top - glm::vec3(cubeMin.x, cubeMax.y, cubeMax.z), - glm::vec3(cubeMax.x, cubeMax.y, cubeMax.z), - glm::vec3(cubeMax.x, cubeMax.y, cubeMin.z), - glm::vec3(cubeMin.x, cubeMax.y, cubeMin.z), - - // Bottom - glm::vec3(cubeMin.x, cubeMin.y, cubeMin.z), - glm::vec3(cubeMax.x, cubeMin.y, cubeMin.z), - glm::vec3(cubeMax.x, cubeMin.y, cubeMax.z), - glm::vec3(cubeMin.x, cubeMin.y, cubeMax.z), - - // Left - glm::vec3(cubeMin.x, cubeMin.y, cubeMin.z), - glm::vec3(cubeMin.x, cubeMin.y, cubeMax.z), - glm::vec3(cubeMin.x, cubeMax.y, cubeMax.z), - glm::vec3(cubeMin.x, cubeMax.y, cubeMin.z), - - // Right - glm::vec3(cubeMax.x, cubeMin.y, cubeMax.z), - glm::vec3(cubeMax.x, cubeMin.y, cubeMin.z), - glm::vec3(cubeMax.x, cubeMax.y, cubeMin.z), - glm::vec3(cubeMax.x, cubeMax.y, cubeMax.z)}; - - // Specify normals - normals = { - // Front - glm::vec3(0, 0, 1), - glm::vec3(0, 0, 1), - glm::vec3(0, 0, 1), - glm::vec3(0, 0, 1), - - // Back - glm::vec3(0, 0, -1), - glm::vec3(0, 0, -1), - glm::vec3(0, 0, -1), - glm::vec3(0, 0, -1), - - // Top - glm::vec3(0, 1, 0), - glm::vec3(0, 1, 0), - glm::vec3(0, 1, 0), - glm::vec3(0, 1, 0), - - // Bottom - glm::vec3(0, -1, 0), - glm::vec3(0, -1, 0), - glm::vec3(0, -1, 0), - glm::vec3(0, -1, 0), - - // Left - glm::vec3(-1, 0, 0), - glm::vec3(-1, 0, 0), - glm::vec3(-1, 0, 0), - glm::vec3(-1, 0, 0), - - // Right - glm::vec3(1, 0, 0), - glm::vec3(1, 0, 0), - glm::vec3(1, 0, 0), - glm::vec3(1, 0, 0)}; - - // Specify indices - indices = { - 0, 1, 2, 0, 2, 3, // Front - 4, 5, 6, 4, 6, 7, // Back - 8, 9, 10, 8, 10, 11, // Top - 12, 13, 14, 12, 14, 15, // Bottom - 16, 17, 18, 16, 18, 19, // Left - 20, 21, 22, 20, 22, 23, // Right - }; - - // Generate a vertex array (VAO) and two vertex buffer objects (VBO). - glGenVertexArrays(1, &VAO); - glGenBuffers(1, &VBO_positions); - glGenBuffers(1, &VBO_normals); - - // Bind to the VAO. - glBindVertexArray(VAO); - - // Bind to the first VBO - We will use it to store the vertices - glBindBuffer(GL_ARRAY_BUFFER, VBO_positions); - glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * positions.size(), positions.data(), GL_STATIC_DRAW); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), 0); - - // Bind to the second VBO - We will use it to store the normals - glBindBuffer(GL_ARRAY_BUFFER, VBO_normals); - glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * normals.size(), normals.data(), GL_STATIC_DRAW); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), 0); - - // Generate EBO, bind the EBO to the bound VAO and send the data - glGenBuffers(1, &EBO); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * indices.size(), indices.data(), GL_STATIC_DRAW); - - // Unbind the VBOs. - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); -} - -Cube::~Cube() { - // Delete the VBOs and the VAO. - glDeleteBuffers(1, &VBO_positions); - glDeleteBuffers(1, &VBO_normals); - glDeleteBuffers(1, &EBO); - glDeleteVertexArrays(1, &VAO); -} - -void Cube::draw(Shader* shader, - glm::mat4 viewProj, - glm::vec3 camPos, - std::array, MAX_POINT_LIGHTS> lightSources, - bool fill) { - - // actiavte the shader program - shader->use(); - - // get the locations and send the uniforms to the shader - shader->setMat4("viewProj", viewProj); - auto model = this->getModelMat(); - shader->setMat4("model", model); - shader->setVec3("DiffuseColor", color); - - // Bind the VAO - glBindVertexArray(VAO); - - // Drawing mode for cube (wireframe vs filled) - if(fill){ - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - } else { - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - } - - // draw the points using triangles, indexed with the EBO - glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); - - // Unbind the VAO and shader program - glBindVertexArray(0); - glUseProgram(0); -} diff --git a/src/client/gui/CMakeLists.txt b/src/client/gui/CMakeLists.txt index a4454728..26ce6526 100644 --- a/src/client/gui/CMakeLists.txt +++ b/src/client/gui/CMakeLists.txt @@ -3,6 +3,7 @@ set(LIB_NAME game_gui_lib) set(FILES imgs/img.cpp imgs/loader.cpp + imgs/logo.cpp font/font.cpp font/loader.cpp @@ -13,6 +14,7 @@ set(FILES widget/dyntext.cpp widget/flexbox.cpp widget/staticimg.cpp + widget/empty.cpp gui.cpp ) diff --git a/src/client/gui/font/font.cpp b/src/client/gui/font/font.cpp index 4bf0fca5..12d2a6fa 100644 --- a/src/client/gui/font/font.cpp +++ b/src/client/gui/font/font.cpp @@ -21,6 +21,7 @@ std::string getFilepath(Font font) { auto dir = getRepoRoot() / "assets/fonts"; switch (font) { case Font::MENU: return (dir / "AncientModernTales-a7Po.ttf").string(); + case Font::TITLE: return (dir / "spqr.ttf").string(); default: case Font::TEXT: return (dir / "AtlantisInternational-jen0.ttf").string(); } @@ -32,11 +33,13 @@ glm::vec3 getRGB(Color color) { return {1.0f, 0.0f, 0.0f}; case Color::BLUE: return {0.0f, 0.0f, 1.0f}; + case Color::GREEN: + return {0.0f, 1.0f, 0.0f}; case Color::GRAY: return {0.5f, 0.5f, 0.5f}; case Color::WHITE: return {1.0f, 1.0f, 1.0f}; - case Color::TORCHLIGHT_GAMES: + case Color::YELLOW: return {0.902, 0.575, 0.055}; case Color::BLACK: @@ -46,6 +49,11 @@ glm::vec3 getRGB(Color color) { } float getRelativePixels(float pixels) { + float screen_factor = static_cast(WINDOW_HEIGHT) / static_cast(UNIT_WINDOW_HEIGHT); + return pixels * screen_factor; +} + +float getRelativePixelsHorizontal(float pixels) { float screen_factor = static_cast(WINDOW_WIDTH) / static_cast(UNIT_WINDOW_WIDTH); return pixels * screen_factor; } diff --git a/src/client/gui/font/loader.cpp b/src/client/gui/font/loader.cpp index daa43b9c..feae7463 100644 --- a/src/client/gui/font/loader.cpp +++ b/src/client/gui/font/loader.cpp @@ -20,11 +20,10 @@ bool Loader::init() { // so this is supposed to prevent seg faults related to that glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - if (!this->_loadFont(Font::MENU)) { - return false; - } - if (!this->_loadFont(Font::TEXT)) { - return false; + for (Font font : ALL_FONTS()) { + if (!this->_loadFont(font)) { // cppcheck-suppress useStlAlgorithm + return false; + } } FT_Done_FreeType(this->ft); // done loading fonts, so can release these resources diff --git a/src/client/gui/gui.cpp b/src/client/gui/gui.cpp index 5693faad..1eb2a457 100644 --- a/src/client/gui/gui.cpp +++ b/src/client/gui/gui.cpp @@ -14,8 +14,10 @@ namespace gui { -GUI::GUI(Client* client): capture_keystrokes(false) { +GUI::GUI(Client* client, const GameConfig& config): capture_keystrokes(false), logo() { this->client = client; + this->config = config; + this->controlDisplayed = true; // start with help on } bool GUI::init() @@ -47,12 +49,18 @@ bool GUI::init() return false; } + if (!this->logo.init()) { + return false; + } + + this->recentEvents.push_back(""); + std::cout << "Initialized GUI\n"; return true; } void GUI::beginFrame() { - std::unordered_map empty; + std::map empty; std::swap(this->widgets, empty); } @@ -70,13 +78,16 @@ void GUI::renderFrame() { glDisable(GL_BLEND); } -void GUI::handleInputs(float mouse_xpos, float mouse_ypos, bool& is_left_mouse_down) { +void GUI::handleInputs(float mouse_xpos, float mouse_ypos, bool& is_left_mouse_down, bool& is_right_mouse_down) { // convert to gui coords, where (0,0) is bottome left mouse_ypos = WINDOW_HEIGHT - mouse_ypos; if (is_left_mouse_down) { this->_handleClick(mouse_xpos, mouse_ypos); is_left_mouse_down = false; } + if (is_right_mouse_down) { + is_right_mouse_down = false; + } this->_handleHover(mouse_xpos, mouse_ypos); } @@ -131,7 +142,7 @@ std::shared_ptr GUI::getFonts() { } void GUI::layoutFrame(GUIState state) { - if (client->config.client.fps_counter) { + if (client->config.client.fps_counter && state != GUIState::INITIAL_LOAD) { _layoutFPSCounter(); } switch (state) { @@ -152,9 +163,13 @@ void GUI::layoutFrame(GUIState state) { glfwSetInputMode(client->window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); this->_layoutLobbyBrowser(); break; + case GUIState::INTRO_CUTSCENE: + glfwSetInputMode(client->window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + // draw nothing + break; case GUIState::GAME_HUD: glfwSetInputMode(client->window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - + //this->_layoutDeadScreen(); this->_sharedGameHUD(); this->_layoutGameHUD(); break; @@ -188,71 +203,97 @@ void GUI::_layoutFPSCounter() { )); } +void GUI::displayControl() { + this->controlDisplayed = this->controlDisplayed ? false : true; +} + void GUI::_layoutLoadingScreen() { - this->addWidget(widget::CenterText::make( - "made by", - font::Font::MENU, - font::Size::MEDIUM, - font::Color::WHITE, - fonts, - FRAC_WINDOW_HEIGHT(7,8) - )); - this->addWidget(widget::CenterText::make( - "Torchlight Games", - font::Font::MENU, - font::Size::XLARGE, - font::Color::TORCHLIGHT_GAMES, - fonts, - FRAC_WINDOW_HEIGHT(2,3) - )); - this->addWidget(widget::CenterText::make( - "Loading...", - font::Font::MENU, - font::Size::MEDIUM, - font::Color::WHITE, - fonts, - FRAC_WINDOW_HEIGHT(1,3) - )); + static bool first = true; + + auto logo_flex = widget::Flexbox::make(glm::vec2(0,0), glm::vec2(WINDOW_WIDTH, 0), + widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::CENTER, 0.0f)); + logo_flex->push(widget::StaticImg::make(glm::vec2(0, 0), this->logo.getNextFrame(), 0.75f)); + this->addWidget(std::move(logo_flex)); + + if (first) { + // the first frame will be rendered before it loads everything, then everything after will + // be once it has loaded + this->addWidget(widget::CenterText::make( + "Loading...", + font::Font::MENU, + font::Size::MEDIUM, + font::Color::YELLOW, + fonts, + FRAC_WINDOW_HEIGHT(1,2) + )); + first = false; + } else { + this->addWidget(widget::CenterText::make( + "Press any key to continue", + font::Font::MENU, + font::Size::MEDIUM, + font::Color::YELLOW, + fonts, + FRAC_WINDOW_HEIGHT(1,2) + )); + first = false; + } } void GUI::_layoutTitleScreen() { - this->addWidget(widget::CenterText::make( - "Arcana", - font::Font::MENU, - font::Size::XLARGE, - font::Color::RED, - fonts, - FRAC_WINDOW_HEIGHT(2, 3) - )); + auto logo_flex = widget::Flexbox::make(glm::vec2(0, 0), glm::vec2(WINDOW_WIDTH, 0), + widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::CENTER, 0.0f)); + auto logo = widget::StaticImg::make(glm::vec2(0,0), this->logo.getNextFrame(), 0.75f); + logo_flex->push(std::move(logo)); + this->addWidget(std::move(logo_flex)); + + auto title_flex = widget::Flexbox::make(glm::vec2(0, FRAC_WINDOW_HEIGHT(2,3)), glm::vec2(WINDOW_WIDTH, 0), + widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::CENTER, 0.0f)); + + title_flex->push(widget::StaticImg::make(glm::vec2(0,0), images.getImg(img::ImgID::Title))); + this->addWidget(std::move(title_flex)); auto start_text = widget::DynText::make( - "(Start Game)", + "Play", fonts, - widget::DynText::Options(font::Font::MENU, font::Size::MEDIUM, font::Color::BLACK) + widget::DynText::Options(font::Font::TITLE, font::Size::MEDIUM, font::Color::WHITE) ); start_text->addOnHover([this](widget::Handle handle) { auto widget = this->borrowWidget(handle); - widget->changeColor(font::Color::RED); + widget->changeColor(font::Color::YELLOW); }); start_text->addOnClick([this](widget::Handle handle) { client->gui_state = GUIState::LOBBY_BROWSER; }); - auto start_flex = widget::Flexbox::make( - glm::vec2(0.0f, FRAC_WINDOW_HEIGHT(1, 3)), + auto exit_text = widget::DynText::make( + "Exit", + fonts, + widget::DynText::Options(font::Font::TITLE, font::Size::MEDIUM, font::Color::WHITE) + ); + exit_text->addOnHover([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + widget->changeColor(font::Color::YELLOW); + }); + exit_text->addOnClick([this](widget::Handle handle) { + glfwSetWindowShouldClose(this->client->getWindow(), GL_TRUE); + }); + auto menu_flex = widget::Flexbox::make( + glm::vec2(0.0f, FRAC_WINDOW_HEIGHT(1, 2)), glm::vec2(WINDOW_WIDTH, 0.0f), - widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::CENTER, 0.0f) + widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::CENTER, font::getRelativePixels(30)) ); - start_flex->push(std::move(start_text)); - this->addWidget(std::move(start_flex)); + menu_flex->push(std::move(exit_text)); + menu_flex->push(std::move(start_text)); + this->addWidget(std::move(menu_flex)); } void GUI::_layoutLobbyBrowser() { this->addWidget(widget::CenterText::make( "Lobbies", - font::Font::MENU, + font::Font::TITLE, font::Size::LARGE, - font::Color::BLACK, + font::Color::YELLOW, this->fonts, WINDOW_HEIGHT - font::getFontSizePx(font::Size::LARGE) )); @@ -265,121 +306,769 @@ void GUI::_layoutLobbyBrowser() { for (const auto& [ip, packet]: client->lobby_finder.getFoundLobbies()) { std::stringstream ss; - ss << "(" << packet.lobby_name << " " << packet.slots_taken << "/" << packet.slots_avail + packet.slots_taken << ")"; + ss << "" << packet.lobby_name << " (" << packet.slots_taken << "/" << packet.slots_avail + packet.slots_taken << ")"; auto entry = widget::DynText::make(ss.str(), this->fonts, - widget::DynText::Options(font::Font::MENU, font::Size::MEDIUM, font::Color::BLACK)); + widget::DynText::Options(font::Font::MENU, font::Size::MEDIUM, font::Color::WHITE)); entry->addOnClick([ip, this](widget::Handle handle){ std::cout << "Connecting to " << ip.address() << " ...\n"; - if (this->client->connectAndListen(ip.address().to_string())) { + if (this->client->connect(ip.address().to_string())) { this->client->gui_state = GUIState::LOBBY; this->clearCapturedKeyboardInput(); } }); entry->addOnHover([this](widget::Handle handle){ auto widget = this->borrowWidget(handle); - widget->changeColor(font::Color::RED); + widget->changeColor(font::Color::YELLOW); }); lobbies_flex->push(std::move(entry)); } if (client->lobby_finder.getFoundLobbies().empty()) { lobbies_flex->push(widget::DynText::make( - "No lobbies found...", + "Searching for lobbies...", this->fonts, - widget::DynText::Options(font::Font::TEXT, font::Size::MEDIUM, font::Color::BLACK) + widget::DynText::Options(font::Font::TITLE, font::Size::MEDIUM, font::Color::YELLOW) )); } this->addWidget(std::move(lobbies_flex)); - auto input_flex = widget::Flexbox::make( - glm::vec2(0.0f, font::getRelativePixels(30) + 2 * font::getFontSizePx(font::Size::MEDIUM)), - glm::vec2(WINDOW_WIDTH, 0.0f), - widget::Flexbox::Options(widget::Dir::HORIZONTAL, widget::Align::CENTER, font::getRelativePixels(20)) - ); - input_flex->push(widget::TextInput::make( + auto connect_flex = widget::Flexbox::make( + glm::vec2(FRAC_WINDOW_WIDTH(1, 3), font::getRelativePixels(30)), glm::vec2(0.0f, 0.0f), - "Manual IP", - this, - fonts, - widget::DynText::Options(font::Font::TEXT, font::Size::MEDIUM, font::Color::BLACK) + widget::Flexbox::Options(widget::Dir::HORIZONTAL, widget::Align::LEFT, font::getRelativePixels(50) )); - this->addWidget(std::move(input_flex)); - - auto connect_flex = widget::Flexbox::make( - glm::vec2(0.0f, font::getRelativePixels(30)), - glm::vec2(WINDOW_WIDTH, 0.0f), - widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::CENTER, font::getRelativePixels(20)) - ); std::stringstream ss; - ss << "(Connect to \"" << this->getCapturedKeyboardInput() << "\")"; auto connect_btn = widget::DynText::make( - ss.str(), + "Connect", fonts, - widget::DynText::Options(font::Font::MENU, font::Size::MEDIUM, font::Color::BLACK) + widget::DynText::Options(font::Font::TITLE, font::Size::MEDIUM, font::Color::WHITE) ); connect_btn->addOnHover([this](widget::Handle handle) { auto btn = this->borrowWidget(handle); - btn->changeColor(font::Color::RED); + btn->changeColor(font::Color::YELLOW); }); connect_btn->addOnClick([this](widget::Handle handle) { auto input = this->getCapturedKeyboardInput(); - if (client->connectAndListen(input)) { + if (client->connect(input)) { client->gui_state = GUIState::LOBBY; this->clearCapturedKeyboardInput(); } }); connect_flex->push(std::move(connect_btn)); + connect_flex->push(widget::TextInput::make( + glm::vec2(0.0f, 0.0f), + "Enter IP", + this, + fonts, + widget::DynText::Options(font::Font::TEXT, font::Size::MEDIUM, font::Color::WHITE) + )); this->addWidget(std::move(connect_flex)); } void GUI::_layoutLobby() { + // The lobby UI has 3 subsections: + // 1. Lobby Name + // 2. Player Status Table + // 3. Start Game Button + + /* GUI Subsection 1: Lobby Name */ + + // Specify lobby title text height + float lobby_title_height = WINDOW_HEIGHT - font::getFontSizePx(font::Size::LARGE); + + // Create lobby title CenterText widget auto lobby_title = widget::CenterText::make( this->client->gameState.lobby.name, font::Font::MENU, font::Size::LARGE, - font::Color::BLACK, + font::Color::WHITE, this->fonts, - WINDOW_HEIGHT - font::getFontSizePx(font::Size::LARGE) + lobby_title_height ); + + // Add lobby title CenterText widget to lobby screen this->addWidget(std::move(lobby_title)); - std::stringstream ss; - ss << this->client->gameState.lobby.players.size() << " / " << this->client->gameState.lobby.max_players; - auto player_count = widget::CenterText::make( - ss.str(), - font::Font::MENU, - font::Size::MEDIUM, - font::Color::BLACK, - this->fonts, - WINDOW_HEIGHT - (2 * font::getFontSizePx(font::Size::LARGE)) - 10.0f + + /* GUI Subsection 2: Player Status Table */ + + // Define table column widths + //glm::vec3 columnWidths(400.0f, 850.0f, 400.0f); + glm::vec3 columnWidths(font::getRelativePixelsHorizontal(400.0f), + font::getRelativePixelsHorizontal(900.0f), + font::getRelativePixelsHorizontal(400.0f)); + + float rowHeight = font::getFontSizePx(font::Size::LARGE); + + // Create max_players rows in the Player Status Table + for (int i = 0; i < this->client->gameState.lobby.max_players; i++) { + // Get iterating LobbyPlayer struct + boost::optional lobbyPlayer = + this->client->gameState.lobby.players[i]; + + // Create a status row for this player + auto player_status_row = _createPlayerStatusRow( + lobbyPlayer, + columnWidths, + glm::vec2(0, lobby_title_height - rowHeight * (i + 1)), + i + 1 + ); + + // Add table row to the screen + this->addWidget(std::move(player_status_row)); + } + + /* GUI Subsection 3: Start Game Button */ + + auto start_game_button = widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::StartGame), 1); + start_game_button->addOnHover([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + widget->changeImage(images.getImg(img::ImgID::StartGameSelected)); + }); + + // Display differently based on whether all players in the lobby are ready + bool allReady = true; + + for (boost::optional player : this->client->gameState.lobby.players) { + if (!player.has_value() || !player.get().ready) { + // Either there aren't enough players in the lobby or at least + // one player isn't ready + allReady = false; + break; + } + } + + if (allReady) { + // Add radio button on click + start_game_button->addOnClick([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + + // Send StartGame event to the server + this->client->session->sendEvent(Event( + this->client->session->getInfo().client_eid.value(), + EventType::LobbyAction, + LobbyActionEvent( + LobbyActionEvent::Action::StartGame, + PlayerRole::Unknown + ) + )); + }); + } + + // Create flexbox to contain start button + auto start_game_flex = widget::Flexbox::make( + glm::vec2(0, 200), + glm::vec2(WINDOW_WIDTH, 0), + widget::Flexbox::Options( + widget::Dir::HORIZONTAL, + widget::Align::CENTER, + 0.0f + ) ); - this->addWidget(std::move(player_count)); - auto players_flex = widget::Flexbox::make( - glm::vec2(0.0f, FRAC_WINDOW_HEIGHT(1, 5)), - glm::vec2(WINDOW_WIDTH, 0.0f), - widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::CENTER, 10.0f) + // Push start button text widget to flex row + start_game_flex->push(std::move(start_game_button)); + + this->addWidget(std::move(start_game_flex)); +} + +gui::widget::Flexbox::Ptr GUI::_createPlayerStatusRow( + boost::optional lobbyPlayer, glm::vec3 columnWidths, + glm::vec2 origin, int playerIndex) { + // Determine whether the player is connected to the lobby + bool connected = lobbyPlayer.has_value(); + + // Create table row Flexbox + + // Row Flexbox configurations + glm::vec2 rowSize(WINDOW_WIDTH, 0); + widget::Flexbox::Options rowFlexboxOptions( + widget::Dir::HORIZONTAL, + widget::Align::CENTER, + 0.0f ); - for (const auto& [_eid, player_name] : this->client->gameState.lobby.players) { - players_flex->push(widget::DynText::make( - player_name, - this->fonts, - widget::DynText::Options(font::Font::MENU, font::Size::MEDIUM, font::Color::BLACK) - )); + + auto playerstatusBG = widget::Flexbox::make( + origin - glm::vec2(65, 26), + rowSize, + widget::Flexbox::Options(widget::Dir::HORIZONTAL, widget::Align::CENTER, 30.0f) + ); + playerstatusBG->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::RowBG), 2)); + this->addWidget(std::move(playerstatusBG)); + + auto player_status_row = widget::Flexbox::make( + origin, + rowSize, + rowFlexboxOptions + ); + + // Optional: Add a left margin Empty widget here if necessary + // at the start of the row + + /* Add Player Identification Column */ + // There are 3 possible values in the Player Identification column, + // all of which are strings + // Case 1: "Empty" + // This is displayed when the lobby player isn't connected + // (i.e., the lobbyPlayer boost::optional doesn't have a value) + // Case 2: "Player X" + // This is displayed if the given player is in the lobby and + // isn't this client's player + // Case 3: "Player X (You)" + // This is displayed if the given player is in the lobby and + // is this client's player + std::string playerIdString; + + if (!connected) { + // Player in this row is not connected (Case 1) + playerIdString = "Empty"; + } + else { + // Player is connected to the lobby (Case 2 or Case 3) + // Note that in both of these cases, the player identification + // string has the same prefix, "Player X" + playerIdString = "Player " + std::to_string(playerIndex); + + // Check whether this player is this client's player (Case 3) + // This code assumes that the client's eid should always have + // a value if we're in GUIState::Lobby + // NOTE: I have gotten an exception here since that wasn't true + // so there might be some sort of race condition. For now, I + // will simply add a has_value() check ahead of this + if (this->client->session.get()->getInfo().client_eid.has_value() + && this->client->session.get()->getInfo().client_eid.value() + == lobbyPlayer.get().id) { + playerIdString += " (You)"; + } } - this->addWidget(std::move(players_flex)); - auto waiting_msg = widget::CenterText::make( - "Waiting for players...", - font::Font::MENU, - font::Size::MEDIUM, - font::Color::GRAY, + // Create the player identification text widget + auto playerID = widget::DynText::make( + playerIdString, this->fonts, - 30.0f + widget::DynText::Options( + font::Font::TEXT, + font::Size::MEDIUM, + font::Color::WHITE + ) ); - this->addWidget(std::move(waiting_msg)); + + // Calculate the remaining column width and create an Empty widget with this width + float column1_empty_width = columnWidths.x - playerID->getSize().first; + + auto column1_empty = widget::Empty::make(column1_empty_width); + + // Push both the text widget and the Empty widget to the row Flexbox + player_status_row->push(std::move(playerID)); + player_status_row->push(std::move(column1_empty)); + + // Optional: Can add column padding here for additional padding between + // columns 1 and 2 + + /* Add Player Role Column */ + // There are two different behaviors based on whether the given lobbyPlayer + // is this client's player or not. + + if (!connected + || (this->client->session.get()->getInfo().client_eid.has_value() + && lobbyPlayer.get().id != this->client->session.get()->getInfo().client_eid.value())) { + // Case 1: This player is NOT the client player + // In this case, there are 4 possible subcases, all of which are composed + // of a single text widget: + // Subcase 1: "..." + // Displays if this player isn't connected + // Subcase 2: "Player X isn't ready..." + // Displays if this player is connected but not ready + // Subcase 3: "Player X wants to play as the DM." + // Displays if this player is ready and their desired player role is + // PlayerRole::DungeonMaster + // Subcase 4: "Player X wants to play as a Player." + // Displays if this player is ready and their desired player role is + // PlayerRole::Player + + std::string playerRoleString; + font::Color color = font::Color::WHITE; + + if (!connected) { + // Subcase 1 + playerRoleString = "..."; + } + else { + // Subcase 2, 3, or 4 + if (!lobbyPlayer.get().ready) { + // Subcase 2 + playerRoleString = "Player " + std::to_string(playerIndex) + + " isn't ready..."; + } + else { + // Subcase 3 or 4 + if (lobbyPlayer.get().desired_role == PlayerRole::DungeonMaster) { + // Subcase 3 + playerRoleString = "Player " + std::to_string(playerIndex) + + " wants to play as Zeus."; + color = font::Color::YELLOW; + } + else if (lobbyPlayer.get().desired_role == PlayerRole::Player) { + // subcase 4 + playerRoleString = "Player " + std::to_string(playerIndex) + + " wants to play as a Player."; + } + else { + // Error - player is in the ready state but their desired + // role isn't the DM or a Player! + playerRoleString = "Error! Player " + std::to_string(playerIndex) + + " wants to play as ???"; + } + } + } + + // Create player role text widget + auto player_role = widget::DynText::make( + playerRoleString, + this->fonts, + widget::DynText::Options( + font::Font::TEXT, + font::Size::MEDIUM, + color + ) + ); + + // Compute the remaining column width and create an Empty widget + float column2_empty_width = columnWidths.y - player_role->getSize().first; + + auto column2_empty = widget::Empty::make(column2_empty_width); + + // Push both the player role text widget and the Empty widget to the row + player_status_row->push(std::move(player_role)); + player_status_row->push(std::move(column2_empty)); + } + else { + + // Case 2: This player IS the client player + + if (this->client->lobbyPlayerState != Client::LobbyPlayerState::Ready) { + + // Add a text widget which says "Play as:" + auto radio_buttons_label = widget::DynText::make( + "Play as:", + this->fonts, + widget::DynText::Options( + font::Font::TEXT, + font::Size::MEDIUM, + font::Color::WHITE + ) + ); + + // Add Empty widget been radio buttons label and first radio + // button + auto radio_label_first_option_empty = widget::Empty::make( + 50.0f + ); + + // Add radio buttons "Player" and "DM" + + // "Player" radio button + auto player_radio_button = widget::DynText::make( + "Player", + fonts, + widget::DynText::Options( + font::Font::TEXT, + font::Size::MEDIUM, + font::Color::WHITE + ) + ); + + // "DM" radio button + auto dm_radio_button = widget::DynText::make( + "Zeus", + fonts, + widget::DynText::Options( + font::Font::TEXT, + font::Size::MEDIUM, + font::Color::WHITE + ) + ); + + // Add radio button hover + player_radio_button->addOnHover([this](widget::Handle handle) { + if (client->lobbyPlayerState != Client::LobbyPlayerState::Ready) { + auto widget = this->borrowWidget(handle); + widget->changeColor(font::Color::YELLOW); + } + }); + + // Add radio button on click + player_radio_button->addOnClick([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + + // TODO: Change button text to be surrounded by two brackets + // Change other radio button text to not be surrounded by brackets + if (client->lobbyPlayerState != Client::LobbyPlayerState::Ready) { + client->roleSelection = Client::RadioButtonState::FirstOption; + client->lobbyPlayerState = Client::LobbyPlayerState::SelectedRole; + + // Update this radio button text to show it's selected + //widget->changeText("[Player]"); + + // Update dm radio button text show it's not selected + //dm_button->changeText("DM"); + } + }); + + // Add Empty widget between first radio button and second radio + // button + auto radio_first_second_option_empty = widget::Empty::make( + 50.0f + ); + + // Add radio button hover + dm_radio_button->addOnHover([this](widget::Handle handle) { + if (client->lobbyPlayerState != Client::LobbyPlayerState::Ready) { + auto widget = this->borrowWidget(handle); + widget->changeColor(font::Color::YELLOW); + } + }); + + // Add radio button on click + dm_radio_button->addOnClick([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + + // TODO: Change button text to be surrounded by two brackets + // Change other radio button text to not be surrounded by brackets + if (client->lobbyPlayerState != Client::LobbyPlayerState::Ready) { + client->roleSelection = Client::RadioButtonState::SecondOption; + client->lobbyPlayerState = Client::LobbyPlayerState::SelectedRole; + + // Update this radio button text to show it's selected + //widget->changeText("[DM]"); + + // Update player radio button text show it's not selected + //player_button->changeText("Player"); + } + }); + + // Compute the remaining column width and create an Empty widget + /* + float column2_empty_width = columnWidths.y + - radio_buttons_label->getSize().first + - radio_label_first_option_empty->getSize().first + - player_radio_button->getSize().first + - radio_first_second_option_empty->getSize().first + - dm_radio_button->getSize().first;*/ + + float column2_empty_width = columnWidths.y - radio_buttons_label->getSize().first; + auto column2_empty = widget::Empty::make(column2_empty_width); + + auto row_buttons = widget::Flexbox::make( + origin - glm::vec2(120, 16), + rowSize, + widget::Flexbox::Options(widget::Dir::HORIZONTAL, widget::Align::CENTER, 20.0f) + ); + + // When first selecting + if (this->client->lobbyPlayerState == Client::LobbyPlayerState::Connected) { + auto playerButton = widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Player), 1); + playerButton->addOnHover([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + widget->changeImage(images.getImg(img::ImgID::PlayerSelected)); + }); + playerButton->addOnClick([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + if (client->lobbyPlayerState != Client::LobbyPlayerState::Ready) { + client->roleSelection = Client::RadioButtonState::FirstOption; + client->lobbyPlayerState = Client::LobbyPlayerState::SelectedRole; + } + }); + + auto zeusButton = widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Zeus), 1); + zeusButton->addOnHover([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + widget->changeImage(images.getImg(img::ImgID::ZeusSelected)); + }); + zeusButton->addOnClick([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + if (client->lobbyPlayerState != Client::LobbyPlayerState::Ready) { + client->roleSelection = Client::RadioButtonState::SecondOption; + client->lobbyPlayerState = Client::LobbyPlayerState::SelectedRole; + } + }); + row_buttons->push(std::move(playerButton)); + row_buttons->push(std::move(zeusButton)); + } + // After one is selected + else { + if (this->client->roleSelection == Client::RadioButtonState::FirstOption) { + auto playerButton = widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::PlayerSelected), 1); + playerButton->addOnClick([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + if (client->lobbyPlayerState != Client::LobbyPlayerState::Ready) { + client->roleSelection = Client::RadioButtonState::FirstOption; + client->lobbyPlayerState = Client::LobbyPlayerState::SelectedRole; + } + }); + + auto zeusButton = widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Zeus), 1); + zeusButton->addOnHover([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + widget->changeImage(images.getImg(img::ImgID::ZeusSelected)); + }); + zeusButton->addOnClick([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + if (client->lobbyPlayerState != Client::LobbyPlayerState::Ready) { + client->roleSelection = Client::RadioButtonState::SecondOption; + client->lobbyPlayerState = Client::LobbyPlayerState::SelectedRole; + } + }); + row_buttons->push(std::move(playerButton)); + row_buttons->push(std::move(zeusButton)); + } + else { + auto playerButton = widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Player), 1); + playerButton->addOnHover([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + widget->changeImage(images.getImg(img::ImgID::PlayerSelected)); + }); + playerButton->addOnClick([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + if (client->lobbyPlayerState != Client::LobbyPlayerState::Ready) { + client->roleSelection = Client::RadioButtonState::FirstOption; + client->lobbyPlayerState = Client::LobbyPlayerState::SelectedRole; + } + }); + + auto zeusButton = widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::ZeusSelected), 1); + zeusButton->addOnClick([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + if (client->lobbyPlayerState != Client::LobbyPlayerState::Ready) { + client->roleSelection = Client::RadioButtonState::SecondOption; + client->lobbyPlayerState = Client::LobbyPlayerState::SelectedRole; + } + }); + row_buttons->push(std::move(playerButton)); + row_buttons->push(std::move(zeusButton)); + } + } + this->addWidget(std::move(row_buttons)); + + // fill in empty spaces in row + player_status_row->push(std::move(radio_buttons_label)); + player_status_row->push(std::move(column2_empty)); + + // Push all widgets to the row + /* + player_status_row->push(std::move(radio_buttons_label)); + player_status_row->push(std::move(radio_label_first_option_empty)); + player_status_row->push(std::move(player_radio_button)); + player_status_row->push(std::move(radio_first_second_option_empty)); + player_status_row->push(std::move(dm_radio_button)); + player_status_row->push(std::move(column2_empty)); */ + } + else { + + // After selected role for yourself + + std::string playerRoleString; + font::Color color; + + if (lobbyPlayer.get().desired_role == PlayerRole::DungeonMaster) { + playerRoleString = "You want to play as Zeus."; + color = font::Color::YELLOW; + } + else if (lobbyPlayer.get().desired_role == PlayerRole::Player) { + playerRoleString = "You want to play as a Player."; + color = font::Color::WHITE; + } + + // Create player role text widget + auto player_role = widget::DynText::make( + playerRoleString, + this->fonts, + widget::DynText::Options( + font::Font::TEXT, + font::Size::MEDIUM, + color + ) + ); + + // Compute the remaining column width and create an Empty widget + float column2_empty_width = columnWidths.y - player_role->getSize().first; + + auto column2_empty = widget::Empty::make(column2_empty_width); + + // Push both the player role text widget and the Empty widget to the row + player_status_row->push(std::move(player_role)); + player_status_row->push(std::move(column2_empty)); + } + } + + // Optional: Can add column padding here for additional padding between + // columns 2 and 3 + + /* Add Ready Status Column */ + // There are two different behaviors based on whether the given lobbyPlayer + // is this client's player or not. + + if (!connected + || (this->client->session.get()->getInfo().client_eid.has_value() + && lobbyPlayer.get().id != this->client->session.get()->getInfo().client_eid.value()) + ) { + // Case 1: This player is NOT the client player + // In this case, there are 2 possible subcases, all of which are composed + // of a single text widget: + // Subcase 1: "..." + // Displays if this player isn't connected or not ready + // Subcase 2: "Ready!" + // Displays if this player is ready + + std::string readyStatusString; + font::Color color = font::Color::WHITE;; + + if (!connected || !lobbyPlayer.get().ready) { + // Subcase 1 + readyStatusString = "..."; + } + else { + // Subcase 2 + readyStatusString = "Ready!"; + if (lobbyPlayer.get().desired_role == PlayerRole::DungeonMaster) { + color = font::Color::YELLOW; + } + } + + // Create ready status string text widget + auto ready_status = widget::DynText::make( + readyStatusString, + this->fonts, + widget::DynText::Options( + font::Font::TEXT, + font::Size::MEDIUM, + color + ) + ); + + // Compute the remaining column width and create an Empty widget + float column3_empty_width = columnWidths.z - ready_status->getSize().first; + + auto column3_empty = widget::Empty::make(column3_empty_width); + + // Push all widgets to the row + player_status_row->push(std::move(ready_status)); + player_status_row->push(std::move(column3_empty)); + } + else { + // Case 2: This player IS the client player + + // In this case, there are 3 possible subcases: + // Subcase 1: Text widget with value "Not Ready" + // Displays initially (when the player's state is Connected) + // Subcase 2: Clickable text widget with value "Ready? (DM/Player)" + // Displays when the client's player is in the SelectedRole state + // Subcase 3: Text widget with value "Ready!" + // Displays when the client's player is in the Ready state. + + std::string readyStatusString; + font::Color color = font::Color::WHITE; + + auto ready_button = widget::Flexbox::make( + origin - glm::vec2(-595, 16), + rowSize, + widget::Flexbox::Options(widget::Dir::HORIZONTAL, widget::Align::CENTER, 20.0f) + ); + + // First, set string text + switch (this->client->lobbyPlayerState) { + case Client::LobbyPlayerState::Connected: { + // Subcase 1 + readyStatusString = "Not Ready"; + break; + } + case Client::LobbyPlayerState::SelectedRole: { + // Subcase 2 + img::ImgID readyRole; + switch (this->client->roleSelection) { + case Client::RadioButtonState::FirstOption: + readyRole = img::ImgID::ReadyPlayer; + break; + case Client::RadioButtonState::SecondOption: + readyRole = img::ImgID::ReadyZeus; + break; + } + + auto readyImg = widget::StaticImg::make(glm::vec2(0.0f), images.getImg(readyRole), 1); + readyImg->addOnHover([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + img::ImgID readyRoleSelected; + switch (this->client->roleSelection) { + case Client::RadioButtonState::FirstOption: + readyRoleSelected = img::ImgID::ReadyPlayerSelected; + break; + case Client::RadioButtonState::SecondOption: + readyRoleSelected = img::ImgID::ReadyZeusSelected; + break; + } + widget->changeImage(images.getImg(readyRoleSelected)); + }); + + readyImg->addOnClick([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + client->lobbyPlayerState = Client::LobbyPlayerState::Ready; + PlayerRole role; + switch (this->client->roleSelection) { + case Client::RadioButtonState::FirstOption: + role = PlayerRole::Player; + break; + case Client::RadioButtonState::SecondOption: + role = PlayerRole::DungeonMaster; + break; + } + this->client->session->sendEvent(Event( + this->client->session->getInfo().client_eid.value(), + EventType::LobbyAction, + LobbyActionEvent( + LobbyActionEvent::Action::Ready, + role + ) + )); + }); + ready_button->push(std::move(readyImg)); + break; + } + case Client::LobbyPlayerState::Ready: { + // Subcase 3 + readyStatusString = "Ready!"; + if (lobbyPlayer.get().desired_role == PlayerRole::DungeonMaster) { + color = font::Color::YELLOW; + } + break; + } + } + + // Create text widget + auto ready_status = widget::DynText::make( + readyStatusString, + this->fonts, + widget::DynText::Options( + font::Font::TEXT, + font::Size::MEDIUM, + color + ) + ); + + // Calculate remaining empty space and create Empty widget + float column3_empty_width = columnWidths.z - ready_status->getSize().first; + + auto column3_empty = widget::Empty::make(column3_empty_width); + + this->addWidget(std::move(ready_button)); + + // Push both widgets to row + player_status_row->push(std::move(ready_status)); + player_status_row->push(std::move(column3_empty)); + } + + return player_status_row; } void GUI::_sharedGameHUD() { @@ -392,8 +1081,74 @@ void GUI::_sharedGameHUD() { return; } - auto self = client->gameState.objects.at(*self_eid); + // Add controls Help + if (this->controlDisplayed) { + auto controlBG = widget::Flexbox::make( + glm::vec2(WINDOW_WIDTH - font::getRelativePixels(360), FRAC_WINDOW_HEIGHT(1, 2) - font::getRelativePixels(10)), + glm::vec2(0.0f, 0.0f), + widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::LEFT, font::getRelativePixels(10)) + ); + if (!is_dm.value()) { + controlBG->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::HelpBG), 2)); + } + else { + controlBG->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::HelpDMBG), 2)); + } + this->addWidget(std::move(controlBG)); + + auto controlsFlex = widget::Flexbox::make( + glm::vec2(WINDOW_WIDTH - font::getRelativePixels(350), FRAC_WINDOW_HEIGHT(1, 2)), + glm::vec2(0.0f, 0.0f), + widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::LEFT, font::getRelativePixels(10)) + ); + auto actionsFlex = widget::Flexbox::make( + glm::vec2(WINDOW_WIDTH - font::getRelativePixels(150), FRAC_WINDOW_HEIGHT(1, 2)), + glm::vec2(0.0f, 0.0f), + widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::LEFT, font::getRelativePixels(10)) + ); + + std::vector> controls; + // Controls for Player + if (!is_dm.value()) { + controls.push_back({ "CONTROLS", " " }); + controls.push_back({ "WASD:", "Move" }); + controls.push_back({ "Left Shift:", "Sprint" }); + controls.push_back({ "Spacebar:", "Jump" }); + controls.push_back({ "Q:", "Drop Item" }); + controls.push_back({ "Left Click:", "Use Item" }); + controls.push_back({ "Mouse Wheel:", "Select Item" }); + controls.push_back({ "ESC:", "Menu" }); + controls.push_back({ "H:", "Controls" }); + } + // Controls for DM + else { + controls.push_back({ "CONTROLS", " " }); + controls.push_back({ "WASD:", "Move" }); + controls.push_back({ "Left Shift:", "Zoom In" }); + controls.push_back({ "Spacebar:", "Zoom Out" }); + controls.push_back({ "Left Control:", "Boost" }); + controls.push_back({ "Left Click:", "Place Trap" }); + controls.push_back({ "Right Click:", "Rotate Trap" }); + controls.push_back({ "Mouse Wheel:", "Select Trap" }); + controls.push_back({ "ESC:", "Menu" }); + controls.push_back({ "H:", "Controls" }); + } + + for (int i = controls.size() - 1; i >= 0; i--) { + controlsFlex->push(widget::DynText::make(controls[i].first, fonts, + widget::DynText::Options(font::Font::TEXT, font::Size::SMALL, font::Color::WHITE)) + ); + actionsFlex->push(widget::DynText::make(controls[i].second, fonts, + widget::DynText::Options(font::Font::TEXT, font::Size::SMALL, font::Color::WHITE)) + ); + } + + this->addWidget(std::move(controlsFlex)); + this->addWidget(std::move(actionsFlex)); + } + + auto self = client->gameState.objects.at(*self_eid); auto inventory_size = !is_dm.value() ? self->inventoryInfo->inventory_size : self->trapInventoryInfo->inventory_size; auto selected = !is_dm.value() ? self->inventoryInfo->selected - 1 : self->trapInventoryInfo->selected - 1; @@ -448,6 +1203,10 @@ void GUI::_sharedGameHUD() { itemString = "Hammer"; break; } + case ModelType::Mirror: { + itemString = "Mirror"; + break; + } } } } else { // DM hotbar @@ -455,28 +1214,115 @@ void GUI::_sharedGameHUD() { switch (self->trapInventoryInfo->inventory[selected]) { case ModelType::FloorSpikeFull: { itemString = "Floor Spike Full"; + + if (self->trapInventoryInfo->trapsInCooldown.find(CellType::FloorSpikeFull) != self->trapInventoryInfo->trapsInCooldown.end()) { + itemString += " (IN COOLDOWN)"; + } break; } case ModelType::FloorSpikeHorizontal: { itemString = "Floor Spike Horizontal"; + + if (self->trapInventoryInfo->trapsInCooldown.find(CellType::FloorSpikeHorizontal) != self->trapInventoryInfo->trapsInCooldown.end()) { + itemString += " (IN COOLDOWN)"; + } break; } case ModelType::FloorSpikeVertical: { itemString = "Floor Spike Vertical"; + + if (self->trapInventoryInfo->trapsInCooldown.find(CellType::FloorSpikeVertical) != self->trapInventoryInfo->trapsInCooldown.end()) { + itemString += " (IN COOLDOWN)"; + } + break; } - case ModelType::FireballTrap: { + case ModelType::SunGod: { itemString = "Fireball Trap"; + + if (self->trapInventoryInfo->trapsInCooldown.find(CellType::FireballTrapUp) != self->trapInventoryInfo->trapsInCooldown.end() + || self->trapInventoryInfo->trapsInCooldown.find(CellType::FireballTrapLeft) != self->trapInventoryInfo->trapsInCooldown.end() + || self->trapInventoryInfo->trapsInCooldown.find(CellType::FireballTrapRight) != self->trapInventoryInfo->trapsInCooldown.end() + || self->trapInventoryInfo->trapsInCooldown.find(CellType::FireballTrapDown) != self->trapInventoryInfo->trapsInCooldown.end()) { + itemString += " (IN COOLDOWN)"; + } break; } case ModelType::SpikeTrap: { itemString = "Ceiling Spike Trap"; + + if (self->trapInventoryInfo->trapsInCooldown.find(CellType::SpikeTrap) != self->trapInventoryInfo->trapsInCooldown.end()) { + itemString += " (IN COOLDOWN)"; + } + break; + } + case ModelType::Lightning: { + itemString = "Lightning Bolt (6)"; + + break; + } + case ModelType::LightCut: { + itemString = "Cut Lights (3)"; + + break; + } + case ModelType::TeleporterTrap: { + itemString = "Teleporter Trap"; + + if (self->trapInventoryInfo->trapsInCooldown.find(CellType::TeleporterTrap) != self->trapInventoryInfo->trapsInCooldown.end()) { + itemString += " (IN COOLDOWN)"; + } + + + break; + } + case ModelType::ArrowTrap: { + itemString = "Arrow Trap"; + + if (self->trapInventoryInfo->trapsInCooldown.find(CellType::ArrowTrapUp) != self->trapInventoryInfo->trapsInCooldown.end() + || self->trapInventoryInfo->trapsInCooldown.find(CellType::ArrowTrapLeft) != self->trapInventoryInfo->trapsInCooldown.end() + || self->trapInventoryInfo->trapsInCooldown.find(CellType::ArrowTrapDown) != self->trapInventoryInfo->trapsInCooldown.end() + || self->trapInventoryInfo->trapsInCooldown.find(CellType::ArrowTrapRight) != self->trapInventoryInfo->trapsInCooldown.end()) { + itemString += " (IN COOLDOWN)"; + } + break; } } } } + auto txtHeight = font::getRelativePixels(145.0); + auto flexHeight = font::getRelativePixels(45.0); + if(this->config.client.presentation){ + txtHeight += font::getRelativePixels(80.0); + flexHeight += font::getRelativePixels(85.0); + } + + // Item Description Background + if (itemString != "" || is_dm.value()) { + auto itemBGFlexHeight = txtHeight - 7; + if (!this->config.client.fullscreen) { + itemBGFlexHeight++; + } + auto itemBGFlex = widget::Flexbox::make( + glm::vec2(0.0f, itemBGFlexHeight), + glm::vec2(WINDOW_WIDTH, 0.0f), + widget::Flexbox::Options(widget::Dir::HORIZONTAL, widget::Align::CENTER, 0.0f) + ); + if (!is_dm.value()) { + itemBGFlex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::ItemBG), 2)); + } + else { + itemBGFlex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::DMTrapBG), 2)); + } + this->addWidget(std::move(itemBGFlex)); + } + + if (!is_dm.value()) { + txtHeight -= font::getRelativePixels(2.0); + } + // Text for item description auto item_txt = widget::CenterText::make( itemString, @@ -484,14 +1330,190 @@ void GUI::_sharedGameHUD() { font::Size::SMALL, font::Color::WHITE, fonts, - font::getRelativePixels(70) + txtHeight ); this->addWidget(std::move(item_txt)); + // Flexbox for the item frames + auto frameflex = widget::Flexbox::make( + glm::vec2(0.0f, flexHeight), //position relative to screen + glm::vec2(WINDOW_WIDTH, 0.0f), //dimensions of the flexbox + widget::Flexbox::Options(widget::Dir::HORIZONTAL, widget::Align::CENTER, 0.0f) //last one is padding + ); + + if (!is_dm.value()) { + frameflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::LeftHotbar), 2)); + for (int i = 0; i < inventory_size; i++) { + if (selected == i) { + frameflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::MiddleSelected), 2)); + + } + else { + frameflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::MiddleHotbar), 2)); + } + } + frameflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::RightHotbar), 2)); + } else { + frameflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::DMLeftHotbar), 2)); + + for (int i = 0; i < inventory_size; i++) { + bool idxInCooldown = false; + int cdRemaining = 0; + + if (self->trapInventoryInfo->inventory[i] != ModelType::Frame) { + switch (self->trapInventoryInfo->inventory[i]) { + case ModelType::FloorSpikeFull: { + if (self->trapInventoryInfo->trapsInCooldown.find(CellType::FloorSpikeFull) != self->trapInventoryInfo->trapsInCooldown.end()) { + cdRemaining = self->trapInventoryInfo->trapsCooldown.at(CellType::FloorSpikeFull); + idxInCooldown = true; + } + break; + } + case ModelType::FloorSpikeVertical: { + if (self->trapInventoryInfo->trapsInCooldown.find(CellType::FloorSpikeVertical) != self->trapInventoryInfo->trapsInCooldown.end()) { + cdRemaining = self->trapInventoryInfo->trapsCooldown.at(CellType::FloorSpikeVertical); + idxInCooldown = true; + } + break; + } + case ModelType::FloorSpikeHorizontal: { + if (self->trapInventoryInfo->trapsInCooldown.find(CellType::FloorSpikeHorizontal) != self->trapInventoryInfo->trapsInCooldown.end()) { + cdRemaining = self->trapInventoryInfo->trapsCooldown.at(CellType::FloorSpikeHorizontal); + idxInCooldown = true; + } + break; + } + case ModelType::SunGod: { + itemString = "Fireball Trap"; + + if (self->trapInventoryInfo->trapsInCooldown.find(CellType::FireballTrapUp) != self->trapInventoryInfo->trapsInCooldown.end() + || self->trapInventoryInfo->trapsInCooldown.find(CellType::FireballTrapLeft) != self->trapInventoryInfo->trapsInCooldown.end() + || self->trapInventoryInfo->trapsInCooldown.find(CellType::FireballTrapRight) != self->trapInventoryInfo->trapsInCooldown.end() + || self->trapInventoryInfo->trapsInCooldown.find(CellType::FireballTrapDown) != self->trapInventoryInfo->trapsInCooldown.end()) + { + if (self->trapInventoryInfo->trapsInCooldown.find(CellType::FireballTrapUp) != self->trapInventoryInfo->trapsInCooldown.end()) { + cdRemaining = self->trapInventoryInfo->trapsCooldown.at(CellType::FireballTrapUp); + } + else if (self->trapInventoryInfo->trapsInCooldown.find(CellType::FireballTrapLeft) != self->trapInventoryInfo->trapsInCooldown.end()) { + cdRemaining = self->trapInventoryInfo->trapsCooldown.at(CellType::FireballTrapLeft); + } + else if (self->trapInventoryInfo->trapsInCooldown.find(CellType::FireballTrapRight) != self->trapInventoryInfo->trapsInCooldown.end()) { + cdRemaining = self->trapInventoryInfo->trapsCooldown.at(CellType::FireballTrapRight); + } + else { + cdRemaining = self->trapInventoryInfo->trapsCooldown.at(CellType::FireballTrapDown); + } + + idxInCooldown = true; + } + break; + } + case ModelType::SpikeTrap: { + itemString = "Ceiling Spike Trap"; + + if (self->trapInventoryInfo->trapsInCooldown.find(CellType::SpikeTrap) != self->trapInventoryInfo->trapsInCooldown.end()) { + cdRemaining = self->trapInventoryInfo->trapsCooldown.at(CellType::SpikeTrap); + idxInCooldown = true; + } + break; + } + case ModelType::Lightning: { + itemString = "Lightning Bolt (6)"; + + break; + }case ModelType::LightCut: { + itemString = "Cut Lights (3)"; + + break; + } + case ModelType::TeleporterTrap: { + itemString = "Teleporter Trap"; + + if (self->trapInventoryInfo->trapsInCooldown.find(CellType::TeleporterTrap) != self->trapInventoryInfo->trapsInCooldown.end()) { + cdRemaining = self->trapInventoryInfo->trapsCooldown.at(CellType::TeleporterTrap); + idxInCooldown = true; + } + + break; + } + case ModelType::ArrowTrap: { + itemString = "Arrow Trap"; + + if (self->trapInventoryInfo->trapsInCooldown.find(CellType::ArrowTrapUp) != self->trapInventoryInfo->trapsInCooldown.end() + || self->trapInventoryInfo->trapsInCooldown.find(CellType::ArrowTrapLeft) != self->trapInventoryInfo->trapsInCooldown.end() + || self->trapInventoryInfo->trapsInCooldown.find(CellType::ArrowTrapDown) != self->trapInventoryInfo->trapsInCooldown.end() + || self->trapInventoryInfo->trapsInCooldown.find(CellType::ArrowTrapRight) != self->trapInventoryInfo->trapsInCooldown.end()) + { + if (self->trapInventoryInfo->trapsInCooldown.find(CellType::ArrowTrapUp) != self->trapInventoryInfo->trapsInCooldown.end()) { + cdRemaining = self->trapInventoryInfo->trapsCooldown.at(CellType::ArrowTrapUp); + } + else if (self->trapInventoryInfo->trapsInCooldown.find(CellType::ArrowTrapLeft) != self->trapInventoryInfo->trapsInCooldown.end()) { + cdRemaining = self->trapInventoryInfo->trapsCooldown.at(CellType::ArrowTrapLeft); + } + else if (self->trapInventoryInfo->trapsInCooldown.find(CellType::ArrowTrapDown) != self->trapInventoryInfo->trapsInCooldown.end()) { + cdRemaining = self->trapInventoryInfo->trapsCooldown.at(CellType::ArrowTrapDown); + } + else { + cdRemaining = self->trapInventoryInfo->trapsCooldown.at(CellType::ArrowTrapRight); + } + idxInCooldown = true; + } + + break; + } + } + } + + if (!idxInCooldown) { + if (selected == i) { + frameflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::DMMiddleSelected), 2)); + + } + else { + frameflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::DMMiddleHotbar), 2)); + } + } + else { + auto imgSelect = img::ImgID::DMCD_10; + if (cdRemaining > 4500) { + imgSelect = (selected != i) ? img::ImgID::DMCD_10 : img::ImgID::DMCD_Selected_10; + } + else if (cdRemaining > 4000) { + imgSelect = (selected != i) ? img::ImgID::DMCD_9 : img::ImgID::DMCD_Selected_9; + } + else if (cdRemaining > 3500) { + imgSelect = (selected != i) ? img::ImgID::DMCD_8 : img::ImgID::DMCD_Selected_8; + } + else if (cdRemaining > 3000) { + imgSelect = (selected != i) ? img::ImgID::DMCD_7 : img::ImgID::DMCD_Selected_7; + } + else if (cdRemaining > 2500) { + imgSelect = (selected != i) ? img::ImgID::DMCD_6 : img::ImgID::DMCD_Selected_6; + } + else if (cdRemaining > 2000) { + imgSelect = (selected != i) ? img::ImgID::DMCD_5 : img::ImgID::DMCD_Selected_5; + } + else if (cdRemaining > 1500) { + imgSelect = (selected != i) ? img::ImgID::DMCD_4 : img::ImgID::DMCD_Selected_4; + } + else if (cdRemaining > 1000) { + imgSelect = (selected != i) ? img::ImgID::DMCD_3 : img::ImgID::DMCD_Selected_3; + } + else { + // Doesn't use DMCD_1 b/c it looks better without it + imgSelect = (selected != i) ? img::ImgID::DMCD_2 : img::ImgID::DMCD_Selected_2; + } + frameflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(imgSelect), 2)); + } + } + frameflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::DMRightHotbar), 2)); + } + this->addWidget(std::move(frameflex)); + // Flexbox for the items // Loading itemframe again if no item auto itemflex = widget::Flexbox::make( - glm::vec2(0.0f, 0.0f), + glm::vec2(0.0f, flexHeight), glm::vec2(WINDOW_WIDTH, 0.0f), widget::Flexbox::Options(widget::Dir::HORIZONTAL, widget::Align::CENTER, 0.0f) ); @@ -540,61 +1562,118 @@ void GUI::_sharedGameHUD() { itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Hammer), 2)); break; } + case ModelType::Mirror: { + // TODO: Replace with an img of a mirror + itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Mirror), 2)); + } } } else { - itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::ItemFrame), 2)); + itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Blank), 2)); } } else { if (self->trapInventoryInfo->inventory[i] != ModelType::Frame) { switch (self->trapInventoryInfo->inventory[i]) { case ModelType::FloorSpikeFull: { // TODO: CHANGE images - itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Orb), 2)); + itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::FloorSpikeTrap), 2)); break; } case ModelType::FloorSpikeHorizontal: { - itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Orb), 2)); + itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Teleporter), 2)); break; } case ModelType::FloorSpikeVertical: { - itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Orb), 2)); + itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::ArrowTrap), 2)); break; } - case ModelType::FireballTrap: { - itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Orb), 2)); + case ModelType::SunGod: { + itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Sungod), 2)); break; } case ModelType::SpikeTrap: { - itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Orb), 2)); + itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::SpikeTrap), 2)); + break; + } + case ModelType::Lightning: { + itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Lightning), 2)); + break; + }case ModelType::LightCut: { + itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::LightCut), 2)); + break; + } + case ModelType::ArrowTrap: { + itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::ArrowTrap), 2)); + break; + } + case ModelType::TeleporterTrap: { + itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Teleporter), 2)); break; } } } else { - itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::ItemFrame), 2)); + itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Blank), 2)); } } } this->addWidget(std::move(itemflex)); - // Flexbox for the item frames - auto frameflex = widget::Flexbox::make( - glm::vec2(0.0f, 0.0f), //position relative to screen - glm::vec2(WINDOW_WIDTH, 0.0f), //dimensions of the flexbox - widget::Flexbox::Options(widget::Dir::HORIZONTAL, widget::Align::CENTER, 0.0f) //last one is padding - ); - - for (int i = 0; i < inventory_size; i++) { - if (selected == i) { - frameflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::SelectedFrame), 2)); - } - else { - frameflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::ItemFrame), 2)); - } + auto barHeight = font::getRelativePixels(5.0); + if (this->config.client.presentation) { + barHeight += font::getRelativePixels(90.0); } + if (!is_dm.value()) { + // Flexbox for the health bar + auto healthflex = widget::Flexbox::make( + glm::vec2(0.0f, barHeight), + glm::vec2(WINDOW_WIDTH, 0.0f), + widget::Flexbox::Options(widget::Dir::HORIZONTAL, widget::Align::CENTER, 0.0f) + ); + healthflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::HealthBar), 2)); + this->addWidget(std::move(healthflex)); + + auto healthtickflex = widget::Flexbox::make( + glm::vec2(0.0f, barHeight), + glm::vec2(WINDOW_WIDTH, 0.0f), + widget::Flexbox::Options(widget::Dir::HORIZONTAL, widget::Align::CENTER, 0.0f) + ); + for (int i = 1; i <= self->stats->health.max(); i++) { + if (i <= self->stats->health.current()) { + healthtickflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::HealthTickFull), 2)); + } + else { + healthtickflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::HealthTickEmpty), 2)); + } + } - this->addWidget(std::move(frameflex)); + this->addWidget(std::move(healthtickflex)); + } + else { + // Flexbox for the mana bar for DM + auto manaflex = widget::Flexbox::make( + glm::vec2(0.0f, barHeight), + glm::vec2(WINDOW_WIDTH, 0.0f), + widget::Flexbox::Options(widget::Dir::HORIZONTAL, widget::Align::CENTER, 0.0f) + ); + manaflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::ManaBar), 2)); + this->addWidget(std::move(manaflex)); + + auto manatickflex = widget::Flexbox::make( + glm::vec2(0.0f, barHeight), + glm::vec2(WINDOW_WIDTH, 0.0f), + widget::Flexbox::Options(widget::Dir::HORIZONTAL, widget::Align::CENTER, 0.0f) + ); + for (int i = 1; i <= 30; i++) { + if (i <= self->DMInfo->mana_remaining) { + manatickflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::ManaTickFull), 2)); + } + else { + manatickflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::ManaTickEmpty), 2)); + } + } + this->addWidget(std::move(manatickflex)); + } } void GUI::_layoutGameHUD() { @@ -613,30 +1692,60 @@ void GUI::_layoutGameHUD() { if (!self_eid.has_value()) { return; } + auto self = client->gameState.objects.at(*self_eid); + auto eventBGHeight = 0; + auto eventTxtHeight = font::getRelativePixels(25); + if (this->config.client.presentation) { + eventBGHeight += font::getRelativePixels(85.0); + eventTxtHeight += font::getRelativePixels(85.0); + } + + auto matchPhaseBGFlex = widget::Flexbox::make( + glm::vec2(font::getRelativePixels(5), eventBGHeight), + glm::vec2(0, 0), + widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::LEFT, font::getRelativePixels(10)) + ); + auto bgSize = 2.5; + if (!is_dm.value()) { + matchPhaseBGFlex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::EventBG), bgSize)); + } + else { + matchPhaseBGFlex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::DMEventBG), bgSize)); + } + this->addWidget(std::move(matchPhaseBGFlex)); + auto matchPhaseFlex = widget::Flexbox::make( - glm::vec2(0, WINDOW_HEIGHT - (font::getRelativePixels(150))), + glm::vec2(font::getRelativePixels(25), eventTxtHeight), glm::vec2(0, 0), - widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::LEFT, 0.0f) + widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::LEFT, font::getRelativePixels(7)) ); + std::optional orb_pos; std::string orbStateString; if (client->gameState.matchPhase == MatchPhase::MazeExploration) { - orbStateString = "The Orb is hidden somewhere in the Labyrinth..."; + orbStateString = "The Orb is hidden..."; } else { bool orbIsCarried = false; - for (auto [id, name] : client->gameState.lobby.players) { - auto player = client->gameState.objects.at(id); - if (!player.has_value()) continue; + for (int i = 0; i < client->gameState.lobby.max_players; i++) { + auto lobbyPlayer = client->gameState.lobby.players[i]; + + if (!lobbyPlayer.has_value()) continue; + + auto player = client->gameState.objects.at(lobbyPlayer.get().id); + + if (!player.has_value()) continue; SharedObject playerObj = player.get(); + if (playerObj.type == ObjectType::DungeonMaster) continue; + if (playerObj.inventoryInfo.get().hasOrb) { orbIsCarried = true; - orbStateString = name + " has the Orb!"; + orbStateString = "Player " + std::to_string(i + 1) + " has the Orb!"; break; } } @@ -645,9 +1754,10 @@ void GUI::_layoutGameHUD() { orbStateString = "The Orb has been dropped!"; } - std::optional orb_pos; - for (const auto& [eid, obj] : client->gameState.objects) { + if (!obj.has_value()) { + continue; + } if (obj->type == ObjectType::Player && obj->inventoryInfo->hasOrb) { orb_pos = obj->physics.corner; orb_pos->y = 0; @@ -657,6 +1767,9 @@ void GUI::_layoutGameHUD() { if (!orb_pos.has_value()) { for (const auto& [eid, obj] : client->gameState.objects) { + if (!obj.has_value()) { + continue; + } if (obj->type == ObjectType::Orb) { orb_pos = obj->physics.corner; orb_pos->y = 0; @@ -675,81 +1788,164 @@ void GUI::_layoutGameHUD() { auto distance = glm::distance(orb_pos.value(), player_pos_ground); std::stringstream ss; - ss << distance << "m to Orb."; - matchPhaseFlex->push(widget::DynText::make(ss.str(), fonts, widget::DynText::Options(font::Font::TEXT, font::Size::MEDIUM, font::Color::WHITE))); + // bruh + ss << std::fixed << std::setprecision(1) << distance << "m to Orb."; + + const float MAX_DIST = 150.0f; + float dist_frac = std::max(std::min(distance / MAX_DIST, 1.0f), 0.0f); + + glm::vec3 close_color = font::getRGB(font::Color::GREEN); + glm::vec3 far_color = font::getRGB(font::Color::RED); + + glm::vec3 color = (dist_frac * far_color) + ((1 - dist_frac) * close_color); + + + matchPhaseFlex->push(widget::DynText::make(ss.str(), fonts, + widget::DynText::Options(font::Font::TEXT, font::Size::SMALLMEDIUM, color)) + ); } } + auto eventSize = this->recentEvents.size(); + if (this->recentEvents[eventSize - 1] != orbStateString) { + if (eventSize >= 5) { + this->recentEvents.erase(this->recentEvents.begin()); + } + this->recentEvents.push_back(orbStateString); + } + + for (int i = this->recentEvents.size() - 1; i >= 0; i--) { + if (i == this->recentEvents.size() - 1) { + matchPhaseFlex->push(widget::DynText::make( + this->recentEvents[i], + fonts, + widget::DynText::Options(font::Font::TEXT, font::Size::SMALLMEDIUM, font::Color::WHITE) + )); + } + else { + matchPhaseFlex->push(widget::DynText::make( + this->recentEvents[i], + fonts, + widget::DynText::Options(font::Font::TEXT, font::Size::SMALL, font::Color::GRAY) + )); + } + } + this->addWidget(std::move(matchPhaseFlex)); - matchPhaseFlex->push(widget::DynText::make( - orbStateString, - fonts, - widget::DynText::Options(font::Font::TEXT, font::Size::MEDIUM, font::Color::WHITE) - )); + // Show death or timer on the top + auto death_flexBG = widget::Flexbox::make( + glm::vec2(0, WINDOW_HEIGHT - (font::getRelativePixels(100))), + glm::vec2(WINDOW_WIDTH, 0), + widget::Flexbox::Options(widget::Dir::HORIZONTAL, widget::Align::CENTER, 0.0f) + ); + death_flexBG->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::SkullBG), 2)); + this->addWidget(std::move(death_flexBG)); // Add timer string if (client->gameState.matchPhase == MatchPhase::RelayRace) { - std::string timerString = "Time Left: "; - int timerSeconds = client->gameState.timesteps_left * ((float)TIMESTEP_LEN.count()) / 1000; - timerString += std::to_string(timerSeconds); + auto remainingTime = client->gameState.relay_finish_time - std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + int min = (int) remainingTime / 60; + int sec = remainingTime % 60; - timerString += (timerSeconds > 1) ? " seconds" : " second"; + auto timeFlex = widget::Flexbox::make( + glm::vec2(0, WINDOW_HEIGHT - (font::getRelativePixels(70))), + glm::vec2(WINDOW_WIDTH, 0), + widget::Flexbox::Options(widget::Dir::HORIZONTAL, widget::Align::CENTER, 0.0f) + ); - matchPhaseFlex->push(widget::DynText::make( + std::string timerString = std::to_string(min) + "min " + std::to_string(sec) + "sec "; + + float time_frac = remainingTime / 300.0f; + glm::vec3 close_color = font::getRGB(font::Color::GREEN); + glm::vec3 far_color = font::getRGB(font::Color::RED); + glm::vec3 color = ((1- time_frac) * far_color) + (time_frac * close_color); + + timeFlex->push(widget::DynText::make( timerString, fonts, - widget::DynText::Options(font::Font::TEXT, font::Size::MEDIUM, font::Color::RED) + widget::DynText::Options(font::Font::TEXT, font::Size::MEDIUM, color) )); + this->addWidget(std::move(timeFlex)); + } + else { + auto death_flex = widget::Flexbox::make( + glm::vec2(0, WINDOW_HEIGHT - (font::getRelativePixels(95))), + glm::vec2(WINDOW_WIDTH, 0), + widget::Flexbox::Options(widget::Dir::HORIZONTAL, widget::Align::CENTER, 0.0f) + ); + for(int i = 0; i < PLAYER_DEATHS_TO_RELAY_RACE; i++){ + if(PLAYER_DEATHS_TO_RELAY_RACE - client->gameState.numPlayerDeaths > i){ + death_flex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Skull), 2)); + } else { + death_flex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::DestroyedSkull), 2)); + } + } + this->addWidget(std::move(death_flex)); } - // Add player deaths string - std::string playerDeathsString = std::to_string(client->gameState.numPlayerDeaths) - + " / " + std::to_string(PLAYER_DEATHS_TO_RELAY_RACE) - + " Player Deaths"; - - matchPhaseFlex->push(widget::DynText::make( - playerDeathsString, - fonts, - widget::DynText::Options(font::Font::TEXT, font::Size::MEDIUM, font::Color::RED) - )); - - this->addWidget(std::move(matchPhaseFlex)); + auto txtHeight = font::getRelativePixels(163); + auto bartxtHeight = font::getRelativePixels(13); + if (this->config.client.presentation) { + txtHeight += font::getRelativePixels(80.0); + bartxtHeight += font::getRelativePixels(90.0); + } if (is_dm.has_value() && is_dm.value()) { + // add some DM specific stuff in here + auto traps_placed_txt = widget::CenterText::make( + "Traps Placed: " + std::to_string(self->trapInventoryInfo->trapsPlaced) + " / " + std::to_string(MAX_TRAPS), + font::Font::TEXT, + font::Size::SMALL, + font::Color::YELLOW, + fonts, + txtHeight + ); + this->addWidget(std::move(traps_placed_txt)); + + auto mana_txt = widget::CenterText::make( + std::to_string(self->DMInfo->mana_remaining) + " / " + std::to_string(30), + font::Font::TEXT, + font::Size::SMALL, + font::Color::WHITE, + fonts, + bartxtHeight + ); + this->addWidget(std::move(mana_txt)); + + // Show a large splash text at the center of the screen for the DM if the DM is paralyzed + if (self.get().DMInfo.get().paralyzed) { + std::cout << "DM is paralyzed!" << std::endl; + auto paralyzedSplashText = widget::CenterText::make( + "PARALYZED!", + gui::font::Font::TITLE, + gui::font::Size::LARGE, + gui::font::Color::RED, + this->fonts, + WINDOW_HEIGHT / 2 + ); + + this->addWidget(std::move(paralyzedSplashText)); + } + return; } auto health_txt = widget::CenterText::make( std::to_string(self->stats->health.current()) + " / " + std::to_string(self->stats->health.max()), - font::Font::MENU, - font::Size::MEDIUM, - font::Color::RED, + font::Font::TEXT, + font::Size::SMALL, + font::Color::WHITE, fonts, - font::getRelativePixels(90) + bartxtHeight ); this->addWidget(std::move(health_txt)); - auto status_flex = widget::Flexbox::make( - glm::vec2(font::getRelativePixels(20), font::getRelativePixels(20)), - glm::vec2(0.0f, 0.0f), - widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::LEFT, font::getRelativePixels(5)) - ); - - for (const std::string& status: self->statuses->getStatusStrings()) { - status_flex->push(widget::DynText::make( - status, - fonts, - widget::DynText::Options(font::Font::TEXT, font::Size::MEDIUM, font::Color::WHITE) - )); - } - - this->addWidget(std::move(status_flex)); // Flexbox for item durations auto durationFlex = widget::Flexbox::make( glm::vec2(10.0f, FRAC_WINDOW_HEIGHT(1, 2)), glm::vec2(0.0f, 0.0f), - widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::LEFT, 0.0f) + widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::LEFT, font::getRelativePixels(5)) ); std::unordered_map>::iterator it = self->inventoryInfo->usedItems.begin(); @@ -762,10 +1958,13 @@ void GUI::_layoutGameHUD() { name = "Invisibility: "; } else if (type == ModelType::InvincibilityPotion) { - name = "INVINCIBILITY: "; + name = "Invincibility: "; } else if (type == ModelType::NauseaPotion) { - name = "Nauseous: "; + name = "Nausea: "; + } + else if (type == ModelType::Mirror) { + name = "Holding Mirror: "; } durationFlex->push(widget::DynText::make( @@ -776,28 +1975,160 @@ void GUI::_layoutGameHUD() { ++it; } this->addWidget(std::move(durationFlex)); + + auto compassHeight = font::getRelativePixels(10); + if (this->config.client.presentation) { + compassHeight += font::getRelativePixels(85.0); + } + + auto compassFlex = widget::Flexbox::make( + glm::vec2(WINDOW_WIDTH - font::getRelativePixelsHorizontal(700), compassHeight), + glm::vec2(0.0f, 0.0f), + widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::LEFT, font::getRelativePixels(5)) + ); + + if (self->compass->angle > 345 || self->compass->angle <= 15) { + compassFlex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Compass0), 2)); + } + else if (self->compass->angle > 15 && self->compass->angle <= 45) { + compassFlex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Compass330), 2)); + } + else if (self->compass->angle > 45 && self->compass->angle <= 75) { + compassFlex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Compass300), 2)); + } + else if (self->compass->angle > 75 && self->compass->angle <= 105) { + compassFlex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Compass270), 2)); + } + else if (self->compass->angle > 105 && self->compass->angle <= 135) { + compassFlex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Compass240), 2)); + } + else if (self->compass->angle > 135 && self->compass->angle <= 165) { + compassFlex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Compass210), 2)); + } + else if (self->compass->angle > 165 && self->compass->angle <= 195) { + compassFlex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Compass180), 2)); + } + else if (self->compass->angle > 195 && self->compass->angle <= 225) { + compassFlex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Compass150), 2)); + } + else if (self->compass->angle > 225 && self->compass->angle <= 255) { + compassFlex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Compass120), 2)); + } + else if (self->compass->angle > 255 && self->compass->angle <= 285) { + compassFlex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Compass90), 2)); + } + else if (self->compass->angle > 285 && self->compass->angle <= 315) { + compassFlex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Compass60), 2)); + } + else { + compassFlex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Compass30), 2)); + } + this->addWidget(std::move(compassFlex)); + + auto needleFlex = widget::Flexbox::make( + glm::vec2(WINDOW_WIDTH - font::getRelativePixelsHorizontal(700), compassHeight), + glm::vec2(0.0f, 0.0f), + widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::LEFT, font::getRelativePixels(5)) + ); + needleFlex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Needle), 2)); + + if (orb_pos.has_value()) { + auto player_pos_ground = self->physics.corner; + player_pos_ground.y = 0; + + auto distance = glm::distance(orb_pos.value(), player_pos_ground); + + std::stringstream ss; + + ss << std::fixed << std::setprecision(1) << " " << distance << "m"; + + const float MAX_DIST = 150.0f; + float dist_frac = std::max(std::min(distance / MAX_DIST, 1.0f), 0.0f); + + glm::vec3 close_color = font::getRGB(font::Color::GREEN); + glm::vec3 far_color = font::getRGB(font::Color::RED); + + glm::vec3 color = (dist_frac * far_color) + ((1 - dist_frac) * close_color); + + needleFlex->push(widget::DynText::make(ss.str(), fonts, + widget::DynText::Options(font::Font::TEXT, font::Size::SMALL, color)) + ); + } + this->addWidget(std::move(needleFlex)); + + // Show a large splash text at the center of the screen for the player if the player + // successfully reflected a lightning bolt + if (self.get().playerInfo.get().used_mirror_to_reflect_lightning) { + std::cout << "Player used a mirror to reflect a lightning bolt!" << std::endl; + auto reflectedLightningSplashText = widget::CenterText::make( + "Reflected Lightning using Mirror!", + gui::font::Font::TITLE, + gui::font::Size::MEDIUM, + gui::font::Color::WHITE, + this->fonts, + WINDOW_HEIGHT / 2 + ); + + this->addWidget(std::move(reflectedLightningSplashText)); + } } void GUI::_layoutGameEscMenu() { - auto exit_game_txt = widget::DynText::make( - "(Exit Game)", - fonts, - widget::DynText::Options(font::Font::MENU, font::Size::MEDIUM, font::Color::BLACK) + auto self_eid = client->session->getInfo().client_eid; + auto is_dm = client->session->getInfo().is_dungeon_master; + + auto exitBG = widget::Flexbox::make( + glm::vec2(font::getRelativePixels(2), FRAC_WINDOW_HEIGHT(1, 2) - font::getRelativePixels(17)), + glm::vec2(WINDOW_WIDTH, 0.0f), + widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::CENTER, 0.0f) ); - exit_game_txt->addOnHover([this](widget::Handle handle) { - auto widget = this->borrowWidget(handle); - widget->changeColor(font::Color::RED); - }); - exit_game_txt->addOnClick([this](widget::Handle handle) { + + /* + auto changeimage = [this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + widget->changeImage(images.getImg(img::ImgID::ExitBG)); + };*/ + + + if(!is_dm.value()){ + auto img = widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::ExitBG), 2); + img->addOnHover([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + widget->changeImage(images.getImg(img::ImgID::ExitBGSelected)); + }); + exitBG->push(std::move(img)); + } + else { + auto img = widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::ExitDMBG), 2); + img->addOnHover([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + widget->changeImage(images.getImg(img::ImgID::ExitDMBGSelected)); + }); + exitBG->push(std::move(img)); + } + + exitBG->addOnClick([this](widget::Handle handle) { glfwSetWindowShouldClose(this->client->getWindow(), GL_TRUE); }); + + this->addWidget(std::move(exitBG)); + auto flex = widget::Flexbox::make( - glm::vec2(0.0f, FRAC_WINDOW_HEIGHT(1, 2)), + glm::vec2(font::getRelativePixels(2), FRAC_WINDOW_HEIGHT(1, 2) - font::getRelativePixels(15)), glm::vec2(WINDOW_WIDTH, 0.0f), widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::CENTER, 0.0f) ); - flex->push(std::move(exit_game_txt)); + auto img = widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Exit), 1); + img->addOnHover([this](widget::Handle handle) { + auto widget = this->borrowWidget(handle); + widget->changeImage(images.getImg(img::ImgID::ExitSelected)); + }); + flex->push(std::move(img)); + + flex->addOnClick([this](widget::Handle handle) { + glfwSetWindowShouldClose(this->client->getWindow(), GL_TRUE); + }); this->addWidget(std::move(flex)); } @@ -807,14 +2138,29 @@ void GUI::_layoutDeadScreen() { return; } auto self = client->gameState.objects.at(*self_eid); - auto time_until_respawn = (self->playerInfo->respawn_time - getMsSinceEpoch()) / 1000; + auto diedBG = widget::Flexbox::make( + glm::vec2(font::getRelativePixels(2), FRAC_WINDOW_HEIGHT(1, 2) - font::getRelativePixels(45)), + glm::vec2(WINDOW_WIDTH, 0.0f), + widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::CENTER, 0.0f) + ); + diedBG->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Death), 2)); + auto respawnBG = widget::Flexbox::make( + glm::vec2(font::getRelativePixels(5), FRAC_WINDOW_HEIGHT(1, 2) - font::getRelativePixels(210)), + glm::vec2(WINDOW_WIDTH, 0.0f), + widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::CENTER, 0.0f) + ); + respawnBG->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Respawn), 2)); + + this->addWidget(std::move(diedBG)); + this->addWidget(std::move(respawnBG)); + this->addWidget(widget::CenterText::make( "You died...", font::Font::MENU, font::Size::LARGE, - font::Color::RED, + font::Color::WHITE, fonts, FRAC_WINDOW_HEIGHT(1, 2) )); @@ -822,7 +2168,7 @@ void GUI::_layoutDeadScreen() { "Respawning in " + std::to_string(time_until_respawn), font::Font::TEXT, font::Size::MEDIUM, - font::Color::BLACK, + font::Color::WHITE, fonts, FRAC_WINDOW_HEIGHT(1, 3) )); @@ -847,6 +2193,21 @@ void GUI::_layoutResultsScreen() { std::string result_string = won ? "Victory" : "Defeat"; + auto victoryBG = widget::Flexbox::make( + glm::vec2(font::getRelativePixels(2), FRAC_WINDOW_HEIGHT(1, 2) - font::getRelativePixels(55)), + glm::vec2(WINDOW_WIDTH, 0.0f), + widget::Flexbox::Options(widget::Dir::VERTICAL, widget::Align::CENTER, 0.0f) + ); + + if (won) { + victoryBG->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Victory), 2)); + } + else { + victoryBG->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::Defeat), 2)); + } + + this->addWidget(std::move(victoryBG)); + this->addWidget(widget::CenterText::make( result_string, font::Font::MENU, diff --git a/src/client/gui/imgs/img.cpp b/src/client/gui/imgs/img.cpp index 4c52f49a..bd9db329 100644 --- a/src/client/gui/imgs/img.cpp +++ b/src/client/gui/imgs/img.cpp @@ -8,22 +8,108 @@ std::string getImgFilepath(ImgID img) { auto img_root = getRepoRoot() / "assets/imgs"; switch (img) { default: + case ImgID::Title: return (img_root / "title.png").string(); case ImgID::Yoshi: return (img_root / "Yoshi.png").string(); case ImgID::AwesomeSauce: return (img_root / "awesomeface.png").string(); - case ImgID::ItemFrame: return (img_root / "frame.png").string(); - case ImgID::SelectedFrame: return (img_root / "selected_frame.png").string(); case ImgID::HealthPotion: return (img_root / "pot_health.png").string(); case ImgID::UnknownPotion: return (img_root / "pot_unknown.png").string(); case ImgID::InvisPotion: return (img_root / "pot_invisibility.png").string(); case ImgID::FireSpell: return (img_root / "fire_wand.png").string(); case ImgID::HealSpell: return (img_root / "heal_wand.png").string(); case ImgID::Orb: return (img_root / "orb.png").string(); + case ImgID::Mirror: return (img_root / "mirror.png").string(); case ImgID::Scroll: return (img_root / "scroll.png").string(); case ImgID::Crosshair: return (img_root / "crosshair046.png").string(); case ImgID::Dagger: return (img_root / "weapon_dagger.png").string(); case ImgID::Sword: return (img_root / "weapon_sword.png").string(); case ImgID::Hammer: return (img_root / "weapon_hammer.png").string(); + case ImgID::LeftHotbar: return (img_root / "left.png").string(); + case ImgID::RightHotbar: return (img_root / "right.png").string(); + case ImgID::MiddleHotbar: return (img_root / "middle.png").string(); + case ImgID::MiddleSelected: return (img_root / "selected_middle.png").string(); + case ImgID::DMLeftHotbar: return (img_root / "dm_left.png").string(); + case ImgID::DMRightHotbar: return (img_root / "dm_right.png").string(); + case ImgID::DMMiddleHotbar: return (img_root / "dm_middle.png").string(); + case ImgID::DMMiddleSelected: return (img_root / "dm_selected.png").string(); + case ImgID::DMMiddleCooldown: return (img_root / "dm_cooldown.png").string(); + case ImgID::Blank: return (img_root / "blank.png").string(); + case ImgID::HealthBar: return (img_root / "healthbar.png").string(); + case ImgID::HealthTickEmpty: return (img_root / "healthtick_empty.png").string(); + case ImgID::HealthTickFull: return (img_root / "healthtick_full.png").string(); + case ImgID::ManaBar: return (img_root / "manabar.png").string(); + case ImgID::ManaTickEmpty: return (img_root / "manatick_empty.png").string(); + case ImgID::ManaTickFull: return (img_root / "manatick_full.png").string(); + case ImgID::Needle: return (img_root / "needle.png").string(); + case ImgID::ItemBG: return (img_root / "itemBackground.png").string(); + case ImgID::DMTrapBG: return (img_root / "dmBackground.png").string(); + case ImgID::EventBG: return (img_root / "eventBG.png").string(); + case ImgID::DMEventBG: return (img_root / "dmEventBG.png").string(); + case ImgID::Compass0: return (img_root / "compasses/compass_0.png").string(); + case ImgID::Compass30: return (img_root / "compasses/compass_30.png").string(); + case ImgID::Compass60: return (img_root / "compasses/compass_60.png").string(); + case ImgID::Compass90: return (img_root / "compasses/compass_90.png").string(); + case ImgID::Compass120: return (img_root / "compasses/compass_120.png").string(); + case ImgID::Compass150: return (img_root / "compasses/compass_150.png").string(); + case ImgID::Compass180: return (img_root / "compasses/compass_180.png").string(); + case ImgID::Compass210: return (img_root / "compasses/compass_210.png").string(); + case ImgID::Compass240: return (img_root / "compasses/compass_240.png").string(); + case ImgID::Compass270: return (img_root / "compasses/compass_270.png").string(); + case ImgID::DMCD_10: return (img_root / "dm_cooldown/dm_cooldown_10.png").string(); + case ImgID::DMCD_9: return (img_root / "dm_cooldown/dm_cooldown_9.png").string(); + case ImgID::DMCD_8: return (img_root / "dm_cooldown/dm_cooldown_8.png").string(); + case ImgID::DMCD_7: return (img_root / "dm_cooldown/dm_cooldown_7.png").string(); + case ImgID::DMCD_6: return (img_root / "dm_cooldown/dm_cooldown_6.png").string(); + case ImgID::DMCD_5: return (img_root / "dm_cooldown/dm_cooldown_5.png").string(); + case ImgID::DMCD_4: return (img_root / "dm_cooldown/dm_cooldown_4.png").string(); + case ImgID::DMCD_3: return (img_root / "dm_cooldown/dm_cooldown_3.png").string(); + case ImgID::DMCD_2: return (img_root / "dm_cooldown/dm_cooldown_2.png").string(); + case ImgID::DMCD_1: return (img_root / "dm_cooldown/dm_cooldown_1.png").string(); + case ImgID::DMCD_Selected_10: return (img_root / "dm_cooldown/dm_selected_cooldown_10.png").string(); + case ImgID::DMCD_Selected_9: return (img_root / "dm_cooldown/dm_selected_cooldown_9.png").string(); + case ImgID::DMCD_Selected_8: return (img_root / "dm_cooldown/dm_selected_cooldown_8.png").string(); + case ImgID::DMCD_Selected_7: return (img_root / "dm_cooldown/dm_selected_cooldown_7.png").string(); + case ImgID::DMCD_Selected_6: return (img_root / "dm_cooldown/dm_selected_cooldown_6.png").string(); + case ImgID::DMCD_Selected_5: return (img_root / "dm_cooldown/dm_selected_cooldown_5.png").string(); + case ImgID::DMCD_Selected_4: return (img_root / "dm_cooldown/dm_selected_cooldown_4.png").string(); + case ImgID::DMCD_Selected_3: return (img_root / "dm_cooldown/dm_selected_cooldown_3.png").string(); + case ImgID::DMCD_Selected_2: return (img_root / "dm_cooldown/dm_selected_cooldown_2.png").string(); + case ImgID::DMCD_Selected_1: return (img_root / "dm_cooldown/dm_selected_cooldown_1.png").string(); + case ImgID::Compass300: return (img_root / "compasses/compass_300.png").string(); + case ImgID::Compass330: return (img_root / "compasses/compass_330.png").string(); + case ImgID::Sungod: return (img_root / "sungod.png").string(); + case ImgID::Lightning: return (img_root / "lightning.png").string(); + case ImgID::LightCut: return (img_root / "lightcut.png").string(); + case ImgID::Teleporter: return (img_root / "teleporter.png").string(); + case ImgID::FloorSpikeTrap: return (img_root / "floorspiketrap.png").string(); + case ImgID::ArrowTrap: return (img_root / "arrowtrap.png").string(); + case ImgID::SpikeTrap: return (img_root / "spiketrap.png").string(); + case ImgID::Skull: return (img_root / "normalSkull.png").string(); + case ImgID::DestroyedSkull: return (img_root / "destroyedSkull.png").string(); + case ImgID::SkullBG: return (img_root / "deathCountBG.png").string(); + case ImgID::HelpBG: return (img_root / "helpBG.png").string(); + case ImgID::HelpDMBG: return (img_root / "helpDMBG.png").string(); + case ImgID::ExitBG: return (img_root / "exitBG.png").string(); + case ImgID::ExitDMBG: return (img_root / "exitDMBG.png").string(); + case ImgID::ExitBGSelected: return (img_root / "exitBGSelected.png").string(); + case ImgID::ExitDMBGSelected: return (img_root / "exitDMBGSelected.png").string(); + case ImgID::Exit: return (img_root / "exit.png").string(); + case ImgID::ExitSelected: return (img_root / "exitSelected.png").string(); + case ImgID::LobbyButton: return (img_root / "lobbyButton.png").string(); + case ImgID::Victory: return (img_root / "victory.png").string(); + case ImgID::Defeat: return (img_root / "defeat.png").string(); + case ImgID::Death: return (img_root / "died.png").string(); + case ImgID::Respawn: return (img_root / "respawn.png").string(); + case ImgID::StartGame: return (img_root / "start.png").string(); + case ImgID::StartGameSelected: return (img_root / "startHover.png").string(); + case ImgID::RowBG: return (img_root / "rowBG.png").string(); + case ImgID::Player: return (img_root / "player.png").string(); + case ImgID::PlayerSelected: return (img_root / "playerSelected.png").string(); + case ImgID::Zeus: return (img_root / "zeus.png").string(); + case ImgID::ZeusSelected: return (img_root / "zeusSelected.png").string(); + case ImgID::ReadyPlayer: return (img_root / "readyPlayer.png").string(); + case ImgID::ReadyPlayerSelected: return (img_root / "readyPlayerSelected.png").string(); + case ImgID::ReadyZeus: return (img_root / "readyZeus.png").string(); + case ImgID::ReadyZeusSelected: return (img_root / "readyZeusSelected.png").string(); } } - } diff --git a/src/client/gui/imgs/loader.cpp b/src/client/gui/imgs/loader.cpp index 364d9b2e..4ef8c662 100644 --- a/src/client/gui/imgs/loader.cpp +++ b/src/client/gui/imgs/loader.cpp @@ -28,8 +28,12 @@ bool Loader::_loadImg(ImgID img_id) { glBindTexture(GL_TEXTURE_2D, texture_id); // set Texture wrap and filter modes - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + // changed for hotbar + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -41,8 +45,10 @@ bool Loader::_loadImg(ImgID img_id) { std::cout << "Loading " << path << "...\n"; unsigned char* img_data = stbi_load(path.c_str(), &width, &height, &channels, 0); - if (stbi_failure_reason()) + if (stbi_failure_reason()) { std::cout << "failure: " << stbi_failure_reason() << std::endl; + return false; + } if (img_data == 0 || width == 0 || height == 0) { std::cerr << "Error loading " << path << std::endl; @@ -62,7 +68,6 @@ bool Loader::_loadImg(ImgID img_id) { }}); stbi_image_free(img_data); - return true; } diff --git a/src/client/gui/imgs/logo.cpp b/src/client/gui/imgs/logo.cpp new file mode 100644 index 00000000..7ad78792 --- /dev/null +++ b/src/client/gui/imgs/logo.cpp @@ -0,0 +1,93 @@ +#include "client/gui/img/logo.hpp" +#include "stb_image.h" +#include +#include +#include + +using namespace std::chrono_literals; + +namespace gui::img { + +bool Logo::init() { + std::cout << "Loading all of the logo frames...\n"; + for (int i = 0; i < NUM_FRAMES; i++) { + if (!_loadFrame(i)) { + return false; + } + } + + return true; +} + +Img Logo::getNextFrame() { + static const auto FRAME_DELAY = 20ms; + + static auto prev_frame_time = std::chrono::system_clock::now(); + auto now = std::chrono::system_clock::now(); + + auto time_since_last_frame = now - prev_frame_time; + + std::size_t index = this->curr_frame; + + if (time_since_last_frame > FRAME_DELAY) { + this->curr_frame++; + prev_frame_time = now; + } + + if (this->curr_frame >= NUM_FRAMES) { + this->curr_frame = 0; + } + return this->frames.at(index); +} + +bool Logo::_loadFrame(std::size_t index) { + GLuint texture_id; + + glGenTextures(1, &texture_id); + glBindTexture(GL_TEXTURE_2D, texture_id); + + // set Texture wrap and filter modes + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + int width, height, channels; + + stbi_set_flip_vertically_on_load(true); + + std::stringstream ss; + ss << "frame_" << index + 1 << ".png"; + + auto path = getRepoRoot() / "assets/imgs/logo_animation" / ss.str(); + unsigned char* img_data = stbi_load(path.string().c_str(), &width, &height, &channels, 0); + + if (stbi_failure_reason()) { + std::cout << "failure: " << stbi_failure_reason() << std::endl; + return false; + } + + if (img_data == 0 || width == 0 || height == 0) { + std::cerr << "Error loading " << path << std::endl; + return false; + } + + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img_data); + glGenerateMipmap(GL_TEXTURE_2D); + + // unbind texture + glBindTexture(GL_TEXTURE_2D, 0); + + this->frames.push_back(Img { + .texture_id = texture_id, + .width = width, + .height = height + }); + + stbi_image_free(img_data); + + return true; +} + +}; diff --git a/src/client/gui/widget/dyntext.cpp b/src/client/gui/widget/dyntext.cpp index f875407e..d3751107 100644 --- a/src/client/gui/widget/dyntext.cpp +++ b/src/client/gui/widget/dyntext.cpp @@ -28,21 +28,8 @@ DynText::DynText(glm::vec2 origin, const std::string& text, glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); - // Calculate size of string of text - this->width = 0; - this->height = 0; - - float scale = font::getScaleFactor(this->options.size); - - for (int i = 0; i < text.size(); i++) { - font::Character ch = this->fonts->loadChar(this->text[i], this->options.font); - this->height = std::max(this->height, static_cast(ch.size.y * scale)); - if (i != text.size() - 1 && i != 0) { - this->width += (ch.advance >> 6) * scale; - } else { - this->width += ch.size.x * scale; - } - } + // Set height and width based on the stored text + _calculateSize(); } DynText::DynText(const std::string& text, std::shared_ptr fonts, DynText::Options options): @@ -53,7 +40,7 @@ void DynText::render() { auto projection = GUI_PROJECTION_MATRIX(); DynText::shader->setMat4("projection", projection); - auto color = font::getRGB(this->options.color); + auto color = this->options.color; DynText::shader->setVec3("textColor", color); glBindVertexArray(VAO); @@ -83,6 +70,7 @@ void DynText::render() { { xpos + w, ypos + h, 1.0f, 0.0f } }; // render glyph texture over quad + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, ch.texture_id); // update content of VBO memory glBindBuffer(GL_ARRAY_BUFFER, VBO); @@ -100,7 +88,33 @@ void DynText::render() { } void DynText::changeColor(font::Color new_color) { - this->options.color = new_color; + this->options.color = font::getRGB(new_color); +} + +void DynText::changeText(const std::string& new_text) { + this->text = new_text; + + // Update height and width based on the new text + _calculateSize(); +} + +void DynText::_calculateSize() { + // Calculate size of string of text + this->width = 0; + this->height = 0; + + float scale = font::getScaleFactor(this->options.size); + + for (int i = 0; i < text.size(); i++) { + font::Character ch = this->fonts->loadChar(this->text[i], this->options.font); + this->height = std::max(this->height, static_cast(ch.size.y * scale)); + if (i != text.size() - 1 && i != 0) { + this->width += (ch.advance >> 6) * scale; + } + else { + this->width += ch.size.x * scale; + } + } } } \ No newline at end of file diff --git a/src/client/gui/widget/empty.cpp b/src/client/gui/widget/empty.cpp new file mode 100644 index 00000000..6411c791 --- /dev/null +++ b/src/client/gui/widget/empty.cpp @@ -0,0 +1,12 @@ +#include "client/gui/widget/empty.hpp" + +namespace gui::widget { +Empty::Empty(glm::vec2 origin, glm::vec2 size) : Widget(Type::Empty, origin) { + this->width = (size.x > 0.0f) ? size.x : 0.0f; + this->height = (size.y > 0.0f) ? size.y : 0.0f; +} + +void Empty::render() { + // Do nothing +} +} \ No newline at end of file diff --git a/src/client/gui/widget/staticimg.cpp b/src/client/gui/widget/staticimg.cpp index 293458d2..554c6112 100644 --- a/src/client/gui/widget/staticimg.cpp +++ b/src/client/gui/widget/staticimg.cpp @@ -9,21 +9,20 @@ namespace gui::widget { std::unique_ptr StaticImg::shader = nullptr; -StaticImg::StaticImg(glm::vec2 origin, gui::img::Img img, int size): +StaticImg::StaticImg(glm::vec2 origin, gui::img::Img img, float size): Widget(Type::StaticImg, origin), img(img) { - this->size = size; - this->width = img.width * size; - this->height = img.height * size; - this->texture_id = img.texture_id; -} + //float screen_factor_width = static_cast(WINDOW_WIDTH) / static_cast(UNIT_WINDOW_WIDTH); + float screen_factor_height = static_cast(WINDOW_HEIGHT) / static_cast(UNIT_WINDOW_HEIGHT); -StaticImg::StaticImg(glm::vec2 origin, gui::img::Img img): - Widget(Type::StaticImg, origin), img(img) -{ - this->size = 1.0f; - this->width = img.width; - this->height = img.height; + if (screen_factor_height > 1) { + screen_factor_height = 1.5; + } + + this->size_width = size * screen_factor_height; + this->size_height = size * screen_factor_height; + this->width = img.width * this->size_width; + this->height = img.height * this->size_height; this->texture_id = img.texture_id; } @@ -36,10 +35,15 @@ StaticImg::~StaticImg() { glDeleteBuffers(1, &EBO); } +void StaticImg::changeImage(gui::img::Img img) { + this->img = img; + this->texture_id = img.texture_id; +} + void StaticImg::render() { // âš  SUS SHIT âš  - float width_percent = (2.0f / (WINDOW_WIDTH)) * (img.width) * size; - float height_percent = (2.0f / (WINDOW_HEIGHT)) * (img.height) * size; + float width_percent = (2.0f / (WINDOW_WIDTH)) * (this->width); + float height_percent = (2.0f / (WINDOW_HEIGHT)) * (this->height); glm::vec2 bottom_left = (2.0f * (origin / glm::vec2(WINDOW_WIDTH, WINDOW_HEIGHT))) - glm::vec2(1.0f, 1.0f); float vertices[] = { @@ -85,7 +89,7 @@ void StaticImg::render() { // // transform = glm::translate(transform, glm::vec3(this->origin.x, this->origin.y, 0)); // shader->setMat4("transform", transform); - // glActiveTexture(GL_TEXTURE0); + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, this->texture_id); glBindVertexArray(quadVAO); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); diff --git a/src/client/gui/widget/textinput.cpp b/src/client/gui/widget/textinput.cpp index 956a8cb4..1f4dc26a 100644 --- a/src/client/gui/widget/textinput.cpp +++ b/src/client/gui/widget/textinput.cpp @@ -23,7 +23,7 @@ TextInput::TextInput(glm::vec2 origin, std::string captured_input = gui->getCapturedKeyboardInput(); if (captured_input.size() == 0) { text_to_display = placeholder; - options.color = font::Color::GRAY; + options.color = font::getRGB(font::Color::GRAY); } else { text_to_display = captured_input; } diff --git a/src/client/lightsource.cpp b/src/client/lightsource.cpp deleted file mode 100644 index 7450e7f4..00000000 --- a/src/client/lightsource.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "client/lightsource.hpp" - -#include - -#include - -#include "client/shader.hpp" - -LightSource::LightSource() : model(1.0f) { - glGenVertexArrays(1, &VAO); - glBindVertexArray(VAO); - // we only need to bind to the VBO, the container's VBO's data already contains the data. - glBindBuffer(GL_ARRAY_BUFFER, VBO); - // set the vertex attribute - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); - glEnableVertexAttribArray(0); -} - -void LightSource::draw(std::shared_ptr shader, - glm::mat4 viewProj) { - shader->use(); - - // get the locations and send the uniforms to the shader - shader->setMat4("viewProj", viewProj); - shader->setMat4("model", model); - - // draw the light cube object - glBindVertexArray(VAO); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glDrawArrays(GL_TRIANGLES, 0, 36); - - glBindVertexArray(0); - glUseProgram(0); -} - -void LightSource::TranslateTo(const glm::vec3 &new_pos) { - model[3] = glm::vec4(new_pos, 1.0f); -} diff --git a/src/client/main.cpp b/src/client/main.cpp index 88bfff47..ba604423 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -49,6 +49,10 @@ void set_callbacks(GLFWwindow* window) { glfwSetCharCallback(window, [](GLFWwindow* w, unsigned int codepoint) { static_cast(glfwGetWindowUserPointer(w))->charCallback(w, codepoint); }); + + glfwSetScrollCallback(window, [](GLFWwindow* w, double xposOffset, double yposOffset) { + static_cast(glfwGetWindowUserPointer(w))->scrollCallback(w, xposOffset, yposOffset); + }); } void set_opengl_settings(GLFWwindow* window) { @@ -64,9 +68,6 @@ void set_opengl_settings(GLFWwindow* window) { // Sets initial background color. glClearColor(0.8f, 0.8f, 0.8f, 1.0f); - - // Set cursor position to (0, 0) - glfwSetCursorPos(window, 0, 0); } int main(int argc, char* argv[]) @@ -108,7 +109,7 @@ int main(int argc, char* argv[]) client->displayCallback(); // Idle callback. Updating objects, etc. can be done here. - client->idleCallback(context); + client->idleCallback(); } client->cleanup(); diff --git a/src/client/model.cpp b/src/client/model.cpp index 0ccf9d13..91ae8972 100644 --- a/src/client/model.cpp +++ b/src/client/model.cpp @@ -14,10 +14,11 @@ #include #include -#include "client/lightsource.hpp" +#include "assimp/postprocess.h" #include "client/renderable.hpp" #include "client/constants.hpp" #include "client/util.hpp" +#include "glm/ext/quaternion_geometric.hpp" #include "server/game/torchlight.hpp" #include "shared/game/sharedobject.hpp" #include "shared/utilities/constants.hpp" @@ -25,9 +26,6 @@ #include "assimp/types.h" #include "assimp/aabb.h" #include "assimp/material.h" -#include -#include -#include #define STB_IMAGE_IMPLEMENTATION #include @@ -48,11 +46,14 @@ Mesh::Mesh( const Material& material) : vertices(vertices), indices(indices), textures(textures), material(material) { + setupNormalMaps(); + glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); glBindVertexArray(VAO); + glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW); @@ -71,6 +72,22 @@ Mesh::Mesh( glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, textureCoords))); + // vertex bone ids + glEnableVertexAttribArray(3); + glVertexAttribIPointer(3, 4, GL_INT, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, m_boneIDs))); + + // vertex bone weights + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, m_weights))); + + // tangents + glEnableVertexAttribArray(5); + glVertexAttribPointer(5, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, tangent))); + + // bitangents + glEnableVertexAttribArray(6); + glVertexAttribPointer(6, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, bitangent))); + glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); @@ -81,69 +98,58 @@ Mesh::Mesh( // std::cout << "\t shininess" << this->material.shininess << std::endl; } +void Mesh::setupNormalMaps() { + for (int i=0; i + 2 < indices.size(); i+=3){ + // if (i + 1 > indices.size()) { + // } + // get 3 vertices for a triangle + Vertex& v0 = vertices.at(indices.at(i)); + Vertex& v1 = vertices.at(indices.at(i+1)); + Vertex& v2 = vertices.at(indices.at(i+2)); + + // Edges of the triangle : position delta + glm::vec3 deltaPos1 = v1.position - v0.position; + glm::vec3 deltaPos2 = v2.position - v0.position; + + // UV delta + glm::vec2 deltaUV1 = v1.textureCoords - v0.textureCoords; + glm::vec2 deltaUV2 = v2.textureCoords - v0.textureCoords; + + float r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x); + glm::vec3 tangent = (deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y)*r; + glm::vec3 bitangent = (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x)*r; + + v0.tangent += tangent; + v1.tangent += tangent; + v2.tangent += tangent; + + v0.bitangent += bitangent; + v1.bitangent += bitangent; + v2.bitangent += bitangent; + } + + for (int i = 0; i < vertices.size(); i++) { + // vertices.at(i).tangent = glm::normalize(vertices.at(i).tangent); + // // std::cout << "tangent " << glm::to_string(vertices.at(i).tangent) << "\n"; + // // exit(1); + // vertices.at(i).bitangent = glm::normalize(vertices.at(i).bitangent); + } +} + void Mesh::draw( Shader* shader, - glm::mat4 viewProj, glm::vec3 camPos, - std::array, MAX_POINT_LIGHTS> lightSources, bool fill) { - // activate the shader program - shader->use(); - // vertex shader uniforms - shader->setMat4("viewProj", viewProj); auto model = this->getModelMat(); + shader->setMat4("model", model); - - if (this->solidColor.has_value()) { - shader->setVec3("material.ambient", this->solidColor.value()); - } - else { - shader->setVec3("material.ambient", this->material.ambient); - } - - - // fragment shader uniforms - shader->setVec3("material.diffuse", this->material.diffuse); - shader->setVec3("material.specular", this->material.specular); - shader->setFloat("material.shininess", this->material.shininess); - shader->setVec3("viewPos", camPos); - // set lightsource uniforms - unsigned int curr_light_num = 0; - for (auto curr_source : lightSources) { - if (curr_light_num > MAX_POINT_LIGHTS) { - break; - } - if (!curr_source.has_value()) { - continue; - } - - SharedPointLightInfo& properties = curr_source->pointLightInfo.value(); - glm::vec3 pos = curr_source->physics.getCenterPosition(); - - std::string pointLight = "pointLights[" + std::to_string(curr_light_num) + "]"; - shader->setBool(pointLight + ".enabled", true); - shader->setFloat(pointLight + ".intensity", properties.intensity); - shader->setVec3(pointLight + ".position", pos); - // needed for attenuation - shader->setFloat(pointLight + ".constant", 1.0f); - shader->setFloat(pointLight + ".linear", properties.attenuation_linear); - shader->setFloat(pointLight + ".quadratic", properties.attenuation_quadratic); - - // light color - shader->setVec3(pointLight + ".ambient", properties.ambient_color); - shader->setVec3(pointLight + ".diffuse", properties.diffuse_color); - shader->setVec3(pointLight + ".specular", properties.specular_color); - - curr_light_num++; - } - - if (textures.size() == 0) { - } else { + if (textures.size() != 0) { unsigned int diffuseNr = 1; unsigned int specularNr = 1; + unsigned int normalNr = 1; for(unsigned int i = 0; i < textures.size(); i++) { glActiveTexture(GL_TEXTURE0 + i); // activate proper texture unit before binding // retrieve texture number (the N in diffuse_textureN) @@ -153,13 +159,28 @@ void Mesh::draw( number = std::to_string(diffuseNr++); else if(name == "texture_specular") number = std::to_string(specularNr++); + else if(name == "texture_normal") + number = std::to_string(normalNr++); - std::string shaderTextureName = "material." + name + number; + std::string shaderTextureName = name + number; shader->setInt(shaderTextureName, i); glBindTexture(GL_TEXTURE_2D, textures[i].getID()); } - glActiveTexture(GL_TEXTURE0); + + if (normalNr == 1) { + // never set any normal map texture uniform + shader->setBool("has_normal_map", false); + } else { + shader->setBool("has_normal_map", true); + } + } else { + // shader->setFloat("shininess", this->material.shininess); } + // if (solidColor.has_value()) { + // shader->setVec3("diffuse", solidColor.value()); + // float s = 1.0f; + // shader->setFloat("shininess", s); + // } // draw mesh glBindVertexArray(VAO); @@ -168,21 +189,32 @@ void Mesh::draw( } else { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); } - glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); + glDrawElements(GL_TRIANGLES, static_cast(indices.size()), GL_UNSIGNED_INT, 0); glBindVertexArray(0); - glUseProgram(0); } -Model::Model(const std::string& filepath) { +Model::Model(const std::string& filepath, bool flip_uvs) { this->directory = std::filesystem::path(filepath).parent_path().string(); Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(filepath, - aiProcess_Triangulate | // flag to only creates geometry made of triangles - aiProcess_FlipUVs | - aiProcess_SplitLargeMeshes | - aiProcess_OptimizeMeshes | - aiProcess_GenBoundingBoxes); // needed to query bounding box of the model later + const aiScene *scene; + if (flip_uvs) { + scene = importer.ReadFile(filepath, + aiProcess_Triangulate | // flag to only creates geometry made of triangles + aiProcess_FlipUVs | + aiProcess_SplitLargeMeshes | + aiProcess_OptimizeMeshes | + aiProcess_GenBoundingBoxes | // needed to query bounding box of the model later + aiProcess_CalcTangentSpace); + } else { + scene = importer.ReadFile(filepath, + aiProcess_Triangulate | // flag to only creates geometry made of triangles + aiProcess_SplitLargeMeshes | + aiProcess_OptimizeMeshes | + aiProcess_GenBoundingBoxes | // needed to query bounding box of the model later + aiProcess_CalcTangentSpace); + } + if(!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { throw std::invalid_argument(std::string("ERROR::ASSIMP::") + importer.GetErrorString()); } @@ -193,13 +225,24 @@ Model::Model(const std::string& filepath) { } void Model::draw(Shader* shader, - glm::mat4 viewProj, glm::vec3 camPos, - std::array, MAX_POINT_LIGHTS> lightSources, bool fill) { for(Mesh& mesh : this->meshes) { - mesh.draw(shader, viewProj, camPos, lightSources, fill); + mesh.draw(shader, camPos, fill); + } +} + +void Model::draw(Shader* shader, + glm::vec3 camPos, + bool fill, + glm::vec3 color) { + float scale = 1.1f; + for(Mesh& mesh : this->meshes) { + glm::vec3 scaledColor = scale * color; + shader->setVec3("lightColor", scaledColor); + mesh.draw(shader, camPos, fill); + scale += 0.15f; } } @@ -213,7 +256,7 @@ void Model::translateAbsolute(const glm::vec3& new_pos) { void Model::translateRelative(const glm::vec3& delta) { Renderable::translateRelative(delta); for(Mesh& mesh : this->meshes) { - mesh.translateAbsolute(delta); + mesh.translateRelative(delta); } } @@ -245,6 +288,27 @@ void Model::scaleRelative(const glm::vec3& scale) { } } +void Model::rotateAbsolute(const glm::vec3& dir, bool is_player, const glm::vec3& axis) { + Renderable::rotateAbsolute(dir, is_player, axis); + for(Mesh& mesh : this->meshes) { + mesh.rotateAbsolute(dir, is_player, axis); + } +} + +void Model::rotateAbsolute(const float& angle, const glm::vec3& axis) { + Renderable::rotateAbsolute(angle, axis); + for(Mesh& mesh : this->meshes) { + mesh.rotateAbsolute(angle, axis); + } +} + +void Model::rotateRelative(const glm::vec3& dir, const glm::vec3& axis) { + Renderable::rotateRelative(dir, axis); + for(Mesh& mesh : this->meshes) { + mesh.rotateRelative(dir, axis); + } +} + void Model::clear() { Renderable::clear(); for(Mesh& mesh : this->meshes) { @@ -279,6 +343,7 @@ void Model::processNode(aiNode *node, const aiScene *scene) { // process all the node's meshes (if any) for(unsigned int i = 0; i < node->mNumMeshes; i++) { aiMesh *mesh = scene->mMeshes[node->mMeshes[i]]; + // std::cout << "processing mesh at index[" << i << "]" << std::endl; meshes.push_back(processMesh(mesh, scene)); // update model's bounding box with new mesh @@ -304,11 +369,16 @@ Mesh Model::processMesh(aiMesh *mesh, const aiScene *scene) { mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z); - glm::vec3 normal( - mesh->mNormals[i].x, - mesh->mNormals[i].y, - mesh->mNormals[i].z); - + + glm::vec3 normal = glm::normalize(glm::vec3(1.0f, 1.0f, 1.0f)); + if (mesh->mNormals) { + normal = glm::vec3( + mesh->mNormals[i].x, + mesh->mNormals[i].y, + mesh->mNormals[i].z); + + } + // check if the mesh contain texture coordinates glm::vec2 texture(0.0f, 0.0f); if(mesh->mTextureCoords[0]) { @@ -322,6 +392,11 @@ Mesh Model::processMesh(aiMesh *mesh, const aiScene *scene) { texture }); } + + for (int i = 0; i < vertices.size(); i++) { + setDefaultVertexBoneData(vertices[i]); + } + // process indices for(unsigned int i = 0; i < mesh->mNumFaces; i++) { aiFace face = mesh->mFaces[i]; @@ -336,7 +411,6 @@ Mesh Model::processMesh(aiMesh *mesh, const aiScene *scene) { float shininess = 0.0f; if(mesh->mMaterialIndex >= 0) { - // std::cout << "processing material of id: " << mesh->mMaterialIndex << std::endl; aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex]; std::vector diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE); @@ -345,6 +419,9 @@ Mesh Model::processMesh(aiMesh *mesh, const aiScene *scene) { std::vector specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR); textures.insert(textures.end(), specularMaps.begin(), specularMaps.end()); + std::vector heightMaps = loadMaterialTextures(material, aiTextureType_HEIGHT); + textures.insert(textures.end(), heightMaps.begin(), heightMaps.end()); + if(AI_SUCCESS != material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse_color)) { std::cout << "couldn't get diffuse color" << std::endl; } @@ -362,6 +439,8 @@ Mesh Model::processMesh(aiMesh *mesh, const aiScene *scene) { } } + extractBoneWeight(vertices, mesh, scene); + return Mesh( vertices, indices, @@ -375,10 +454,62 @@ Mesh Model::processMesh(aiMesh *mesh, const aiScene *scene) { ); } +void Model::setDefaultVertexBoneData(Vertex& vertex) { + for (int i = 0; i < MAX_BONE_INFLUENCE; i++) { + vertex.m_boneIDs[i] = -1; + vertex.m_weights[i] = 0.0f; + } +} + +void Model::setVertexBoneData(Vertex& vertex, int boneID, float weight) { + // std::cout << "setting vertex bone data" << std::endl; + for (int i = 0; i < MAX_BONE_INFLUENCE; i++) { + if (vertex.m_boneIDs[i] < 0) { + vertex.m_boneIDs[i] = boneID; + vertex.m_weights[i] = weight; + return; + } + } +} + +void Model::extractBoneWeight(std::vector& vertices, aiMesh* mesh, const aiScene* scene) { + auto& boneInfoMap = m_boneInfoMap; + int& boneCount = m_boneCounter; + + for (int boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex) + { + int boneID = -1; + std::string boneName = mesh->mBones[boneIndex]->mName.C_Str(); + if (boneInfoMap.find(boneName) == boneInfoMap.end()) + { + BoneInfo newBoneInfo; + newBoneInfo.id = boneCount; + newBoneInfo.offset = matrixToGLM(mesh->mBones[boneIndex]->mOffsetMatrix); + boneInfoMap[boneName] = newBoneInfo; + boneID = boneCount; + boneCount++; + } + else + { + boneID = boneInfoMap[boneName].id; + } + assert(boneID != -1); + auto weights = mesh->mBones[boneIndex]->mWeights; + int numWeights = mesh->mBones[boneIndex]->mNumWeights; + + for (int weightIndex = 0; weightIndex < numWeights; ++weightIndex) + { + int vertexId = weights[weightIndex].mVertexId; + float weight = weights[weightIndex].mWeight; + assert(vertexId <= vertices.size()); + setVertexBoneData(vertices[vertexId], boneID, weight); + } + } +} std::vector Model::loadMaterialTextures(aiMaterial* mat, const aiTextureType& type) { std::vector textures; - // std::cout << "material has " << mat->GetTextureCount(type) << " textures of type " << aiTextureTypeToString(type) << std::endl; + std::cout << "material has " << mat->GetTextureCount(type) << " textures of type " << aiTextureTypeToString(type) << std::endl; for(unsigned int i = 0; i < mat->GetTextureCount(type); i++) { aiString str; mat->GetTexture(type, i, &str); @@ -403,6 +534,9 @@ Texture::Texture(const std::string& filepath, const aiTextureType& type) { case aiTextureType_SPECULAR: this->type = "texture_specular"; break; + case aiTextureType_HEIGHT: + this->type = "texture_normal"; + break; default: throw std::invalid_argument(std::string("Unimplemented texture type ") + aiTextureTypeToString(type)); } @@ -412,13 +546,14 @@ Texture::Texture(const std::string& filepath, const aiTextureType& type) { int width, height, nrComponents; // std::cout << "Attempting to load texture at " << filepath << std::endl; + stbi_set_flip_vertically_on_load(true); unsigned char *data = stbi_load(filepath.c_str(), &width, &height, &nrComponents, 0); if (!data) { std::cout << "Texture failed to load at path: " << filepath << std::endl; stbi_image_free(data); throw std::exception(); } - // std::cout << "Succesfully loaded texture at " << filepath << std::endl; + // std::cout << "Succesfully loaded " << this->type << " texture at " << filepath << std::endl; GLenum format = GL_RED; if (nrComponents == 1) format = GL_RED; diff --git a/src/client/renderable.cpp b/src/client/renderable.cpp index 36289214..4c7c601e 100644 --- a/src/client/renderable.cpp +++ b/src/client/renderable.cpp @@ -1,11 +1,15 @@ #include "client/renderable.hpp" #include "glm/fwd.hpp" +#include #include + #define GLM_ENABLE_EXPERIMENTAL #include +#include + -Renderable::Renderable() : model(1.0f) { } +Renderable::Renderable() : model(1.0f), rotation(1.0f, 0.0f, 0.0f, 0.0f) { } void Renderable::translateAbsolute(const glm::vec3 &new_pos) { this->model[3] = glm::vec4(new_pos, 1.0f); @@ -36,8 +40,24 @@ void Renderable::scaleAbsolute(const glm::vec3& scale) { this->model[2][2] = scale.z; } +void Renderable::rotateAbsolute(const glm::vec3& dir, bool is_player, const glm::vec3& axis) { + float r = is_player ? glm::atan(dir.x, dir.z) : glm::atan(-dir.z, dir.x); + this->rotation = glm::angleAxis(r, axis); +} + +void Renderable::rotateAbsolute(const float& angle, const glm::vec3& axis) { + this->rotation = glm::angleAxis(angle, axis); +} + +void Renderable::rotateRelative(const glm::vec3& dir, const glm::vec3& axis) { + // float rot = glm::acos(glm::dot(dir, this->facing)); + float r1 = glm::atan(dir.x, dir.z); + float r2 = glm::angle(rotation); + this->rotation = glm::rotate(rotation, r1 - r2, axis); +} + glm::mat4 Renderable::getModelMat() { - return this->model; + return this->model * glm::mat4_cast(rotation); } void Renderable::clear() { diff --git a/src/client/shaders/cube.frag b/src/client/shaders/cube.frag index 9c30ceae..ffd1b0c1 100644 --- a/src/client/shaders/cube.frag +++ b/src/client/shaders/cube.frag @@ -20,5 +20,5 @@ void main() { vec3 reflectance = irradiance * DiffuseColor; // Gamma correction - fragColor = vec4(sqrt(reflectance), 1); + fragColor = vec4(sqrt(reflectance), 0.5); } \ No newline at end of file diff --git a/src/client/shaders/deferred_geometry.frag b/src/client/shaders/deferred_geometry.frag new file mode 100644 index 00000000..b7269600 --- /dev/null +++ b/src/client/shaders/deferred_geometry.frag @@ -0,0 +1,52 @@ +#version 330 core + +#define NR_POINT_LIGHTS 32 + +layout (location = 0) out vec3 gPosition; +layout (location = 1) out vec3 gNormal; +layout (location = 2) out vec4 gAlbedoSpec; +// layout (location = 3) out vec3 gTangentNormal; + +in vec3 FragPos; +in vec3 FragNormal; +in vec2 TexCoords; +in mat3 TBN; + +uniform sampler2D texture_diffuse1; +uniform sampler2D texture_specular1; +uniform sampler2D texture_normal1; +uniform bool has_normal_map; + +void main() +{ + // store the fragment position vector in the first gbuffer texture + gPosition = FragPos; + // also store the per-fragment normals into the gbuffer + if (has_normal_map) { + // obtain normal from normal map in range [0,1] + gNormal = texture(texture_normal1, TexCoords).rgb; + // transform normal vector to range [-1,1] + gNormal = gNormal * 2.0 - 1.0; + gNormal = normalize(TBN * gNormal); + gNormal = normalize(gNormal); + // if (gNormal == vec3(0.0, 0.0, 0.0)) { + // gAlbedoSpec.rgb = vec3(1.0, 0.0, 0.0); + // } else { + // gAlbedoSpec.rgb = vec3(0.0, 1.0, 0.0); + // } + + // gNormal = normalize(TBN * (texture(texture_normal1, TexCoords).rgb * 2.0 - 1.0)); + // gNormal = vec3(0.0, 1.0, 0.0); + // gAlbedoSpec.rgb = texture(texture_normal1, TexCoords).rgb; + } else { + gNormal = normalize(FragNormal); + // gAlbedoSpec.rgb = texture(texture_diffuse1, TexCoords).rgb; + } + + // and the diffuse per-fragment color + gAlbedoSpec.rgb = texture(texture_diffuse1, TexCoords).rgb; + // store specular intensity in gAlbedoSpec's alpha component + gAlbedoSpec.a = texture(texture_specular1, TexCoords).r; + + // gTangentNormal = texture(texture_normal, TexCoords).rgb; +} diff --git a/src/client/shaders/deferred_geometry.vert b/src/client/shaders/deferred_geometry.vert new file mode 100644 index 00000000..07b4e461 --- /dev/null +++ b/src/client/shaders/deferred_geometry.vert @@ -0,0 +1,93 @@ +#version 330 core + +#define NR_POINT_LIGHTS 32 + +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; +layout (location = 2) in vec2 aTexCoords; +layout (location = 3) in ivec4 boneIds; +layout (location = 4) in vec4 weights; +layout (location = 5) in vec3 aTangent; +layout (location = 6) in vec3 aBitangent; + +const int MAX_BONES = 100; +const int MAX_BONE_INFLUENCE = 4; + +out vec3 FragPos; +out vec3 FragNormal; +out vec2 TexCoords; +out mat3 TBN; + +uniform mat4 model; +uniform mat4 viewProj; +uniform mat4 finalBonesMatrices[MAX_BONES]; + +void main() +{ + int skipped = 0; + vec4 totalPosition = vec4(0); + vec4 totalNormal = vec4(0); + + for(int i = 0 ; i < MAX_BONE_INFLUENCE ; i++) { + if(boneIds[i] <= -1) { + skipped += 1; + continue; + } + + if(boneIds[i] >= MAX_BONES) { + totalPosition = vec4(aPos, 1); + totalNormal = vec4(aNormal, 0); + break; + } + + vec4 localPosition = finalBonesMatrices[boneIds[i]] * vec4(aPos, 1); + totalPosition += localPosition * weights[i]; + vec4 localNormal = finalBonesMatrices[boneIds[i]] * vec4(aNormal, 0); + totalNormal += localNormal * weights[i]; + } + + if (skipped == MAX_BONE_INFLUENCE) { + totalPosition = vec4(aPos, 1); + totalNormal = vec4(aNormal, 0); + } + + + vec4 worldPos = model * totalPosition; + FragPos = worldPos.xyz; + TexCoords = aTexCoords; + + vec3 normal0 = (model * vec4(aNormal, 1.0)).xyz; + vec3 tangent0 = (model * vec4(aTangent, 1.0)).xyz; + + vec3 Normal = normalize(normal0); + vec3 Tangent = normalize(tangent0); + Tangent = normalize(Tangent - dot(Tangent, Normal) * Normal); + vec3 Bitangent = cross(Normal, Tangent); + TBN = mat3(Tangent, Bitangent, Normal); + + mat3 normalMatrix = transpose(inverse(mat3(model))); + // vec3 T = normalize(normalMatrix * aTangent); + // vec3 B = normalize(normalMatrix * aBitangent); + // vec3 N = normalize(normalMatrix * aNormal); + // // T = normalize(T - dot(T, N) * N); + // // vec3 B = cross(N, T); + // + // TBN = mat3(T, B, N); + + FragNormal = normalMatrix * aNormal; + + gl_Position = viewProj * worldPos; + + /* + vec4 worldPos = model * vec4(aPos, 1.0); + FragPos = worldPos.xyz; + TexCoords = aTexCoords; + + mat3 normalMatrix = transpose(inverse(mat3(model))); + FragNormal = normalMatrix * aNormal; + + gl_Position = viewProj * worldPos; + */ +} + + diff --git a/src/client/shaders/deferred_light_box.frag b/src/client/shaders/deferred_light_box.frag new file mode 100644 index 00000000..79509fc5 --- /dev/null +++ b/src/client/shaders/deferred_light_box.frag @@ -0,0 +1,11 @@ +#version 330 core +layout (location = 0) out vec4 FragColor; + +uniform vec3 lightColor; + +void main() +{ + FragColor = vec4(lightColor, 1.0); +} + + diff --git a/src/client/shaders/deferred_light_box.vert b/src/client/shaders/deferred_light_box.vert new file mode 100644 index 00000000..3c49110e --- /dev/null +++ b/src/client/shaders/deferred_light_box.vert @@ -0,0 +1,14 @@ +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; +layout (location = 2) in vec2 aTexCoords; + +uniform mat4 viewProj; +uniform mat4 model; + +void main() +{ + gl_Position = viewProj * model * vec4(aPos, 1.0); +} + + diff --git a/src/client/shaders/deferred_lighting.frag b/src/client/shaders/deferred_lighting.frag new file mode 100644 index 00000000..1889c8d5 --- /dev/null +++ b/src/client/shaders/deferred_lighting.frag @@ -0,0 +1,75 @@ + + +#version 330 core +out vec4 FragColor; + +in vec2 TexCoords; + +uniform sampler2D gPosition; +uniform sampler2D gNormal; +uniform sampler2D gAlbedoSpec; + +struct PointLight { + // from 0-1 + float intensity; + + vec3 position; + + vec3 ambient_color; + vec3 diffuse_color; + vec3 specular_color; + + float attn_linear; + float attn_quadratic; +}; + +#define NR_POINT_LIGHTS 32 +uniform PointLight pointLights[NR_POINT_LIGHTS]; + +uniform vec3 viewPos; + +vec3 CalcPointLight(in PointLight light, in vec3 fragPos, in vec3 normal, in vec3 viewDir, + vec3 mat_diffuse, float mat_shininess) { + + vec3 lightDir = normalize(light.position - fragPos); + vec3 halfwayDir = normalize(lightDir + viewDir); + float diff = max(dot(normal, lightDir), 0.0); + + vec3 ambient = light.ambient_color * mat_diffuse; + vec3 diffuse = light.diffuse_color * diff * mat_diffuse; + float spec = pow(max(dot(normal, halfwayDir), 0.0), 16.0); + vec3 specular = light.specular_color * spec * mat_shininess; + + // attenuation + float distance = length(light.position - fragPos); + float attenuation = 1.0 / (1.0 + light.attn_linear * distance + + light.attn_quadratic * (distance * distance)); + ambient *= attenuation; + diffuse *= attenuation; + specular *= attenuation; + return (ambient + diffuse + specular) * light.intensity; +} + +void main() { + // retrieve data from gbuffer + vec3 FragPos = texture(gPosition, TexCoords).rgb; + vec3 Normal = normalize(texture(gNormal, TexCoords).rgb); + vec3 Diffuse = texture(gAlbedoSpec, TexCoords).rgb; + float Specular = texture(gAlbedoSpec, TexCoords).a; + + // then calculate lighting as usual + vec3 viewDir = normalize(viewPos - FragPos); + vec3 result = vec3(0.0, 0.0, 0.0); + for(int i = 0; i < NR_POINT_LIGHTS; ++i) { + result += CalcPointLight( + pointLights[i], + FragPos, + Normal, + viewDir, + Diffuse, + Specular); + } + FragColor = vec4(result, 1.0); +} + + diff --git a/src/client/shaders/deferred_lighting.vert b/src/client/shaders/deferred_lighting.vert new file mode 100644 index 00000000..f2aaa901 --- /dev/null +++ b/src/client/shaders/deferred_lighting.vert @@ -0,0 +1,13 @@ +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec2 aTexCoords; + +out vec2 TexCoords; + +void main() +{ + TexCoords = aTexCoords; + gl_Position = vec4(aPos, 1.0); +} + + diff --git a/src/client/shaders/dm_deferred_lighting.frag b/src/client/shaders/dm_deferred_lighting.frag new file mode 100644 index 00000000..f7ea8e65 --- /dev/null +++ b/src/client/shaders/dm_deferred_lighting.frag @@ -0,0 +1,102 @@ +#version 330 core +out vec4 FragColor; + +in vec2 TexCoords; + +uniform sampler2D gPosition; +uniform sampler2D gNormal; +uniform sampler2D gAlbedoSpec; + +struct DirLight { + vec3 direction; + + vec3 ambient_color; + vec3 diffuse_color; + vec3 specular_color; +}; + +struct PointLight { + // from 0-1 + float intensity; + + vec3 position; + + vec3 ambient_color; + vec3 diffuse_color; + vec3 specular_color; + + float attn_linear; + float attn_quadratic; +}; + +#define NR_DIR_LIGHTS 4 +uniform DirLight dirLights[NR_DIR_LIGHTS]; + +#define NR_POINT_LIGHTS 32 +uniform PointLight pointLights[NR_POINT_LIGHTS]; + +uniform vec3 viewPos; + +vec3 CalcPointLight(in PointLight light, in vec3 fragPos, in vec3 normal, in vec3 viewDir, + vec3 mat_diffuse, float mat_shininess) { + + vec3 lightDir = normalize(light.position - fragPos); + vec3 halfwayDir = normalize(lightDir + viewDir); + float diff = max(dot(normal, lightDir), 0.0); + + vec3 ambient = light.ambient_color * mat_diffuse; + vec3 diffuse = light.diffuse_color * diff * mat_diffuse; + float spec = pow(max(dot(normal, halfwayDir), 0.0), 16.0); + vec3 specular = light.specular_color * spec * mat_shininess; + + // attenuation + float distance = length(light.position - fragPos); + float attenuation = 1.0 / (1.0 + light.attn_linear * distance + + light.attn_quadratic * (distance * distance)); + ambient *= attenuation; + diffuse *= attenuation; + specular *= attenuation; + return (ambient + diffuse + specular) * light.intensity; +} + +vec3 CalcDirLight(in DirLight light, in vec3 normal, in vec3 viewDir, + in vec3 mat_diffuse, in float mat_shininess) { + vec3 lightDir = normalize(-light.direction); + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + // specular shading + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), 16.0); + // combine results + vec3 ambient = light.ambient_color * mat_diffuse; + vec3 diffuse = light.diffuse_color * diff * mat_diffuse; + vec3 specular = light.specular_color * spec * mat_shininess; + return (ambient + diffuse + specular); +} + +void main() { + // retrieve data from gbuffer + vec3 FragPos = texture(gPosition, TexCoords).rgb; + vec3 Normal = normalize(texture(gNormal, TexCoords).rgb); + vec3 Diffuse = texture(gAlbedoSpec, TexCoords).rgb; + float Specular = texture(gAlbedoSpec, TexCoords).a; + + // then calculate lighting as usual + vec3 viewDir = normalize(viewPos - FragPos); + vec3 result = vec3(0.0, 0.0, 0.0); + for(int i = 0; i < NR_DIR_LIGHTS; ++i) { + result += CalcDirLight(dirLights[i], Normal, viewDir, Diffuse, Specular); + } + for(int i = 0; i < NR_POINT_LIGHTS; ++i) { + result += CalcPointLight( + pointLights[i], + FragPos, + Normal, + viewDir, + Diffuse, + Specular); + } + FragColor = vec4(result, 1.0); +} + + diff --git a/src/client/shaders/model.frag b/src/client/shaders/model.frag index edbd9d8b..1bb40c9f 100644 --- a/src/client/shaders/model.frag +++ b/src/client/shaders/model.frag @@ -19,10 +19,11 @@ struct Material { uniform Material material; uniform vec3 viewPos; -// uniform vec3 lightPos; -// uniform vec3 lightColor; +uniform vec3 lightPos; +uniform vec3 lightColor; -struct PointLight { +struct PointLight { + bool enabled; vec3 position; float constant; @@ -32,18 +33,57 @@ struct PointLight { vec3 ambient; vec3 diffuse; vec3 specular; + + // from 0-1 + float intensity; }; -#define NR_POINT_LIGHTS 8 +#define NR_POINT_LIGHTS 16 uniform PointLight pointLights[NR_POINT_LIGHTS]; -uniform PointLight light; - out vec4 fragColor; +vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir) { + vec3 lightDir = normalize(light.position - fragPos); + vec3 halfwayDir = normalize(lightDir + viewDir); + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + // specular shading + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(normal, halfwayDir), 0.0), material.shininess); + // attenuation + float distance = length(light.position - fragPos); + float attenuation = 1.0 / (light.constant + light.linear * distance + + light.quadratic * (distance * distance)); + // combine results + // TODO: put textures here once we want textured surfaces + vec3 ambient = light.ambient * vec3(texture(material.texture_diffuse1, TexCoords)); + vec3 diffuse = light.diffuse * diff * vec3(texture(material.texture_diffuse1, TexCoords)); + vec3 specular = light.specular * spec; + ambient *= attenuation; + diffuse *= attenuation; + specular *= attenuation; + return (ambient + diffuse + specular) * light.intensity; +} + +void main() { + vec3 norm = normalize(fragNormal); + vec3 viewDir = normalize(viewPos - fragPos); + + vec3 result = vec3(0.0, 0.0, 0.0); + for(int i = 0; i < NR_POINT_LIGHTS; i++) { + if (!pointLights[i].enabled) { + continue; + } + result += CalcPointLight(pointLights[i], norm, fragPos, viewDir); + } + fragColor = vec4(result, 1.0); +} +/* void main() { fragColor = vec4(0.0, 1.0, 1.0, 1.0); } +*/ // #version 330 core // diff --git a/src/client/shaders/model.vert b/src/client/shaders/model.vert index 7d681852..510c97a1 100644 --- a/src/client/shaders/model.vert +++ b/src/client/shaders/model.vert @@ -6,10 +6,18 @@ layout (location = 0) in vec3 position; layout (location = 1) in vec3 normal; layout (location = 2) in vec2 uvs; +layout (location = 3) in vec3 tangent; +layout (location = 4) in vec3 bitangent; +layout (location = 5) in ivec4 boneIds; +layout (location = 6) in vec4 weights; + +const int MAX_BONES = 100; +const int MAX_BONE_INFLUENCE = 4; // Uniform variables uniform mat4 viewProj; uniform mat4 model; +uniform mat4 finalBonesMatrices[MAX_BONES]; // Outputs of the vertex shader are the inputs of the same name of the fragment shader. // The default output, gl_Position, should be assigned something. @@ -18,12 +26,32 @@ out vec3 fragPos; out vec2 TexCoords; void main() { - // OpenGL maintains the D matrix so you only need to multiply by P, V (aka C inverse), and M - gl_Position = viewProj * model * vec4(position, 1.0); + vec4 totalPosition = vec4(0); + vec4 totalNormal = vec4(0); + + for(int i = 0 ; i < MAX_BONE_INFLUENCE ; i++) { + if(boneIds[i] <= -1) { + continue; + } + if(boneIds[i] >= MAX_BONES) { + totalPosition = vec4(position, 1); + totalNormal = vec4(normal, 0); + break; + } + + vec4 localPosition = finalBonesMatrices[boneIds[i]] * vec4(position, 1); + totalPosition += localPosition * weights[i]; + vec4 localNormal = finalBonesMatrices[boneIds[i]] * vec4(normal, 0); + totalNormal += localNormal * weights[i]; + } + + // OpenGL maintains the D matrix so you only need to multiply by P, V (aka C inverse), and M // for shading - fragNormal = vec3(model * vec4(normal, 0)); - //fragNormal = normal; - fragPos = vec3(model * vec4(position, 1.0)); + + gl_Position = viewProj * model * totalPosition; + fragNormal = vec3(model * totalNormal); + fragPos = vec3(model * totalPosition); + TexCoords = uvs; } diff --git a/src/client/shaders/sungod.frag b/src/client/shaders/sungod.frag new file mode 100644 index 00000000..cc1972c1 --- /dev/null +++ b/src/client/shaders/sungod.frag @@ -0,0 +1,81 @@ +#version 330 core + +// Fragment shader for loaded models. +// This shader currently expects textured models. Untextured +// models will show up as black. + +in vec3 fragNormal; +in vec3 fragPos; +in vec2 TexCoords; + +struct Material { + vec3 ambient; + vec3 diffuse; + vec3 specular; + float shininess; + sampler2D texture_diffuse1; +}; + +uniform Material material; +uniform vec3 viewPos; + +uniform vec3 lightPos; +uniform vec3 lightColor; + +struct PointLight { + bool enabled; + vec3 position; + + float constant; + float linear; + float quadratic; + + vec3 ambient; + vec3 diffuse; + vec3 specular; + + // from 0-1 + float intensity; +}; + +#define NR_POINT_LIGHTS 16 +uniform PointLight pointLights[NR_POINT_LIGHTS]; + +out vec4 fragColor; + +vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir) { + vec3 lightDir = normalize(light.position - fragPos); + vec3 halfwayDir = normalize(lightDir + viewDir); + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + // specular shading + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(normal, halfwayDir), 0.0), material.shininess); + // attenuation + float distance = length(light.position - fragPos); + float attenuation = 1.0 / (light.constant + light.linear * distance + + light.quadratic * (distance * distance)); + // combine results + // TODO: put textures here once we want textured surfaces + vec3 ambient = light.ambient * vec3(texture(material.texture_diffuse1, TexCoords)); + vec3 diffuse = light.diffuse * diff * vec3(texture(material.texture_diffuse1, TexCoords)); + vec3 specular = light.specular * spec; + ambient *= attenuation; + diffuse *= attenuation; + specular *= attenuation; + return (ambient + diffuse + specular) * light.intensity; +} + +void main() { + vec3 norm = normalize(fragNormal); + vec3 viewDir = normalize(viewPos - fragPos); + + vec3 result = vec3(0.0, 0.0, 0.0); + for(int i = 0; i < NR_POINT_LIGHTS; i++) { + if (!pointLights[i].enabled) { + continue; + } + result += CalcPointLight(pointLights[i], norm, fragPos, viewDir); + } + fragColor = vec4(result, 1.0); +} diff --git a/src/client/shaders/sungod.vert b/src/client/shaders/sungod.vert new file mode 100644 index 00000000..7d681852 --- /dev/null +++ b/src/client/shaders/sungod.vert @@ -0,0 +1,29 @@ +#version 330 core +# +// Vertex shader for loaded models. +// Also forwards texture coordinates to fragment shader. + +layout (location = 0) in vec3 position; +layout (location = 1) in vec3 normal; +layout (location = 2) in vec2 uvs; + +// Uniform variables +uniform mat4 viewProj; +uniform mat4 model; + +// Outputs of the vertex shader are the inputs of the same name of the fragment shader. +// The default output, gl_Position, should be assigned something. +out vec3 fragNormal; +out vec3 fragPos; +out vec2 TexCoords; + +void main() { + // OpenGL maintains the D matrix so you only need to multiply by P, V (aka C inverse), and M + gl_Position = viewProj * model * vec4(position, 1.0); + + // for shading + fragNormal = vec3(model * vec4(normal, 0)); + //fragNormal = normal; + fragPos = vec3(model * vec4(position, 1.0)); + TexCoords = uvs; +} diff --git a/src/client/util.cpp b/src/client/util.cpp index 81ba35f6..f8d22c31 100644 --- a/src/client/util.cpp +++ b/src/client/util.cpp @@ -11,7 +11,23 @@ glm::vec3 aiColorToGLM(const aiColor3D& color) { return glm::vec3(color.r, color.g, color.b); } -glm::vec3 Bbox::getDimensions() { +glm::mat4 matrixToGLM(const aiMatrix4x4& from) { + glm::mat4 to; + //the a,b,c,d in assimp is the row ; the 1,2,3,4 is the column + to[0][0] = from.a1; to[1][0] = from.a2; to[2][0] = from.a3; to[3][0] = from.a4; + to[0][1] = from.b1; to[1][1] = from.b2; to[2][1] = from.b3; to[3][1] = from.b4; + to[0][2] = from.c1; to[1][2] = from.c2; to[2][2] = from.c3; to[3][2] = from.c4; + to[0][3] = from.d1; to[1][3] = from.d2; to[2][3] = from.d3; to[3][3] = from.d4; + return to; +} + +glm::vec3 getGLMVec(const aiVector3D& vec) { + return glm::vec3(vec.x, vec.y, vec.z); +} + +glm::quat getGLMQuat(const aiQuaternion& pOrientation) { + return glm::quat(pOrientation.w, pOrientation.x, pOrientation.y, pOrientation.z); +}glm::vec3 Bbox::getDimensions() { return glm::abs(this->corners.first - this->corners.second); } @@ -77,3 +93,25 @@ Bbox combineBboxes(const Bbox& bbox1, const Bbox& bbox2) { ) }; } + +glm::vec3 rotate90DegreesAroundXAxis(const glm::vec3& direction) { + // Create a 90-degree rotation matrix around the Y-axis + glm::mat4 rotationMatrix = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); + + // Apply the rotation to the direction vector + glm::vec4 rotatedDirection = rotationMatrix * glm::vec4(direction, 1.0f); + + // Return the rotated vector as a vec3 + return glm::vec3(rotatedDirection); +} + +glm::vec3 rotate90DegreesAroundYAxis(const glm::vec3& direction) { + // Create a 90-degree rotation matrix around the Y-axis + glm::mat4 rotationMatrix = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f)); + + // Apply the rotation to the direction vector + glm::vec4 rotatedDirection = rotationMatrix * glm::vec4(direction, 1.0f); + + // Return the rotated vector as a vec3 + return glm::vec3(rotatedDirection); +} diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index e991464a..834900db 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -22,6 +22,7 @@ set(FILES game/arrowtrap.cpp game/projectile.cpp game/floorspike.cpp + game/lava.cpp game/fakewall.cpp game/dungeonmaster.cpp game/potion.cpp @@ -33,7 +34,11 @@ set(FILES game/orb.cpp game/weapon.cpp game/weaponcollider.cpp - + game/spawner.cpp + game/minotaur.cpp + game/python.cpp + game/introcutscene.cpp + game/mirror.cpp audio/soundtable.cpp ) diff --git a/src/server/audio/soundtable.cpp b/src/server/audio/soundtable.cpp index 1955555d..76a941ed 100644 --- a/src/server/audio/soundtable.cpp +++ b/src/server/audio/soundtable.cpp @@ -12,20 +12,26 @@ SoundTable::SoundTable() { this->next_id = 0; } -std::unordered_map> SoundTable::getCommandsPerPlayer(SmartVector& players) { +std::unordered_map> SoundTable::getCommandsPerPlayer(const std::vector& players) { std::vector commands; std::swap(this->current_commands, commands); // current commands is now empty, and we can parse these commands as we wish std::unordered_map> commands_per_player; - for (int i = 0; i < players.size(); i++) { - Player* player = players.get(i); - if (player == nullptr) continue; - + for (const auto& obj : players) { for (const SoundCommand& command : commands) { - if (command.source.canBeHeardFrom(player->physics.shared.getCenterPosition())) { - commands_per_player[player->globalID].push_back(command); + // hack to prevent the dm and players from hearing the wrong sounds in intro cutscene... + if ((command.source.sfx == ServerSFX::ZeusStartTheme || + command.source.sfx == ServerSFX::Wind) && obj->type ==ObjectType::Player) { + continue; + } + if (command.source.sfx == ServerSFX::PlayersStartTheme && obj->type == ObjectType::DungeonMaster) { + continue; + } + + if (command.source.canBeHeardFrom(obj->physics.shared.getCenterPosition())) { + commands_per_player[obj->globalID].push_back(command); } } } diff --git a/src/server/game/arrowtrap.cpp b/src/server/game/arrowtrap.cpp index 39e10d98..484f7325 100644 --- a/src/server/game/arrowtrap.cpp +++ b/src/server/game/arrowtrap.cpp @@ -1,4 +1,5 @@ #include "server/game/arrowtrap.hpp" +#include "server/game/object.hpp" #include "server/game/servergamestate.hpp" #include "shared/utilities/rng.hpp" #include "server/game/objectmanager.hpp" @@ -9,31 +10,30 @@ using namespace std::chrono_literals; -const std::chrono::seconds ArrowTrap::TIME_UNTIL_RESET = 4s; - -ArrowTrap::ArrowTrap(glm::vec3 corner, glm::vec3 dimensions, ArrowTrap::Direction dir): - Trap(ObjectType::ArrowTrap, false, corner, Collider::Box, ModelType::Cube, dimensions) +ArrowTrap::ArrowTrap(glm::vec3 corner, Direction dir): + Trap(ObjectType::ArrowTrap, false, corner, Collider::None, ModelType::ArrowTrap) { this->dir = dir; this->shoot_time = std::chrono::system_clock::now(); - switch (dir) { - case ArrowTrap::Direction::LEFT: - this->physics.shared.facing = glm::vec3(-1.0f, 0.0f, 0.0f); - break; - case ArrowTrap::Direction::RIGHT: - this->physics.shared.facing = glm::vec3(1.0f, 0.0f, 0.0f); - break; - case ArrowTrap::Direction::UP: - this->physics.shared.facing = glm::vec3(0.0f, 0.0f, -1.0f); - break; - case ArrowTrap::Direction::DOWN: - this->physics.shared.facing = glm::vec3(0.0f, 0.0f, 1.0f); - break; - } + this->physics.shared.facing = directionToFacing(dir); + // switch (dir) { + // case Direction::LEFT: + // this->physics.shared.facing = glm::vec3(0.0f, 0.0f, 1.0f); + // break; + // case Direction::RIGHT: + // this->physics.shared.facing = glm::vec3(0.0f, 0.0f, -1.0f); + // break; + // case Direction::UP: + // this->physics.shared.facing = glm::vec3(1.0f, 0.0f, 0.0f); + // break; + // case Direction::DOWN: + // this->physics.shared.facing = glm::vec3(-1.0f, 0.0f, 0.0f); + // break; + // } } bool ArrowTrap::shouldTrigger(ServerGameState& state) { - if (this->info.triggered) { + if (this->info.triggered || this->info.dm_hover) { return false; } @@ -41,14 +41,18 @@ bool ArrowTrap::shouldTrigger(ServerGameState& state) { for (int i = 0; i < state.objects.getPlayers().size(); i++) { Player* player = state.objects.getPlayers().get(i); if (player == nullptr) continue; + if (!player->canBeTargetted()) continue; player_grid_positions.push_back(state.getGrid().getGridCellFromPosition(player->physics.shared.getCenterPosition())); } glm::ivec2 curr_grid_pos = state.getGrid().getGridCellFromPosition(this->physics.shared.getCenterPosition()); int dist = 0; - while (dist < 10) { // max sightline - if (state.getGrid().getCell(curr_grid_pos.x, curr_grid_pos.y)->type == CellType::Wall) { - return false; // didnt find a player before a wall + while (dist < ArrowTrap::SIGHTLINE_M) { // max sightline + if (!state.getGrid().getCell(curr_grid_pos.x, curr_grid_pos.y)) { + break; } + //if (state.getGrid().getCell(curr_grid_pos.x, curr_grid_pos.y)->type == CellType::Wall) { + // return false; // didnt find a player before a wall + //} for (const auto& curr_player_pos : player_grid_positions) { if (curr_grid_pos == curr_player_pos) { // cppcheck-suppress useStlAlgorithm @@ -70,22 +74,22 @@ void ArrowTrap::trigger(ServerGameState& state) { glm::vec3 arrow_origin( this->physics.shared.getCenterPosition().x, - 2.0f, + 3.0f, this->physics.shared.getCenterPosition().z ); // TODO scale with grid size? switch (this->dir) { - case ArrowTrap::Direction::UP: - arrow_origin.z -= 3.0f; - break; - case ArrowTrap::Direction::DOWN: - arrow_origin.z += 2.0f; - break; - case ArrowTrap::Direction::LEFT: - arrow_origin.x -= 3.0f; + // case Direction::UP: + // arrow_origin.z -= 3.0f; + // break; + // case Direction::DOWN: + // arrow_origin.z += 2.0f; + // break; + case Direction::LEFT: + // arrow_origin.z -= Grid::grid_cell_width / 2.0f; break; - case ArrowTrap::Direction::RIGHT: + case Direction::RIGHT: arrow_origin.x += 2.0f; break; } diff --git a/src/server/game/dungeonmaster.cpp b/src/server/game/dungeonmaster.cpp index 1564cf72..0cee222d 100644 --- a/src/server/game/dungeonmaster.cpp +++ b/src/server/game/dungeonmaster.cpp @@ -1,10 +1,12 @@ #include "server/game/dungeonmaster.hpp" #include "shared/game/sharedobject.hpp" +#include "server/game/weapon.hpp" #include SharedObject DungeonMaster::toShared() { auto so = Creature::toShared(); so.trapInventoryInfo = this->sharedTrapInventory; + so.DMInfo = this->dmInfo; return so; } @@ -12,19 +14,37 @@ DungeonMaster::DungeonMaster(glm::vec3 corner, glm::vec3 facing) : Creature(ObjectType::DungeonMaster, corner, facing, ModelType::Cube, SharedStats( Stat(0, 100, 100), Stat(0, 10, 5) -)), sharedTrapInventory(SharedTrapInventory{ .selected = 1, .inventory_size = TRAP_INVENTORY_SIZE, .inventory = std::vector(TRAP_INVENTORY_SIZE, ModelType::Frame) }) { +)), sharedTrapInventory(SharedTrapInventory{ .selected = 1, .inventory_size = TRAP_INVENTORY_SIZE, \ + .inventory = std::vector(TRAP_INVENTORY_SIZE, ModelType::Frame) }), \ + dmInfo(SharedDMInfo{ .paralyzed = false, .mana_remaining = 15 }) +{ this->physics.feels_gravity = false; this->physics.velocityMultiplier = glm::vec3(3.0f, 1.0f, 3.0f); - - + this->lightning = nullptr; + this->mana_used = std::chrono::system_clock::now(); this->placedTraps = 0; // TODO: fill in rest of traps - this->sharedTrapInventory.inventory[0] = ModelType::FloorSpikeFull; - this->sharedTrapInventory.inventory[1] = ModelType::FloorSpikeHorizontal; - this->sharedTrapInventory.inventory[2] = ModelType::FloorSpikeVertical; - this->sharedTrapInventory.inventory[3] = ModelType::FireballTrap; + this->sharedTrapInventory.inventory[0] = ModelType::Lightning; + this->sharedTrapInventory.inventory[1] = ModelType::LightCut; + this->sharedTrapInventory.inventory[2] = ModelType::ArrowTrap; + this->sharedTrapInventory.inventory[3] = ModelType::SunGod; this->sharedTrapInventory.inventory[4] = ModelType::SpikeTrap; + this->sharedTrapInventory.inventory[5] = ModelType::FloorSpikeFull; + this->sharedTrapInventory.inventory[6] = ModelType::TeleporterTrap; + + // DungeonMaster paralysis (relevant when the DM is paralyzed by a Player + // reflecting a lightning bolt back at the DM using a Mirror) + + // Initially, the DM isn't paralyzed + + // This will be overwritten when the DM is actually paralyzed using + // setParalysis() + this->paralysisDuration = -1; + + // This will be overwritten when the DM is actually paralyzed using + // setParalysis() + this->paralysis_start_time = std::chrono::system_clock::now(); } int DungeonMaster::getPlacedTraps() { @@ -35,6 +55,49 @@ void DungeonMaster::setPlacedTraps(int traps) { this->placedTraps = traps; } +void DungeonMaster::useMana(int mana) { + if (this->dmInfo.mana_remaining == DM_MANA_TOTAL) { + this->mana_used = std::chrono::system_clock::now(); + } + this->dmInfo.mana_remaining -= mana; +} + +void DungeonMaster::manaRegen() { + if (this->dmInfo.mana_remaining == DM_MANA_TOTAL) return; + + auto now = std::chrono::system_clock::now(); + std::chrono::duration elapsed_seconds{ now - this->mana_used }; + + // Manually set to 0.5 seconds regen + if (elapsed_seconds > std::chrono::milliseconds(500)) { + this->dmInfo.mana_remaining += DM_MANA_REGEN; + this->mana_used = now; + } +} + DungeonMaster::~DungeonMaster() { +} + +void DungeonMaster::setParalysis(bool isParalyzed, double paralysis_duration) { + if (isParalyzed) { + // DM is now paralyzed - set duration and mark start timestamp + std::cout << "Paralyzing the DM!" << std::endl; + this->paralysisDuration = paralysis_duration; + this->paralysis_start_time = std::chrono::system_clock::now(); + } + + this->dmInfo.paralyzed = isParalyzed; +} + +bool DungeonMaster::isParalyzed() const { + return this->dmInfo.paralyzed; +} + +double DungeonMaster::getParalysisDuration() const { + return this->paralysisDuration; +} + +std::chrono::time_point DungeonMaster::getParalysisStartTime() const { + return this->paralysis_start_time; } \ No newline at end of file diff --git a/src/server/game/enemy.cpp b/src/server/game/enemy.cpp index d26e31cb..34ee5965 100644 --- a/src/server/game/enemy.cpp +++ b/src/server/game/enemy.cpp @@ -1,5 +1,6 @@ #include "server/game/enemy.hpp" #include "shared/game/sharedobject.hpp" +#include "server/game/servergamestate.hpp" SharedObject Enemy::toShared() { auto so = Creature::toShared(); @@ -10,4 +11,9 @@ Enemy::Enemy(glm::vec3 corner, glm::vec3 facing, ObjectType type, ModelType mode Creature(type, corner, facing, model, std::move(stats)) {} -Enemy::~Enemy() {} \ No newline at end of file +Enemy::~Enemy() {} + +bool Enemy::doDeath(ServerGameState& state) { + state.spawner->decreaseValue(this->typeID); + return true; +} \ No newline at end of file diff --git a/src/server/game/exit.cpp b/src/server/game/exit.cpp index 94fc940d..1fdb1d8e 100644 --- a/src/server/game/exit.cpp +++ b/src/server/game/exit.cpp @@ -1,12 +1,19 @@ #include "server/game/exit.hpp" #include "server/game/servergamestate.hpp" +#include "shared/game/point_light.hpp" #include "shared/game/sharedgamestate.hpp" -Exit::Exit(bool open, glm::vec3 corner, glm::vec3 dimensions) +Exit::Exit(bool open, glm::vec3 corner, glm::vec3 dimensions, const PointLightProperties& properties) : Object(ObjectType::Exit, - Physics(false, Collider::Box, corner, glm::vec3(0.0f), dimensions), ModelType::Cube) + Physics(false, Collider::Box, corner, glm::vec3(0.0f), dimensions), ModelType::Cube), + properties(properties) { this->shared.open = open; + this->intensity = 1.0f; +} + +void Exit::setIntensity(float val) { + this->intensity = val; } void Exit::doCollision(Object* other, ServerGameState& state) { @@ -33,5 +40,13 @@ void Exit::doCollision(Object* other, ServerGameState& state) { SharedObject Exit::toShared() { auto so = Object::toShared(); so.exit = this->shared; + so.pointLightInfo = SharedPointLightInfo { + .intensity = this->intensity, + .ambient_color = this->properties.ambient_color, + .diffuse_color = this->properties.diffuse_color, + .specular_color = this->properties.specular_color, + .attenuation_linear = this->properties.attenuation_linear, + .attenuation_quadratic = this->properties.attenuation_quadratic + }; return so; } \ No newline at end of file diff --git a/src/server/game/fireballtrap.cpp b/src/server/game/fireballtrap.cpp index 6cc418f4..0c0677ee 100644 --- a/src/server/game/fireballtrap.cpp +++ b/src/server/game/fireballtrap.cpp @@ -1,4 +1,5 @@ #include "server/game/fireballtrap.hpp" +#include "server/game/object.hpp" #include "server/game/servergamestate.hpp" #include "shared/utilities/rng.hpp" #include "server/game/objectmanager.hpp" @@ -12,16 +13,16 @@ using namespace std::chrono_literals; const std::chrono::seconds FireballTrap::TIME_UNTIL_RESET = 4s; const int FireballTrap::SHOOT_DIST = 15; -FireballTrap::FireballTrap(glm::vec3 corner, glm::vec3 dimensions): - Trap(ObjectType::FireballTrap, false, corner, Collider::None, ModelType::Cube, dimensions) +FireballTrap::FireballTrap(glm::vec3 corner, Direction dir): + Trap(ObjectType::FireballTrap, false, corner, Collider::None, ModelType::SunGod) { this->shoot_time = std::chrono::system_clock::now(); - this->physics.shared.facing = glm::vec3(1.0f, 0.0f, 0.0f); + this->physics.shared.facing = directionToFacing(dir); this->target = 0; // wont be accessed until set elsewhere, so safe to set to 0 } bool FireballTrap::shouldTrigger(ServerGameState& state) { - if (this->info.triggered) { + if (this->info.triggered || this->info.dm_hover) { return false; } @@ -33,6 +34,7 @@ bool FireballTrap::shouldTrigger(ServerGameState& state) { for (int p = 0; p < players.size(); p++) { auto player = players.get(p); if (player == nullptr) continue; + if (!player->canBeTargetted()) continue; glm::vec3 player_pos = player->physics.shared.getCenterPosition(); @@ -49,7 +51,7 @@ bool FireballTrap::shouldTrigger(ServerGameState& state) { // convert grid units to actual distance values const float SHOOT_DIST_UNITS = Grid::grid_cell_width * FireballTrap::SHOOT_DIST; if (closest_dist <= SHOOT_DIST_UNITS && player_to_shoot_at != nullptr) { - this->physics.shared.facing = glm::normalize(player_to_shoot_at->physics.shared.getCenterPosition() - this_pos); + // this->physics.shared.facing = glm::normalize(player_to_shoot_at->physics.shared.getCenterPosition() - this_pos); this->target = player_to_shoot_at->globalID; return (randomInt(1, 5) == 1); } @@ -65,8 +67,13 @@ void FireballTrap::trigger(ServerGameState& state) { Trap::trigger(state); + // auto pos_to_go_to = target_obj->physics.shared.getCenterPosition(); + // auto dir_to_target = glm::normalize(pos_to_go_to - this->physics.shared.getCenterPosition()); + + const float CENTER_TO_BEAK_ADJUSTMENT = 0.5f; + state.objects.createObject(new HomingFireball( - this->physics.shared.getCenterPosition(), + this->physics.shared.getCenterPosition() + glm::vec3(0, CENTER_TO_BEAK_ADJUSTMENT, 0), this->physics.shared.facing, this->target )); @@ -122,6 +129,14 @@ float FireballTrap::canSee(Object* other, ServerGameState* state) { } } + glm::vec3 facing = glm::normalize(this->physics.shared.facing); + glm::vec3 dir_to_other = glm::normalize(other_pos - this_pos); + // not really an angle, just a dot product + float angle_to_other = glm::dot(facing, dir_to_other); + if (angle_to_other <= 0.25) { + return -1.0f; + } + float curr_dist = glm::distance(other_pos, this_pos); return curr_dist; } diff --git a/src/server/game/floorspike.cpp b/src/server/game/floorspike.cpp index 544bc506..36c23588 100644 --- a/src/server/game/floorspike.cpp +++ b/src/server/game/floorspike.cpp @@ -1,23 +1,12 @@ #include "server/game/floorspike.hpp" #include "server/game/object.hpp" +#include "shared/game/sharedmodel.hpp" const int FloorSpike::DAMAGE = 1; -FloorSpike::FloorSpike(glm::vec3 corner, FloorSpike::Orientation orientation, float grid_width): - Trap(ObjectType::FloorSpike, false, corner, Collider::Box, ModelType::Cube, glm::vec3(0.0f)) -{ - const float HEIGHT = 0.1f; - - this->physics.shared.dimensions.y = HEIGHT; - this->physics.shared.dimensions.x = grid_width; - this->physics.shared.dimensions.z = grid_width; - - if (orientation == FloorSpike::Orientation::Horizontal) { - this->physics.shared.dimensions.z /= 2.0f; - } else if (orientation == FloorSpike::Orientation::Vertical) { - this->physics.shared.dimensions.x /= 2.0f; - } -} +FloorSpike::FloorSpike(glm::vec3 corner, float grid_width): + Trap(ObjectType::FloorSpike, false, corner, Collider::Box, ModelType::FloorSpikeFull, glm::vec3(0.0f)) +{ } bool FloorSpike::shouldTrigger(ServerGameState& state) { return false; @@ -28,6 +17,10 @@ bool FloorSpike::shouldReset(ServerGameState& state) { } void FloorSpike::doCollision(Object* obj, ServerGameState& state) { + if (this->info.dm_hover) { + return; + } + auto creature = dynamic_cast(obj); if (creature == nullptr) return; diff --git a/src/server/game/grid.cpp b/src/server/game/grid.cpp index 20fcd872..4cd809bb 100644 --- a/src/server/game/grid.cpp +++ b/src/server/game/grid.cpp @@ -131,6 +131,7 @@ glm::ivec2 Grid::getGridCellFromPosition(glm::vec3 position) { std::vector Grid::getCellsFromPositionRange(glm::vec3 p1, glm::vec3 p2) { std::vector cellPositions; + // Get GridCell positions for p1 and p2 glm::ivec2 gridCellStart = Grid::getGridCellFromPosition(p1); glm::ivec2 gridCellEnd = Grid::getGridCellFromPosition(p2); diff --git a/src/server/game/gridcell.cpp b/src/server/game/gridcell.cpp index 620383de..85a6f268 100644 --- a/src/server/game/gridcell.cpp +++ b/src/server/game/gridcell.cpp @@ -19,14 +19,20 @@ CellType charToCellType(char c) { return CellType::Enemy; case 'X': return CellType::SpikeTrap; - case '&': - return CellType::FireballTrap; + case '1': + return CellType::FireballTrapLeft; + case '2': + return CellType::FireballTrapRight; + case '3': + return CellType::FireballTrapDown; + case '4': + return CellType::FireballTrapUp; case '+': - return CellType::FloorSpikeFull; + return CellType::LavaCross; case '|': - return CellType::FloorSpikeVertical; + return CellType::LavaVertical; case '-': - return CellType::FloorSpikeHorizontal; + return CellType::LavaHorizontal; case 'F': return CellType::FakeWall; case '^': @@ -79,6 +85,12 @@ CellType charToCellType(char c) { return CellType::TeleporterTrap; case 'o': return CellType::Exit; + case 'M': + return CellType::Mirror; + case '5': + return CellType::FloorSpikeHorizontal; + case '6': + return CellType::FloorSpikeVertical; default: std::cerr << "Unknown cell type: " << c << "\n"; return CellType::Unknown; @@ -99,13 +111,19 @@ char cellTypeToChar(CellType type) { return 'E'; case CellType::SpikeTrap: return 'X'; - case CellType::FireballTrap: - return '&'; - case CellType::FloorSpikeFull: + case CellType::FireballTrapLeft: + return '1'; + case CellType::FireballTrapRight: + return '2'; + case CellType::FireballTrapDown: + return '3'; + case CellType::FireballTrapUp: + return '4'; + case CellType::LavaCross: return '+'; - case CellType::FloorSpikeVertical: + case CellType::LavaVertical: return '|'; - case CellType::FloorSpikeHorizontal: + case CellType::LavaHorizontal: return '-'; case CellType::FakeWall: return 'F'; @@ -159,6 +177,12 @@ char cellTypeToChar(CellType type) { return 'T'; case CellType::Exit: return 'o'; + case CellType::Mirror: + return 'M'; + case CellType::FloorSpikeHorizontal: + return '5'; + case CellType::FloorSpikeVertical: + return '6'; default: return '?'; } diff --git a/src/server/game/introcutscene.cpp b/src/server/game/introcutscene.cpp new file mode 100644 index 00000000..791679fb --- /dev/null +++ b/src/server/game/introcutscene.cpp @@ -0,0 +1,288 @@ +#include "server/game/introcutscene.hpp" +#include "shared/game/sharedgamestate.hpp" +#include "shared/utilities/config.hpp" +#include "server/game/weaponcollider.hpp" +#include "shared/audio/constants.hpp" +#include "shared/utilities/rng.hpp" +#include "server/game/exit.hpp" + +#include + + +GameConfig getCutsceneConfig() { + GameConfig config = getDefaultConfig(); + config.game.maze.maze_file = "cutscene/intro.maze"; + config.game.maze.procedural = false; + return config; +} + +IntroCutscene::IntroCutscene(): + state(GamePhase::INTRO_CUTSCENE, getCutsceneConfig()) +{ + this->ticks = 0; + // state has loaded in the maze file we use for this cutscene, + + // hard code direction to face right based on the intro cutscene maze orientation + Player* player = new Player(this->state.getGrid().getRandomSpawnPoint(), directionToFacing(Direction::RIGHT)); + Player* player_left = new Player(this->state.getGrid().getRandomSpawnPoint(), directionToFacing(Direction::RIGHT)); + player_left->modelType = ModelType::PlayerLightning; + Player* player_right = new Player(this->state.getGrid().getRandomSpawnPoint(), directionToFacing(Direction::RIGHT)); + player_right->modelType = ModelType::PlayerWater; + + this->state.objects.createObject(player); + this->state.objects.createObject(player_left); + this->state.objects.createObject(player_right); + + this->pov_eid = player->globalID; + + const glm::vec3 VELOCITY = glm::normalize(player->physics.shared.facing) * 0.10f; + + player->physics.velocity = VELOCITY; + player_left->physics.velocity = VELOCITY; + player_right->physics.velocity = VELOCITY; + + player->animState = AnimState::WalkAnim; + player_left->animState = AnimState::WalkAnim; + player_right->animState = AnimState::WalkAnim; + + // move half a grid cell down to be in center of 4 wide hall + player->physics.shared.corner += directionToFacing(Direction::DOWN) * (Grid::grid_cell_width / 2.0f); + + // adjust left and up a bit, and a random offset forward/back + player_left->physics.shared.corner = player->physics.shared.corner; + player_left->physics.shared.corner.z += Grid::grid_cell_width; + player_left->physics.shared.corner.x += Grid::grid_cell_width; + + // adjust right and down a bit, and a random offset forward/back + player_right->physics.shared.corner = player->physics.shared.corner; + player_right->physics.shared.corner.z -= Grid::grid_cell_width; + player_right->physics.shared.corner.x += Grid::grid_cell_width; + + DungeonMaster* dm = new DungeonMaster(player->physics.shared.corner + glm::vec3(-20.0f, 10.0f, 0), directionToFacing(Direction::RIGHT)); + dm->physics.velocity = player->physics.velocity; + dm->physics.velocityMultiplier = player->physics.velocityMultiplier; + this->state.objects.createObject(dm); + this->dm_eid = dm->globalID; + + auto exits = this->state.objects.getExits(); + for (int i = 0; i < exits.size(); i++) { + auto exit = exits.get(i); + if (exit == nullptr) continue; + + exit->setIntensity(0.0f); + } + + // load this->lights before sending down so the torch tick stuff works right +} + +bool IntroCutscene::update() { + this->state.updateMovement(); + this->state.updateItems(); + this->state.deleteEntities(); + this->state.updateAttacks(); + + ticks++; + + Player* player = this->state.objects.getPlayer(0); + Player* player_left = this->state.objects.getPlayer(1); + Player* player_right = this->state.objects.getPlayer(2); + DungeonMaster* dm = this->state.objects.getDM(); + + + if (ticks == START_TICK) { + this->state.soundTable().addNewSoundSource(SoundSource( + ServerSFX::TorchLoop, + player->physics.shared.getCenterPosition(), + FULL_VOLUME, + MEDIUM_DIST, + MEDIUM_ATTEN + )); + this->state.soundTable().addNewSoundSource(SoundSource( + ServerSFX::Wind, + dm->physics.shared.getCenterPosition(), + FULL_VOLUME, + MEDIUM_DIST, + MEDIUM_ATTEN + )); + } + + if (ticks == STOP_MOVING_TICK) { + player->physics.velocity = glm::vec3(0.0f); + player_left->physics.velocity = glm::vec3(0.0f); + player_right->physics.velocity = glm::vec3(0.0f); + dm->physics.velocity = glm::vec3(0.0f); + + player->animState = AnimState::IdleAnim; + player_left->animState = AnimState::IdleAnim; + player_right->animState = AnimState::IdleAnim; + } + + if (ticks >= GATE_RAISE_TICK && ticks <= GATE_STOP_RAISE_TICK) { + const int RAISE_TICK_DUR = GATE_STOP_RAISE_TICK - GATE_RAISE_TICK; + float intensity = static_cast(ticks - GATE_RAISE_TICK) / RAISE_TICK_DUR; + + // make exits slowly brighter as it opens + auto exits = this->state.objects.getExits(); + for (int i = 0; i < exits.size(); i++) { + auto exit = exits.get(i); + if (exit == nullptr) continue; + + exit->setIntensity(intensity); + } + + bool played_sound = false; + + auto walls = this->state.objects.getSolidSurfaces(); + for (int i = 0; i < walls.size(); i++) { + auto wall = walls.get(i); + if (wall == nullptr || wall->shared.surfaceType != SurfaceType::Pillar) continue; + + wall->physics.shared.corner.y += 0.042f; + + if (!played_sound && ticks == GATE_RAISE_TICK) { + played_sound = true; + this->state.soundTable().addNewSoundSource(SoundSource( + ServerSFX::IntroGateOpen, + wall->physics.shared.getCenterPosition(), + QUIET_VOLUME, // the sound effect is already so fucking loud geez + FAR_DIST, + FAR_ATTEN + )); + } + } + } + + glm::vec3 lightning_pos1 = player->physics.shared.corner + glm::normalize(player->physics.shared.facing) * 10.0f; + PointLightProperties light_properties{ + .flickering = false, + .min_intensity = 1.0f, + .max_intensity = 1.0f, + .ambient_color = glm::vec3(1.0f, 0.94f, 0.0f), + .diffuse_color = glm::vec3(1.0f, 0.94f, 0.0f), + .specular_color = glm::vec3(0.1f, 0.1f, 0.1f), + .attenuation_linear = 0.045f, + .attenuation_quadratic = 0.0075f + }; + + const int TORCH_DECAY_NUM_TICKS = 100; + static int decay_tick = 0; + + if (ticks < LIGHTNING_1_TICK - TORCH_DECAY_NUM_TICKS) { + this->state.doTorchlightTicks(); + } else if (ticks < LIGHTNING_1_TICK) { + auto torches = this->state.objects.getTorchlights(); + for (int i = 0; i < torches.size(); i++) { + auto torch = torches.get(i); + if (torch == nullptr) continue; + + float intensity = 0.3f * (static_cast(TORCH_DECAY_NUM_TICKS - decay_tick) / TORCH_DECAY_NUM_TICKS) + 0.1f; + + torch->overrideIntensity(intensity); + } + decay_tick++; + } + + + if (ticks == LIGHTNING_1_TICK) { + this->state.soundTable().addNewSoundSource(SoundSource( + ServerSFX::ZeusStartTheme, + player->physics.shared.getCenterPosition(), + FULL_VOLUME, + FAR_DIST, + FAR_ATTEN + )); + this->state.objects.createObject(new Lightning(lightning_pos1, player->physics.shared.facing, light_properties)); + + } + + if (ticks == LIGHTNING_2_TICK) { + glm::vec3 lightning_pos2 = lightning_pos1 + glm::vec3(3.0f, 0.0f, -3.0f); + this->state.objects.createObject(new Lightning(lightning_pos2, player->physics.shared.facing, light_properties)); + } + + if (ticks == LIGHTNING_3_TICK) { + glm::vec3 lightning_pos3 = player->physics.shared.getCenterPosition() + directionToFacing(Direction::RIGHT) * Grid::grid_cell_width * 2.0f; + this->state.objects.createObject(new Lightning(lightning_pos3, player->physics.shared.facing, light_properties)); + } + + if (ticks == START_PLAYER_THEME_TICK) { + this->state.soundTable().addNewSoundSource(SoundSource( + ServerSFX::PlayersStartTheme, + player->physics.shared.getCenterPosition(), + FULL_VOLUME, + FAR_DIST, + FAR_ATTEN + )); + } + + if (ticks == EXIT_CUTSCENE_TICK) { + player->animState = AnimState::SprintAnim; + player->physics.velocity = glm::normalize(player->physics.shared.facing) * 0.20f; + } + + if (ticks == EXIT_CUTSCENE_TICK + 20) { + player_left->animState = AnimState::SprintAnim; + player_left->physics.velocity = glm::normalize(player->physics.shared.facing) * 0.20f; + player_right->animState = AnimState::SprintAnim; + player_right->physics.velocity = glm::normalize(player->physics.shared.facing) * 0.20f; + } + + if (ticks == EXIT_CUTSCENE_TICK + 100) { + this->state.soundTable().addNewSoundSource(SoundSource( + ServerSFX::Teleport, + player->physics.shared.getCenterPosition(), + FULL_VOLUME, + FAR_DIST, + FAR_ATTEN + )); + } + if (ticks == EXIT_CUTSCENE_TICK + 105) { + return true; + } + + return false; +} + +LoadIntroCutsceneEvent IntroCutscene::toNetwork() { + std::vector updates = this->state.generateSharedGameState(true); + if (updates.size() != 1) { + // just put them all in one packet and pray it works... + SharedGameState& main = updates.at(0); + for (int i = 1; i < updates.size(); i++) { + for (const auto& [eid, obj] : updates.at(i).objects) { + main.objects.insert({eid, obj}); + } + } + } + + std::array, MAX_POINT_LIGHTS> empty; + std::swap(this->lights, empty); + + std::size_t lights_idx = 0; + for (int i = 0; i < this->state.objects.getObjects().size(); i++) { + auto object = this->state.objects.getObject(i); + if (object == nullptr) continue; + + if (lights_idx < MAX_POINT_LIGHTS && + (object->type == ObjectType::Torchlight || + object->type == ObjectType::Exit || + object->type == ObjectType::WeaponCollider // lightning + )) { + + this->lights[lights_idx] = object->toShared(); + lights_idx++; + } + if (lights_idx > MAX_POINT_LIGHTS) { + std::cerr << "WARNING: can't fit all lights in intro cutscene world.\n"; + } + } + + for (int i = lights_idx; i < MAX_POINT_LIGHTS; i++) { + this->lights[i] = {}; // make sure this is initialized, idk if this is needed but just in case + // because i dont trust c++ to do anything ever + } + + auto evt = LoadIntroCutsceneEvent(updates.at(0), this->pov_eid, this->dm_eid, this->lights); + + return evt; +} diff --git a/src/server/game/item.cpp b/src/server/game/item.cpp index 2790451f..8b82618c 100644 --- a/src/server/game/item.cpp +++ b/src/server/game/item.cpp @@ -2,10 +2,11 @@ #include "shared/game/sharedobject.hpp" #include "server/game/servergamestate.hpp" #include "server/game/objectmanager.hpp" +#include "shared/audio/constants.hpp" /* Constructors and Destructors */ Item::Item(ObjectType type, bool movable, glm::vec3 corner, ModelType model, glm::vec3 dimensions): - Object(type, Physics(movable, Collider::Box, corner, dimensions), ModelType::Cube), + Object(type, Physics(movable, Collider::Box, corner, dimensions), ModelType::Chest), iteminfo(SharedItemInfo{ .held = false, .used = false }) {} @@ -27,10 +28,18 @@ void Item::dropItem(Object* other, ServerGameState& state, int itemSelected, flo this->iteminfo.held = false; this->physics.collider = Collider::Box; - state.objects.moveObject(this, (player->physics.shared.corner + (player->physics.shared.facing * dropDistance)) * glm::vec3(1.0f, 0.0f, 1.0f)); + state.objects.moveObject(this, (player->physics.shared.getCenterPosition() + (player->physics.shared.facing * dropDistance)) * glm::vec3(1.0f, 0.0f, 1.0f)); player->inventory[itemSelected] = -1; player->sharedInventory.inventory[itemSelected] = ModelType::Frame; + + state.soundTable().addNewSoundSource(SoundSource( + ServerSFX::ItemDrop, + other->physics.shared.getCenterPosition(), + DEFAULT_VOLUME, + SHORT_DIST, + SHORT_ATTEN + )); } void Item::doCollision(Object* other, ServerGameState& state) { @@ -51,6 +60,18 @@ void Item::doCollision(Object* other, ServerGameState& state) { if (pickedUp) { this->iteminfo.held = true; this->physics.collider = Collider::None; + state.soundTable().addNewSoundSource(SoundSource( + ServerSFX::ItemPickUp, + other->physics.shared.getCenterPosition(), + DEFAULT_VOLUME, + SHORT_DIST, + SHORT_ATTEN + )); + + // update cell type in game state to empty + GridCell* cell = state.getGrid().getCell(this->physics.shared.corner.x / Grid::grid_cell_width, this->physics.shared.corner.z / Grid::grid_cell_width); + + cell->type = CellType::Empty; } } diff --git a/src/server/game/lava.cpp b/src/server/game/lava.cpp new file mode 100644 index 00000000..d1086758 --- /dev/null +++ b/src/server/game/lava.cpp @@ -0,0 +1,44 @@ +#include "server/game/lava.hpp" +#include "server/game/object.hpp" +#include "shared/game/point_light.hpp" +#include "shared/game/sharedmodel.hpp" + +const int Lava::DAMAGE = 1; + +Lava::Lava(glm::vec3 corner, ModelType model_type, float grid_width, const PointLightProperties& light_properties): + Trap(ObjectType::Lava, false, corner, Collider::Box, model_type, glm::vec3(0.0f)), + light_properties(light_properties) +{ +} + +bool Lava::shouldTrigger(ServerGameState& state) { + return false; +} + +bool Lava::shouldReset(ServerGameState& state) { + return false; +} + +void Lava::doCollision(Object* obj, ServerGameState& state) { + if (this->info.dm_hover) { + return; + } + + auto creature = dynamic_cast(obj); + if (creature == nullptr) return; + + creature->stats.health.decrease(DAMAGE); +} + +SharedObject Lava::toShared() { + auto so = Object::toShared(); + so.pointLightInfo = SharedPointLightInfo { + .intensity = 1.0f, + .ambient_color = this->light_properties.ambient_color, + .diffuse_color = this->light_properties.diffuse_color, + .specular_color = this->light_properties.specular_color, + .attenuation_linear = this->light_properties.attenuation_linear, + .attenuation_quadratic = this->light_properties.attenuation_quadratic + }; + return so; +} diff --git a/src/server/game/mazegenerator.cpp b/src/server/game/mazegenerator.cpp index 23fb2c71..794fce7b 100644 --- a/src/server/game/mazegenerator.cpp +++ b/src/server/game/mazegenerator.cpp @@ -84,7 +84,6 @@ std::optional MazeGenerator::generate() { this->frontier.pop(); if (_num_rooms_placed == REQUIRED_NUM_ROOMS - 2) { - std::cout << "placing exit\n"; // optimize the placing of the exit and orb to make the exit as far away from the entrance as possible // essentially go through the frontier and throw out everything but the farthest away // from the spawn @@ -103,7 +102,7 @@ std::optional MazeGenerator::generate() { coord = other_coord; required_entryway = other_req_entry; } else { - this->frontier.push({other_coord, required_entryway}); + this->frontier.push({other_coord, other_req_entry}); } } exit_coord_f = coord; @@ -112,7 +111,6 @@ std::optional MazeGenerator::generate() { } if (_num_rooms_placed == REQUIRED_NUM_ROOMS - 1) { - std::cout << "placing orb\n"; // go through frontier, and get coord and required entry to be as far from exit_coord as possible int size = this->frontier.size(); int count = 0; @@ -129,7 +127,7 @@ std::optional MazeGenerator::generate() { coord = other_coord; required_entryway = other_req_entry; } else { - this->frontier.push({other_coord, required_entryway}); + this->frontier.push({other_coord, other_req_entry}); } } } @@ -142,6 +140,8 @@ std::optional MazeGenerator::generate() { auto room = _pullRoomByPolicy(); if ( (room->rclass.entries & required_entryway) == 0) { + // we don't want to skip this room type, so put back at the front of the frontier + _policy.push_front(room->rclass.type); continue; // can't connect, so pull again } @@ -170,10 +170,31 @@ std::optional MazeGenerator::generate() { } _placeRoom(room, origin_coord.value()); + + // stupid extra complicated logic about keeping track of the "number" of rooms + // could refactor this or just keep slapping bandaids on everything + if (_num_rooms_placed >= REQUIRED_NUM_ROOMS - 2) { + _num_rooms_placed++; // always transition to placing orb , which is + // signified by _num_rooms_placed being at REQUIRED_NUM_ROOMS - 1 + } else { + if (room->rclass.size == RoomSize::_10x10) { + _num_rooms_placed++; + } else if (room->rclass.size == RoomSize::_20x20) { + _num_rooms_placed += 4; + } else if (room->rclass.size == RoomSize::_40x40) { + _num_rooms_placed += 16; + } + + if (_num_rooms_placed > REQUIRED_NUM_ROOMS - 2) { + // mark ready to place exit, since we met the quota + _num_rooms_placed = REQUIRED_NUM_ROOMS - 2; + } + } + + break; } - _num_rooms_placed++; } std::cout << "Done: created " << _num_rooms_placed << " rooms.\n"; @@ -379,7 +400,6 @@ void MazeGenerator::_loadRoom(boost::filesystem::path path, bool procedural) { rclass.type = this->_getRoomType(path); rclass.size = size; - if (procedural) { this->_validateRoom(grid, rclass); } @@ -585,9 +605,22 @@ std::shared_ptr MazeGenerator::_pullRoomByPolicy() { } std::shared_ptr MazeGenerator::_pullRoomByType(RoomType type) { - int random_index = randomInt(0, this->rooms_by_type.at(type).size() - 1); + std::shared_ptr room = nullptr; + while (true) { + int random_index = randomInt(0, this->rooms_by_type.at(type).size() - 1); + room = this->rooms_by_type.at(type).at(random_index); + if (!this->used_room_ids.contains(room->id)) { + // keep going until we find a new room we haven't placed yet + break; + } + } + + if (room->rclass.size == RoomSize::_20x20 || room->rclass.size == RoomSize::_40x40) { + // don't place the same 20x20 or 40x40 rooms in the same maze twice + this->used_room_ids.insert(room->id); + } - return this->rooms_by_type.at(type).at(random_index); + return room; } std::vector MazeGenerator::_getRoomCoordsTakenBy(RoomSize size, glm::ivec2 top_left) { @@ -637,12 +670,12 @@ std::vector> MazeGenerator::_getAdjRoomCoords(s if ((room->rclass.entries & RoomEntry::T) != 0) { adj_coords.push_back({origin_coord + glm::ivec2(0, -1), RoomEntry::B}); // need bottom entry for whatever would be placed here if (room->rclass.size == RoomSize::_20x20) { - adj_coords.push_back({origin_coord + glm::ivec2(1, 1), RoomEntry::B}); + adj_coords.push_back({origin_coord + glm::ivec2(1, -1), RoomEntry::B}); } else if (room->rclass.size == RoomSize::_40x40) { - adj_coords.push_back({origin_coord + glm::ivec2(1, 1), RoomEntry::B}); + adj_coords.push_back({origin_coord + glm::ivec2(1, -1), RoomEntry::B}); - adj_coords.push_back({origin_coord + glm::ivec2(2, 1), RoomEntry::B}); - adj_coords.push_back({origin_coord + glm::ivec2(3, 1), RoomEntry::B}); + adj_coords.push_back({origin_coord + glm::ivec2(2, -1), RoomEntry::B}); + adj_coords.push_back({origin_coord + glm::ivec2(3, -1), RoomEntry::B}); } } @@ -773,7 +806,7 @@ void MazeGenerator::_generatePolicy() { using RatioMapping = const std::pair; - RatioMapping NUM_EASY = {7, RoomType::EASY}; // X easy + RatioMapping NUM_EASY = {15, RoomType::EASY}; // X easy RatioMapping NUM_MEDIUM = {5, RoomType::MEDIUM}; // for every Y mediums RatioMapping NUM_HARD = {3, RoomType::HARD}; // for every Z hards RatioMapping NUM_LOOT = {1, RoomType::LOOT}; // for every alpha loots diff --git a/src/server/game/minotaur.cpp b/src/server/game/minotaur.cpp new file mode 100644 index 00000000..6ce2be26 --- /dev/null +++ b/src/server/game/minotaur.cpp @@ -0,0 +1,103 @@ +#include "server/game/minotaur.hpp" +#include "server/game/enemy.hpp" +#include "server/game/servergamestate.hpp" +#include "server/game/weapon.hpp" +#include "shared/audio/constants.hpp" +#include "shared/utilities/rng.hpp" + +Minotaur::Minotaur(glm::vec3 corner, glm::vec3 facing) : + Enemy(corner, facing, ObjectType::Minotaur, ModelType::WarrenBear, SharedStats( + Stat(0, 50, 50), + Stat(0, 7, 3) + )) +{ + this->last_charge_time = std::chrono::system_clock::now(); + this->chargeDelay = 8; + this->chargeDuration = 3; + this->stopped = false; + + this->physics.velocityMultiplier.y = 0.2; + this->physics.velocityMultiplier.x = 0.3; + this->physics.velocityMultiplier.z = 0.3; +} + +bool Minotaur::doBehavior(ServerGameState& state) { + auto now = std::chrono::system_clock::now(); + std::chrono::duration elapsed_seconds{ now - this->last_charge_time }; + + if (elapsed_seconds > std::chrono::seconds(this->chargeDelay)) { + auto players = state.objects.getPlayers(); + float closest_dist = std::numeric_limits::max(); + Player* target = nullptr; + for (int p = 0; p < players.size(); p++) { + auto player = players.get(p); + if (player == nullptr) continue; + if (!player->canBeTargetted()) continue; + + float distance_to_player = glm::distance(this->physics.shared.corner, player->physics.shared.corner); + if (distance_to_player < closest_dist) { + closest_dist = distance_to_player; + target = player; + } + } + + if (closest_dist < Grid::grid_cell_width * Minotaur::SIGHT_LIMIT_GRID_CELLS) { + this->physics.shared.facing = glm::normalize( + target->physics.shared.getCenterPosition() - this->physics.shared.getCenterPosition() + ); + } + else { + this->physics.shared.facing = glm::normalize(glm::vec3( + randomDouble(-1, 1), randomDouble(-1, 1), randomDouble(-1, 1) + )); + } + + this->physics.velocity.x = 1.5f * this->physics.shared.facing.x; + this->physics.velocity.z = 1.5f * this->physics.shared.facing.z; + this->last_charge_time = now; + this->stopped = false; + + state.soundTable().addNewSoundSource(SoundSource( + ServerSFX::Minotaur, + this->physics.shared.getCenterPosition(), + DEFAULT_VOLUME, + MEDIUM_DIST, + MEDIUM_ATTEN + )); + + return true; + } + else if (elapsed_seconds > std::chrono::seconds(this->chargeDuration) && !this->stopped) { + this->physics.velocity.x = 0; + this->physics.velocity.z = 0; + this->stopped = true; + return true; + } + + return false; +} + +void Minotaur::doCollision(Object* other, ServerGameState& state) { + Creature* creature = dynamic_cast(other); + if (creature == nullptr) return; + + if (creature->type == ObjectType::Player) { + creature->stats.health.decrease(3); + auto knockback = glm::normalize( + other->physics.shared.getCenterPosition() - this->physics.shared.getCenterPosition()); + knockback.y = 0; + creature->physics.currTickVelocity = 0.7f * knockback; + } +} + +bool Minotaur::doDeath(ServerGameState& state) { + Enemy::doDeath(state); + + // Drop health potion upon death + auto newCorner = this->physics.shared.corner; + newCorner.y *= 0; + if (randomInt(1, 4) == 4) { + state.objects.createObject(new Weapon(newCorner, glm::vec3(1), WeaponType::Hammer)); + } + return true; +} \ No newline at end of file diff --git a/src/server/game/mirror.cpp b/src/server/game/mirror.cpp new file mode 100644 index 00000000..5f880118 --- /dev/null +++ b/src/server/game/mirror.cpp @@ -0,0 +1,78 @@ +#include "server/game/mirror.hpp" +#include "server/game/constants.hpp" + +Mirror::Mirror(glm::vec3 corner, glm::vec3 dimensions) + : Item(ObjectType::Mirror, false, corner, ModelType::Cube, dimensions) { + this->modelType = ModelType::Mirror; + this->used_player = nullptr; +} + +void Mirror::useItem(Object* other, ServerGameState& state, int itemSelected) { + // Get Player object that used the mirror + this->used_player = state.objects.getPlayer(other->typeID); + + // Get use time + this->used_time = std::chrono::system_clock::now(); + + auto now = std::chrono::system_clock::now(); + std::chrono::duration elapsed_seconds{ this->used_time - now }; + this->iteminfo.remaining_time = (double)(MIRROR_USE_DURATION) - elapsed_seconds.count(); + + // Set used to true (since mirror is being used) + this->iteminfo.used = true; + + // Add mirror to used items map + this->used_player->sharedInventory.usedItems.insert( + { this->typeID, + { + ModelType::Mirror, + this->iteminfo.remaining_time + } + }); + + // Don't call Item::useItem() to avoid consuming the mirror +} + +void Mirror::dropItem(Object* other, ServerGameState& state, int itemSelected, float dropDistance) { + // Stop mirror's effect (returns immediately if mirror wasn't used) + this->revertEffect(state); + + Item::dropItem(other, state, itemSelected, dropDistance); +} + +bool Mirror::timeOut() { + // Determine whether mirror use has timed out + auto now = std::chrono::system_clock::now(); + + std::chrono::duration elapsed_seconds{ now - this->used_time }; + + // Update remaining time + this->iteminfo.remaining_time = + (double)MIRROR_USE_DURATION - elapsed_seconds.count(); + + // Update used items' remaining time + if (this->used_player->sharedInventory.usedItems.find(this->typeID) + != this->used_player->sharedInventory.usedItems.end()) { + this->used_player->sharedInventory.usedItems[this->typeID].second + = this->iteminfo.remaining_time; + } + + // Return true if duration has elapsed + return elapsed_seconds.count() > MIRROR_USE_DURATION; +} + +void Mirror::revertEffect(ServerGameState& state) { + if (!this->iteminfo.used) { + return; + } + + // Mark as no longer used (but still held) + this->iteminfo.used = false; + + // Update used items list to mark mirror as no longer used + auto it = this->used_player->sharedInventory.usedItems.find(this->typeID); + + if (it != this->used_player->sharedInventory.usedItems.end()) { + it = this->used_player->sharedInventory.usedItems.erase(it); + } +} \ No newline at end of file diff --git a/src/server/game/object.cpp b/src/server/game/object.cpp index 259800b5..a421c11d 100644 --- a/src/server/game/object.cpp +++ b/src/server/game/object.cpp @@ -2,6 +2,7 @@ #include "server/game/constants.hpp" #include "shared/game/sharedmodel.hpp" #include "shared/game/sharedobject.hpp" +#include "shared/game/constants.hpp" /* Constructors and Destructors */ @@ -12,6 +13,8 @@ Object::Object(ObjectType type, Physics physics, ModelType modelType): this->type = type; this->setModel(modelType); this->distance_moved = 0.0f; + this->animState = AnimState::IdleAnim; + this->is_sprinting = false; } Object::~Object() {} @@ -38,9 +41,26 @@ std::unordered_map Object::models ({ // can't move around anywhere. we should eventually solve this // by tucking in the player's arms since right now they're // spread out in the model - {ModelType::Player, (FIRE_PLAYER_DIMENSIONS / 4.0f)}, + {ModelType::PlayerFire, {FIRE_PLAYER_DIMENSIONS * PLAYER_BBOX_SCALE}}, + {ModelType::PlayerLightning, {LIGHTNING_PLAYER_DIMENSIONS * PLAYER_BBOX_SCALE}}, + {ModelType::PlayerWater, {WATER_PLAYER_DIMENSIONS * PLAYER_BBOX_SCALE}}, {ModelType::WarrenBear, (BEAR_DIMENSIONS / 4.0f)}, - {ModelType::Torchlight, glm::vec3(1.0f)} + {ModelType::Torchlight, glm::vec3(1.0f)}, + {ModelType::SunGod, (SUNGOD_DIMENSIONS / 2.0f)}, + {ModelType::Arrow, glm::vec3(0.5f, 0.5f, 2.0f)}, + {ModelType::ArrowTrap, (ARROW_TRAP_DIMENSIONS * 1.2f)}, + {ModelType::LavaCross, LAVA_DIMENSIONS}, + {ModelType::LavaHorizontal, LAVA_DIMENSIONS}, + {ModelType::LavaVertical, LAVA_DIMENSIONS}, + {ModelType::FloorSpikeFull, FLOOR_SPIKE_DIMENSIONS}, + {ModelType::FloorSpikeHorizontal, FLOOR_SPIKE_DIMENSIONS}, + {ModelType::FloorSpikeVertical, FLOOR_SPIKE_DIMENSIONS}, + {ModelType::Lightning, glm::vec3(3.0f, 100.0f, 3.0f)}, + {ModelType::Orb, glm::vec3(0.887116, 0.941508, 0.950092)}, + {ModelType::Chest, glm::vec3(1.377020, 1.355794, 1.092905)}, + {ModelType::Fireball, glm::vec3(0.4f, 0.4f, 0.4f)}, + {ModelType::SpellOrb, glm::vec3(0.4f, 0.4f, 0.4f)}, + {ModelType::TeleporterTrap, glm::vec3(1.0f, 3.0f, 1.0f)} }); /* SharedGameState generation */ @@ -51,6 +71,7 @@ SharedObject Object::toShared() { shared.type = this->type; shared.physics = this->physics.shared; shared.modelType = this->modelType; + shared.animState = this->animState; return shared; } @@ -93,4 +114,17 @@ std::string Physics::to_string(unsigned int tab_offset) { representation += tabs + "}"; return representation; +} + +glm::vec3 directionToFacing(const Direction& dir) { + switch (dir) { + case Direction::LEFT: + return glm::vec3(-1.0f, 0.0f, 0.0f); + case Direction::RIGHT: + return glm::vec3(1.0f, 0.0f, 0.0f); + case Direction::UP: + return glm::vec3(0.0f, 0.0f, -1.0f); + case Direction::DOWN: + return glm::vec3(0.0f, 0.0f, 1.0f); + } } \ No newline at end of file diff --git a/src/server/game/objectmanager.cpp b/src/server/game/objectmanager.cpp index 1eb6191a..ee3d7953 100644 --- a/src/server/game/objectmanager.cpp +++ b/src/server/game/objectmanager.cpp @@ -10,6 +10,7 @@ #include "server/game/spell.hpp" #include "server/game/weaponcollider.hpp" #include "server/game/weapon.hpp" +#include "server/game/mirror.hpp" #include "shared/utilities/rng.hpp" #include @@ -23,6 +24,7 @@ ObjectManager::ObjectManager() { // cppcheck-suppress uninitMemberVar //// Initialize type-specific SmartVectors //this->base_objects = SmartVector(); //this->items = SmartVector(); + this->dm = nullptr; } ObjectManager::~ObjectManager() { @@ -32,11 +34,25 @@ ObjectManager::~ObjectManager() { /* Object CRUD methods */ SpecificID ObjectManager::createObject(Object* object) { + return this->_createObject(object); +} + +SpecificID ObjectManager::_createObject(Object* object, boost::optional id) { // Create a new object with the given type - EntityID globalID = this->objects.push(object); + EntityID globalID; + + // If an EntityID is specified, put object at the specific EntityID + if (id.has_value()) { + globalID = id.get(); + this->objects.set(object, globalID); + } else { + globalID = this->objects.push(object); + } + object->globalID = globalID; object->gridCellPositions = this->objectGridCells(object); + for (auto pos : object->gridCellPositions) { if (!this->cellToObjects.contains(pos)) { this->cellToObjects.insert({pos, std::vector()}); @@ -54,10 +70,14 @@ SpecificID ObjectManager::createObject(Object* object) { case ObjectType::FakeWall: case ObjectType::SpikeTrap: case ObjectType::FloorSpike: + case ObjectType::Lava: case ObjectType::ArrowTrap: case ObjectType::TeleporterTrap: object->typeID = this->traps.push(dynamic_cast(object)); break; + case ObjectType::Item: + object->typeID = this->items.push(dynamic_cast(object)); + break; case ObjectType::Weapon: object->typeID = this->items.push(dynamic_cast(object)); break; @@ -80,6 +100,8 @@ SpecificID ObjectManager::createObject(Object* object) { case ObjectType::Player: object->typeID = this->players.push(dynamic_cast(object)); break; + case ObjectType::Python: + case ObjectType::Minotaur: case ObjectType::Slime: object->typeID = this->enemies.push(dynamic_cast(object)); break; @@ -89,6 +111,9 @@ SpecificID ObjectManager::createObject(Object* object) { case ObjectType::Orb: object->typeID = this->items.push(dynamic_cast(object)); break; + case ObjectType::Mirror: + object->typeID = this->items.push(dynamic_cast(object)); + break; default: std::cerr << "FATAL: invalid object type being created: " << static_cast(object->type) << "\nDid you remember to add a new switch statement to ObjectManager::createObject?\n"; @@ -110,6 +135,10 @@ bool ObjectManager::removeObject(EntityID globalID) { // Check that the given object exists Object* object = this->objects.get(globalID); + // DEBUG + //std::cout << "Object to be deleted: " << object->to_string() << std::endl; + // DEBUG + if (object == nullptr) { // Object with the given index doesn't exist std::cout << "obj doesn't exist? in ObjectManager::removeObject" << std::endl; @@ -130,29 +159,31 @@ bool ObjectManager::removeObject(EntityID globalID) { case ObjectType::FakeWall: case ObjectType::SpikeTrap: case ObjectType::FloorSpike: + case ObjectType::Lava: case ObjectType::ArrowTrap: case ObjectType::TeleporterTrap: this->traps.remove(object->typeID); break; - case ObjectType::Item: - this->items.remove(object->typeID); - break; case ObjectType::Player: this->players.remove(object->typeID); break; case ObjectType::Projectile: this->projectiles.remove(object->typeID); break; + case ObjectType::Python: + case ObjectType::Minotaur: case ObjectType::Slime: this->enemies.remove(object->typeID); break; case ObjectType::WeaponCollider: this->weaponColliders.remove(object->typeID); break; + case ObjectType::Item: case ObjectType::Weapon: case ObjectType::Spell: case ObjectType::Potion: case ObjectType::Orb: + case ObjectType::Mirror: this->items.remove(object->typeID); break; case ObjectType::Exit: @@ -215,6 +246,24 @@ bool ObjectManager::removeObject(Object** object_dbl_ptr) { return false; } +bool ObjectManager::replaceObject(EntityID id, Object* object) { + // Get current object + Object* currentObject = this->getObject(id); + + if (currentObject == nullptr) { + // No object with the given EntityID exists; can't replace + return false; + } + + // Remove current object + this->removeObject(id); + + // Add new object at the given EntityID + this->_createObject(object, id); + + return true; +} + Object* ObjectManager::getObject(EntityID globalID) { return this->objects.get(globalID); } diff --git a/src/server/game/orb.cpp b/src/server/game/orb.cpp index 861029d0..76834160 100644 --- a/src/server/game/orb.cpp +++ b/src/server/game/orb.cpp @@ -1,44 +1,110 @@ #include "server/game/orb.hpp" #include "server/game/constants.hpp" +#include "shared/game/point_light.hpp" #include "shared/game/sharedobject.hpp" -#include #include "server/game/exit.hpp" +#include "shared/audio/constants.hpp" +#include -Orb::Orb(glm::vec3 corner, glm::vec3 dimensions) : Item(ObjectType::Orb, true, corner, ModelType::Cube, dimensions) { +Orb::Orb(glm::vec3 corner, glm::vec3 dimensions, const PointLightProperties& properties) : Item(ObjectType::Orb, true, corner, ModelType::Orb, dimensions), + properties(properties) { this->modelType = ModelType::Orb; } void Orb::doCollision(Object* other, ServerGameState& state) { - // Player can pick up the orb - Item::doCollision(other, state); - - // If the other object is a Player, then the Player picks up the Orb - if (other->type == ObjectType::Player) { - Player* player = state.objects.getPlayer(other->typeID); - - player->sharedInventory.hasOrb = true; - // update match phase to MatchPhase::RelayRace - state.transitionToRelayRace(); - } else if (other->type == ObjectType::Exit) { + if (other->type == ObjectType::Exit) { state.setPlayerVictory(true); state.setPhase(GamePhase::RESULTS); } + + auto player = dynamic_cast(other); + if (player == nullptr) return; // only allow players to pick up items + + bool pickedUp = false; + for (int i = 0; i < player->inventory.size(); i++) { + if (player->inventory[i] != -1) { continue; } + + player->inventory[i] = this->typeID; + player->sharedInventory.inventory[i] = this->modelType; + pickedUp = true; + break; + } + + if (pickedUp) { + player->sharedInventory.hasOrb = true; + this->iteminfo.held = true; + this->physics.collider = Collider::None; + state.transitionToRelayRace(); + + state.soundTable().addNewSoundSource(SoundSource( + ServerSFX::ItemPickUp, + other->physics.shared.getCenterPosition(), + DEFAULT_VOLUME, + SHORT_DIST, + SHORT_ATTEN + )); + } } -// TODO: MAY NOT BE NEEDED AT ALL void Orb::useItem(Object* other, ServerGameState& state, int itemSelected) { auto player = dynamic_cast(other); - dropItem(other, state, itemSelected, 0.0f); + player->sharedInventory.hasOrb = false; + Item::dropItem(other, state, itemSelected, 3.0f); this->physics.velocity = 0.8f * glm::normalize(other->physics.shared.facing); state.objects.moveObject(this, this->physics.shared.corner + glm::vec3(0.0f, 3.0f, 0.0f)); + + // check to make sure that not colliding with anything + // lazy copy paste with below... keep in sync + auto grid_cells = state.objects.objectGridCells(this); + for (glm::ivec2 grid_cell : grid_cells) { + auto potential_collision_objects = state.objects.cellToObjects.at(grid_cell); + for (Object* obj : potential_collision_objects) { + if (obj->type != ObjectType::Orb && obj->type != ObjectType::Player && detectCollision(this->physics, obj->physics)) { // cppcheck-suppress useStlAlgorithm + // go back in the inventory b/c inside a wall or something + this->doCollision(player, state); // get picked up by the player again + return; + } + } + } } void Orb::dropItem(Object* other, ServerGameState& state, int itemSelected, float dropDistance) { - Item::dropItem(other, state, itemSelected, 3.0f); - - // Player dropped the orb - Player* player = state.objects.getPlayer(other->typeID); + auto player = dynamic_cast(other); + Item::dropItem(other, state, itemSelected, dropDistance); player->sharedInventory.hasOrb = false; + + if (dropDistance == 0.0f) { + return; + } + + // make sure isn't in wall + // lazy copy paste with above... keep in sync + auto grid_cells = state.objects.objectGridCells(this); + for (glm::ivec2 grid_cell : grid_cells) { + auto potential_collision_objects = state.objects.cellToObjects.at(grid_cell); + for (Object* obj : potential_collision_objects) { + if (obj->type != ObjectType::Orb && obj->type != ObjectType::Player && detectCollision(this->physics, obj->physics)) { // cppcheck-suppress useStlAlgorithm + // go back in the inventory b/c inside a wall or something + this->doCollision(player, state); // get picked up by the player again + return; + } + } + } +} + +/* SharedGameState generation */ +SharedObject Orb::toShared() { + auto so = Object::toShared(); + so.iteminfo = this->iteminfo; + so.pointLightInfo = SharedPointLightInfo { + .intensity = 1.0f, + .ambient_color = this->properties.ambient_color, + .diffuse_color = this->properties.diffuse_color, + .specular_color = this->properties.specular_color, + .attenuation_linear = this->properties.attenuation_linear, + .attenuation_quadratic = this->properties.attenuation_quadratic + }; + return so; } \ No newline at end of file diff --git a/src/server/game/player.cpp b/src/server/game/player.cpp index 5c2a83d9..a816c3d3 100644 --- a/src/server/game/player.cpp +++ b/src/server/game/player.cpp @@ -9,26 +9,69 @@ SharedObject Player::toShared() { auto so = Creature::toShared(); so.playerInfo = this->info; so.inventoryInfo = this->sharedInventory; + so.compass = this->compass; return so; } Player::Player(glm::vec3 corner, glm::vec3 facing): - Creature(ObjectType::Player, corner, facing, ModelType::Player, SharedStats( + Creature(ObjectType::Player, corner, facing, ModelType::PlayerFire, SharedStats( Stat(0, 100, 100), Stat(0, 10, 5) )), sharedInventory(SharedInventory { .selected = 1, .inventory_size = INVENTORY_SIZE, .inventory = std::vector(INVENTORY_SIZE, ModelType::Frame), - .usesRemaining = std::vector(INVENTORY_SIZE, 0) }) + .usesRemaining = std::vector(INVENTORY_SIZE, 0), .hasOrb = false }), + compass(SharedCompass { .angle = 0 }) { this->info.is_alive = true; this->info.respawn_time = NULL; this->info.render = true; + this->info.used_mirror_to_reflect_lightning = false; // initialize inventory as empty this->inventory = std::vector(INVENTORY_SIZE, -1); + + // Player lightning invulnerability is initially false + this->invulnerableToLightning = false; + + // Set invulnerability duration to -1 (this will be overwritten when the player + // actually gains lightning invulnerability using setInvulnerableToLightning()) + this->lightningInvulnerabilityDuration = -1; + + // Set invulnerability time to right now (this will be overwritten when the player + // actually gains lightning invulnerability using setInvulnerableToLightning()) + this->lightning_invulnerability_start_time = std::chrono::system_clock::now(); } Player::~Player() { +} + +bool Player::canBeTargetted() const { + // cannot be seen / targetted if the player is dead or invisible + return this->info.is_alive && this->info.render; +} + +void Player::setInvulnerableToLightning(bool isInvulnerable, double duration) { + if (isInvulnerable) { + // Player is now invulnerable to lightning - remember timestamp and set + // lightning invulnerability duration + this->lightning_invulnerability_start_time = std::chrono::system_clock::now(); + + this->lightningInvulnerabilityDuration = duration; + } + + this->invulnerableToLightning = isInvulnerable; +} + +bool Player::isInvulnerableToLightning() const { + return this->invulnerableToLightning; +} + +double Player::getLightningInvulnerabilityDuration() const { + return this->lightningInvulnerabilityDuration; +} + +std::chrono::time_point Player::getLightningInvulnerabilityStartTime() const { + return this->lightning_invulnerability_start_time; } \ No newline at end of file diff --git a/src/server/game/potion.cpp b/src/server/game/potion.cpp index ecbdbc54..42e70709 100644 --- a/src/server/game/potion.cpp +++ b/src/server/game/potion.cpp @@ -3,6 +3,7 @@ #include "server/game/potion.hpp" #include "server/game/constants.hpp" #include "shared/game/sharedobject.hpp" +#include "shared/audio/constants.hpp" #include class Player; @@ -90,6 +91,15 @@ void Potion::useItem(Object* other, ServerGameState& state, int itemSelected) { } } + // Play sound + state.soundTable().addNewSoundSource(SoundSource( + ServerSFX::Potion, + this->usedPlayer->physics.shared.getCenterPosition(), + DEFAULT_VOLUME, + SHORT_DIST, + SHORT_ATTEN + )); + this->iteminfo.used = true; this->iteminfo.held = false; diff --git a/src/server/game/projectile.cpp b/src/server/game/projectile.cpp index d76d1607..9eb64d86 100644 --- a/src/server/game/projectile.cpp +++ b/src/server/game/projectile.cpp @@ -3,6 +3,7 @@ #include "server/game/servergamestate.hpp" #include "server/game/spell.hpp" #include "shared/audio/constants.hpp" +#include "shared/game/sharedmodel.hpp" #include @@ -16,9 +17,22 @@ Projectile::Projectile(glm::vec3 corner, glm::vec3 facing, } bool Projectile::doTick(ServerGameState& state) { - if (!this->opt.homing) return false; + if (this->physics.shared.corner.y == 0.0f) { + state.markForDeletion(this->globalID); + } + + if (!this->opt.homing) { + return false; + } + this->opt.homing_duration--; + if (this->opt.homing_duration <= 0) { + return false; + } + Object* target = state.objects.getObject(*this->opt.target); - if (target == nullptr) return false; + if (target == nullptr) { + return false; + } auto pos_to_go_to = target->physics.shared.getCenterPosition(); auto dir_to_target = glm::normalize(pos_to_go_to - this->physics.shared.getCenterPosition()); @@ -56,6 +70,12 @@ void Projectile::doCollision(Object* other, ServerGameState& state) { //handle cases for spell projectiles else { SpellOrb* orb = dynamic_cast(this); + + if (orb == nullptr) { + std::cout << "Just saved us from a seg fault 6/7/2024 1:07am BRUHHHHHHHHHHHHHhhhhhhhhh\n"; + return; + } + switch (orb->sType) { case SpellType::Fireball: { // do damage if creature @@ -87,4 +107,50 @@ void Projectile::doCollision(Object* other, ServerGameState& state) { } } } +} + +/* SharedGameState generation */ +SharedObject Projectile::toShared() { + auto so = Object::toShared(); + if (so.modelType == ModelType::Fireball) { + so.pointLightInfo = SharedPointLightInfo { + .intensity = 1.0f, + .ambient_color = glm::vec3(0.72f, 0.14f, 0.01f), + .diffuse_color = glm::vec3(1.0f, 0.5f, 0.03f), + .specular_color = glm::vec3(0.1f, 0.1f, 0.1f), + .attenuation_linear = 0.35f, + .attenuation_quadratic = 0.44f + }; + } else if (so.modelType == ModelType::SpellOrb) { + SpellOrb* sOrb = dynamic_cast(this); + if (sOrb->sType == SpellType::Fireball) { + so.pointLightInfo = SharedPointLightInfo { + .intensity = 1.0f, + .ambient_color = glm::vec3(0.55f, 0.05f, 0.67f), + .diffuse_color = glm::vec3(0.65f, 0.12f, 0.75f), + .specular_color = glm::vec3(0.1f, 0.1f, 0.1f), + .attenuation_linear = 0.35f, + .attenuation_quadratic = 0.44f + }; + } else if (sOrb->sType == SpellType::HealOrb) { + so.pointLightInfo = SharedPointLightInfo { + .intensity = 1.0f, + .ambient_color = glm::vec3(0.0, 0.72f, 0.14f), + .diffuse_color = glm::vec3(0.12f, 1.0f, 0.35f), + .specular_color = glm::vec3(0.1f, 0.1f, 0.1f), + .attenuation_linear = 0.35f, + .attenuation_quadratic = 0.44f + }; + } + } else { + so.pointLightInfo = SharedPointLightInfo { + .intensity = 0.5f, + .ambient_color = glm::vec3(1.0f, 1.0f, 1.0f), + .diffuse_color = glm::vec3(1.0f, 1.0f, 1.0f), + .specular_color = glm::vec3(0.1f, 0.1f, 0.1f), + .attenuation_linear = 0.35f, + .attenuation_quadratic = 0.44f + }; + } + return so; } \ No newline at end of file diff --git a/src/server/game/python.cpp b/src/server/game/python.cpp new file mode 100644 index 00000000..149c6091 --- /dev/null +++ b/src/server/game/python.cpp @@ -0,0 +1,130 @@ +#include "server/game/python.hpp" +#include "server/game/enemy.hpp" +#include "server/game/servergamestate.hpp" +#include "server/game/potion.hpp" +#include "shared/audio/constants.hpp" +#include "shared/utilities/rng.hpp" + +Python::Python(glm::vec3 corner, glm::vec3 facing) : + Enemy(corner, facing, ObjectType::Python, ModelType::Cube, SharedStats( + Stat(0, 40, 40), + Stat(0, 5, 2) + )) +{ + this->last_move_time = std::chrono::system_clock::now(); + this->moveDelay = 3; + this->moveDuration = 1; + this->diagonal = false; + this->stopped = false; + + this->physics.velocityMultiplier.y = 0.3; + this->physics.velocityMultiplier.x = 0.4; + this->physics.velocityMultiplier.z = 0.4; + this->physics.shared.dimensions = glm::vec3(2.0f, 3.0f, 2.0f); +} + +bool Python::doBehavior(ServerGameState& state) { + auto now = std::chrono::system_clock::now(); + std::chrono::duration elapsed_seconds{ now - this->last_move_time }; + + if (elapsed_seconds > std::chrono::seconds(this->moveDelay)) { + auto players = state.objects.getPlayers(); + float closest_dist = std::numeric_limits::max(); + Player* target = nullptr; + for (int p = 0; p < players.size(); p++) { + auto player = players.get(p); + if (player == nullptr) continue; + if (!player->canBeTargetted()) continue; + + float distance_to_player = glm::distance(this->physics.shared.corner, player->physics.shared.corner); + if (distance_to_player < closest_dist) { + closest_dist = distance_to_player; + target = player; + } + } + + if (closest_dist < Grid::grid_cell_width * Python::SIGHT_LIMIT_GRID_CELLS) { + this->physics.shared.facing = glm::normalize( + target->physics.shared.getCenterPosition() - this->physics.shared.getCenterPosition() + ); + } + else { + this->physics.shared.facing = glm::normalize(glm::vec3( + randomDouble(-0.5, 0.5), 0, randomDouble(-0.5, 0.5) + )); + } + + if (this->diagonal) { + if (randomInt(0, 1) == 0) { + this->physics.velocity.x = (this->physics.shared.facing.x * 0.5) * 0.525 + + (this->physics.shared.facing.z * 0.5) * 0.85; + this->physics.velocity.z = (this->physics.shared.facing.x * 0.5) * -0.85 + + (this->physics.shared.facing.z * 0.5) * 0.525; + } + else { + this->physics.velocity.x = (this->physics.shared.facing.x * 0.5) * 0.525 + + (this->physics.shared.facing.z * 0.5) * -0.85; + this->physics.velocity.z = (this->physics.shared.facing.x * 0.5) * 0.85 + + (this->physics.shared.facing.z * 0.5) * 0.525; + } + this->diagonal = false; + } + else { + this->physics.velocity.x = this->physics.shared.facing.x * 0.5; + this->physics.velocity.z = this->physics.shared.facing.z * 0.5; + this->diagonal = true; + } + + this->last_move_time = now; + this->stopped = false; + + state.soundTable().addNewSoundSource(SoundSource( + ServerSFX::Python, + this->physics.shared.getCenterPosition(), + MIDDLE_VOLUME, + MEDIUM_DIST, + MEDIUM_ATTEN + )); + + return true; + } + else if (elapsed_seconds > std::chrono::seconds(this->moveDuration) && !this->stopped) { + this->physics.velocity.x = 0; + this->physics.velocity.z = 0; + this->stopped = true; + return true; + } + + return false; +} + +void Python::doCollision(Object* other, ServerGameState& state) { + Creature* creature = dynamic_cast(other); + if (creature == nullptr) return; + + if (creature->type == ObjectType::Player) { + creature->stats.health.decrease(2); + auto knockback = glm::normalize( + other->physics.shared.getCenterPosition() - this->physics.shared.getCenterPosition()); + knockback.y = 0; + creature->physics.currTickVelocity = 0.5f * knockback; + } +} + +bool Python::doDeath(ServerGameState& state) { + Enemy::doDeath(state); + + // Drop health potion upon death + auto newCorner = this->physics.shared.corner; + newCorner.y *= 0; + + auto rand = randomInt(1, 4); + if (rand == 1) { + state.objects.createObject(new Potion(newCorner, glm::vec3(1), PotionType::Nausea)); + } + else if (rand == 4) { + state.objects.createObject(new Potion(newCorner, glm::vec3(1), PotionType::Invincibility)); + } + + return true; +} \ No newline at end of file diff --git a/src/server/game/servergamestate.cpp b/src/server/game/servergamestate.cpp index 749a162c..ef9687d3 100644 --- a/src/server/game/servergamestate.cpp +++ b/src/server/game/servergamestate.cpp @@ -17,10 +17,15 @@ #include "server/game/orb.hpp" #include "server/game/weapon.hpp" #include "server/game/weaponcollider.hpp" +#include "server/game/mirror.hpp" +#include "server/game/spawner.hpp" +#include "server/game/lava.hpp" +#include "shared/game/celltype.hpp" #include "shared/game/sharedgamestate.hpp" #include "shared/audio/constants.hpp" #include "shared/audio/utilities.hpp" +#include "shared/game/sharedmodel.hpp" #include "shared/game/sharedobject.hpp" #include "shared/utilities/root_path.hpp" #include "shared/utilities/time.hpp" @@ -29,15 +34,17 @@ #include "shared/utilities/rng.hpp" #include "server/game/mazegenerator.hpp" +#include #include /* Constructors and Destructors */ ServerGameState::ServerGameState() : ServerGameState(getDefaultConfig()) {} -ServerGameState::ServerGameState(GameConfig config) { +ServerGameState::ServerGameState(GameConfig config) : config(config) { this->phase = GamePhase::LOBBY; this->timestep = FIRST_TIMESTEP; + this->lobby = Lobby(config.server.max_players); this->lobby.max_players = config.server.max_players; this->lobby.name = config.server.lobby_name; @@ -47,7 +54,7 @@ ServerGameState::ServerGameState(GameConfig config) { // Initialize game instance match phase data // Match begins in MazeExploration phase (no timer) this->matchPhase = MatchPhase::MazeExploration; - this->timesteps_left = TIME_LIMIT_MS / TIMESTEP_LEN; + this->relay_finish_time = 0; // Player victory is by default false (need to collide with an open exit // while holding the Orb to win, whereas DM wins on time limit expiration) this->playerVictory = false; @@ -55,6 +62,15 @@ ServerGameState::ServerGameState(GameConfig config) { // No player died yet this->numPlayerDeaths = 0; + this->currentGhostTrap = nullptr; + this->spawner = std::make_unique(); + this->spawner->spawnDummy(*this); + this->spawner->spawnSmallDummy(*this); + + this->dmLightningCutLights = {}; + this->dmActionCutLights = {}; + this->lastLightCut = 0; + MazeGenerator generator(config); int attempts = 1; auto grid = generator.generate(); @@ -98,7 +114,6 @@ ServerGameState::~ServerGameState() {} /* SharedGameState generation */ std::vector ServerGameState::generateSharedGameState(bool send_all) { std::vector partial_updates; - auto all_objects = this->objects.toShared(); auto getUpdateTemplate = [this]() { SharedGameState curr_update; @@ -106,13 +121,15 @@ std::vector ServerGameState::generateSharedGameState(bool send_ curr_update.lobby = this->lobby; curr_update.phase = this->phase; curr_update.matchPhase = this->matchPhase; - curr_update.timesteps_left = this->timesteps_left; + curr_update.relay_finish_time = this->relay_finish_time; curr_update.playerVictory = this->playerVictory; curr_update.numPlayerDeaths = this->numPlayerDeaths; return curr_update; }; if (send_all) { + auto all_objects = this->objects.toShared(); + for (int i = 0; i < all_objects.size(); i += OBJECTS_PER_UPDATE) { SharedGameState curr_update = getUpdateTemplate(); @@ -128,7 +145,14 @@ std::vector ServerGameState::generateSharedGameState(bool send_ int num_in_curr_update = 0; for (EntityID id : this->updated_entities) { - curr_update.objects.insert({id, all_objects.at(id)}); + + Object* obj = this->objects.getObject(id); + if (obj == nullptr) { + curr_update.objects.insert({ id, boost::none }); + } else { + curr_update.objects.insert({ id, obj->toShared() }); + } + num_in_curr_update++; if (num_in_curr_update >= OBJECTS_PER_UPDATE) { @@ -138,7 +162,11 @@ std::vector ServerGameState::generateSharedGameState(bool send_ } } - if (num_in_curr_update > 0) { + // Make sure that SharedGameState updates are sent while the server is in the + // Lobby phase (to ensure players can see other players lobby status updates) + if (num_in_curr_update > 0 || + this->getPhase() == GamePhase::LOBBY || + this->getPhase() == GamePhase::INTRO_CUTSCENE) { partial_updates.push_back(curr_update); } @@ -171,6 +199,11 @@ void ServerGameState::update(const EventList& events) { case EventType::ChangeFacing: { auto changeFacingEvent = boost::get(event.data); obj = this->objects.getObject(changeFacingEvent.entity_to_change_face); + + // If the object is the DM and the DM is paralyzed, ignore the event + if (obj->type == ObjectType::DungeonMaster + && dynamic_cast(obj)->isParalyzed()) break; + obj->physics.shared.facing = changeFacingEvent.facing; this->updated_entities.insert({ obj->globalID }); break; @@ -179,6 +212,11 @@ void ServerGameState::update(const EventList& events) { case EventType::StartAction: { auto startAction = boost::get(event.data); obj = this->objects.getObject(startAction.entity_to_act); + + // If the object is the DM and the DM is paralyzed, ignore the event + if (obj->type == ObjectType::DungeonMaster + && dynamic_cast(obj)->isParalyzed()) break; + this->updated_entities.insert({ obj->globalID }); //switch case for action (currently using keys) @@ -186,11 +224,17 @@ void ServerGameState::update(const EventList& events) { case ActionType::MoveCam: { obj->physics.velocity.x = (startAction.movement * PLAYER_SPEED).x; obj->physics.velocity.z = (startAction.movement * PLAYER_SPEED).z; + if (obj->is_sprinting) { + obj->animState = (obj->animState == AnimState::JumpAnim) ? obj->animState : AnimState::SprintAnim; + } else { + obj->animState = (obj->animState == AnimState::JumpAnim) ? obj->animState : AnimState::WalkAnim; + } break; } case ActionType::Jump: { if (!obj->physics.feels_gravity || obj->physics.velocity.y != 0) { break; } obj->physics.velocity.y += (startAction.movement * JUMP_SPEED / 2.0f).y; + obj->animState = AnimState::JumpAnim; this->sound_table.addNewSoundSource(SoundSource( ServerSFX::PlayerJump, obj->physics.shared.corner, @@ -201,15 +245,27 @@ void ServerGameState::update(const EventList& events) { break; } case ActionType::Sprint: { - obj->physics.velocityMultiplier = glm::vec3(1.5f, 1.1f, 1.5f); + if (obj->type == ObjectType::DungeonMaster) { + DungeonMaster* dm = this->objects.getDM(); + + obj->physics.velocityMultiplier = (dm->physics.shared.corner.y/5.0f) * glm::vec3(1.5f, 1.1f, 1.5f); + } + else { + obj->physics.velocityMultiplier = glm::vec3(1.5f, 1.1f, 1.5f); + obj->animState = (obj->animState == AnimState::WalkAnim) ? AnimState::SprintAnim : obj->animState; + obj->is_sprinting = true; + } break; } case ActionType::Zoom: { // only for DM DungeonMaster * dm = this->objects.getDM(); - if ((dm->physics.shared.corner.y + startAction.movement.y >= 10.0f) && (dm->physics.shared.corner.y + startAction.movement.y <= 100.0f)) + if (dm != nullptr && (dm->physics.shared.corner.y + startAction.movement.y >= 10.0f) && (dm->physics.shared.corner.y + startAction.movement.y <= 100.0f)) { dm->physics.shared.corner += startAction.movement; + obj->physics.velocityMultiplier = (dm->physics.shared.corner.y / 10.0f) * glm::vec3(1.5f, 1.1f, 1.5f); + } + break; } default: {} @@ -220,16 +276,36 @@ void ServerGameState::update(const EventList& events) { case EventType::StopAction: { auto stopAction = boost::get(event.data); obj = this->objects.getObject(stopAction.entity_to_act); + + // If the object is the DM and the DM is paralyzed, ignore the event + if (obj->type == ObjectType::DungeonMaster + && dynamic_cast(obj)->isParalyzed()) break; + this->updated_entities.insert({ obj->globalID }); //switch case for action (currently using keys) switch (stopAction.action) { case ActionType::MoveCam: { obj->physics.velocity.x = 0.0f; obj->physics.velocity.z = 0.0f; + obj->animState = AnimState::IdleAnim; break; } case ActionType::Sprint: { obj->physics.velocityMultiplier = glm::vec3(1.0f, 1.0f, 1.0f); + + // if DM gotta re-adjust velocity to be based on height + if (obj->type == ObjectType::DungeonMaster) { + obj->physics.velocityMultiplier = (obj->physics.shared.corner.y / 10.0f) * glm::vec3(1.5f, 1.1f, 1.5f); + } + + if (obj->physics.velocity.x != 0.0f && obj->physics.velocity.z != 0.0f) { + obj->animState = AnimState::WalkAnim; + } else { + obj->animState = AnimState::IdleAnim; + } + + obj->is_sprinting = false; + break; } default: { break; } @@ -242,11 +318,17 @@ void ServerGameState::update(const EventList& events) { //currently just sets the velocity to given auto moveRelativeEvent = boost::get(event.data); obj = this->objects.getObject(moveRelativeEvent.entity_to_move); + + // If the object is the DM and the DM is paralyzed, ignore the event + if (obj->type == ObjectType::DungeonMaster + && dynamic_cast(obj)->isParalyzed()) break; + obj->physics.velocity += moveRelativeEvent.movement; this->updated_entities.insert(obj->globalID); break; } + case EventType::SelectItem: { auto selectItemEvent = boost::get(event.data); @@ -260,6 +342,9 @@ void ServerGameState::update(const EventList& events) { if (obj->type == ObjectType::DungeonMaster) { DungeonMaster* dm = this->objects.getDM(); + // If the dungeon master is paralyzed, do nothing + if (dm->isParalyzed()) break; + if (dm->sharedTrapInventory.selected + selectItemEvent.itemNum == 0) dm->sharedTrapInventory.selected = TRAP_INVENTORY_SIZE; else if (dm->sharedTrapInventory.selected + selectItemEvent.itemNum == TRAP_INVENTORY_SIZE + 1) @@ -270,11 +355,32 @@ void ServerGameState::update(const EventList& events) { this->updated_entities.insert(dm->globalID); } else { - player->sharedInventory.selected = selectItemEvent.itemNum; + // If the current selected item is a Mirror and it is used, set it to not be used + SpecificID currentItemSelected = player->inventory[player->sharedInventory.selected - 1]; + + if (currentItemSelected != -1) { + Item* selectedItem = this->objects.getItem(currentItemSelected); + + if (selectedItem->type == ObjectType::Mirror && selectedItem->iteminfo.used) { + // Set mirror to be unused + Mirror* mirror = dynamic_cast(selectedItem); + + mirror->revertEffect(*this); + } + } + + if (player->sharedInventory.selected + selectItemEvent.itemNum == 0) + player->sharedInventory.selected = INVENTORY_SIZE; + else if (player->sharedInventory.selected + selectItemEvent.itemNum == INVENTORY_SIZE + 1) + player->sharedInventory.selected = 1; + else + player->sharedInventory.selected = player->sharedInventory.selected + selectItemEvent.itemNum; + this->updated_entities.insert(player->globalID); } break; } + case EventType::UseItem: { auto useItemEvent = boost::get(event.data); @@ -284,11 +390,18 @@ void ServerGameState::update(const EventList& events) { Item* item = this->objects.getItem(player->inventory[itemSelected]); item->useItem(player, *this, itemSelected); + if (dynamic_cast(item) != nullptr) { + player->animState = AnimState::DrinkPotionAnim; + } else if (dynamic_cast(item) != nullptr) { + player->animState = AnimState::AttackAnim; + } + this->updated_entities.insert(player->globalID); this->updated_entities.insert(item->globalID); } break; } + case EventType::DropItem: { auto dropItemEvent = boost::get(event.data); @@ -303,31 +416,27 @@ void ServerGameState::update(const EventList& events) { } break; } + case EventType::TrapPlacement: { + DungeonMaster* dm = this->objects.getDM(); + + // exit if DM is null? + if (dm == nullptr) + break; + auto trapPlacementEvent = boost::get(event.data); Grid& currGrid = this->getGrid(); float cellWidth = currGrid.grid_cell_width; - DungeonMaster* dm = this->objects.getDM(); + // If the DM is paralyzed, do nothing + if (dm->isParalyzed()) + break; glm::vec3 dir = glm::normalize(trapPlacementEvent.world_pos-dm->physics.shared.corner); - //if (trapPlacementEvent.world_pos.z < glm::floor(trapPlacementEvent.world_pos.z) + 0.5) { - // trapPlacementEvent.world_pos.z = glm::floor(trapPlacementEvent.world_pos.z) - DM_Z_DISCOUNT; - //} else { - // trapPlacementEvent.world_pos.z = glm::floor(trapPlacementEvent.world_pos.z + DM_Z_DISCOUNT); - //} - - //if (trapPlacementEvent.world_pos.x > glm::floor(trapPlacementEvent.world_pos.x) + 0.5) { - // trapPlacementEvent.world_pos.x = glm::ceil(trapPlacementEvent.world_pos.x) + DM_Z_DISCOUNT; - //} - //else { - // trapPlacementEvent.world_pos.x = glm::ceil(trapPlacementEvent.world_pos.x - DM_Z_DISCOUNT); - //} - trapPlacementEvent.world_pos += (dir*(float)DM_Z_DISCOUNT); glm::ivec2 gridCellPos = currGrid.getGridCellFromPosition(trapPlacementEvent.world_pos); @@ -337,46 +446,117 @@ void ServerGameState::update(const EventList& events) { if (cell == nullptr) break; + this->updated_entities.insert(dm->globalID); - // unhighlight if highlighted - for (SolidSurface* surface : this->previouslyHighlighted) { - this->updated_entities.insert(surface->globalID); - surface->setDMHighlight(false); + // mark previous ghost trap for deletion, if exists + if (this->currentGhostTrap != nullptr) { + markForDeletion(this->currentGhostTrap->globalID); + this->currentGhostTrap = nullptr; // reset ghost trap variable } - // std::vector surfaces = solidSurfaceInGridCells[{cell->x, cell->y}]; + if (trapPlacementEvent.hover) { + // only hover for traps, not lightning + if (trapPlacementEvent.cell == CellType::Lightning || trapPlacementEvent.cell == CellType::LightCut) + break; - // this->previouslyHighlighted = surfaces; + Trap* trap = placeTrapInCell(cell, trapPlacementEvent.cell); - if (trapPlacementEvent.hover) { - /*for (SolidSurface* surface : surfaces) { - this->updated_entities.insert(surface->globalID); - surface->setDMHighlight(true); - }*/ + if (trap == nullptr) + break; + + this->currentGhostTrap = trap; + + this->currentGhostTrap->setIsDMTrapHover(true); + + this->updated_entities.insert(trap->globalID); } else if(trapPlacementEvent.place) { + auto curr_time = std::chrono::system_clock::now(); + + // Lightning now has its own mana system + if (trapPlacementEvent.cell == CellType::Lightning) { + if (dm->dmInfo.mana_remaining >= LIGHTNING_MANA) { + Weapon* lightning = dm->lightning; + glm::vec3 corner( + cell->x * Grid::grid_cell_width, + 0.0f, + cell->y * Grid::grid_cell_width + ); + + lightning->useLightning(dm, *this, corner); + dm->useMana(LIGHTNING_MANA); + + this->dmLightningCutLights = corner; + this->lastLightningLightCut = this->timestep; + } + + break; + } + + if (trapPlacementEvent.cell == CellType::LightCut) { + if (dm->dmInfo.mana_remaining >= LIGHT_CUT_MANA) { + this->dmActionCutLights = trapPlacementEvent.world_pos; + + // go back 150 ticks (light cut is longer) + this->lastLightCut = this->timestep; + + dm->useMana(LIGHT_CUT_MANA); + } + + break; + } + int trapsPlaced = dm->getPlacedTraps(); if (trapsPlaced == MAX_TRAPS) { break; } + auto end = dm->sharedTrapInventory.trapsInCooldown.end(); + // handle arrowtrap / sungod seperately + if (trapPlacementEvent.cell == CellType::ArrowTrapDown || + trapPlacementEvent.cell == CellType::ArrowTrapUp || + trapPlacementEvent.cell == CellType::ArrowTrapLeft || + trapPlacementEvent.cell == CellType::ArrowTrapRight) { + + if (!(dm->sharedTrapInventory.trapsInCooldown.find(CellType::ArrowTrapDown) == end && + dm->sharedTrapInventory.trapsInCooldown.find(CellType::ArrowTrapUp) == end && + dm->sharedTrapInventory.trapsInCooldown.find(CellType::ArrowTrapLeft) == end && + dm->sharedTrapInventory.trapsInCooldown.find(CellType::ArrowTrapRight) == end)) { + break; + } + } + + if (trapPlacementEvent.cell == CellType::FireballTrapDown || + trapPlacementEvent.cell == CellType::FireballTrapUp || + trapPlacementEvent.cell == CellType::FireballTrapLeft || + trapPlacementEvent.cell == CellType::FireballTrapRight) { + + if (!(dm->sharedTrapInventory.trapsInCooldown.find(CellType::FireballTrapDown) == end && + dm->sharedTrapInventory.trapsInCooldown.find(CellType::FireballTrapUp) == end && + dm->sharedTrapInventory.trapsInCooldown.find(CellType::FireballTrapLeft) == end && + dm->sharedTrapInventory.trapsInCooldown.find(CellType::FireballTrapRight) == end)) { + break; + } + } + + // handle remaining traps auto it = dm->sharedTrapInventory.trapsInCooldown.find(trapPlacementEvent.cell); - // in cooldown and haven't elapsed enough time yet - if (it != dm->sharedTrapInventory.trapsInCooldown.end() && - std::chrono::round(std::chrono::system_clock::now() - std::chrono::system_clock::from_time_t(it->second)) < std::chrono::seconds(TRAP_COOL_DOWN)) { + // in cooldown map sadly + if (it != end){ break; } - auto curr_time = std::chrono::system_clock::now(); - Trap* trap = placeTrapInCell(cell, trapPlacementEvent.cell); if (trap == nullptr) { break; } + // change cell type + cell->type = trapPlacementEvent.cell; + trap->setIsDMTrap(true); trap->setExpiration(curr_time + std::chrono::seconds(10)); @@ -384,17 +564,170 @@ void ServerGameState::update(const EventList& events) { dm->sharedTrapInventory.trapsInCooldown[trapPlacementEvent.cell] = std::chrono::system_clock::to_time_t(curr_time); + // Store remaining CD in milliseconds + dm->sharedTrapInventory.trapsCooldown[trapPlacementEvent.cell] = TRAP_COOL_DOWN * 1000; + dm->setPlacedTraps(trapsPlaced + 1); + + dm->sharedTrapInventory.trapsPlaced = trapsPlaced + 1; + + // SPAWN AN ITEM FOR EACH PLAYER + float randFloat = randomDouble(0.0, 1.0); + + if (randFloat <= ITEM_SPAWN_PROB) { + auto players = this->objects.getPlayers(); + + for (int p = 0; p < players.size(); p++) { + auto _player = players.get(p); + + if (_player == nullptr) + continue; + + GridCell* _cell = this->getGrid().getCell(_player->physics.shared.corner.x / Grid::grid_cell_width, _player->physics.shared.corner.z / Grid::grid_cell_width); + + int randomC = randomInt(std::max(_cell->x - ITEM_SPAWN_BOUND, 0), std::min(this->grid.getColumns() - 1, _cell->x + ITEM_SPAWN_BOUND)); + int randomR = randomInt(std::max(_cell->y - ITEM_SPAWN_BOUND, 0), std::min(this->grid.getRows() - 1, _cell->y + ITEM_SPAWN_BOUND)); + + GridCell* random_cell = grid.getCell(randomC, randomR); + CellType celltype = random_cell->type; + + int counter = 0; + + // keep finding that cell! + while ((randomC == _cell->x && randomR == _cell->y) || celltype != CellType::Empty) { + randomC = randomInt(std::max(_cell->x - ITEM_SPAWN_BOUND, 0), std::min(this->grid.getColumns() - 1, _cell->x + ITEM_SPAWN_BOUND)); + randomR = randomInt(std::max(_cell->y - ITEM_SPAWN_BOUND, 0), std::min(this->grid.getRows() - 1, _cell->y + ITEM_SPAWN_BOUND)); + + random_cell = grid.getCell(randomC, randomR); + celltype = random_cell->type; + + counter += 1; + + // this allows us to break out in case infinite loop + if (counter >= ((ITEM_SPAWN_BOUND + 1) * (ITEM_SPAWN_BOUND + 1))) { + break; + } + } + + // early exit, just couldn't place anything sadly + if ((randomC == _cell->x && randomR == _cell->y) || celltype != CellType::Empty) { + std::cout << "COULDNT PLACE ANY ITEMS!" << std::endl; + break; + } + + if (!this->hasObjectCollided(this->spawner->smallDummyItem, + glm::vec3(randomC * Grid::grid_cell_width + 0.01f, 0, randomR * Grid::grid_cell_width + 0.01f))) + { + this->objects.moveObject(this->spawner->smallDummyItem, glm::vec3(-1, 0, -1)); + + glm::vec3 dimensions(1.0f); + + glm::vec3 corner( + random_cell->x * Grid::grid_cell_width + 1, + 0, + random_cell->y * Grid::grid_cell_width + 1 + ); + + int randomCellType = randomInt(1, 3); + + if (randomCellType == 1) { + int r = randomInt(1, 3); + if (r == 1) { + random_cell->type = CellType::HealthPotion; + } + else if (r == 2) { + random_cell->type = CellType::InvisibilityPotion; + } + else { + random_cell->type = CellType::InvincibilityPotion; + } + } + else if (randomCellType == 2) { + int r = randomInt(1, 3); + if (r == 1) { + random_cell->type = CellType::FireSpell; + } + else if (r == 2) { + random_cell->type = CellType::HealSpell; + } + else { + random_cell->type = CellType::TeleportSpell; + } + } + else { + int r = randomInt(1, 4); + if (r == 1) { + random_cell->type = CellType::Dagger; + } + else if (r == 2) { + random_cell->type = CellType::Sword; + } + else if (r == 3) { + random_cell->type = CellType::Mirror; + } + else { + random_cell->type = CellType::Hammer; + } + } + + Object* spawned_object = nullptr; + + switch (random_cell->type) { + case CellType::Dagger: { + spawned_object= new Weapon(corner, dimensions, WeaponType::Dagger); + break; + } + case CellType::Sword: { + spawned_object = new Weapon(corner, dimensions, WeaponType::Sword); + break; + } + case CellType::Hammer: { + spawned_object = new Weapon(corner, dimensions, WeaponType::Hammer); + break; + } + case CellType::TeleportSpell: { + spawned_object = new Spell(corner, dimensions, SpellType::Teleport); + break; + } + case CellType::FireSpell: { + spawned_object = new Spell(corner, dimensions, SpellType::Fireball); + break; + } + case CellType::HealSpell: { + spawned_object = new Spell(corner, dimensions, SpellType::HealOrb); + break; + } + case CellType::HealthPotion: { + spawned_object = new Potion(corner, dimensions, PotionType::Health); + break; + } + case CellType::InvisibilityPotion: { + spawned_object = new Potion(corner, dimensions, PotionType::Invisibility); + break; + } + case CellType::InvincibilityPotion: { + spawned_object = new Potion(corner, dimensions, PotionType::Invincibility); + break; + } + default: + std::cerr << "WARNING: unknown item spawned in DM care package code." << std::endl; + } + + if (spawned_object != nullptr) { + this->objects.createObject(spawned_object); + this->updated_entities.insert(spawned_object->globalID); + } + } + } + } + } + break; } - - // default: - // std::cerr << "Unimplemented EventType (" << event.type << ") received" << std::endl; } } - // TODO: fill update() method with updating object movement doProjectileTicks(); doTorchlightTicks(); @@ -406,18 +739,36 @@ void ServerGameState::update(const EventList& events) { handleDeaths(); handleRespawns(); deleteEntities(); - spawnEnemies(); + if (!this->config.game.disable_enemies) { + spawnEnemies(); + } + handleTickVelocity(); + handleDM(); tickStatuses(); - + updateCompass(); + updatePlayerLightningInvulnerabilityStatus(); + + // Only do this if the DM exists + if (this->objects.getDM() != nullptr) + updateDungeonMasterParalysis(); + + // after some amount of timesteps uncut lights + if ((this->timestep - this->lastLightningLightCut) >= LIGHTNING_LIGHT_CUT_TICKS && this->dmLightningCutLights.has_value()) { + this->dmLightningCutLights = {}; + } + + // after some amount of timesteps uncut lights + if ((this->timestep - this->lastLightCut) >= LIGHT_CUT_TICKS && this->dmActionCutLights.has_value()) { + this->dmActionCutLights = {}; + } + // Increment timestep this->timestep++; // Countdown timer if the Orb has been picked up by a Player and the match // phase is now RelayRace if (this->matchPhase == MatchPhase::RelayRace) { - this->timesteps_left--; - - if (this->timesteps_left == 0) { + if (std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) > this->relay_finish_time) { // Dungeon Master won on time limit expiration this->phase = GamePhase::RESULTS; } @@ -468,7 +819,7 @@ void ServerGameState::updateMovement() { // Object is movable - compute total movement step glm::vec3 totalMovementStep = - object->physics.velocity * object->physics.velocityMultiplier; + object->physics.velocity * object->physics.velocityMultiplier + object->physics.currTickVelocity; totalMovementStep.x *= object->physics.nauseous; totalMovementStep.z *= object->physics.nauseous; @@ -581,11 +932,38 @@ void ServerGameState::updateMovement() { currentPosition = object->physics.shared.corner; } + const float spike_low_y = 2.9f; + if (object->type == ObjectType::SpikeTrap && object->physics.shared.corner.y < spike_low_y) { + object->physics.shared.corner.y = spike_low_y; + object->physics.feels_gravity = false; + object->physics.velocity.y = 0; + if (starting_corner_pos.y != 3.0f) { + this->sound_table.addNewSoundSource(SoundSource( + ServerSFX::CeilingSpikeImpact, + object->physics.shared.corner, + FULL_VOLUME, + FAR_DIST, + FAR_ATTEN + )); + } + } + // Vertical movement if (object->physics.shared.corner.y < 0) { // Clamp object to floor if corner's y position is lower than the floor object->physics.shared.corner.y = 0; + // After landing, set object's animation to non-jump (idle) + if (object->physics.velocity.x != 0.0f && object->physics.velocity.z != 0.0f) { + if (object->is_sprinting) { + object->animState = AnimState::SprintAnim; + } else { + object->animState = AnimState::WalkAnim; + } + } else { + object->animState = AnimState::IdleAnim; + } + // Play relevant landing sounds if (starting_corner_pos.y != 0.0f) { if (object->type == ObjectType::Player) { @@ -596,15 +974,7 @@ void ServerGameState::updateMovement() { MEDIUM_DIST, MEDIUM_ATTEN )); - } else if (object->type == ObjectType::SpikeTrap) { - this->sound_table.addNewSoundSource(SoundSource( - ServerSFX::CeilingSpikeImpact, - object->physics.shared.corner, - FULL_VOLUME, - FAR_DIST, - FAR_ATTEN - )); - } + } } } @@ -675,7 +1045,9 @@ bool ServerGameState::hasObjectCollided(Object* object, glm::vec3 newCornerPosit // doesn't have a collider if (object->globalID == otherObj->globalID || otherObj->physics.collider == Collider::None - || (object->type == ObjectType::Player && otherObj->type == ObjectType::Player)) { + || (object->type == ObjectType::Player && otherObj->type == ObjectType::Player) + || (object->type == ObjectType::Item && otherObj->type == ObjectType::Player) + || (object->type == ObjectType::Player && otherObj->type == ObjectType::Item)) { continue; } @@ -699,12 +1071,16 @@ bool ServerGameState::hasObjectCollided(Object* object, glm::vec3 newCornerPosit // perform collision handling but do not return true as the // trap doesn't affect the movement of the object it hits if (otherObj->type == ObjectType::FloorSpike || + otherObj->type == ObjectType::Lava || otherObj->type == ObjectType::Potion || otherObj->type == ObjectType::Spell || otherObj->type == ObjectType::Weapon || otherObj->type == ObjectType::Orb || otherObj->type == ObjectType::WeaponCollider || - otherObj->type == ObjectType::Slime) { + otherObj->type == ObjectType::Slime || + otherObj->type == ObjectType::TeleporterTrap || + otherObj->type == ObjectType::Torchlight || + otherObj->type == ObjectType::Mirror) { continue; } @@ -717,31 +1093,7 @@ bool ServerGameState::hasObjectCollided(Object* object, glm::vec3 newCornerPosit } void ServerGameState::spawnEnemies() { - // temp numbers, to tune later... - // 1/300 chance every 30ms -> expected spawn every 9s - // TODO 1: small slimes are weighted the same as large slimes - // TODO 2: check that no collision in the cell you are spawning in - if (randomInt(1, 300) == 1 && this->objects.getEnemies().numElements() < MAX_ALIVE_ENEMIES) { - int cols = this->grid.getColumns(); - int rows = this->grid.getRows(); - - glm::ivec2 random_cell; - while (true) { - random_cell.x = randomInt(0, cols - 1); - random_cell.y = randomInt(0, rows - 1); // corresponds to z in the world - - if (this->grid.getCell(random_cell.x, random_cell.y)->type == CellType::Empty) { - break; - } - } - - int size = randomInt(2, 4); - this->objects.createObject(new Slime( - glm::vec3(random_cell.x * Grid::grid_cell_width, 0, random_cell.y * Grid::grid_cell_width), - glm::vec3(0, 0, 0), - size - )); - } + this->spawner->spawn(*this); } void ServerGameState::updateItems() { @@ -765,6 +1117,16 @@ void ServerGameState::updateItems() { } } + if (item->type == ObjectType::Mirror) { + Mirror* mirror = dynamic_cast(item); + if (mirror->iteminfo.used) { + if (mirror->timeOut()) { + mirror->revertEffect(*this); + this->updated_entities.insert(mirror->globalID); + } + } + } + if (item->type == ObjectType::Weapon) { Weapon* weapon = dynamic_cast(item); weapon->reset(*this); @@ -803,7 +1165,7 @@ void ServerGameState::updateAttacks() { auto weaponCollider = weaponColliders.get(i); if (weaponCollider == nullptr) { continue; } weaponCollider->updateMovement(*this); - if(weaponCollider->readyTime()){ + if(weaponCollider->readyTime(*this)){ if (weaponCollider->timeOut(*this)) { this->markForDeletion(weaponCollider->globalID); } @@ -818,43 +1180,77 @@ void ServerGameState::updateAttacks() { void ServerGameState::doTorchlightTicks() { auto torchlights = this->objects.getTorchlights(); + for (int t = 0; t < torchlights.size(); t++) { auto torchlight = torchlights.get(t); - if (torchlight == nullptr) continue; - torchlight->doTick(*this); + if (torchlight == nullptr) + continue; + + if (torchlight->doTick(*this, this->dmLightningCutLights, this->dmActionCutLights)) { + this->updated_entities.insert(torchlight->globalID); + } } } void ServerGameState::updateTraps() { - // check for activations + // get current time when calling this function + auto current_time = std::chrono::system_clock::now(); + DungeonMaster* dm = this->objects.getDM(); + + // update DM trap cooldown + if (dm != nullptr) { + auto& coolDownMap = dm->sharedTrapInventory.trapsInCooldown; + + for (auto it = dm->sharedTrapInventory.trapsInCooldown.cbegin(); it != dm->sharedTrapInventory.trapsInCooldown.cend();) { + auto key = it->first; + auto passedTime = std::chrono::round(current_time - std::chrono::system_clock::from_time_t(it->second)); + auto diff = std::chrono::milliseconds(TRAP_COOL_DOWN * 1000) - passedTime; + + // update remaining time + dm->sharedTrapInventory.trapsCooldown[key] = diff.count(); + + if (std::chrono::round(current_time - std::chrono::system_clock::from_time_t(it->second)) >= std::chrono::seconds(TRAP_COOL_DOWN)) { + dm->sharedTrapInventory.trapsCooldown.erase(key); + it = dm->sharedTrapInventory.trapsInCooldown.erase(it); + } + else { + it++; + } + } + + this->updated_entities.insert(dm->globalID); + } - // This object moved, so we should check to see if a trap should trigger because of it auto traps = this->objects.getTraps(); for (int i = 0; i < traps.size(); i++) { auto trap = traps.get(i); - if (trap == nullptr) { continue; } // unsure if i need this? - if (trap->getIsDMTrap()) { - auto current_time = std::chrono::system_clock::now(); - + if (trap == nullptr) { continue; } + if (trap->getIsDMTrap() && dm != nullptr) { if (current_time >= trap->getExpiration()) { - DungeonMaster* dm = this->objects.getDM(); int trapsPlaced = dm->getPlacedTraps(); - this->markForDeletion(trap->globalID); dm->setPlacedTraps(trapsPlaced - 1); + dm->sharedTrapInventory.trapsPlaced = trapsPlaced - 1; + + // change cell type to empty + GridCell* _cell = this->getGrid().getCell(trap->physics.shared.corner.x / Grid::grid_cell_width, trap->physics.shared.corner.z / Grid::grid_cell_width); + + _cell->type = CellType::Empty; + continue; } } + // check for activations if (trap->shouldTrigger(*this)) { trap->trigger(*this); this->updated_entities.insert(trap->globalID); } - if (trap->shouldReset(*this)) { - trap->reset(*this); + if (trap->shouldReset(*this)) { + trap->reset(*this); this->updated_entities.insert(trap->globalID); - } + } } } @@ -876,6 +1272,16 @@ void ServerGameState::handleDeaths() { // Player died - increment number of player deaths this->numPlayerDeaths++; + if (numPlayerDeaths < PLAYER_DEATHS_TO_RELAY_RACE) { + this->soundTable().addNewSoundSource(SoundSource( + ServerSFX::Thunder, + player->physics.shared.getCenterPosition(), + DEFAULT_VOLUME, + FAR_DIST, + FAR_ATTEN + )); + } + if (numPlayerDeaths == PLAYER_DEATHS_TO_RELAY_RACE) { this->transitionToRelayRace(); } @@ -907,6 +1313,7 @@ void ServerGameState::handleDeaths() { } this->updated_entities.insert(player->globalID); + player->physics.velocity = glm::vec3(0.0f); player->info.is_alive = false; player->info.respawn_time = getMsSinceEpoch() + 5000; // currently hardcode to wait 5s } @@ -921,7 +1328,15 @@ void ServerGameState::handleDeaths() { this->updated_entities.insert(enemy->globalID); if (enemy->doDeath(*this)) { this->entities_to_delete.insert(enemy->globalID); - this->alive_enemy_weight--; + } + if (enemy->type == ObjectType::Minotaur) { + this->soundTable().addNewSoundSource(SoundSource( + ServerSFX::MinotaurDeath, + enemy->physics.shared.getCenterPosition(), + DEFAULT_VOLUME, + MEDIUM_DIST, + MEDIUM_ATTEN + )); } } } @@ -965,7 +1380,7 @@ void ServerGameState::tickStatuses() { player->statuses.tickStatus(); } auto enemies = this->objects.getEnemies(); - for (auto e = 0; e < players.size(); e++) { + for (auto e = 0; e < enemies.size(); e++) { auto enemy = enemies.get(e); if (enemy == nullptr) continue; @@ -973,6 +1388,184 @@ void ServerGameState::tickStatuses() { } } +void ServerGameState::handleDM() { + DungeonMaster* dm = this->objects.getDM(); + if (dm != nullptr) { + dm->manaRegen(); + } +} + +void ServerGameState::updateCompass() { + /* + std::optional orb_pos; + + + for (auto p = 0; p < players.size(); p++) { + Player* player = players.get(p); + if (player == nullptr) continue; + + if (player->sharedInventory.hasOrb) { + orb_pos = player->physics.shared.corner; + orb_pos->y = 0; + break; + } + } + + if (!orb_pos.has_value()) { + auto items = this->objects.getItems(); + for (auto i = 0; i < items.size(); i++) { + Item* item = items.get(i); + if (item == nullptr) continue; + if (item->type == ObjectType::Orb) { + orb_pos = item->physics.shared.corner; + orb_pos->y = 0; + break; + } + } + }*/ + + auto players = this->objects.getPlayers(); + for (auto p = 0; p < players.size(); p++) { + auto player = players.get(p); + if (player == nullptr) continue; + + //auto x = player->physics.shared.getCenterPosition().x - orb_pos->x; + //auto y = player->physics.shared.getCenterPosition().y - orb_pos->y; + + auto angle = atan2(player->physics.shared.facing.z, player->physics.shared.facing.x) + * (180.0 / 3.141592653589793238463); + if (angle < 0) { + angle += 360; + } + player->compass.angle = angle; + } +} + +void ServerGameState::handleTickVelocity() { + auto players = this->objects.getPlayers(); + for (auto p = 0; p < players.size(); p++) { + auto player = players.get(p); + if (player == nullptr) continue; + + // is this actually the best i can do...? -ted + if (player->physics.currTickVelocity != glm::vec3(0.0f)) { + if (player->physics.currTickVelocity.x > 0) { + player->physics.currTickVelocity.x -= 0.05f; + } + else if (player->physics.currTickVelocity.x < 0) { + player->physics.currTickVelocity.x += 0.05f; + } + + if (player->physics.currTickVelocity.y > 0) { + player->physics.currTickVelocity.y -= 0.05f; + } + else if (player->physics.currTickVelocity.y < 0) { + player->physics.currTickVelocity.y += 0.05f; + } + + if (player->physics.currTickVelocity.z > 0) { + player->physics.currTickVelocity.z -= 0.05f; + } + else if (player->physics.currTickVelocity.z < 0) { + player->physics.currTickVelocity.z += 0.05f; + } + + if (abs(player->physics.currTickVelocity.x) <= 0.05f) { + player->physics.currTickVelocity.x = 0.0f; + } + if (abs(player->physics.currTickVelocity.y) <= 0.05f) { + player->physics.currTickVelocity.y = 0.0f; + } + if (abs(player->physics.currTickVelocity.z) <= 0.05f) { + player->physics.currTickVelocity.z = 0.0f; + } + } + } + + auto enemies = this->objects.getEnemies(); + for (auto e = 0; e < enemies.size(); e++) { + auto enemy = enemies.get(e); + if (enemy == nullptr) continue; + + if (enemy->physics.currTickVelocity != glm::vec3(0.0f)) { + if (enemy->physics.currTickVelocity.x > 0) { + enemy->physics.currTickVelocity.x -= 0.05f; + } + else if (enemy->physics.currTickVelocity.x < 0) { + enemy->physics.currTickVelocity.x += 0.05f; + } + + if (enemy->physics.currTickVelocity.y > 0) { + enemy->physics.currTickVelocity.y -= 0.05f; + } + else if (enemy->physics.currTickVelocity.y < 0) { + enemy->physics.currTickVelocity.y += 0.05f; + } + + if (enemy->physics.currTickVelocity.z > 0) { + enemy->physics.currTickVelocity.z -= 0.05f; + } + else if (enemy->physics.currTickVelocity.z < 0) { + enemy->physics.currTickVelocity.z += 0.05f; + } + + if (abs(enemy->physics.currTickVelocity.x) <= 0.05f) { + enemy->physics.currTickVelocity.x = 0.0f; + } + if (abs(enemy->physics.currTickVelocity.y) <= 0.05f) { + enemy->physics.currTickVelocity.y = 0.0f; + } + if (abs(enemy->physics.currTickVelocity.z) <= 0.05f) { + enemy->physics.currTickVelocity.z = 0.0f; + } + } + } +} + +void ServerGameState::updatePlayerLightningInvulnerabilityStatus() { + // Iterate through all players. If one of the players has a + // lightning invulernability, check whether it should be turned + // off, and if so, turn it off. + for (int i = 0; i < this->objects.getPlayers().size(); i++) { + Player* player = this->objects.getPlayers().get(i); + + if (player == nullptr) + continue; + + if (player->isInvulnerableToLightning()) { + // Player is invulnerable to lightning - check whether timeout + // has occurred and if so, set as vulnerable to lightning again + auto now = std::chrono::system_clock::now(); + std::chrono::duration elapsed_seconds{ now - player->getLightningInvulnerabilityStartTime()}; + + if (elapsed_seconds.count() > player->getLightningInvulnerabilityDuration()) { + std::cout << "Removing a player's lightning invulnerability." << std::endl; + player->setInvulnerableToLightning(false, -1); + + // If the player gained invulnerability due to reflecting a + // lightning bolt with a mirror, undo that boolean + player->info.used_mirror_to_reflect_lightning = false; + } + } + } +} + +void ServerGameState::updateDungeonMasterParalysis() { + // Check whether the DM is paralyzed + DungeonMaster* dm = this->objects.getDM(); + if (dm->isParalyzed()) { + // Check whether timeout has occurred and if so, set as not paralyzed + auto now = std::chrono::system_clock::now(); + std::chrono::duration elapsed_seconds{ now - dm->getParalysisStartTime() }; + + if (elapsed_seconds.count() > dm->getParalysisDuration()) { + std::cout << "Ending DM's paralysis" << std::endl; + dm->setParalysis(false, -1); + } + } +} + + unsigned int ServerGameState::getTimestep() const { return this->timestep; } @@ -996,6 +1589,7 @@ void ServerGameState::transitionToRelayRace() { this->matchPhase = MatchPhase::RelayRace; + this->relay_finish_time = getSecSinceEpoch() + TIME_LIMIT_S.count(); // Open all exits! for (int i = 0; i < this->objects.getExits().size(); i++) { Exit* exit = this->objects.getExits().get(i); @@ -1011,12 +1605,68 @@ void ServerGameState::setPlayerVictory(bool playerVictory) { this->playerVictory = playerVictory; } -void ServerGameState::addPlayerToLobby(EntityID id, const std::string& name) { - this->lobby.players[id] = name; +void ServerGameState::addPlayerToLobby(LobbyPlayer player) { + //this->lobby.players[id] = name; + + // Only add the player if a player with the given EntityID doesn't exist + for (int i = 0; i < this->lobby.max_players; i++) { + if (this->lobby.players[i].has_value() + && this->lobby.players[i].get().id == player.id) + { + // A player with this EntityID already exists in this + // ServerGameState's Lobby - return without adding player + // again to this server's Lobby + return; + } + } + + bool freeIndex = false; + + for (int i = 0; i < this->lobby.max_players; i++) { + if (!this->lobby.players[i].has_value()) { + // Found a free index! Adding player here + freeIndex = true; + this->lobby.players[i] = player; + std::cout << "Added new player in index " << std::to_string(i) << std::endl; + std::cout << "Player's eid: " << player.id << std::endl; + break; + } + } + + // Crash server if no free index was found + assert(freeIndex); +} + +void ServerGameState::updateLobbyPlayer(EntityID id, LobbyPlayer player) { + // Iterate through the players vector and update the player with the given + // EntityID + for (int i = 0; i < this->lobby.max_players; i++) { + if (!this->lobby.players[i].has_value()) + continue; + + if (this->lobby.players[i].get().id == id) { + // Update player + this->lobby.players[i] = player; + } + } } void ServerGameState::removePlayerFromLobby(EntityID id) { - this->lobby.players.erase(id); + // Iterate through the players vector and remove the player with the given + // EntityID + for (int i = 0; i < this->lobby.max_players; i++) { + if (!this->lobby.players[i].has_value()) + continue; + + if (this->lobby.players[i].get().id == id) { + // Remove player + this->lobby.players[i] = boost::none; + } + } + + // Note: this method doesn't check that the removal was successful. It's + // possible that an EntityID was passed in that no player in the lobby has + // and so the removal had no effect } const Lobby& ServerGameState::getLobby() const { @@ -1025,22 +1675,43 @@ const Lobby& ServerGameState::getLobby() const { Trap* ServerGameState::placeTrapInCell(GridCell* cell, CellType type) { switch (type) { - case CellType::FireballTrap: { + case CellType::FireballTrapLeft: + case CellType::FireballTrapRight: + case CellType::FireballTrapUp: + case CellType::FireballTrapDown: { if (cell->type != CellType::Empty) return nullptr; - glm::vec3 dimensions( - Grid::grid_cell_width / 2, - 0.5f, - Grid::grid_cell_width / 2 - ); + glm::vec3 dimensions = Object::models.at(ModelType::SunGod); glm::vec3 corner( - cell->x * Grid::grid_cell_width, - 1.0f, - cell->y * Grid::grid_cell_width + (cell->x * Grid::grid_cell_width), + 0.0f, + (cell->y * Grid::grid_cell_width) ); + Direction dir; + switch (type) { + case CellType::FireballTrapLeft: + dir = Direction::LEFT; + // corner.z -= (dimensions.z / 2.0f); + break; + case CellType::FireballTrapRight: + dir = Direction::RIGHT; + // corner.z -= (dimensions.z / 2.0f); + break; + case CellType::FireballTrapUp: + dir = Direction::UP; + corner.x += (dimensions.x / 2.0f); + break; + case CellType::FireballTrapDown: + corner.x += (dimensions.x / 2.0f); + dir = Direction::DOWN; + break; + default: + dir = Direction::LEFT; + break; + } - FireballTrap* fireBallTrap = new FireballTrap(corner, dimensions); + FireballTrap* fireBallTrap = new FireballTrap(corner, dir); this->objects.createObject(fireBallTrap); return fireBallTrap; } @@ -1048,7 +1719,7 @@ Trap* ServerGameState::placeTrapInCell(GridCell* cell, CellType type) { if (cell->type != CellType::Empty) return nullptr; - const float HEIGHT_SHOWING = 0.5; + const float HEIGHT_SHOWING = 0.4; glm::vec3 dimensions( Grid::grid_cell_width, MAZE_CEILING_HEIGHT, @@ -1059,7 +1730,7 @@ Trap* ServerGameState::placeTrapInCell(GridCell* cell, CellType type) { MAZE_CEILING_HEIGHT - HEIGHT_SHOWING, cell->y * Grid::grid_cell_width ); - + SpikeTrap* spikeTrap = new SpikeTrap(corner, dimensions); this->objects.createObject(spikeTrap); return spikeTrap; @@ -1087,79 +1758,59 @@ Trap* ServerGameState::placeTrapInCell(GridCell* cell, CellType type) { case CellType::FloorSpikeHorizontal: case CellType::FloorSpikeVertical: { if (cell->type != CellType::Empty) { - - std::cout << "trying to place in non empty cell\n"; return nullptr; - - } - - glm::vec3 corner( - cell->x * Grid::grid_cell_width, - 0.0f, - cell->y * Grid::grid_cell_width - ); - - FloorSpike::Orientation orientation; - if (type == CellType::FloorSpikeFull) { - orientation = FloorSpike::Orientation::Full; - } - else if (type == CellType::FloorSpikeHorizontal) { - orientation = FloorSpike::Orientation::Horizontal; - corner.z += Grid::grid_cell_width * 0.25f; - } - else { - orientation = FloorSpike::Orientation::Vertical; - corner.x += Grid::grid_cell_width * 0.25f; } - FloorSpike* floorSpike = new FloorSpike(corner, orientation, Grid::grid_cell_width); - this->objects.createObject(floorSpike); - return floorSpike; + return spawnFloorSpike(cell); } - /* - TODO: ADD BACK ARROWS! - case CellType::ArrowTrapDown: case CellType::ArrowTrapLeft: case CellType::ArrowTrapRight: case CellType::ArrowTrapUp: { - ArrowTrap::Direction dir; - if (cell->type == CellType::ArrowTrapDown) { - dir = ArrowTrap::Direction::DOWN; - } - else if (cell->type == CellType::ArrowTrapUp) { + if (cell->type != CellType::Empty) { + return nullptr; + } + glm::vec3 corner( + (cell->x * Grid::grid_cell_width), + -3.0f, + (cell->y * Grid::grid_cell_width) + ); + const float z_nudge = 0.55f; + const float x_nudge = 0.15f; + Direction dir; + if (type == CellType::ArrowTrapDown) { + dir = Direction::DOWN; + corner.x -= x_nudge; + } + else if (type == CellType::ArrowTrapUp) { + dir = Direction::UP; + corner.x -= x_nudge; } - else if (cell->type == CellType::ArrowTrapLeft) { - dir = ArrowTrap::Direction::LEFT; + else if (type == CellType::ArrowTrapLeft) { + dir = Direction::LEFT; + corner.z += z_nudge; } else { - dir = ArrowTrap::Direction::RIGHT; + dir = Direction::RIGHT; + corner.z += z_nudge; } - glm::vec3 dimensions( - Grid::grid_cell_width, - MAZE_CEILING_HEIGHT, - Grid::grid_cell_width - ); - glm::vec3 corner( - cell->x * Grid::grid_cell_width, - 0.0f, - cell->y * Grid::grid_cell_width - ); - this->objects.createObject(new ArrowTrap(corner, dimensions, dir)); - break; - }*/ + ArrowTrap* arrowTrap = new ArrowTrap(corner, dir); + + this->objects.createObject(arrowTrap); + return arrowTrap; + } case CellType::TeleporterTrap: { if (cell->type != CellType::Empty) return nullptr; glm::vec3 corner( - cell->x * Grid::grid_cell_width, + cell->x * Grid::grid_cell_width + 1, 0.0f, - cell->y * Grid::grid_cell_width + cell->y * Grid::grid_cell_width + 1 ); TeleporterTrap* teleporterTrap = new TeleporterTrap(corner); @@ -1214,28 +1865,59 @@ void ServerGameState::loadMaze(const Grid& grid) { } } - // Step 5: Add floor and ceiling SolidSurfaces. + std::optional orb_pos; + std::optional exit_pos; + // go through and mark distance to orb and exit + for (int c = 0; c < this->grid.getColumns(); c++) { + for (int r = 0; r < this->grid.getRows(); r++) { + CellType type = this->grid.getCell(c, r)->type; + if (type == CellType::Orb || type == CellType::Exit) { + glm::vec3 corner(c * Grid::grid_cell_width, 0.0f, r * Grid::grid_cell_width); - // Create Ceiling - this->objects.createObject(new SolidSurface(false, Collider::Box, SurfaceType::Ceiling, - glm::vec3(0.0f, MAZE_CEILING_HEIGHT, 0.0f), - glm::vec3(this->grid.getColumns() * Grid::grid_cell_width, 0.1, - this->grid.getRows() * Grid::grid_cell_width) - )); + if (type == CellType::Orb) { + orb_pos = corner; + } else { + exit_pos = corner; + } - // create floor - glm::vec3 corner = glm::vec3(0.0f, -0.1f, 0.0f); + } + } - SolidSurface* floor = new SolidSurface(false, Collider::None, SurfaceType::Floor, - corner, - glm::vec3(this->grid.getColumns() * Grid::grid_cell_width, 0.1, - this->grid.getRows() * Grid::grid_cell_width) - ); + if (orb_pos.has_value() && exit_pos.has_value()) { + break; // early exit, not really needed though probably + } + } - this->objects.createObject(floor); + // create multiple floor and ceiling objects to populate the size of the entire maze. + // currently doing this to stretch out the floor texture by the desired factor. here + // in the maze generation we can have a lot of control over how frequently the floor texture + // will repeat. I'd like to have this done on the client side instead and repeat the texture + // many times across one huge floor, but unfortuantely I cannot figure out how to get + // OpenGL to repeat the texture that many times. + for (int c = 0; c < this->grid.getColumns(); c+=GRIDS_PER_FLOOR_OBJECT) { + for (int r = 0; r < this->grid.getRows(); r+=GRIDS_PER_FLOOR_OBJECT) { + auto type = this->grid.getCell(c, r)->type; + if(type == CellType::OutsideTheMaze) { + continue; + } - // this is for floor highlighting - std::vector> freeSpots(grid.getRows(), std::vector(grid.getColumns(), false)); + glm::vec3 floor_corner = glm::vec3(c * Grid::grid_cell_width, -0.1f, r * Grid::grid_cell_width); + SolidSurface* floor = new SolidSurface(false, Collider::None, SurfaceType::Floor, + floor_corner, + glm::vec3(Grid::grid_cell_width * GRIDS_PER_FLOOR_OBJECT, 0.1, + Grid::grid_cell_width * GRIDS_PER_FLOOR_OBJECT) + ); + this->objects.createObject(floor); + + glm::vec3 ceiling_corner = glm::vec3(c * Grid::grid_cell_width, MAZE_CEILING_HEIGHT, r * Grid::grid_cell_width); + SolidSurface* ceiling = new SolidSurface(false, Collider::Box, SurfaceType::Ceiling, + ceiling_corner, + glm::vec3(Grid::grid_cell_width * GRIDS_PER_FLOOR_OBJECT, 0.1, + Grid::grid_cell_width * GRIDS_PER_FLOOR_OBJECT) + ); + this->objects.createObject(ceiling); + } + } // Step 6: For each GridCell, add an object (if not empty) at the // GridCell's position. @@ -1265,13 +1947,16 @@ void ServerGameState::loadMaze(const Grid& grid) { cell->type = CellType::TeleportSpell; } } else if (cell->type == CellType::RandomWeapon) { - int r = randomInt(1, 3); + int r = randomInt(1, 4); if (r == 1) { cell->type = CellType::Dagger; } else if (r == 2) { cell->type = CellType::Sword; } + else if (r == 3) { + cell->type = CellType::Mirror; + } else { cell->type = CellType::Hammer; } @@ -1286,23 +1971,27 @@ void ServerGameState::loadMaze(const Grid& grid) { 0, cell->y * Grid::grid_cell_width + 1); - this->objects.createObject(new Orb(corner, dimensions)); + PointLightProperties lightProperties{ + .flickering = false, + .min_intensity = 1.0f, + .max_intensity = 1.0f, + .ambient_color = glm::vec3(0.0f, 0.75f, 0.67f), + .diffuse_color = glm::vec3(0.0f, 0.75f, 0.67f), + .specular_color = glm::vec3(0.0f, 0.35f, 0.33f), + .attenuation_linear = 0.07f, + .attenuation_quadratic = 0.017f + }; + + + this->objects.createObject(new Orb(corner, dimensions, lightProperties)); break; } - case CellType::FireballTrap: { - glm::vec3 dimensions( - Grid::grid_cell_width / 2, - 0.5f, - Grid::grid_cell_width / 2 - ); - glm::vec3 corner( - cell->x * Grid::grid_cell_width, - 1.0f, - cell->y * Grid::grid_cell_width - ); - this->objects.createObject(new FireballTrap(corner, dimensions)); + case CellType::FireballTrapLeft: + case CellType::FireballTrapRight: + case CellType::FireballTrapUp: + case CellType::FireballTrapDown: + spawnFireballTrap(cell); break; - } case CellType::Dagger: { glm::vec3 dimensions(1.0f); @@ -1410,7 +2099,7 @@ void ServerGameState::loadMaze(const Grid& grid) { break; } case CellType::SpikeTrap: { - const float HEIGHT_SHOWING = 0.5; + const float HEIGHT_SHOWING = 0.4; glm::vec3 dimensions( Grid::grid_cell_width, MAZE_CEILING_HEIGHT, @@ -1443,31 +2132,22 @@ void ServerGameState::loadMaze(const Grid& grid) { case CellType::TorchDown: case CellType::TorchRight: case CellType::TorchLeft: { - this->spawnTorch(cell); + // if no orb or exit in maze then just tell it that they are very far off so not + // considered in color calculations + const glm::vec3 FAR_OFF_POS(10000, 10000, 10000); + this->spawnTorch(cell, orb_pos.value_or(FAR_OFF_POS), exit_pos.value_or(FAR_OFF_POS)); this->spawnWall(cell, col, row, internal_walls.contains(glm::ivec2(col, row))); break; } - case CellType::FloorSpikeFull: + case CellType::LavaCross: + case CellType::LavaHorizontal: + case CellType::LavaVertical: { + this->spawnLava(cell); + break; + } case CellType::FloorSpikeHorizontal: case CellType::FloorSpikeVertical: { - glm::vec3 corner( - cell->x * Grid::grid_cell_width, - 0.0f, - cell->y * Grid::grid_cell_width - ); - - FloorSpike::Orientation orientation; - if (cell->type == CellType::FloorSpikeFull) { - orientation = FloorSpike::Orientation::Full; - } else if (cell->type == CellType::FloorSpikeHorizontal) { - orientation = FloorSpike::Orientation::Horizontal; - corner.z += Grid::grid_cell_width * 0.25f; - } else { - orientation = FloorSpike::Orientation::Vertical; - corner.x += Grid::grid_cell_width * 0.25f; - } - - this->objects.createObject(new FloorSpike(corner, orientation, Grid::grid_cell_width)); + this->spawnFloorSpike(cell); break; } @@ -1475,37 +2155,15 @@ void ServerGameState::loadMaze(const Grid& grid) { case CellType::ArrowTrapLeft: case CellType::ArrowTrapRight: case CellType::ArrowTrapUp: { - ArrowTrap::Direction dir; - if (cell->type == CellType::ArrowTrapDown) { - dir = ArrowTrap::Direction::DOWN; - } else if (cell->type == CellType::ArrowTrapUp) { - dir = ArrowTrap::Direction::UP; - } else if (cell->type == CellType::ArrowTrapLeft) { - dir = ArrowTrap::Direction::LEFT; - } else { - dir = ArrowTrap::Direction::RIGHT; - } - - glm::vec3 dimensions( - Grid::grid_cell_width, - MAZE_CEILING_HEIGHT, - Grid::grid_cell_width - ); - glm::vec3 corner( - cell->x * Grid::grid_cell_width, - 0.0f, - cell->y * Grid::grid_cell_width - ); - - this->objects.createObject(new ArrowTrap(corner, dimensions, dir)); + spawnArrowTrap(cell); break; } case CellType::TeleporterTrap: { glm::vec3 corner( - cell->x * Grid::grid_cell_width, + cell->x * Grid::grid_cell_width + 1, 0.0f, - cell->y * Grid::grid_cell_width + cell->y * Grid::grid_cell_width + 1 ); this->objects.createObject(new TeleporterTrap(corner)); @@ -1513,9 +2171,9 @@ void ServerGameState::loadMaze(const Grid& grid) { } case CellType::Exit: { glm::vec3 corner( - cell->x* Grid::grid_cell_width, + cell->x * Grid::grid_cell_width, 0.0f, - cell->y* Grid::grid_cell_width + cell->y * Grid::grid_cell_width ); glm::vec3 dimensions( @@ -1523,41 +2181,37 @@ void ServerGameState::loadMaze(const Grid& grid) { MAZE_CEILING_HEIGHT, Grid::grid_cell_width ); + PointLightProperties lightProperties{ + .flickering = false, + .min_intensity = 1.0f, + .max_intensity = 1.0f, + .ambient_color = glm::vec3(1.05f, 1.05f, 1.05f), + .diffuse_color = glm::vec3(1.0f, 1.0f, 1.0f), + .specular_color = glm::vec3(0.5f, 0.5f, 0.5f), + .attenuation_linear = 0.07f, + .attenuation_quadratic = 0.017f + }; + + + this->objects.createObject(new Exit(false, corner, dimensions, lightProperties)); + break; + } + case CellType::Mirror: { + glm::vec3 dimensions(1.0f); + + glm::vec3 corner(cell->x* Grid::grid_cell_width + 1, + 0, + cell->y* Grid::grid_cell_width + 1); - this->objects.createObject(new Exit(false, corner, dimensions)); + this->objects.createObject(new Mirror(corner, dimensions)); break; } default: { - freeSpots[row][col] = true; + } } } } - - // Create Floor - //for (int c = 0; c < this->grid.getColumns(); c++) { - // for (int r = 0; r < this->grid.getRows(); r++) { - // auto type = this->grid.getCell(c, r)->type; - // if (isWallLikeCell(type) || type == CellType::OutsideTheMaze) { - // continue; - // } - - // glm::vec3 corner = glm::vec3(c * Grid::grid_cell_width, -0.1f, r * Grid::grid_cell_width); - - // SolidSurface* floor = new SolidSurface(false, Collider::None, SurfaceType::Floor, - // corner, - // glm::vec3(Grid::grid_cell_width, 0.1, - // Grid::grid_cell_width) - // ); - - // this->objects.createObject(floor); - - // if(freeSpots[r][c]) { - // solidSurfaceInGridCells.insert({{c, r}, {floor}}); - // } - // } - //} - } void ServerGameState::spawnWall(GridCell* cell, int col, int row, bool is_internal) { @@ -1585,14 +2239,10 @@ void ServerGameState::spawnWall(GridCell* cell, int col, int row, bool is_intern SolidSurface* wall = new SolidSurface(false, Collider::Box, surface_type, corner, dimensions); wall->shared.is_internal = is_internal; this->objects.createObject(wall); - if (cell->type == CellType::Wall || cell->type == CellType::Pillar) { - // don't let the DM select walls with torches - solidSurfaceInGridCells.insert({{col, row}, { wall }}); - } } } -void ServerGameState::spawnTorch(GridCell *cell) { +void ServerGameState::spawnTorch(GridCell *cell, glm::vec3 orb_pos, glm::vec3 exit_pos) { glm::vec3 dimensions = Object::models.at(ModelType::Torchlight); glm::vec3 corner( cell->x * this->grid.grid_cell_width, @@ -1600,6 +2250,9 @@ void ServerGameState::spawnTorch(GridCell *cell) { cell->y * this->grid.grid_cell_width ); + float orb_dist = glm::distance(corner, orb_pos); + float exit_dist = glm::distance(corner, exit_pos); + switch (cell->type) { case CellType::TorchDown: { corner.x += (this->grid.grid_cell_width / 2.0f) - (dimensions.x / 2.0f); @@ -1636,7 +2289,120 @@ void ServerGameState::spawnTorch(GridCell *cell) { true )); - this->objects.createObject(new Torchlight(corner)); + this->objects.createObject(new Torchlight(corner, orb_dist, exit_dist)); +} + +Trap* ServerGameState::spawnFireballTrap(GridCell *cell) { + glm::vec3 dimensions = Object::models.at(ModelType::SunGod); + glm::vec3 corner( + (cell->x * Grid::grid_cell_width), + 0.0f, + (cell->y * Grid::grid_cell_width) + ); + Direction dir; + switch (cell->type) { + case CellType::FireballTrapLeft: + dir = Direction::LEFT; + // corner.z -= (dimensions.z / 2.0f); + break; + case CellType::FireballTrapRight: + dir = Direction::RIGHT; + // corner.z -= (dimensions.z / 2.0f); + break; + case CellType::FireballTrapUp: + dir = Direction::UP; + corner.x += (dimensions.x / 2.0f); + break; + case CellType::FireballTrapDown: + corner.x += (dimensions.x / 2.0f); + dir = Direction::DOWN; + break; + default: + dir = Direction::LEFT; + break; + } + FireballTrap* fireBallTrap = new FireballTrap(corner, dir); + this->objects.createObject(fireBallTrap); + return fireBallTrap; +} + +Trap* ServerGameState::spawnArrowTrap(GridCell* cell) { + glm::vec3 corner( + (cell->x* Grid::grid_cell_width), + -3.0f, + (cell->y* Grid::grid_cell_width) + ); + + const float z_nudge = 0.55f; + const float x_nudge = 0.15f; + Direction dir; + if (cell->type == CellType::ArrowTrapDown) { + dir = Direction::DOWN; + corner.x -= x_nudge; + } + else if (cell->type == CellType::ArrowTrapUp) { + dir = Direction::UP; + corner.x -= x_nudge; + } + else if (cell->type == CellType::ArrowTrapLeft) { + dir = Direction::LEFT; + corner.z += z_nudge; + } + else { + dir = Direction::RIGHT; + corner.z += z_nudge; + } + + + ArrowTrap* arrowTrap = new ArrowTrap(corner, dir); + + this->objects.createObject(arrowTrap); + + return arrowTrap; +} + +Trap* ServerGameState::spawnFloorSpike(GridCell* cell) { + glm::vec3 corner( + cell->x * Grid::grid_cell_width, + -0.5f, + cell->y * Grid::grid_cell_width + ); + + FloorSpike* floorSpike = new FloorSpike(corner, Grid::grid_cell_width); + this->objects.createObject(floorSpike); + return floorSpike; +} + +Trap* ServerGameState::spawnLava(GridCell* cell) { + glm::vec3 corner( + cell->x * Grid::grid_cell_width, + 0.0f, + cell->y * Grid::grid_cell_width + ); + + ModelType model_type; + if (cell->type == CellType::LavaCross) { + model_type = ModelType::LavaCross; + } else if (cell->type == CellType::LavaHorizontal) { + model_type = ModelType::LavaHorizontal; + } else { + model_type = ModelType::LavaVertical; + } + + PointLightProperties light_properties{ + .flickering = false, + .min_intensity = 1.0f, + .max_intensity = 1.0f, + .ambient_color = glm::vec3(0.72f, 0.14f, 0.01f), + .diffuse_color = glm::vec3(0.8f, 0.14f, 0.0f), + .specular_color = glm::vec3(0.1f, 0.1f, 0.1f), + .attenuation_linear = 0.35f, + .attenuation_quadratic = 0.44f + }; + + Lava* lava = new Lava(corner, model_type, Grid::grid_cell_width, light_properties); + this->objects.createObject(lava); + return lava; } Grid& ServerGameState::getGrid() { diff --git a/src/server/game/slime.cpp b/src/server/game/slime.cpp index 2e4cb82a..8d5650e5 100644 --- a/src/server/game/slime.cpp +++ b/src/server/game/slime.cpp @@ -4,6 +4,7 @@ #include "shared/utilities/rng.hpp" #include "shared/game/status.hpp" #include "shared/audio/constants.hpp" +#include "server/game/potion.hpp" #include @@ -76,6 +77,7 @@ bool Slime::doBehavior(ServerGameState& state) { for (int p = 0; p < players.size(); p++) { auto player = players.get(p); if (player == nullptr) continue; + if (!player->canBeTargetted()) continue; float distance_to_player = glm::distance(this->physics.shared.corner, player->physics.shared.corner); if (distance_to_player < closest_dist) { @@ -99,6 +101,7 @@ bool Slime::doBehavior(ServerGameState& state) { this->physics.velocity.z = this->jump_strengths.at(this->jump_index) * this->physics.shared.facing.z; this->last_jump_time = now; + this->animState = AnimState::JumpAnim; mutated = true; } @@ -133,8 +136,21 @@ bool Slime::doDeath(ServerGameState& state) { slime2->physics.velocity.x += 0.5f; slime2->physics.velocity.z += 0.5f; } - state.objects.createObject(slime1); - state.objects.createObject(slime2); + SpecificID id1 = state.objects.createObject(slime1); + SpecificID id2 = state.objects.createObject(slime2); + + state.spawner->addEnemy(state, id1); + state.spawner->addEnemy(state, id2); + } + + Enemy::doDeath(state); + + // Drop health potion upon death + auto newCorner = this->physics.shared.corner; + newCorner.y *= 0; + // size 4 slime = 15 kills -> every 2 slime kill, get a potion + if (randomInt(1,30) == 30) { + state.objects.createObject(new Potion(newCorner, glm::vec3(1), PotionType::Health)); } return true; diff --git a/src/server/game/solidsurface.cpp b/src/server/game/solidsurface.cpp index f1584353..13ce5f15 100644 --- a/src/server/game/solidsurface.cpp +++ b/src/server/game/solidsurface.cpp @@ -9,7 +9,6 @@ SolidSurface::SolidSurface( ModelType::Cube) { this->shared.surfaceType = type; - this->shared.dm_highlight = false; this->shared.is_internal = false; } @@ -19,10 +18,6 @@ void SolidSurface::setSurfaceType(SurfaceType type) { this->shared.surfaceType = type; } -void SolidSurface::setDMHighlight(bool highlight) { - this->shared.dm_highlight = highlight; -} - /* SharedGameState generation */ SharedObject SolidSurface::toShared() { SharedObject sharedSolidSurface = Object::toShared(); diff --git a/src/server/game/spawner.cpp b/src/server/game/spawner.cpp new file mode 100644 index 00000000..a555b948 --- /dev/null +++ b/src/server/game/spawner.cpp @@ -0,0 +1,197 @@ +#include "server/game/spawner.hpp" +#include "server/game/slime.hpp" +#include "server/game/python.hpp" +#include "server/game/item.hpp" +#include "server/game/minotaur.hpp" +#include "server/game/servergamestate.hpp" +#include "shared/utilities/rng.hpp" + +/* + * When adding new enemies to spawner class: + * 1) add the enemy value in the constructor + * 2) Create a new case in spawnEnemy with the index from valueMap + * 3) add it to addEnemy() method if needed + */ +Spawner::Spawner() { + this->enemyValueCap = MAX_ENEMY_VALUE; + this->currentEnemyValue = 0; + this->dummyItem = nullptr; + this->smallDummyItem = nullptr; + + this->valueMap.push_back(20); // 0: Big slime (size = 4) + this->valueMap.push_back(10); // 1: Medium slime (size = 3) + this->valueMap.push_back(5); // 2: Small slime (size = 2) + this->valueMap.push_back(2); // 3: Mini slime (size = 1) + this->valueMap.push_back(25); // 4: Minotaur + this->valueMap.push_back(10); // 5: Python +} + +void Spawner::spawn(ServerGameState& state) { + auto val = this->currentEnemyValue; + + if (val >= MAX_ENEMY_VALUE) { return; } + + auto valRemaining = this->enemyValueCap - val; + + // temp numbers, to tune later... + // fill a portion of the cap immediately + if (val < MAX_ENEMY_VALUE * 0.1) { + spawnEnemy(state, valRemaining); + } + else { + // 1/300 chance every 30ms -> expected spawn every 9s + if (randomInt(1, 300) == 1) { + spawnEnemy(state, valRemaining); + } + } +} + +void Spawner::spawnEnemy(ServerGameState& state, int valueRemaining) { + glm::vec3 spawnLocation = findEmptyPosition(state); + + int index = 0; + // Get enemy that can fit within value + while (true) { + //index = 5; //spawn pythons only + index = randomInt(0, valueMap.size()-1); + + // Dont spawn mini slimes + if (index == 3) { continue; } + break; + } + + SpecificID enemyID = 0; + auto value = 0; + switch (index) { + case 0: + case 1: + case 2: { + auto size = 4 - index; + enemyID = state.objects.createObject(new Slime( + spawnLocation, + glm::vec3(1, 0, 1), + size + )); + if (size == 4) { + value = valueMap[0]; + } + else if (size == 3) { + value = valueMap[1]; + } + else { + value = valueMap[2]; + } + break; + } + case 4: { + enemyID = state.objects.createObject(new Minotaur( + spawnLocation, + glm::vec3(1, 0, 1) + )); + value = valueMap[4]; + break; + } + case 5: { + enemyID = state.objects.createObject(new Python( + spawnLocation, + glm::vec3(1, 0, 1) + )); + value = valueMap[5]; + break; + } + } + + this->enemiesAlive[enemyID] = value; + this->currentEnemyValue += value; +} + +glm::vec3 Spawner::findEmptyPosition(ServerGameState& state) { + // check that no collision in the cell you are spawning in + + auto& grid = state.getGrid(); + int cols = grid.getColumns(); + int rows = grid.getRows(); + + glm::ivec2 random_cell; + CellType celltype; + while (true) { + random_cell.x = randomInt(0, cols - 1); + random_cell.y = randomInt(0, rows - 1); // corresponds to z in the world + celltype = grid.getCell(random_cell.x, random_cell.y)->type; + + // Manually check where spawning should always not happen + if (celltype == CellType::OutsideTheMaze || celltype == CellType::Wall || celltype == CellType::Pillar || \ + celltype == CellType::FireballTrapLeft || celltype == CellType::FireballTrapRight || \ + celltype == CellType::FireballTrapDown || celltype == CellType::FireballTrapUp || \ + celltype == CellType::FloorSpikeFull || celltype == CellType::FloorSpikeVertical || \ + celltype == CellType::FloorSpikeHorizontal || celltype == CellType::FakeWall || + celltype == CellType::ArrowTrapUp || celltype == CellType::ArrowTrapDown || \ + celltype == CellType::ArrowTrapRight || celltype == CellType::ArrowTrapLeft || \ + celltype == CellType::TeleporterTrap || celltype == CellType::Exit + ) { + continue; + } + + // add small offset for spawn + if (!state.hasObjectCollided(this->dummyItem, + glm::vec3(random_cell.x * Grid::grid_cell_width + 0.01f, 0, random_cell.y * Grid::grid_cell_width + 0.01f))) { + state.objects.moveObject(this->dummyItem, glm::vec3(-1, 0, -1)); + break; + } + } + + return glm::vec3(random_cell.x * Grid::grid_cell_width + 0.01f, 0, random_cell.y * Grid::grid_cell_width + 0.01f); +} + +void Spawner::spawnDummy(ServerGameState& state) { + // Set dummy item with the biggest possible enemy size + // Used for checking spawnable tile + SpecificID itemID = state.objects.createObject(new Item(ObjectType::Item, true, glm::vec3(-1, 0, -1), ModelType::Cube, glm::vec3(1))); + auto dummy = state.objects.getItem(itemID); + dummy->physics.shared.dimensions = glm::vec3(4.0f, 7.0f, 4.0f); + this->dummyItem = dummy; + dummy->iteminfo.held = true; +} + +void Spawner::spawnSmallDummy(ServerGameState& state) { + // Set dummy item with the biggest possible enemy size + // Used for checking spawnable tile + SpecificID itemID = state.objects.createObject(new Item(ObjectType::Item, true, glm::vec3(-1, 0, -1), ModelType::Cube, glm::vec3(1))); + auto dummy = state.objects.getItem(itemID); + dummy->physics.shared.dimensions = glm::vec3(1.0f, 1.0f, 1.0f); + this->smallDummyItem = dummy; + dummy->iteminfo.held = true; +} + +void Spawner::addEnemy(ServerGameState& state, SpecificID id) { + auto enemy = state.objects.getEnemy(id); + auto type = enemy->type; + + auto value = 0; + switch (type) { + case ObjectType::Slime: + Slime* slime = dynamic_cast(enemy); + if (slime->size == 4) { + value = valueMap[0]; + } + else if (slime->size == 3) { + value = valueMap[1]; + } + else if (slime->size == 2) { + value = valueMap[2]; + } + else { + value = valueMap[3]; + } + break; + } + + this->enemiesAlive[id] = value; + this->currentEnemyValue += value; +} + +void Spawner::decreaseValue(SpecificID id) { + auto value = this->enemiesAlive[id]; + this->enemiesAlive.erase(id); + this->currentEnemyValue -= value; +} \ No newline at end of file diff --git a/src/server/game/spell.cpp b/src/server/game/spell.cpp index 09652f86..0d6ca871 100644 --- a/src/server/game/spell.cpp +++ b/src/server/game/spell.cpp @@ -7,9 +7,10 @@ #include "server/game/object.hpp" #include "server/game/projectile.hpp" #include "shared/utilities/rng.hpp" +#include "shared/audio/constants.hpp" Spell::Spell(glm::vec3 corner, glm::vec3 dimensions, SpellType spelltype): - Item(ObjectType::Spell, false, corner, ModelType::Cube, dimensions) + Item(ObjectType::Spell, false, corner, ModelType::SpellOrb, dimensions) { this->spellType = spelltype; @@ -42,6 +43,7 @@ void Spell::useItem(Object* other, ServerGameState& state, int itemSelected) { spell_origin += player->physics.shared.facing * 2.0f; + auto sound = ServerSFX::Spell; switch (this->spellType) { case SpellType::Fireball: { state.objects.createObject(new SpellOrb(spell_origin, player->physics.shared.facing, SpellType::Fireball)); @@ -52,18 +54,23 @@ void Spell::useItem(Object* other, ServerGameState& state, int itemSelected) { break; } case SpellType::Teleport: { - auto players = state.objects.getPlayers(); - Player* rand_player; - while (true) { - rand_player = players.get(randomInt(0, players.size() - 1)); - if (players.size() == 1) { - break; - } - if (player->typeID != rand_player->typeID) { - break; + sound = ServerSFX::Teleport; + + std::vector valid_players; + + auto all_players = state.objects.getPlayers(); + for (int i = 0; i < all_players.size(); i++) { + if (all_players.get(i) != nullptr && all_players.get(i)->typeID != player->typeID) { + valid_players.push_back(all_players.get(i)); } } + if (valid_players.size() < 1) { + return; + } + + Player* rand_player = valid_players.at(randomInt(0, valid_players.size() - 1)); + auto& grid = state.getGrid(); int r_col = 0; int r_row = 0; @@ -87,6 +94,15 @@ void Spell::useItem(Object* other, ServerGameState& state, int itemSelected) { } } + // Play sound + state.soundTable().addNewSoundSource(SoundSource( + sound, + player->physics.shared.getCenterPosition(), + DEFAULT_VOLUME, + SHORT_DIST, + SHORT_ATTEN + )); + this->castLimit -= 1; player->sharedInventory.usesRemaining[itemSelected] = this->castLimit; @@ -118,5 +134,12 @@ void Spell::doCollision(Object* other, ServerGameState& state) { if (pickedUp) { this->iteminfo.held = true; this->physics.collider = Collider::None; + state.soundTable().addNewSoundSource(SoundSource( + ServerSFX::ItemPickUp, + other->physics.shared.getCenterPosition(), + DEFAULT_VOLUME, + SHORT_DIST, + SHORT_ATTEN + )); } } \ No newline at end of file diff --git a/src/server/game/spiketrap.cpp b/src/server/game/spiketrap.cpp index a7c45213..e1eafc34 100644 --- a/src/server/game/spiketrap.cpp +++ b/src/server/game/spiketrap.cpp @@ -14,15 +14,16 @@ const std::chrono::seconds SpikeTrap::TIME_UNTIL_RESET = 10s; SpikeTrap::SpikeTrap(glm::vec3 corner, glm::vec3 dimensions): Trap(ObjectType::SpikeTrap, true, corner, Collider::Box, ModelType::Cube, dimensions) { - this->dropped_time = std::chrono::system_clock::now(); + this->dropped_time = std::chrono::system_clock::now() - 100000s; this->physics.feels_gravity = false; } bool SpikeTrap::shouldTrigger(ServerGameState& state) { - if (this->info.triggered) { + if (this->info.triggered || this->info.dm_hover) { return false; } + auto now = std::chrono::system_clock::now(); // only drop if it isn't currently triggered, and it has been at least 5 seconds since the // last drop @@ -30,6 +31,7 @@ bool SpikeTrap::shouldTrigger(ServerGameState& state) { return false; } + auto players = state.objects.getPlayers(); for (int p = 0; p < players.size(); p++) { auto player = players.get(p); @@ -42,6 +44,7 @@ bool SpikeTrap::shouldTrigger(ServerGameState& state) { } } + return false; } diff --git a/src/server/game/teleportertrap.cpp b/src/server/game/teleportertrap.cpp index 9e62b97e..bcb13ff0 100644 --- a/src/server/game/teleportertrap.cpp +++ b/src/server/game/teleportertrap.cpp @@ -2,6 +2,8 @@ #include "server/game/servergamestate.hpp" #include "shared/utilities/rng.hpp" #include "server/game/objectmanager.hpp" +#include "shared/audio/constants.hpp" +#include "server/game/exit.hpp" #include using namespace std::chrono_literals; @@ -28,18 +30,48 @@ void TeleporterTrap::reset(ServerGameState& state) { } void TeleporterTrap::doCollision(Object* other, ServerGameState& state) { + if (this->info.dm_hover) { + return; + } + int r_col = 0; int r_row = 0; auto& grid = state.getGrid(); + std::optional exit_pos; + + auto exits = state.objects.getExits(); + for (int i = 0; i < exits.size(); i++) { + auto exit = exits.get(i); + if (exit == nullptr) continue; + + exit_pos = exit->physics.shared.getCenterPosition(); + break; + } + + int attempts = 0; + while (true) { + attempts++; r_col = randomInt(0, grid.getColumns() - 1); r_row = randomInt(0, grid.getRows() - 1); + if (attempts < 10 && exit_pos.has_value() && glm::distance(glm::vec2(Grid::getGridCellFromPosition(exit_pos.value())), glm::vec2(r_col, r_row)) < 20.0f) { + continue; + } + if (grid.getCell(r_col, r_row)->type == CellType::Empty) { break; } } state.objects.moveObject(other, glm::vec3(r_col * grid.grid_cell_width, 0.0f, r_row * grid.grid_cell_width)); + + state.soundTable().addNewSoundSource(SoundSource( + ServerSFX::Teleport, + other->physics.shared.getCenterPosition(), + DEFAULT_VOLUME, + SHORT_DIST, + SHORT_ATTEN + )); } diff --git a/src/server/game/torchlight.cpp b/src/server/game/torchlight.cpp index 6042dc7e..b4a8fd0b 100644 --- a/src/server/game/torchlight.cpp +++ b/src/server/game/torchlight.cpp @@ -3,57 +3,109 @@ #include "glm/fwd.hpp" #include "server/game/collider.hpp" #include "server/game/object.hpp" +#include "shared/game/point_light.hpp" #include "shared/game/sharedobject.hpp" #include "shared/utilities/rng.hpp" +#include "server/game/grid.hpp" SharedObject Torchlight::toShared() { auto so = Object::toShared(); - so.pointLightInfo = SharedPointLightInfo { + so.pointLightInfo = SharedPointLightInfo{ .intensity = this->curr_intensity, .ambient_color = this->properties.ambient_color, .diffuse_color = this->properties.diffuse_color, .specular_color = this->properties.specular_color, .attenuation_linear = this->properties.attenuation_linear, .attenuation_quadratic = this->properties.attenuation_quadratic, + .is_cut = this->is_cut }; return so; } Torchlight::Torchlight( - glm::vec3 corner): + glm::vec3 corner, float dist_orb, float dist_exit): Object(ObjectType::Torchlight, Physics(false, Collider::Box, corner, glm::vec3(0.0f), glm::vec3(1.0f)), ModelType::Torchlight) { - // for amber orange lights - // this->properties = TorchlightProperties { - // .flickering = true, - // .min_intensity = 0.3f, - // .max_intensity = 1.0f, - // .ambient_color = glm::vec3(0.5f, 0.25f, 0.015f), - // .diffuse_color = glm::vec3(1.0f, 0.5f, 0.03f), - // .specular_color = glm::vec3(0.5f, 0.25f, 0.015f), - // .attenuation_linear = 0.07f, - // .attenuation_quadratic = 0.017f - // }; - - // for blue lights - this->properties = TorchlightProperties { - .flickering = true, - .min_intensity = 0.1f, - .max_intensity = 0.5f, - .ambient_color = glm::vec3(0.0f, 0.75f, 0.67f), - .diffuse_color = glm::vec3(0.0f, 0.75f, 0.67f), - .specular_color = glm::vec3(0.0f, 0.35f, 0.33f), - .attenuation_linear = 0.07f, - .attenuation_quadratic = 0.017f + const float MIN_ORB_DIST = Grid::grid_cell_width * 60.0f; + const float MIN_EXIT_DIST = Grid::grid_cell_width * 60.0f; // min distance to start shade white + + const float AMBER_MIN_INTENSITY = 0.3f; + const float AMBER_MAX_INTENSITY = 1.0f; + const glm::vec3 AMBER_AMBIENT(0.05f, 0.05f, 0.05f); + const glm::vec3 AMBER_DIFFUSE(1.0f, 0.5f, 0.03f); + const glm::vec3 AMBER_SPECULAR(0.5f, 0.25f, 0.015f); + + const float BLUE_MIN_INTENSITY = 0.1f; + const float BLUE_MAX_INTENSITY = 0.5f; + const glm::vec3 BLUE_AMBIENT(0.0f, 0.75f, 0.67f); + const glm::vec3 BLUE_DIFFUSE(0.0f, 0.75f, 0.67f); + const glm::vec3 BLUE_SPECULAR(0.0f, 0.35, 0.33f); + + const float WHITE_MIN_INTENSITY = 0.1f; + const float WHITE_MAX_INTENSITY = 0.3f; + const glm::vec3 WHITE_AMBIENT(1.05f, 1.05f, 1.05f); + const glm::vec3 WHITE_DIFFUSE(1.0f, 1.0f, 1.0f); + const glm::vec3 WHITE_SPECULAR(0.5f, 0.5f, 0.5f); + + const float ATTEN_LINEAR = 0.07f; + const float ATTEN_QUAD = 0.017f; + + // higher intensity takes you closer to color2 + const auto interpolateColor = [](float intensity, glm::vec3 color1, glm::vec3 color2) { + return color1 + intensity * (color2 - color1); }; + + if (dist_orb < MIN_ORB_DIST) { + // the closer you get, the higher the factor to make it more white is multiplied + const float blue_intensity = (MIN_ORB_DIST - dist_orb) / MIN_ORB_DIST; + + // close to orb, so shade blue + this->properties = PointLightProperties { + .flickering = true, + .min_intensity = BLUE_MIN_INTENSITY, + .max_intensity = BLUE_MAX_INTENSITY, + .ambient_color = interpolateColor(blue_intensity, AMBER_AMBIENT, BLUE_DIFFUSE), + .diffuse_color = interpolateColor(blue_intensity, AMBER_DIFFUSE, BLUE_DIFFUSE), + .specular_color = interpolateColor(blue_intensity, AMBER_SPECULAR, BLUE_SPECULAR), + .attenuation_linear = ATTEN_LINEAR, + .attenuation_quadratic = ATTEN_QUAD + }; + } else if (dist_exit < MIN_EXIT_DIST) { + const float white_intensity = (MIN_EXIT_DIST - dist_exit) / MIN_EXIT_DIST; + // close to exit, so shade white + // TEMP: still amber + this->properties = PointLightProperties { + .flickering = true, + .min_intensity = WHITE_MIN_INTENSITY, + .max_intensity = WHITE_MAX_INTENSITY, + .ambient_color = interpolateColor(white_intensity, AMBER_AMBIENT, WHITE_AMBIENT), + .diffuse_color = interpolateColor(white_intensity, AMBER_DIFFUSE, WHITE_DIFFUSE), + .specular_color = interpolateColor(white_intensity, AMBER_SPECULAR, WHITE_SPECULAR), + .attenuation_linear = ATTEN_LINEAR, + .attenuation_quadratic = ATTEN_QUAD + }; + } else { + // shade normal amber + this->properties = PointLightProperties { + .flickering = true, + .min_intensity = AMBER_MIN_INTENSITY, + .max_intensity = AMBER_MAX_INTENSITY, + .ambient_color = AMBER_AMBIENT, + .diffuse_color = AMBER_DIFFUSE, + .specular_color = AMBER_SPECULAR, + .attenuation_linear = ATTEN_LINEAR, + .attenuation_quadratic = ATTEN_QUAD + }; + } + init(); } Torchlight::Torchlight( glm::vec3 corner, - const TorchlightProperties& properties): + const PointLightProperties& properties): Object(ObjectType::Torchlight, Physics(false, Collider::Box, corner, glm::vec3(0.0f), glm::vec3(1.0f)), ModelType::Torchlight), @@ -63,6 +115,8 @@ Torchlight::Torchlight( } void Torchlight::init() { + this->is_cut = false; + this->inc_intensity = true; // if not flickering, set intensity to a static // value @@ -72,17 +126,41 @@ void Torchlight::init() { // if flickering randomize initial // animation step to offset flickering this->curr_step = randomDouble(0.0f, 1.0f); - this->flickering_speed = randomDouble(0.005f, 0.01f); + this->flickering_speed = randomDouble(0.008, 0.014f); } } Torchlight::~Torchlight() {} -void Torchlight::doTick(ServerGameState& state) { +bool Torchlight::doTick(ServerGameState& state, std::optional lightning_light_cut_pos, std::optional action_light_cut_pos) { if(!this->properties.flickering) { - return; + return false; + } + + // cut this light if within position of light cut + if (lightning_light_cut_pos.has_value()) { + glm::vec3 pos = lightning_light_cut_pos.value(); + + // if within threshold, get out + if (glm::distance(pos, this->physics.shared.getCenterPosition()) <= LIGHT_CUT_RANGE_LIGHTNING) { + this->curr_intensity = 0.2f; + return false; + } + } + + if (action_light_cut_pos.has_value()) { + glm::vec3 pos = action_light_cut_pos.value(); + + // if within threshold, black out (slightly expand the light cut action range) + if (glm::distance(pos, this->physics.shared.getCenterPosition()) <= (LIGHT_CUT_RANGE)) { + this->curr_intensity = 0.2f; + this->is_cut = true; + return true; + } } + this->is_cut = false; + // either increment or decrement intensity if (inc_intensity) { this->curr_step += this->flickering_speed; @@ -110,8 +188,14 @@ void Torchlight::doTick(ServerGameState& state) { } else if (this->curr_intensity < this->properties.min_intensity) { this->curr_intensity = this->properties.min_intensity; } + return false; } float Torchlight::getIntensity() const { return this->curr_intensity; } + + +void Torchlight::overrideIntensity(float val) { + this->curr_intensity = val; +} \ No newline at end of file diff --git a/src/server/game/trap.cpp b/src/server/game/trap.cpp index f324f340..8cf429b4 100644 --- a/src/server/game/trap.cpp +++ b/src/server/game/trap.cpp @@ -2,7 +2,7 @@ Trap::Trap(ObjectType type, bool movable, glm::vec3 corner, Collider collider, ModelType model, glm::vec3 dimensions): Object(type, Physics(movable, collider, corner, glm::vec3(0.0f), dimensions), model), - info(SharedTrapInfo {.triggered = false} ) + info(SharedTrapInfo {.triggered = false, .dm_hover = false } ) { this->is_dm_trap = false; this->expiration = std::chrono::system_clock::now(); @@ -26,6 +26,10 @@ void Trap::setIsDMTrap(bool is_dm_trap) { this->is_dm_trap = is_dm_trap; } +void Trap::setIsDMTrapHover(bool is_dm_trap_hover) { + this->info.dm_hover = is_dm_trap_hover; +} + void Trap::setExpiration(std::chrono::time_point expiration) { this->expiration = expiration; } diff --git a/src/server/game/weapon.cpp b/src/server/game/weapon.cpp index 06467807..97f87374 100644 --- a/src/server/game/weapon.cpp +++ b/src/server/game/weapon.cpp @@ -26,6 +26,10 @@ Weapon::Weapon(glm::vec3 corner, glm::vec3 dimensions, WeaponType weaponType): this->modelType = ModelType::Hammer; this->delay = HAMMER_TOTAL; break; + case WeaponType::Lightning: + this->modelType = ModelType::Lightning; + this->delay = 0; + break; } } @@ -49,6 +53,8 @@ void Weapon::useItem(Object* other, ServerGameState& state, int itemSelected) { case WeaponType::Hammer: state.objects.createObject(new BigAttack(player, attack_origin, player->physics.shared.facing)); break; + default: + break; } this->attacked_time = std::chrono::system_clock::now(); this->resetAttack = false; @@ -57,6 +63,26 @@ void Weapon::useItem(Object* other, ServerGameState& state, int itemSelected) { // Item::useItem(other, state, itemSelected); } +void Weapon::useLightning(Object* other, ServerGameState& state, glm::vec3 corner) { + if (this->resetAttack) { + DungeonMaster* dm = dynamic_cast(other); + PointLightProperties light_properties{ + .flickering = false, + .min_intensity = 1.0f, + .max_intensity = 1.0f, + .ambient_color = glm::vec3(1.0f, 0.94f, 0.0f), + .diffuse_color = glm::vec3(1.0f, 0.94f, 0.0f), + .specular_color = glm::vec3(0.1f, 0.1f, 0.1f), + .attenuation_linear = 0.045f, + .attenuation_quadratic = 0.0075f + }; + state.objects.createObject(new Lightning(corner, dm->physics.shared.facing, light_properties)); + + this->attacked_time = std::chrono::system_clock::now(); + this->resetAttack = false; + } +} + void Weapon::reset(ServerGameState& state) { auto now = std::chrono::system_clock::now(); std::chrono::duration elapsed_milliseconds{ now - this->attacked_time }; diff --git a/src/server/game/weaponcollider.cpp b/src/server/game/weaponcollider.cpp index eabae696..940098c8 100644 --- a/src/server/game/weaponcollider.cpp +++ b/src/server/game/weaponcollider.cpp @@ -3,6 +3,9 @@ #include "server/game/creature.hpp" #include "server/game/weaponcollider.hpp" #include "server/game/servergamestate.hpp" +#include "server/game/item.hpp" +#include "server/game/mirror.hpp" +#include "shared/audio/constants.hpp" #include @@ -14,21 +17,105 @@ WeaponCollider::WeaponCollider(Player* usedPlayer, glm::vec3 corner, glm::vec3 f this->preparing_time = std::chrono::system_clock::now(); this->usedPlayer = usedPlayer; this->info.attacked = false; + this->info.lightning = false; + if(!this->opt.followPlayer) { + this->info.lightning = true; + } + + this->playSound = false; + this->sound = ServerSFX::TEMP; } void WeaponCollider::doCollision(Object* other, ServerGameState& state) { - Creature* creature = dynamic_cast(other); if (creature == nullptr) return; // don't dmg yourself - if (creature->globalID == this->usedPlayer->globalID) return; + if (this->usedPlayer != nullptr) { + + if (creature->globalID == this->usedPlayer->globalID) return; + } + + // If this weapon collider is a lightning bolt and it collides with + // a Player whose currently using a Mirror, then don't do any damage + // to the player and destroy the player's mirror. + // Also, paralyze the DM for some amount of time. + if (this->info.lightning && creature->type == ObjectType::Player) { + std::cout << "Applying lightning damage!" << std::endl; + Player* player = dynamic_cast(creature); + + // Return early if this player is currently invulnerable to lightning + if (player->isInvulnerableToLightning()) { + std::cout << "Player is invulnerable to lightning - applying no damage." << std::endl; + return; + } + + for (int i = 0; i < player->inventory.size(); i++) { + if (player->inventory[i] == -1) + continue; + + // Get item + Item* item = state.objects.getItem(player->inventory[i]); + + // If the item is a mirror and is used, then apply special + // behavior + if (item->type == ObjectType::Mirror && item->iteminfo.used) { + std::cout << "Player using a mirror got hit by a lightning bolt!" << std::endl; + std::cout << "Deleting mirror!" << std::endl; + + Mirror* mirror = dynamic_cast(item); + + // Add mirror shatter sound effect + state.soundTable().addNewSoundSource(SoundSource( + ServerSFX::MirrorShatter, + player->physics.shared.corner, + FULL_VOLUME, + FAR_DIST, + FAR_ATTEN + )); + + // Mark player as invulnerable to lightning for 1 second + player->setInvulnerableToLightning(true, 1); + + // Inform player they successfully relfected a lightning bolt + // using a mirror + player->info.used_mirror_to_reflect_lightning = true; + // Destroy the mirror that the player is holding + mirror->dropItem(player, state, i, 0.0f); + state.markForDeletion(item->globalID); + + // Remove mirror from player's list of used items + player->sharedInventory.usedItems.erase(item->typeID); + + // Paralyze the DM for 5 seconds + state.objects.getDM()->setParalysis(true, 5); + + // Don't apply damage to the player + return; + } + } + } + // do damage if creature creature->stats.health.decrease(this->opt.damage); + + if (this->usedPlayer == nullptr) { + auto knockback = glm::normalize( + other->physics.shared.getCenterPosition() - this->physics.shared.getCenterPosition()); + + creature->physics.currTickVelocity = 0.7f * knockback; + } + else { + auto knockback = glm::normalize( + other->physics.shared.getCenterPosition() - this->usedPlayer->physics.shared.getCenterPosition()); + + creature->physics.currTickVelocity = 0.4f * knockback; + } } void WeaponCollider::updateMovement(ServerGameState& state) { + if(!this->opt.followPlayer) { return; } glm::vec3 attack_origin( this->usedPlayer->physics.shared.getCenterPosition().x, @@ -52,14 +139,46 @@ void WeaponCollider::updateMovement(ServerGameState& state) { state.objects.moveObject(this, attack_origin); } -bool WeaponCollider::readyTime() { +bool WeaponCollider::readyTime(ServerGameState& state) { if (this->info.attacked) { return true; } + + if (!this->playSound && this->info.lightning) { + state.soundTable().addNewSoundSource(SoundSource( + ServerSFX::ElectricHum, + this->physics.shared.getCenterPosition(), + DEFAULT_VOLUME, + MEDIUM_DIST, + MEDIUM_ATTEN + )); + this->playSound = true; + } + else if (!this->playSound) { + state.soundTable().addNewSoundSource(SoundSource( + this->sound, + this->physics.shared.getCenterPosition(), + MIDDLE_VOLUME, + SHORT_DIST, + SHORT_ATTEN + )); + this->playSound = true; + } + auto now = std::chrono::system_clock::now(); std::chrono::duration elapsed_milliseconds{ now - this->preparing_time }; if (elapsed_milliseconds > std::chrono::milliseconds(this->opt.timeUntilAttack)) { this->info.attacked = true; this->attacked_time = now; this->physics.collider = Collider::Box; + if (this->info.lightning) { + state.soundTable().addNewSoundSource(SoundSource( + ServerSFX::Thunder, + this->physics.shared.getCenterPosition(), + DEFAULT_VOLUME, + FAR_DIST, + FAR_ATTEN + )); + } + this->playSound = true; return true; } return false; diff --git a/src/server/lobbybroadcaster.cpp b/src/server/lobbybroadcaster.cpp index 92e1db96..fb737f97 100644 --- a/src/server/lobbybroadcaster.cpp +++ b/src/server/lobbybroadcaster.cpp @@ -38,8 +38,8 @@ void LobbyBroadcaster::setLobbyInfo(const Lobby& lobby_info) { std::unique_lock lock(this->mut); this->bcast_info = ServerLobbyBroadcastPacket { .lobby_name = lobby_info.name, - .slots_taken = static_cast(lobby_info.players.size()), - .slots_avail = static_cast(lobby_info.max_players - lobby_info.players.size()) + .slots_taken = static_cast(lobby_info.numPlayersInLobby()), + .slots_avail = static_cast(lobby_info.max_players - lobby_info.numPlayersInLobby()) }; } diff --git a/src/server/main.cpp b/src/server/main.cpp index a99bab66..5094b6a9 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -22,9 +22,8 @@ int main(int argc, char** argv) { if (wait_time <= 0ms) { std::cerr << "WARNING: did not meet tick rate!\n"; - context.run_for(5ms); } else { - // Wait until next tick + // Wait until next tick, and while idle accept new TCP connections context.run_for(wait_time); } } diff --git a/src/server/server.cpp b/src/server/server.cpp index 0653b7c2..87e103d5 100644 --- a/src/server/server.cpp +++ b/src/server/server.cpp @@ -13,15 +13,22 @@ #include #include #include +#include #include "boost/variant/get.hpp" +#include "server/game/exit.hpp" #include "server/game/objectmanager.hpp" +#include "server/game/weaponcollider.hpp" #include "server/game/potion.hpp" +#include "server/game/weapon.hpp" #include "server/game/enemy.hpp" #include "server/game/player.hpp" #include "shared/game/event.hpp" #include "server/game/servergamestate.hpp" #include "server/game/object.hpp" +#include "shared/game/sharedmodel.hpp" +#include "server/game/trap.hpp" +#include "server/game/projectile.hpp" #include "shared/network/session.hpp" #include "shared/network/packet.hpp" #include "shared/network/constants.hpp" @@ -30,6 +37,9 @@ #include "shared/utilities/constants.hpp" #include "shared/utilities/light.hpp" #include "shared/utilities/typedefs.hpp" +#include "shared/utilities/config.hpp" +#include "shared/utilities/rng.hpp" + using namespace std::chrono_literals; using namespace boost::asio::ip; @@ -39,7 +49,8 @@ Server::Server(boost::asio::io_context& io_context, GameConfig config) acceptor(io_context, tcp::endpoint(tcp::v4(), config.network.server_port)), socket(io_context), world_eid(0), - state(ServerGameState(GamePhase::LOBBY, config)) + state(ServerGameState(GamePhase::LOBBY, config)), + config(config) { _doAccept(); // start asynchronously accepting @@ -69,10 +80,10 @@ EventList Server::getAllClientEvents() { EventList allEvents; // Loop through each session - for (const auto& [eid, is_dm, _ip, session] : this->sessions) { // cppcheck-suppress unusedVariable - if (auto s = session.lock()) { + for (const auto& [eid, _is_dm, _ip, session] : this->sessions) { // cppcheck-suppress unusedVariable + if (session->isOkay()) { // Get events from the current session - std::vector sessionEvents = s->getEvents(); + std::vector sessionEvents = session->handleAllReceivedPackets(); // Put events into the allEvents vector, prepending each event with the id of the // client that requested it @@ -88,10 +99,11 @@ EventList Server::getAllClientEvents() { void Server::sendUpdateToAllClients(Event event) { for (const auto& [_eid, is_dm, _ip, session] : this->sessions) { // cppcheck-suppress unusedVariable - if (auto s = session.lock()) { - s->sendEventAsync(event); + if (session->isOkay()) { + session->sendEvent(event); } } + } void Server::sendLightSourceUpdates(EntityID playerID) { @@ -121,6 +133,42 @@ void Server::sendLightSourceUpdates(EntityID playerID) { closestPointLights.push(torch->globalID); } + for(int i = 0; i < this->state.objects.getExits().size(); i++) { + auto exit = this->state.objects.getExits().get(i); + if (exit == nullptr) continue; + closestPointLights.push(exit->globalID); + } + + for(int i = 0; i < this->state.objects.getItems().size(); i++) { + auto item = this->state.objects.getItems().get(i); + if (item == nullptr) continue; + if (item->type != ObjectType::Orb) continue; + closestPointLights.push(item->globalID); + break; // only one orb + } + + for(int i = 0; i < this->state.objects.getWeaponColliders().size(); i++) { + auto item = this->state.objects.getWeaponColliders().get(i); + if (item == nullptr) continue; + if (item->modelType != ModelType::Lightning) continue; + closestPointLights.push(item->globalID); + break; // only one lightning + } + + for(int i = 0; i < this->state.objects.getTraps().size(); i++) { + auto lava = this->state.objects.getTraps().get(i); + if (lava == nullptr) continue; + if (lava->type != ObjectType::Lava) continue; + closestPointLights.push(lava->globalID); + } + + for(int i = 0; i < this->state.objects.getProjectiles().size(); i++) { + auto proj = this->state.objects.getProjectiles().get(i); + if (proj == nullptr) continue; + if (proj->modelType != ModelType::Arrow && proj->modelType != ModelType::Fireball && proj->modelType != ModelType::SpellOrb) continue; + closestPointLights.push(proj->globalID); + + } // put set into an array UpdateLightSourcesEvent event_data; @@ -129,13 +177,22 @@ void Server::sendLightSourceUpdates(EntityID playerID) { EntityID light_id = closestPointLights.top(); closestPointLights.pop(); - auto torchlight = dynamic_cast(this->state.objects.getObject(light_id)); - if (torchlight != nullptr) { + if (this->state.objects.getObject(light_id)->type == ObjectType::Torchlight) { + auto torchlight = dynamic_cast(this->state.objects.getObject(light_id)); + if (torchlight != nullptr) { + event_data.lightSources[curr_light_num] = UpdateLightSourcesEvent::UpdatedLightSource { + .eid = light_id, + .intensity = torchlight->getIntensity(), + .is_cut = torchlight->is_cut + }; + } + } else { event_data.lightSources[curr_light_num] = UpdateLightSourcesEvent::UpdatedLightSource { .eid = light_id, - .intensity = torchlight->getIntensity() + .intensity = 1.0f, + .is_cut = false }; - } + } curr_light_num++; } @@ -143,8 +200,8 @@ void Server::sendLightSourceUpdates(EntityID playerID) { auto session_ref = by_id.find(playerID); if (session_ref != by_id.end()) { auto session = session_ref->session; - if (!session.expired()) { - session.lock()->sendEventAsync(Event( + if (session->isOkay()) { + session->sendEvent(Event( this->world_eid, EventType::UpdateLightSources, event_data)); @@ -156,33 +213,214 @@ std::chrono::milliseconds Server::doTick() { auto start = std::chrono::high_resolution_clock::now(); switch (this->state.getPhase()) { - case GamePhase::LOBBY: - // Go through sessions and update GameState lobby info - // TODO: move this into updateGameState or something else - for (const auto& [eid, is_dm, ip, session]: this->sessions) { - if (auto s = session.lock()) { - this->state.addPlayerToLobby(eid, s->getInfo().client_name.value_or("UNKNOWN NAME")); - } else { + case GamePhase::LOBBY: { + // Go through sessions and update GameState lobby info + for (const auto& [eid, is_dm, ip, session] : this->sessions) { + if (session->isOkay()) { + this->state.addPlayerToLobby(LobbyPlayer(eid, PlayerRole::Unknown, false)); + } + else { this->state.removePlayerFromLobby(eid); } } - if (this->state.getLobby().players.size() >= this->state.getLobby().max_players) { - this->state.setPhase(GamePhase::GAME); - // TODO: figure out how to selectively broadcast to only the players that were already in the lobby - // this->lobby_broadcaster.stopBroadcasting(); + if (this->state.getLobby().numPlayersInLobby() >= this->state.getLobby().max_players) { + // Selectively broadcast only to players that were already in the lobby? + // Note: This is currently marked as a TODO in the dungeon master branch + } + + // Handle ready and start game events + EventList clientEvents = getAllClientEvents(); + + + for (const auto& [src_eid, event] : clientEvents) { + // Skip non-lobby action events + // std::cout << event << "\n"; + if (event.type != EventType::LobbyAction) { + continue; + } + + LobbyActionEvent lobbyEvent = boost::get(event.data); + + switch (lobbyEvent.action) { + case LobbyActionEvent::Action::Ready: { + // Client player declares themselves ready + boost::optional player = this->state.getLobby().getPlayer(src_eid); + + if (!player.has_value()) { + // Client's src EntityID doesn't match any players + // in the lobby! Crash the server + std::cerr << "Client's src eid doesn't match any players!" << std::endl; + std::exit(1); + } + + if (lobbyEvent.role != PlayerRole::Unknown) { + // Update player's role from lobby event if + // player chose a role + player.get().desired_role = lobbyEvent.role; + player.get().ready = true; + + this->state.updateLobbyPlayer(src_eid, player.get()); + } + break; + } + case LobbyActionEvent::Action::StartGame: { + // Client tries to start the game + // Verify that all players are indeed ready + bool allReady = true; + for (boost::optional player : this->state.getLobby().players) { + if (!player.has_value() || !player.get().ready) { + // Either there aren't enough players or at least + // one player isn't ready + allReady = false; + break; + } + } + + if (allReady) { + + if (!this->config.server.disable_dm) { + // Randomly select a player from those whose desired role is + // PlayerRole::DungeonMaster (or from all players if no player + // has desired role set to PlayerRole::DungeonMaster) to be + // the Dungeon Master. Replace that player's Player object in + // the ObjectManager to be the DungeonMaster + + // Determine list of players that want to play as the DM + std::vector wannabe_dms; + + for (boost::optional player : this->state.getLobby().players) { + if (player.get().desired_role == PlayerRole::DungeonMaster) { + wannabe_dms.push_back(player.get()); + } + } + + // If no player wants to be a DM, then randomly choose one of them + if (wannabe_dms.size() == 0) { + for (boost::optional player : this->state.getLobby().players) { + wannabe_dms.push_back(player.get()); // cppcheck-suppress useStlAlgorithm + } + } + + // Randomly select a DM + size_t randomPlayerIndex = randomInt(0, wannabe_dms.size() - 1); + LobbyPlayer new_dm = wannabe_dms[randomPlayerIndex]; + + this->state.objects.replaceObject(new_dm.id, new DungeonMaster(this->state.getGrid().getRandomSpawnPoint() + glm::vec3(0.0f, 25.0f, 0.0f), glm::vec3(0.0f))); + DungeonMaster* dm = this->state.objects.getDM(); + + // Initialize DM's lightning bolt + SpecificID lightningID = this->state.objects.createObject(new Weapon(glm::vec3(-1.0f, 0, -1.0f), glm::vec3(0.0f), WeaponType::Lightning)); + Weapon* lightning = dynamic_cast(this->state.objects.getItem(lightningID)); + lightning->iteminfo.held = true; + lightning->physics.collider = Collider::None; + dm->lightning = lightning; + + auto& by_id = this->sessions.get(); + auto session_entry = by_id.find(dm->globalID); + + if (session_entry != by_id.end()) { + auto& session = session_entry->session; + by_id.modify(session_entry, [](SessionEntry& entry) { entry.is_dungeon_master = true;}); + session_entry->session->setDM(true); + if (session != nullptr) { + auto& by_ip = this->sessions.get(); + + auto addr = session_entry->ip; + + auto old_session = by_ip.find(addr); + + by_ip.modify(old_session, [](SessionEntry& entry) { + entry.is_dungeon_master = true; + }); + + session->sendPacket(PackagedPacket::make_shared(PacketType::ServerAssignEID, + ServerAssignEIDPacket{ .eid = dm->globalID, .is_dungeon_master = true })); + } + } + + // Get DM's player index + int index = 1; + for (boost::optional player : this->state.getLobby().players) { + if (player.get().id == dm->globalID) { + break; + } + index++; + } + + this->state.markAsUpdated(dm->globalID); + for (const auto& partial_update : this->state.generateSharedGameState(false)) { + sendUpdateToAllClients(Event(this->world_eid, EventType::LoadGameState, LoadGameStateEvent(partial_update))); + } + + std::cout << "Assigned player " + std::to_string(index) + " to be the DM" << std::endl; + } + + int player_idx = 0; // only increment when assigning a model + + auto players = this->state.objects.getPlayers(); + for (int i = 0; i < players.size(); i++) { + auto player = players.get(i); + if (player == nullptr) continue; + + if (player_idx == 0) { + player->modelType = ModelType::PlayerFire; + } + else if (player_idx == 1) { + player->modelType = ModelType::PlayerLightning; + } + else if (player_idx == 2) { + player->modelType = ModelType::PlayerWater; + } + + player_idx++; + + if (player_idx > 2) { + player_idx = 0; + } + } + + if (this->config.server.skip_intro) { + this->state.setPhase(GamePhase::GAME); + } else { + this->state.setPhase(GamePhase::INTRO_CUTSCENE); + } + } + + break; + } + } } this->lobby_broadcaster.setLobbyInfo(this->state.getLobby()); + + //std::cout << this->state.getLobby().to_string() << std::endl; + + break; + } + case GamePhase::INTRO_CUTSCENE: { + bool finished = this->intro_cutscene.update(); + if (finished) { + this->state.setPhase(GamePhase::GAME); + } else { + LoadIntroCutsceneEvent update = this->intro_cutscene.toNetwork(); + sendUpdateToAllClients(Event(this->world_eid, EventType::LoadIntroCutscene, update)); + } + break; + } + case GamePhase::GAME: { EventList allClientEvents = getAllClientEvents(); updateGameState(allClientEvents); - for (auto& [playerID, name] : this->state.getLobby().players) { - sendLightSourceUpdates(playerID); - } + static int curr_player_idx = 0; + + EntityID player_id = this->state.getLobby().players.at(curr_player_idx).get().id; + + sendLightSourceUpdates(player_id); + curr_player_idx = (curr_player_idx + 1) % this->state.getLobby().max_players; break; } @@ -197,32 +435,16 @@ std::chrono::milliseconds Server::doTick() { std::exit(1); } - // send partial updates to the clients - for (const auto& partial_update: this->state.generateSharedGameState(false)) { - sendUpdateToAllClients(Event(this->world_eid, EventType::LoadGameState, LoadGameStateEvent(partial_update))); - } - - // TODO: send sound effects to DM? - auto players = this->state.objects.getPlayers(); - auto audio_commands_per_player = this->state.soundTable().getCommandsPerPlayer(players); - - for (auto& session_entry : this->sessions) { - if (!audio_commands_per_player.contains(session_entry.id)) { - continue; // no sounds to send to that player - } + this->sendSoundCommands(); - auto session = session_entry.session.lock(); - if (session == nullptr) { - continue; // lost connection with this session, so can't send audio updates to it - } + auto shared_gamestate = this->state.generateSharedGameState(false); - session->sendEventAsync(Event(this->world_eid, EventType::LoadSoundCommands, LoadSoundCommandsEvent( - audio_commands_per_player.at(session_entry.id) - ))); + // send partial updates to the clients + // ALSO where the packets actually get sent + for (const auto& partial_update: shared_gamestate) { + sendUpdateToAllClients(Event(this->world_eid, EventType::LoadGameState, LoadGameStateEvent(partial_update))); } - this->state.soundTable().tickSounds(); - // Calculate how long we need to wait until the next tick auto stop = std::chrono::high_resolution_clock::now(); auto wait = std::chrono::duration_cast( @@ -234,19 +456,19 @@ void Server::_doAccept() { this->acceptor.async_accept(this->socket, [this](boost::system::error_code ec) { if (!ec) { + this->socket.set_option(boost::asio::ip::tcp::no_delay(true)); + // boost::asio::socket_base::send_buffer_size option(10000000); // 10x buffer size + // this->socket.set_option(option); auto addr = this->socket.remote_endpoint().address(); auto new_session = this->_handleNewSession(addr); // send complete gamestate to the new person who connected + int i = 0; for (auto& partial_update : this->state.generateSharedGameState(true)) { - new_session->sendEventAsync(Event(0, EventType::LoadGameState, LoadGameStateEvent(partial_update))); + new_session->sendEvent(Event(0, EventType::LoadGameState, LoadGameStateEvent(partial_update))); } - new_session->startListen(); - std::cout << "sending assign eid\n"; - std::cout << new_session->getInfo().is_dungeon_master.value() << "\n"; - std::cout << new_session->getInfo().client_eid.value() << "\n"; - new_session->sendPacketAsync(PackagedPacket::make_shared(PacketType::ServerAssignEID, + new_session->sendPacket(PackagedPacket::make_shared(PacketType::ServerAssignEID, ServerAssignEIDPacket { .eid = new_session->getInfo().client_eid.value(), .is_dungeon_master = new_session->getInfo().is_dungeon_master.value()})); } else { @@ -264,15 +486,19 @@ void Server::_doAccept() { std::shared_ptr Server::_handleNewSession(boost::asio::ip::address addr) { auto& by_ip = this->sessions.get(); auto old_session = by_ip.find(addr); + if (old_session != by_ip.end()) { // We already had a session with this IP - if (old_session->session.expired()) { + if (!old_session->session->isOkay()) { EntityID old_id = old_session->id; - // The old session is expired, so create new one + // The old session is dead, so create new one auto new_session = std::make_shared(std::move(this->socket), SessionInfo({}, old_id, old_session->is_dungeon_master)); + + std::cout << "OLD ID: " << old_id << " OLD IS DM: " << old_session->is_dungeon_master << std::endl; + by_ip.replace(old_session, SessionEntry(old_id, old_session->is_dungeon_master, addr, new_session)); std::cout << "Reestablished connection with " << addr @@ -286,37 +512,10 @@ std::shared_ptr Server::_handleNewSession(boost::asio::ip::address addr std::cerr << "Error: incoming connection request from " << addr << " with which we already have an active session" << std::endl; - return old_session->session.lock(); + return old_session->session; } } - static bool first_player = false; - - // first player is Dungeon Master - if (first_player) { - this->state.objects.createObject(new DungeonMaster(this->state.getGrid().getRandomSpawnPoint() + glm::vec3(0.0f, 25.0f, 0.0f), glm::vec3(0.0f))); - DungeonMaster* dm = this->state.objects.getDM(); - - // Spawn player in random spawn point - - // TODO: Possibly replace this random spawn point with player assignments? - // I.e., assign each player a spawn point to avoid multiple players getting - // the same spawn point? - - auto session = std::make_shared(std::move(this->socket), - SessionInfo({}, dm->globalID, true)); - - - this->sessions.insert(SessionEntry(dm->globalID, true, addr, session)); - - std::cout << "Established new connection with " << addr << ", which was assigned eid " - << dm->globalID << std::endl; - - first_player = false; - - return session; - } - // Brand new connection // TODO: reject connection if not in LOBBY GamePhase Player* player = new Player(this->state.getGrid().getRandomSpawnPoint(), glm::vec3(0.0f)); @@ -331,4 +530,56 @@ std::shared_ptr Server::_handleNewSession(boost::asio::ip::address addr << player->globalID << std::endl; return session; +} + +void Server::sendSoundCommands() { + bool is_intro_cutscene = this->state.getPhase() == GamePhase::INTRO_CUTSCENE; + + ServerGameState& curr_state = (is_intro_cutscene) ? this->intro_cutscene.state : this->state; + + // TODO: send sound effects to DM? + std::vector players; // hold players and DM + for (int i = 0; i < curr_state.objects.getPlayers().size(); i++) { + auto player = curr_state.objects.getPlayer(i); + if (player != nullptr) { + players.push_back(player); + } + } + if (curr_state.objects.getDM() != nullptr) { + players.push_back(curr_state.objects.getDM()); + } + + auto audio_commands_per_player = curr_state.soundTable().getCommandsPerPlayer(players); + + for (auto& session_entry : this->sessions) { + EntityID eid; + if (is_intro_cutscene) { + if (!session_entry.session->getInfo().is_dungeon_master.has_value()){ + continue; + } + + if (session_entry.session->getInfo().is_dungeon_master.value()) { + eid = this->intro_cutscene.dm_eid; + } else { + eid = this->intro_cutscene.pov_eid; + } + } else { + eid = session_entry.id; + } + + if (!audio_commands_per_player.contains(eid)) { + continue; // no sounds to send to that player + } + + auto session = session_entry.session; + if (!session->isOkay()) { + continue; // lost connection with this session, so can't send audio updates to it + } + + session->sendEvent(Event(this->world_eid, EventType::LoadSoundCommands, LoadSoundCommandsEvent( + audio_commands_per_player.at(eid) + ))); + } + + curr_state.soundTable().tickSounds(); } \ No newline at end of file diff --git a/src/shared/audio/soundtype.cpp b/src/shared/audio/soundtype.cpp index cffda769..150b338d 100644 --- a/src/shared/audio/soundtype.cpp +++ b/src/shared/audio/soundtype.cpp @@ -9,8 +9,11 @@ static auto audio_dir = getRepoRoot() / "assets" / "sounds"; std::string getAudioPath(ClientSFX sound) { static auto dir = audio_dir / "client_sfx"; switch (sound) { - case ClientSFX::TEMP: - return (dir / "vine-boom-mono.mp3").string(); + case ClientSFX::VictoryThemePlayers: + return (dir / "victory_players.flac").string(); + case ClientSFX::VictoryThemeDM: + // TODO: Replace with DM Victory theme! + return (dir / "victory_dm.flac").string(); default: std::cerr << "FATAL: no known path for ClientSFX " << static_cast(sound) << std::endl; std::exit(1); @@ -46,12 +49,48 @@ std::string getAudioPath(ServerSFX sfx) { return (dir / "player_walk_4_mono.wav").string(); case ServerSFX::PlayerWalk5: return (dir / "player_walk_5_mono.wav").string(); + case ServerSFX::Dagger: + return (dir / "dagger.wav").string(); + case ServerSFX::Sword: + return (dir / "sword.wav").string(); + case ServerSFX::Hammer: + return (dir / "hammer.wav").string(); + case ServerSFX::Minotaur: + return (dir / "minotaur.wav").string(); + case ServerSFX::Python: + return (dir / "python.wav").string(); case ServerSFX::CeilingSpikeImpact: return (dir / "ceiling_spike_impact_short_mono.wav").string(); case ServerSFX::CeilingSpikeTrigger: return (dir / "ceiling_spike_trigger_mono.wav").string(); + case ServerSFX::Thunder: + return (dir / "thunder.wav").string(); case ServerSFX::TorchLoop: return (dir / "torch_loop_mono.wav").string(); + case ServerSFX::PlayersStartTheme: + return (dir / "players_start_theme.mp3").string(); + case ServerSFX::ElectricHum: + return (dir / "electric_hum.wav").string(); + case ServerSFX::IntroGateOpen: + return (dir / "cutscene_gate_open.wav").string(); + case ServerSFX::ZeusStartTheme: + return (dir / "start_game_dm.flac").string(); + case ServerSFX::Wind: + return (dir / "wind.wav").string(); + case ServerSFX::Teleport: + return (dir / "teleport.wav").string(); + case ServerSFX::Potion: + return (dir / "potion.wav").string(); + case ServerSFX::Spell: + return (dir / "spell.wav").string(); + case ServerSFX::ItemPickUp: + return (dir / "itempickup.wav").string(); + case ServerSFX::ItemDrop: + return (dir / "itemdrop.wav").string(); + case ServerSFX::MirrorShatter: + return (dir / "mirror_shatter.mp3").string(); + case ServerSFX::MinotaurDeath: + return (dir / "minotaur_death.wav").string(); default: std::cerr << "FATAL: no known path for ServerSFX " << static_cast(sfx) << std::endl; @@ -63,10 +102,18 @@ std::string getAudioPath(ClientMusic music) { static auto dir = audio_dir / "client_music"; switch (music) { - case ClientMusic::TitleTheme: - return (dir / "piano.wav").string(); - case ClientMusic::GameTheme: - return (dir / "mono-retrowave.mp3").string(); + case ClientMusic::MenuTheme: + // TODO: Replace with menu theme! + return (dir / "menu.flac").string(); + case ClientMusic::MazeExplorationPlayersTheme: + return (dir / "maze_exploration_players.flac").string(); + case ClientMusic::MazeExplorationDMTheme: + return (dir / "maze_exploration_dm.flac").string(); + case ClientMusic::RelayRacePlayersTheme: + return (dir / "relay_race_players.flac").string(); + case ClientMusic::RelayRaceDMTheme: + // TODO: Replace with DM Relay Race theme! + return (dir / "relay_race_dm.flac").string(); default: std::cerr << "FATAL: no known path for ClientMusic " << static_cast(music) << std::endl; std::exit(1); diff --git a/src/shared/game/sharedgamestate.cpp b/src/shared/game/sharedgamestate.cpp index c1c7dc2b..b00acbab 100644 --- a/src/shared/game/sharedgamestate.cpp +++ b/src/shared/game/sharedgamestate.cpp @@ -1,12 +1,14 @@ #include "shared/game/sharedgamestate.hpp" +#include "shared/game/event.hpp" void SharedGameState::update(const SharedGameState& other) { // copy over static data that is sent every update this->lobby = other.lobby; + this->phase = other.phase; this->timestep = other.timestep; this->matchPhase = other.matchPhase; - this->timesteps_left = other.timesteps_left; + this->relay_finish_time = other.relay_finish_time; this->playerVictory = other.playerVictory; this->numPlayerDeaths = other.numPlayerDeaths; @@ -14,4 +16,93 @@ void SharedGameState::update(const SharedGameState& other) { for (const auto& [id, updated_obj] : other.objects) { // cppcheck-suppress unassignedVariable this->objects[id] = updated_obj; } +} + +const boost::optional& Lobby::getPlayer(int playerIndex) const { + return this->players[playerIndex - 1]; +} + +const boost::optional& Lobby::getPlayer(EntityID id) const { + for (auto& player : this->players) { + if (player.has_value() && player.get().id == id) + return player; + } + + return boost::none; +} + +int Lobby::numPlayersInLobby() const { + int numPlayers = 0; + for (auto& player : this->players) { + if (player.has_value()) { + numPlayers++; + } + } + + return numPlayers; +} + +/* Debug Methods */ + +std::string LobbyPlayer::to_string(unsigned int tab_offset) { + // Return a string representation of this LobbyPlayer struct + + std::string tabs; + + for (unsigned int i = 0; i < tab_offset; i++) + tabs += '\t'; + + std::string representation = tabs + "{\n"; + representation += tabs + "\tglobal id:\t\t" + std::to_string(this->id) + '\n'; + + std::string role; + + switch (this->desired_role) { + case PlayerRole::Player: + role = "Player"; + break; + case PlayerRole::DungeonMaster: + role = "Dungeon Master"; + break; + case PlayerRole::Unknown: + role = "Unknown"; + break; + } + + representation += tabs + "\tdesired role:\t\t" + role + '\n'; + representation += tabs + "\tready:\t\t" + (this->ready ? "true" : "false") + '\n'; + representation += tabs + "}"; + + return representation; +} + +std::string Lobby::to_string(unsigned int tab_offset) const { + // Return a string representation of this Lobby struct + + std::string tabs; + + for (unsigned int i = 0; i < tab_offset; i++) + tabs += '\t'; + + std::string representation = tabs + "{\n"; + representation += tabs + "\tname:\t\t" + this->name + '\n'; + representation += tabs + "\tplayers: [\n"; + + for (int i = 0; i < this->max_players; i++) { + boost::optional player = this->players[i]; + + if (!player.has_value()) { + representation += tabs + "\t\tEmpty\n"; + } + else { + representation += player.get().to_string(tab_offset + 2) + '\n'; + } + } + + representation += tabs + "\t]\n"; + + representation += tabs + "\tmax players:\t\t" + std::to_string(max_players) + '\n'; + representation += tabs + "}"; + + return representation; } \ No newline at end of file diff --git a/src/shared/game/sharedobject.cpp b/src/shared/game/sharedobject.cpp index 2786a97d..371e7d4d 100644 --- a/src/shared/game/sharedobject.cpp +++ b/src/shared/game/sharedobject.cpp @@ -12,6 +12,44 @@ std::string objectTypeString(ObjectType type) { return "Player"; case ObjectType::Slime: return "Slime"; + case ObjectType::Potion: + return "Potion"; + case ObjectType::Enemy: + return "Enemy"; + case ObjectType::Torchlight: + return "Torchlight"; + case ObjectType::SpikeTrap: + return "SpikeTrap"; + case ObjectType::DungeonMaster: + return "DungeonMaster"; + case ObjectType::FireballTrap: + return "FireballTrap"; + case ObjectType::Projectile: + return "Projectile"; + case ObjectType::FloorSpike: + return "FloorSpike"; + case ObjectType::FakeWall: + return "FakeWall"; + case ObjectType::ArrowTrap: + return "ArrowTrap"; + case ObjectType::TeleporterTrap: + return "TeleporterTrap"; + case ObjectType::Spell: + return "Spell"; + case ObjectType::Minotaur: + return "Minotaur"; + case ObjectType::Python: + return "Python"; + case ObjectType::Item: + return "Item"; + case ObjectType::Exit: + return "Exit"; + case ObjectType::Orb: + return "Orb"; + case ObjectType::Weapon: + return "Weapon"; + case ObjectType::WeaponCollider: + return "WeaponCollider"; default: return "Unknown"; } diff --git a/src/shared/network/session.cpp b/src/shared/network/session.cpp index 18837723..b9602ccb 100644 --- a/src/shared/network/session.cpp +++ b/src/shared/network/session.cpp @@ -15,18 +15,75 @@ Session::Session(tcp::socket socket, SessionInfo info): socket(std::move(socket)), info(info) { + this->okay = true; } Session::~Session() { } +bool Session::isOkay() const { + return this->okay; +} + const SessionInfo& Session::getInfo() const { return this->info; } -void Session::startListen() { - // This starts a chain that will continue on and on - _receivePacketAsync(); +std::vector Session::handleAllReceivedPackets() { + if (!this->isOkay()) { + return {}; + } + + std::vector events; + + while (true) { + if (!prev_hdr.has_value()) { + // we haven't already pulled in a header, see if there is a header waiting for us + if (!socketHasEnoughBytes(sizeof(PacketHeader))) { + return events; + } + + boost::system::error_code ec; + boost::asio::read(this->socket, boost::asio::buffer(this->buffer), + boost::asio::transfer_exactly(sizeof(PacketHeader)), ec); + switch (_classifySocketError(ec, "receiving header")) { + case SocketError::NONE: break; + case SocketError::FATAL: + this->okay = false; + return events; + } + + this->prev_hdr = PacketHeader(static_cast(&this->buffer[0])); + } + + // if (!socketHasEnoughBytes(this->prev_hdr->size)) { + // std::cout << "dont have enough bytes for " << this->prev_hdr->size << "\n"; + // return events; + // } + + boost::system::error_code ec; + boost::asio::read(this->socket, boost::asio::buffer(this->buffer), + boost::asio::transfer_exactly(this->prev_hdr->size), ec); + + switch (_classifySocketError(ec, "receiving data")) { + case SocketError::NONE: break; + case SocketError::FATAL: + this->okay = false; + std::cout << "setting ok to bad" << std::endl; + return events; + } + + std::string data(this->buffer.begin(), this->buffer.begin() + this->prev_hdr->size); + auto event = _handleReceivedPacket(this->prev_hdr->type, data); + if (event.has_value()) { + events.push_back(event.value()); + } + + this->prev_hdr = {}; + } + + // shouldnt get here but for safety + return events; } bool Session::connectTo(basic_resolver_results endpoints) { @@ -36,14 +93,15 @@ bool Session::connectTo(basic_resolver_results endpo std::cerr << "ERROR Connecting: " << ec.what() << std::endl; return false; } + std::cout << "connected\n"; return true; } -void Session::_handleReceivedPacket(PacketType type, const std::string& data) { +std::optional Session::_handleReceivedPacket(PacketType type, const std::string& data) { // First figure out if packet is event or non-event if (type == PacketType::Event) { auto event = deserialize(data).event; - this->received_events.push_back(event); + return event; } else if (type == PacketType::ServerAssignEID) { this->info.client_eid = deserialize(data).eid; this->info.is_dungeon_master = deserialize(data).is_dungeon_master; @@ -53,75 +111,30 @@ void Session::_handleReceivedPacket(PacketType type, const std::string& data) { this->info.client_name = deserialize(data).player_name; std::cout << "Handling ClientDeclareInfo from " << *this->info.client_name << "...\n"; } else { - std::cerr << "Unknown packet type received in Session::_addReceivedPacket" << (int) type << std::endl; + std::cerr << "Unknown packet type received in Session::_addReceivedPacket " << (int) type << std::endl; } + return {}; } -std::vector Session::getEvents() { - std::vector vec; - - std::swap(vec, this->received_events); - - return vec; -} - -void Session::sendPacketAsync(std::shared_ptr packet) { - auto self = shared_from_this(); +void Session::sendPacket(std::shared_ptr packet) { + if (!this->isOkay()) { + return; + } - // pass packet shared ptr into closure capture list to keep it alive until written to buffer - boost::asio::async_write(socket, packet->toBuffer(), - [this, packet, self](boost::system::error_code ec, std::size_t /*length*/) { - switch (_classifySocketError(ec, "sending packet")) { - case SocketError::NONE: - break; - case SocketError::FATAL: - return; - case SocketError::RETRY: - sendPacketAsync(packet); - return; - } - }); + boost::system::error_code ec; + boost::asio::write(this->socket, packet->toBuffer(), ec); + + switch (_classifySocketError(ec, "sending packet")) { + case SocketError::NONE: break; + // not currently handling retry + case SocketError::FATAL: + this->okay = false; + return; + } } -void Session::sendEventAsync(Event event) { - this->sendPacketAsync(PackagedPacket::make_shared(PacketType::Event, EventPacket(event))); -} - -void Session::_receivePacketAsync() { - auto self(shared_from_this()); - const std::size_t BUF_SIZE = NETWORK_BUFFER_SIZE; - auto buf = std::make_shared>(); - - boost::asio::async_read(socket, boost::asio::buffer(&buf.get()[0], BUF_SIZE), - boost::asio::transfer_exactly(sizeof(PacketHeader)), - [this, buf, self](boost::system::error_code ec, std::size_t length) { - switch (_classifySocketError(ec, "receiving header")) { - case SocketError::NONE: - break; - case SocketError::FATAL: - return; - } - - PacketHeader hdr(static_cast(&buf.get()[0])); - - boost::asio::async_read(socket, boost::asio::buffer(buf.get(), BUF_SIZE), - boost::asio::transfer_exactly(hdr.size), - [this, buf, hdr, self](boost::system::error_code ec, - std::size_t length) - { - switch (_classifySocketError(ec, "receiving data")) { - case SocketError::NONE: - break; - case SocketError::FATAL: - return; - } - - std::string data(buf->begin(), buf->begin() + hdr.size); - self->_handleReceivedPacket(hdr.type, data); - _receivePacketAsync(); - }); - - }); +void Session::sendEvent(Event event) { + this->sendPacket(PackagedPacket::make_shared(PacketType::Event, EventPacket(event))); } SocketError Session::_classifySocketError(boost::system::error_code ec, const char* where) { @@ -135,7 +148,6 @@ SocketError Session::_classifySocketError(boost::system::error_code ec, const ch // all fatal right now return SocketError::FATAL; - // if (ec == boost::asio::error::connection_reset || // ec == boost::asio::error::eof || // ec == boost::asio::error::access_denied || @@ -145,4 +157,16 @@ SocketError Session::_classifySocketError(boost::system::error_code ec, const ch // } else { // return SocketError::RETRY; // } +} + +bool Session::socketHasEnoughBytes(std::size_t bytes) { + boost::asio::socket_base::bytes_readable command(true); + socket.io_control(command); + std::size_t bytes_readable = command.get(); + + return bytes_readable >= bytes; +} + +void Session::setDM(bool is_dm) { + this->info.is_dungeon_master = is_dm; } \ No newline at end of file diff --git a/src/shared/utilities/config.cpp b/src/shared/utilities/config.cpp index b997655a..25c9f614 100644 --- a/src/shared/utilities/config.cpp +++ b/src/shared/utilities/config.cpp @@ -41,7 +41,8 @@ GameConfig GameConfig::parse(int argc, char** argv) { // cppcheck-suppress const .directory = json.at("game").at("maze").at("directory"), .procedural = json.at("game").at("maze").at("procedural"), .maze_file = json.at("game").at("maze").at("maze_file") - } + }, + .disable_enemies = json.at("game").at("disable_enemies") }, .network = { .server_ip = json.at("network").at("server_ip"), @@ -51,13 +52,17 @@ GameConfig GameConfig::parse(int argc, char** argv) { // cppcheck-suppress const .lobby_name = json.at("server").at("lobby_name"), .lobby_broadcast = json.at("server").at("lobby_broadcast"), .max_players = json.at("server").at("max_players"), + .disable_dm = json.at("server").at("disable_dm"), + .skip_intro = json.at("server").at("skip_intro") }, .client = { .default_name = json.at("client").at("default_name"), .lobby_discovery = json.at("client").at("lobby_discovery"), - .window_width = json.at("client").at("window_width"), + .fullscreen = json.at("client").at("fullscreen"), .draw_bboxes = json.at("client").at("draw_bboxes"), - .fps_counter = json.at("client").at("fps_counter") + .fps_counter = json.at("client").at("fps_counter"), + .presentation = json.at("client").at("presentation"), + .render = json.at("client").at("render") } }; } catch (nlohmann::json::exception& ex) { @@ -73,7 +78,8 @@ GameConfig getDefaultConfig() { .directory = "maps", .procedural = true, .maze_file = "default_maze.maze" - } + }, + .disable_enemies = false }, .network = { .server_ip = "localhost", @@ -83,10 +89,16 @@ GameConfig getDefaultConfig() { .lobby_name = "My Test Lobby", .lobby_broadcast = false, .max_players = 1, + .disable_dm = false, + .skip_intro = false }, .client = { .default_name = "Player", - .lobby_discovery = false + .lobby_discovery = false, + .fullscreen = false, + .draw_bboxes = false, + .fps_counter = false, + .presentation = false } }; } \ No newline at end of file diff --git a/src/shared/utilities/time.cpp b/src/shared/utilities/time.cpp index d78b8dd9..c6c33936 100644 --- a/src/shared/utilities/time.cpp +++ b/src/shared/utilities/time.cpp @@ -2,8 +2,14 @@ #include -long getMsSinceEpoch() { +long long getMsSinceEpoch() { return std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch() ).count(); } + +long long getSecSinceEpoch() { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch() + ).count(); +}