From 02241bbd9e9d089952b61acd1ee7ca5c0421076b Mon Sep 17 00:00:00 2001 From: Daanish Date: Wed, 29 Apr 2026 21:54:47 +0100 Subject: [PATCH] Initial commit --- .clang-format | 11 + .gitignore | 4 + Makefile | 99 ++++ assets/fonts/charybdis.ttf | Bin 0 -> 20508 bytes assets/fonts/hardpixel.otf | Bin 0 -> 18496 bytes assets/fonts/tn.ttf | Bin 0 -> 399403 bytes assets/images/arrow.aseprite | Bin 0 -> 407 bytes assets/images/arrow.png | Bin 0 -> 233 bytes assets/images/blade_of_recursion.aseprite | Bin 0 -> 488 bytes assets/images/blade_of_recursion.png | Bin 0 -> 352 bytes assets/images/chest.aseprite | Bin 0 -> 531 bytes assets/images/chest.png | Bin 0 -> 380 bytes assets/images/direction.aseprite | Bin 0 -> 388 bytes assets/images/direction.png | Bin 0 -> 162 bytes assets/images/event_horizon.aseprite | Bin 0 -> 538 bytes assets/images/event_horizon.png | Bin 0 -> 505 bytes assets/images/floor.aseprite | Bin 0 -> 614 bytes assets/images/floor.png | Bin 0 -> 742 bytes assets/images/force.aseprite | Bin 0 -> 708 bytes assets/images/force.png | Bin 0 -> 811 bytes assets/images/game_over_dead_bg.aseprite | Bin 0 -> 6077 bytes assets/images/game_over_dead_bg.png | Bin 0 -> 2924 bytes assets/images/game_over_victory_bg.aseprite | Bin 0 -> 5621 bytes assets/images/game_over_victory_bg.png | Bin 0 -> 4466 bytes assets/images/heart.aseprite | Bin 0 -> 408 bytes assets/images/heart.png | Bin 0 -> 187 bytes assets/images/hud.png | Bin 0 -> 4170 bytes assets/images/hud_fg.aseprite | Bin 0 -> 2513 bytes assets/images/hud_fg.png | Bin 0 -> 5026 bytes assets/images/inventory.aseprite | Bin 0 -> 2546 bytes assets/images/komodo.aseprite | Bin 0 -> 952 bytes assets/images/komodo.png | Bin 0 -> 1150 bytes assets/images/landmine.aseprite | Bin 0 -> 526 bytes assets/images/landmine.png | Bin 0 -> 453 bytes assets/images/memory.aseprite | Bin 0 -> 517 bytes assets/images/memory.png | Bin 0 -> 186 bytes assets/images/menu_bg.png | Bin 0 -> 2647 bytes assets/images/player.aseprite | Bin 0 -> 5411 bytes assets/images/player.png | Bin 0 -> 7276 bytes assets/images/quantum_bow.aseprite | Bin 0 -> 496 bytes assets/images/quantum_bow.png | Bin 0 -> 386 bytes assets/images/radar.aseprite | Bin 0 -> 655 bytes assets/images/radar.png | Bin 0 -> 574 bytes assets/images/shrodingers_mine.aseprite | Bin 0 -> 507 bytes assets/images/shrodingers_mine.png | Bin 0 -> 367 bytes assets/images/torch.aseprite | Bin 0 -> 637 bytes assets/images/torch.png | Bin 0 -> 622 bytes assets/images/walls.aseprite | Bin 0 -> 4250 bytes assets/images/walls.png | Bin 0 -> 4677 bytes assets/shaders/fog.frag | 99 ++++ assets/shaders/fog.vert | 5 + include/main.h | 0 include/pch.h | 15 + include/utils.h | 145 ++++++ include/window/window.h | 174 +++++++ notes.md | 110 ++++ samples/main.rb | 185 +++++++ src/main.cc | 70 +++ src/test.c++ | 110 ++++ src/window/window.cc | 534 ++++++++++++++++++++ 60 files changed, 1561 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 assets/fonts/charybdis.ttf create mode 100644 assets/fonts/hardpixel.otf create mode 100644 assets/fonts/tn.ttf create mode 100644 assets/images/arrow.aseprite create mode 100644 assets/images/arrow.png create mode 100644 assets/images/blade_of_recursion.aseprite create mode 100644 assets/images/blade_of_recursion.png create mode 100644 assets/images/chest.aseprite create mode 100644 assets/images/chest.png create mode 100644 assets/images/direction.aseprite create mode 100644 assets/images/direction.png create mode 100644 assets/images/event_horizon.aseprite create mode 100644 assets/images/event_horizon.png create mode 100644 assets/images/floor.aseprite create mode 100644 assets/images/floor.png create mode 100644 assets/images/force.aseprite create mode 100644 assets/images/force.png create mode 100644 assets/images/game_over_dead_bg.aseprite create mode 100644 assets/images/game_over_dead_bg.png create mode 100644 assets/images/game_over_victory_bg.aseprite create mode 100644 assets/images/game_over_victory_bg.png create mode 100644 assets/images/heart.aseprite create mode 100644 assets/images/heart.png create mode 100644 assets/images/hud.png create mode 100644 assets/images/hud_fg.aseprite create mode 100644 assets/images/hud_fg.png create mode 100644 assets/images/inventory.aseprite create mode 100644 assets/images/komodo.aseprite create mode 100644 assets/images/komodo.png create mode 100644 assets/images/landmine.aseprite create mode 100644 assets/images/landmine.png create mode 100644 assets/images/memory.aseprite create mode 100644 assets/images/memory.png create mode 100644 assets/images/menu_bg.png create mode 100644 assets/images/player.aseprite create mode 100644 assets/images/player.png create mode 100644 assets/images/quantum_bow.aseprite create mode 100644 assets/images/quantum_bow.png create mode 100644 assets/images/radar.aseprite create mode 100644 assets/images/radar.png create mode 100644 assets/images/shrodingers_mine.aseprite create mode 100644 assets/images/shrodingers_mine.png create mode 100644 assets/images/torch.aseprite create mode 100644 assets/images/torch.png create mode 100644 assets/images/walls.aseprite create mode 100644 assets/images/walls.png create mode 100644 assets/shaders/fog.frag create mode 100644 assets/shaders/fog.vert create mode 100644 include/main.h create mode 100644 include/pch.h create mode 100644 include/utils.h create mode 100644 include/window/window.h create mode 100644 notes.md create mode 100644 samples/main.rb create mode 100644 src/main.cc create mode 100644 src/test.c++ create mode 100644 src/window/window.cc diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..56f0e02 --- /dev/null +++ b/.clang-format @@ -0,0 +1,11 @@ +BasedOnStyle: LLVM +IndentWidth: 2 +TabWidth: 2 +ContinuationIndentWidth: 2 +ColumnLimit: 0 +UseTab: Never +BreakBeforeBraces: Attach +AllowShortFunctionsOnASingleLine: Empty +AlignAfterOpenBracket: BlockIndent +BinPackArguments: false +AllowAllArgumentsOnNextLine: false \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c50c3c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +**old** + +build/* +bin/* \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b61f350 --- /dev/null +++ b/Makefile @@ -0,0 +1,99 @@ +SRC_DIR := src +BIN_DIR := bin +OBJ_DIR := build +INCLUDE_DIR := include + +TARGET_DEBUG := $(BIN_DIR)/game-dbg +TARGET_RELEASE := $(BIN_DIR)/game + +PCH_DEBUG := $(OBJ_DIR)/debug/pch.h.gch +PCH_RELEASE := $(OBJ_DIR)/release/pch.h.gch + +CCACHE := ccache +CXX := $(CCACHE) clang++ +CC := $(CCACHE) clang + +# ---------------- SDL3 ---------------- +SDL_CFLAGS := $(shell pkg-config --cflags sdl3 sdl3-ttf sdl3-image) +SDL_LIBS_DEBUG := $(shell pkg-config --libs sdl3 sdl3-ttf sdl3-image) +SDL_LIBS_RELEASE := $(shell pkg-config --libs sdl3 sdl3-ttf sdl3-image) + +# ---------------- FLAGS ---------------- + +CFLAGS_DEBUG := \ + -std=c++20 -Wall -Wextra \ + -O0 -g -fno-omit-frame-pointer \ + -Wno-unused-command-line-argument \ + -I./include \ + $(SDL_CFLAGS) + +CFLAGS_RELEASE := \ + -std=c++20 -O3 -march=x86-64 -mtune=generic \ + -flto=thin \ + -fno-rtti -fomit-frame-pointer -DNDEBUG -s \ + -Wno-unused-command-line-argument \ + -I./include \ + $(SDL_CFLAGS) + +PCH_CFLAGS_DEBUG := $(CFLAGS_DEBUG) -x c++-header +PCH_CFLAGS_RELEASE := $(CFLAGS_RELEASE) -x c++-header + +# ---------------- SOURCES ---------------- + +SRC := $(wildcard $(SRC_DIR)/*.cc) $(wildcard $(SRC_DIR)/**/*.cc) + +OBJ_DEBUG := $(patsubst $(SRC_DIR)/%.cc,$(OBJ_DIR)/debug/%.o,$(SRC)) +OBJ_RELEASE := $(patsubst $(SRC_DIR)/%.cc,$(OBJ_DIR)/release/%.o,$(SRC)) + +DEP_DEBUG := $(OBJ_DEBUG:.o=.d) +DEP_RELEASE := $(OBJ_RELEASE:.o=.d) + +# ---------------- TARGETS ---------------- + +.PHONY: all debug release clean + +all: debug + +debug: $(TARGET_DEBUG) + +release: $(TARGET_RELEASE) + +# ---------------- PCH ---------------- + +$(PCH_DEBUG): $(INCLUDE_DIR)/pch.h + mkdir -p $(dir $@) + $(CXX) $(PCH_CFLAGS_DEBUG) -o $@ $< + +$(PCH_RELEASE): $(INCLUDE_DIR)/pch.h + mkdir -p $(dir $@) + $(CXX) $(PCH_CFLAGS_RELEASE) -o $@ $< + +# ---------------- LINK ---------------- + +$(TARGET_DEBUG): $(PCH_DEBUG) $(OBJ_DEBUG) + mkdir -p $(BIN_DIR) + $(CXX) $(CFLAGS_DEBUG) -o $@ $(OBJ_DEBUG) $(SDL_LIBS_DEBUG) + +$(TARGET_RELEASE): $(PCH_RELEASE) $(OBJ_RELEASE) + mkdir -p $(BIN_DIR) + $(CXX) $(CFLAGS_RELEASE) -o $@ $(OBJ_RELEASE) $(SDL_LIBS_RELEASE) + +# ---------------- COMPILE ---------------- + +$(OBJ_DIR)/debug/%.o: $(SRC_DIR)/%.cc $(PCH_DEBUG) + mkdir -p $(dir $@) + $(CXX) $(CFLAGS_DEBUG) -include $(INCLUDE_DIR)/pch.h -MMD -MP -c $< -o $@ + +$(OBJ_DIR)/release/%.o: $(SRC_DIR)/%.cc $(PCH_RELEASE) + mkdir -p $(dir $@) + $(CXX) $(CFLAGS_RELEASE) -include $(INCLUDE_DIR)/pch.h -MMD -MP -c $< -o $@ + +# ---------------- DEP ---------------- + +-include $(DEP_DEBUG) +-include $(DEP_RELEASE) + +# ---------------- CLEAN ---------------- + +clean: + rm -rf $(OBJ_DIR) $(BIN_DIR) \ No newline at end of file diff --git a/assets/fonts/charybdis.ttf b/assets/fonts/charybdis.ttf new file mode 100644 index 0000000000000000000000000000000000000000..b6325cd5295adfc56c17c8b4b895e912056814ff GIT binary patch literal 20508 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 O;XX*kwndz7Fd6_5{g;IR literal 0 HcmV?d00001 diff --git a/assets/fonts/hardpixel.otf b/assets/fonts/hardpixel.otf new file mode 100644 index 0000000000000000000000000000000000000000..0912b3e888939d886ca334ea7bcaa0e90866aeaf GIT binary patch literal 18496 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 aXBH<0kUnsF2iXU5FY`7~4g~uZ%m4tef&Tme literal 0 HcmV?d00001 diff --git a/assets/fonts/tn.ttf b/assets/fonts/tn.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9d12258c9df2a930731c56bc69124985682efd72 GIT binary patch literal 399403 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 Q1ZQAwJRTtYGN_ohCKC5Ymrs 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 JGz6#_0sy_dqeB1y literal 0 HcmV?d00001 diff --git a/assets/images/arrow.aseprite b/assets/images/arrow.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..8b998c358ed726ad2936426a1caa2e2d7e897f86 GIT binary patch literal 407 zcmbQv$iVPmDI)_P10RC|10w?iLka^jP+(vL2{H&U2p|hnjV;c|!0_uM3)of`1_lN( z1_lOp1+bM!c4RRyFtC7Jr@+9#prl~ps$r96Y1kiTeZNrc=7O|$hiCo%WH$3@K-NA3 z^C@}WnfgKADn>Sz_Vy(P2~E*?50hvAk3ak5^zA?Y|No!8c6&=*duCQvWLQv{Q`Z_J zqnqK$*Q{dQ;QS~GZ+{cSV4|pU|{&q1XBeHyox!( zq5}W*xsLFtFin`pX&gRbjrg+un7xT+&;DQ5;dTEyX+`*ymI}9P+=qDT!c<&w7Dhv#BW5O>0032joeEU|8(w z;uxYaF!oT^AE(eYrIB+1~uI0qWkh4ApY%6C;N=isbxUe4iZ~aQcfaA~K zqZ|JHGH$yrA7>=AbfU~zjY&%~j!jSqGY)f1GI?CWwJS}cL9)r5VS=f#fq}spp{FSV zwk6Xaer=!L!_q9fta7S=adYo>US?)y=EG+{wYF?=G%@-S`FH!w$ZztNl}3V>e*8M@ m?RL?*-0+WwiGzP1!@)H%(ZxqyelRdFFnGH9xvXSz_Vy(P2~E*?50hvAk3ak5^zA?Y|No!8c6&=*duCQvWLQv{Q`Z_J zqnqK$*Q{dQ;QS~cQPmj#jL6UXx0lPs=z7SS zZ9P#Hvv={qUmbz7x1N7s-#lM5hTm@M=J5Pq`(M6L+#r`yz2PNer|*oNOs;ZbaXYR& No^+Uv!D!w>7XbX}hadm| literal 0 HcmV?d00001 diff --git a/assets/images/blade_of_recursion.png b/assets/images/blade_of_recursion.png new file mode 100644 index 0000000000000000000000000000000000000000..27930be8e13a9047bc8790e253566e1b163f15d8 GIT binary patch literal 352 zcmeAS@N?(olHy`uVBq!ia0y~yVBle3VBq6mV_;yA-uw3r0|NtNage(c!@6@aFBupZ zr9536Lo_D#PTK3m>?q=P-pz4RRu|7=g)aMyg{`q&Tg1dNbw4p=uBl;g%(-+#YeQ?w z)(sOyW~ucGCHYA68q#OU|Ue@O6mu`G#?J0RjuBB&1 zmjwKJw$GNu{_WM5ZH=P27F+rkNgec7UnSVis53cAV_HOfoXYg*gB!JNo;_!*J2)|6 zN$(%E&Bfd{Yid;vF$lBW+c#t3yRQ=zb$FNUTsx2NAcLmn6us+NIliGcZ@zuJTQ|=7 zyhzv5Bkv73FI2z3yyH{GyuBBBnNOET-zuMc|9IDOmCzs7_rFS4@rIY=xG*pxK; literal 0 HcmV?d00001 diff --git a/assets/images/chest.aseprite b/assets/images/chest.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..8ca436ad66cb3fedde8261b7aecd8af4a8c17431 GIT binary patch literal 531 zcmWe;Vqkc%l#xM%L4-kpfsuiMA%y`MC@?UB1Q`Sv1dxTP#-7Z`!0_uM3)of`1_lN( z1_lOp1+bM!c4RRyFtC7Jr@+9#prl~ps$r96Y1kiTeZNrc=7O|$hiCo%WH$3@K-NA3 z^C@}WnfgKADn>Sz_Vy(P2~E*?50hvAk3ak5^zA?Y|No!8c6&=*duCQvWLQv{Q`Z_J zqnqK$*Q{dQ;QS~pD-{mu!0=Hz{v2Q38qSfL71Up zPI5v6LpATm|5Fm!Oc;c$cbZ7`eE+sgpf&N;#u>{wjr$`uPdM8$Yk6m$oR9C0Ua3Hd zWpZb21l~61#05@x`oA^xUqVlRZ`wTmD>}1gx+nffH0_t^IcvklcI~6GZ z@C5T;>OO6gbk@mCczUIQsoN(em&6%WNoPJ^na-SClaq9|`1&9Yr)}l&Jh{X5T!yEI==+X?0!8w)QSJb1)K-2C7X`-lDo#YLahf2hmE#3rN_ zCLg$ZWl^eP;$ps{>C@sqD%DP2c7N~RcPFdAtF}%G6+C*hadr;VtA0Ce2De)^d-k&= zh=pxcFwN&XaQE=)FWaWQcaYd+;F>?dTSIEzJocyqw`YHjVMzAOTYlE|__Gy{89tcx zalIDZ>M6U^B(*Q*^RqLDw8GQdFMYoI>{b+y8)t~=AM3<=5kr|j_q@NDUcL3^joFj9 zg?D6M9qOv$4Ly9iU=8b=xg|vx4r%UAlfAxX{o2q|ysQf{-#v)FUGz5EG<9u|+CwGJ z2$f&|IYRT*5>IdY@uP32-QDT;3Sa%slJ!fnSslnT<$~v$sT)$Gz3drwIc$1(nYF3p m(7doU_9_kUQx7oIF)Un@x@k>ub_4?h1B0ilpUXO@geCyJ8>*uK literal 0 HcmV?d00001 diff --git a/assets/images/direction.aseprite b/assets/images/direction.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..bc771d8e696f56b120270b69f983aab50f0ef885 GIT binary patch literal 388 zcmZo+WMFu(l#zjtfsa9ffsuiMA%y`MC@?UB1Q`Sv1dxTP#%5t;VEFZs1#Bw|0|SE? z0|NuQ0@z9!?S*WGMo7{AZwq2 z`IJ2GO#L8l6(bu(#sZr+anf8_0ufQeZ~1`#;D<3_gjKsYMEgp$rTRtO^VaObiSR|3L&;AtwVL zL&cor1P8|d6AjE5rtOYj(dI21BiDBE%E71kdLGQlIV2JT_ zaSYLzn4EHe@1OEU$&H!`hnBvqTPS7bTrjI`U8m#5|6*ZVdv2Kgy;Pn62D>X5mZym< zSGvt|xkaz^&j0`b literal 0 HcmV?d00001 diff --git a/assets/images/event_horizon.aseprite b/assets/images/event_horizon.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..62da2c8a62a8c94b0b672615ac731cf8d8cd3f1c GIT binary patch literal 538 zcmb1QVqkc%l#xM%L4-kpfsuiMA%y`MC@?UB1Q`Sv1dxTP#-7ESz_Vy(P2~E*?50hvAk3ak5^zA?Y|No!8c6&=*duCQvWLQv{Q`Z_J zqnqK$*Q{dQ;QS~Uo$W;uqrSxFfuSQ{AYrx5@HZy zsF;(SaDZhI)1&_%6Lq3CFinX1tXY}BbGiGB`cawi_b>j>+&QtJSV-ybfBzXfn>30i z+~qT3U&b#jKfm(CJBF8z$NtX?Y08`+b0YV}vjy#n9;+9S^p7=E3VS}9A z0t+X8`I?758Z`+o#cbD2ym{iKO!O=lzwJBD9GuEmxYRR3>fljzv-yg*yYu)I|DJdj Ru=P*Z3~6KX8_upJV@V_JkiZ*;i?OKZMDr4 z1?*%GAAfpq(iS#`jHUZjr8a6Uii>A3`z*mbU-s?o>qj{`ojTVo>7I9aZ^6orCVP#E zA%Y+DKY7L_M+NrrG?Yc?X|U_s9KU<^t5@Hy zAJ1u8S8kqn{PyNIKRz{mI(^b;&JF3A53YXfjCqr~R_lcRQKLCOKKxK(Qx2aM^Y*&2 zUjBqWox`1HtK`f+v&3ulnpf?!i`9{`vwpfWlXdY1*YZPa?Keapw_lUGH0aeXmA?!O O3=E#GelF{r5}E*#n&OoJ literal 0 HcmV?d00001 diff --git a/assets/images/floor.aseprite b/assets/images/floor.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..206cd5ad41004d32fe50385e1479a7f06278afea GIT binary patch literal 614 zcmYdeVqkc%l#xM%L4-kpfsuiMA%y`MC@?UB1Q`Sv1dxTP#(u`g!0_uM3)of`1_lN( z1_lOp1+bM!c4RRyFtC7Jr@+9#prl~ps$r96Y1kiTeZNrc=7O|$hiCo%WH$3@K-NA3 z^C@}WnfgKADn>Sz_Vy(P2~E*?50hvAk3ak5^zA?Y|No!8c6&=*duCQvWLQv{Q`Z_J zqnqK$*Q{dQ;QS~%^4XOSV4|pU|{&q1XBeHyox!! zA&z{940wJsDjamUEFfjjwnL$p+1;;0|HJ?3JUb5s8@zjvu+~?2dEuq2x1Hn9gmHeY zuH9|R7IO93W1D%5vE^^ye^0l{z8$=pdQFhZ3fYGGo`bV0H^li&e_3r8-)8%uZPLx!%>SR*Vz-cbY6e)^~RDZ!$XhqYIi-FbD`rl$R) z@1nfg)vLN+o1Rb7^%D=bw=AxGaQDug%-sENp7hwQYJdIMzE-ilT72T<%A)c!dl|3J rw|c#272lTZ`o&`J>*61@|L6WvRUZie$X3w+ literal 0 HcmV?d00001 diff --git a/assets/images/floor.png b/assets/images/floor.png new file mode 100644 index 0000000000000000000000000000000000000000..8b5be205beca554dd20cd9315bcf090bb4fc7f81 GIT binary patch literal 742 zcmeAS@N?(olHy`uVBq!ia0y~yU=RUe4mJh`hTcwDUIqpR#^NA%Cx&(BWL`2bFfH(O zaSXBOt)1fNf7?Nz)!yd-=fuYf+}ceo2}?a%m>ao5A2nHgE&saw>oxDI)8x2>N_(4| zb=>pjL?~|5T*O{(UZ(WbbK14toAdAQnR$0B^Xm_#RF zny`BxufzX?nsao_S{5FD_wJxqkAsBXGZDjz%ayx@G&)+lo|b$L5MlLQtk4E zzFef?@x4s9in4F-OxG**Dcc;M`hRw)=;a)brG+vNfA@!MTgC9gbgsgOz|*3$s&lOYreP-Vz}AlCF{*15FMsa?}fep-_FB0(Z@JdW>7aA| z&uY1u8TT;ib&PWy(|bygX!H6uz#!ZtBGOUtW~-Em@xU;=cl@F1vA3~1)9l6E_=VH7 zTg|#2cPM=L$d>obQg83))k#wsXN5_hFXUv3-`1UR=VMBbc|q!F4#vb6Da9`{KHlmG zcE8y1cbe<}gN}hBlYXwb(EgWw`3F6&r7uk!jbHs?{gCPR>YbSGeg*~x22WQ%mvv4F FO#ti}Wbgn0 literal 0 HcmV?d00001 diff --git a/assets/images/force.aseprite b/assets/images/force.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..f4479cf5b2f0517365edf084f70cfa2f89ce0190 GIT binary patch literal 708 zcmX@Y#K7=iDISz_Vy(P2~E*?50hvAk3ak5^zA?Y|No!8c6&=*duCQvWLQv{Q`Z_J zqnqK$*Q{dQ;QS~r!q1yu!0=Hz`*dI38o4ZcolQ* zPTT0kVkpq|UNGl~`v&10zc)1usc)yfW)a;epyRUPg4d4a8w7q=chxd$Zxpz4T;TEF z#Qz`CPaN~UcB*y$-7{xvW}ordWIl6B?CWglbp|S@o8oRIho4q$F7oG2&s}O`pwZKx z=DTC{-j+A`?v1B&Qqt9$g5Ap_Vw)FUE8zdPH5#RmgMYeeW9IOeGKk= zUbuVKyItE~d;jOTEukiHf4=#@zD@`hVf`4C&u{=ReMm`4ne=uUY@yKK`lc-yHh9)2^2$^6iRmE7@Sb z$6R-^;hVQpe%uxiH(O(EF#UDw`ezrOiByKkN3%aW*7fn>n-@DDe6$I*U(5P@>DMK5 z_XPZ}>r1&+wd(A=_Fr|e)|IEWybhh6>Sj6BrfK@BvjJ=0%&6Qb_4(};o8bDRXKwzJ ozWi-%s#uZ#BI6@(+Na1GYW9Ub+8=l=Z%V58sd)RT6M9br0M`!%>Hq)$ literal 0 HcmV?d00001 diff --git a/assets/images/force.png b/assets/images/force.png new file mode 100644 index 0000000000000000000000000000000000000000..594e7f4d1afd6af7bfb58e6c5021fc60d3259c5a GIT binary patch literal 811 zcmeAS@N?(olHy`uVBq!ia0y~yU=U?sU{K&-V_;zTyTG`Efq{XsILO_JVcj{ImkbO{ zPd!~6Lp(b7PV@JWb`&|b|L&Hhw@!2Ag(-_M$2HCP62zIQ5s`6pmNWO%IbIuYIP-DO z+w=XE=M85*U60F?m#}l+wr6~)*|H!YV&RSE4O<=^I~#Q)C_p=9d%MNsbu)WA+wD#y z-rw`{ta<*u*_;=ab~ZhU6FFSYEX;aR*FRN{rFgE^D(%Xar*rg=ZSsBgN89nhu1U-9 zCt9}e?9XRlFuv@$#-_&Yj~@4#n9dyI*dRmWxi0?o`V0pwI+crZx!>QbV~jM>c^)NV zJT+?D$)(<_xEXB!?ltgxRC30mQ+aByvd;5>d8alk+kQS%Yc~UfM~m`4!?k$~3|gyC zpV?zHwMtj8FTpqV<4?KNXBsn7#iw1FwTo%3!h?fX_gB>a7u(WwVUx%8fVBz--gYaB z?d_cSad*M*qzhb34@~qpOQi~aCowcUpQ%$Se1GXwySmE7oU`t+ zYtG257MXgW?~|pgW7bQ7zzKI|zEs&)_useHwANaHBj2;L>B+Svf;>zjI$V=}KY3{@ zaXh)huVq%FAG2xgY|R79r!ai|suc8id*V$sb*@RXCr@AS_B~V03G+@9=eWoet#7hZ zem+(YojZB@EY>={j>~=5I(DW7OS-eSutF(qDRoek=@Bo8k2J>!X?1 zGY{On?O2l3RrkB=Zsr5ODuHwN_?r}(lTUSTKhbh!y}IdE zmHj8hjNiL2d@E0s={F6H``@;>`LI|)T;vj_iY=AdbN0@C_Hy!k_owUa|N3l5ogH(# zPbv4+l`qp+SEp{>+}g z-(Kg1%}b*XiXWN-xfJT^GOj#lH+auF;mG;<>fKFWPW{fk`K-IFPHf65o;&&J-=rA+ aG43=84STnXS%QIqfx*+&&t;ucLK6Th5rSC& literal 0 HcmV?d00001 diff --git a/assets/images/game_over_dead_bg.aseprite b/assets/images/game_over_dead_bg.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..f7597e839f26f64c2f83b93cedcb1219352649e6 GIT binary patch literal 6077 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 literal 0 HcmV?d00001 diff --git a/assets/images/game_over_dead_bg.png b/assets/images/game_over_dead_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..6b52e08349adddaf141016f80c40424899d81cb0 GIT binary patch literal 2924 zcmeAS@N?(olHy`uVBq!ia0y~yV7kD-!1#cJje&uo=V;U#1_lPk;vjb?hIQv;UNSIn zt9iOOhE&XXd-q`OYc~;xK+6F3S$v@#AG<$Xa$TYS?ZFzmCWY=FpZZ<9Y|`^zot>~r zpN;KD+3vgh^cWZpZ2zI5HR*IEIe0gQc$-&t*hTTX|hN*=O&)dCzCt-o7{Y<-Ity^XAptZhrfxb9?{a z8=t?w5uNig|H8ff+ua|r(9ru2c70{ou=Mxe+E-ug?CX8^%GF$dlU@mT-kGoG{@pvb zPrTav_q};1Z|)<_HBH-Zzx}rLt3dkZ@8+y;EBD!+FEhL^Kil&Dz55GFmsJ0zzkS9ob z!RyVd?HKy9dp3UM_)tQMQ$MI3;DzY==M+YwN?A#cMoTk zuHE|mZ^Ul#>}#c0&R40ue$+9C`~qcNH2;CZ9>Z;N58c;9Uf-l+Z*&Pew$FaMt3vmD4PT<#`FHzo@@rASx23E)3;AmiJH%6U|?YIboFyt=akR{0A8nsM*si- literal 0 HcmV?d00001 diff --git a/assets/images/game_over_victory_bg.aseprite b/assets/images/game_over_victory_bg.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..965c05df496e9c0685e25cc5fddfc0e8aef96203 GIT binary patch literal 5621 zcmew=%E0hoDI>!LrU#4)42%p63@HrAK!Je~B*-AZAb>1PHFl{e1H-S6EMQw%7#JAD z7#JAX6~I;^*^$M-z`z1>odN>`gOY-YtA+S`{HBs4|mJxrecKmP2K)3^Wp|NnpX+U+fM?U`9wkzqk)PF-t^ zjBbV}U$5TPKi#V<-#{K@lL9l6-Ty%@V(>|BVL;d9Wj0_KI zKN&MH?D4N-V`%t(@;n2>2fLq=3=H?w?U@-4{C<+oz)&&&4-W&wd+dt$>HhqA*K+xL zMuvv-K4*FZBP$Q-F)$eUo(a5b^!V=09k=Zm)H>Gr{>@$On=XAf{@8X~Q}@u*-8Xke zw6RshmE^w4kIuEa{o>!ef9uqrui&aQv}Qkjo?+*r?BM#_S(k6RegFQ%+}74R^~upU zceL%9uL#A?{&hQS_Bpd%`_=cqd>Q%b<(?UHE1#w}RH10utLSZOm|S*k>RiTGU2yB~ zPrm=9&8GBw&&|m2GPV`EyUf0BUu!lux1`>`?y_{z)w!Qj(_?Q+F4*_t)|LC)Zk24i z_}BKQZ~lp}^0U+DmM)(wd?4iF)|LBnx4z6-{JZ+o?0XZ=nmu{`MwYoi2g#`V>z9k8 zuK%e?f6ri}jw1f4*st!~8`*~EF4vd++je!?=H7Suk8jVLbNStzw|!@7zP?X?FWGRv z8}43Ea9J+CYkPa{?@99+f1yb%w>7*TTRp2rk0Bb}yj|z7?yYZliwKhGA9GKiFMa;% zp8bI`gcnZ#+P6II#XDnPTk!^6B*kBA{m*9JO`lbTYV_;h^JZP%b>8G_j8WONyW$&y z5z%L~|6`|R?jaxHv=y6Hzxmft3o`BE*T28z7cR;MMdS3Jz5ajpm-@|ES^fVTOO9CV z>|fn_KP&Uq_jxYIKQ6+!7d|M^^ z?DXYrFK%5y=%1>Yf4;>s<>2ezH}-5l``Nl)3zQQ>-}%;W{dM}sw)mN!-ucXM=G(Ea z|7`Vbi~XOMPr}qLsdo0`6Fsx^6}_9St5<g0wP>(kG*i3g|T~k6etBvt-c(; z_Mg$dx9fc>+IM}of2p(STygZWTSlwj_>|sW_37QJ`D_2Z`XZKiDEi9Y|DNd+E9cGL zbhXX$X`0oIu&L$Ye{;TuFW=4~d~XS-@tnD;yU(w(o>3xhch~>9<{2B->LD zNt-6Nd4goCzTL75e=a#QJv#JW?AOg#_zUY&y>jzB3XZ>CF7>e>(N@GfV5>ePu=Xf*~dn{>=ugk>p6{m=BQTx^Zc1}NhkgJOC#amIjc|YUR}5C*XqmB zQ#jvWZ;@0xH_7(@lpeFhw$A5FD2|f`iER#(pr~jSnzw>vQ?N>X$ zs@dvigHqr1{al-Te*T$&O1>YmKY#9e(PbC?_v%ai-`4h%rbb$wHGJu#b^1sC&Q;ZO zCt0hXVQ_!){PiO3U!df<|IPb9Ct~w&Uw;2|iFwr}ovF{>O?hhdRm#x$spW%?KQ*x> zuHZyI{qJ1)osV-*R~W1By%=rW{^!o->;AvjTQS$TqB<<)`STUqi!WL4H7!4Je($fI z7yCcSd=N!-RFI;*`=0re@4Z~R^Ks`)>o?n+`vd=m&t<%)XdjOp5>KAbT6Q)#^%>vJ zb;39)mVfnF`sdHd z*;a4Q&NK60I#c`ko>jG__cTicV{d*gnHLHQ^nI({A@0e2wkmRcR(zms_$%pEo$KCI zTAg3D-|BDb{j17XKYhOXz4X@Yy@vfc=g*5)cfHvebUz@lB=!8|$}n(@tdr}k`L49- z^WC32PfOe7&RhOI>9}n8YxPa{y=t99FHW`fe;)ZYeEI%2yQ`KY)$RLq&ae9L^0S}6 z#J<__IP0&=x9FwmD|B{kdj8U8^}knNrq_n|yX>uhFqRvP^N{{F>gr~MavOH$8&ezNB3>3{3Ozs1@H+1k&qY+khB9t@6v{&iwmp z{p-v-QL9^%tl!PpT6OdO*HimeyDQy0tX;2cY+qh^d-+*m)Avha%Po^{O1|5!T7Jhc z`|GCtucy|A_uI^zzy9C5-S0nc`Cfa=Ch)W0t%V+Mi(8I-5jR>dw1N;@~ba6|i*7;v0Zf_=drlwb%TXtvT z+Bfg3Qtw|4pLb$^`IGl2B3I2a^M4xo^!Ys7Ln5<_j&FlFB)(F~zW&$kPoH~0`cuN* zOz=Mc#o{TS(=y+k$&Ych3FaLAJVzT698=%?k@*Q% zaMODKCI6?!Q|Bt4M|RGc^A~Mh(q6pTd%x07Hgr~S-MZ`2KHHHD+5G-#Z2p=*dy?nz z6^HGAeAC!}M}ELrBqPr4n<={K=<5BpZ~x9W^Pg_{e#i5vpEIA{OK3(4laQ0=1M91! z*k*uKm_L1PTDFaKWjMf!5lfhCbvXJ^iwzxrL>A=~Cv2rt(y^{TV` z{Qc?ky3HlCjX&vy?wy|gYgWPe1*;G?#asSv3p;s!|L?h(kqMV{?mn9`KmD%Zik#qg zH^RP^@7?tNRk+sE=bQi5e0sn6yWGz1H+JghH0{stN)EUQDy&bnnD<`?Krw$#Vh6m))5-_vEhh18*v=-s?pzeN(e^`Re~>)$b=(pLkdNYL4Ld zeOZ2MZ;9t@(JW6%-}Fw>pnBdpBxCOU?Xom|Je#dA_mu3-nQN-q&dgi>`Q4(@^XLEn zJL|bK;grnW=Xd7K-~65}X9J1}VTShe-)cX)A8L}?cdpoO>U`~U#t-ux*=HBIm#Mv; z$GG|V%RRd@ggglwg3Nl`|>Bvn#Cpai<9fl?GxT`64n1t z|I{qqZ~R}a`g`E!^zW9n=d{1?j|w~cudgu~)nQXNf1dj9+@INR?z*3yclUXl<@;UD zqMwVu)iN}^TJ`+-3pwl5R!(D|nVWyhuDQm~P;y<7z2ux;`T02@wbu9dXZ>Y3z#_ZJ z;rXAU?`Lk-=FQyv`tRy%{0u%9RJlt|>zAkg%bdCV^cvaoYm@vL3>>H1T+pnxn>tth z$$Y=hrPixK+C_vnJEYx@ylMHnX5NkP<#F0)g4W72%vj_t*754=xqX|9i>~fp+7Hb5qj8e@Zem z_-UnF=dxvBIAD#1@#lU2=6ZPshI^{u0fr0f+c{rl1)2T>xT}AY%Xk literal 0 HcmV?d00001 diff --git a/assets/images/game_over_victory_bg.png b/assets/images/game_over_victory_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..bcdaed4f687124d64fe7dfc8e51b48e698fe7731 GIT binary patch literal 4466 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 literal 0 HcmV?d00001 diff --git a/assets/images/heart.png b/assets/images/heart.png new file mode 100644 index 0000000000000000000000000000000000000000..c94689083344f47f4a7997ab153b721fa9856f14 GIT binary patch literal 187 zcmeAS@N?(olHy`uVBq!ia0y~yVBiB`4mJh`26+$Niwq15jKx9jP7LeL$-HD>U?}x; zaSYLzxcAaV(FO$`mJ8gwBce{|^u5%Hh_LcmBP8te8`oE>VvD6t+4z zJNwdZzi+XI{@e@?EXr-K#B2O^6|-d$Q1aZ!-R&0mgVi?f59_kWCz6gUN+uacf47d? n=qkul*l+ZrpvCRsgU8u=JFXf_J$6b5xrV{h)z4*}Q$iB}-iATV literal 0 HcmV?d00001 diff --git a/assets/images/hud.png b/assets/images/hud.png new file mode 100644 index 0000000000000000000000000000000000000000..f812c625d28260ca9f9331f5392ff9a363d0e599 GIT binary patch literal 4170 zcmeAS@N?(olHy`uVBq!ia0y~yV7kD-!1#cJje&uo=V;U#1_lPk;vjb?hIQv;UNSHU zuz0#ShE&XXd)KjJ$;;T|AD2(Jd-5Rhn}J`Muv3^oIa3=`6FVNb3KTM>8ZFq-%6_xN|Lj4P2dTntXSyQAr38%_C*PHIKOrOJKkMYe z=e*HduZI3jeqB3%`6~PQr{`8EI(}zW zZuz%cITdf;$bTvQ6Epc^#;eb7)UMWa>&{+3B`ELSjyjVkJ`4=btH1Glx$trNJgEPC zr#7sUOiH(e`kpEOAp=80Lj&6Y6An`w=JmLn_St^E`7HZ$4nu=j$Zdz~92=kbeBN59 z#^8{>?boi4Q&(RvdmcN#$GyFF?#vrE?QhI3zgv@De5=MhKgM3FPKu%7AJKa4| zGY{6B-fFAOl9Rn=v3>LA`VHG(RDagp zmSe}z;A{4SZCBYJ-Pv%vqHFK|ICV99_vY(qhqKfFFIfNh`K%uI;Q8t1E5W&H>6D_+ z$G$FBOlM+{h}xRIpuKPZ7nuK6v6P$@us#j*eFZl=0|N_-34-vQ@%@?@LxUJ+#;gZl z%NY*rqHi`r zHl4%zpVGsh5A+3?7@oKCnozl-Bg(h~VfWc%_7uEKyq_L$|1bOZs{gS}3>gQLx3V)Z zG?ZSteQm>SoBA)4=f8T*A@wQ${;xDmvx^J|EEAY{85kO;=5y!HJ8wQagCXJaO$TNM z2L}+5u=^Xs=6UyP6JLFPyEytBJHrCWv-u0&@BQigoSET5&ogEz28Jl51&j;|0e?-) zHlP2axA|3i)A`>&S3ff?V>qxZdN*&$Td)8F!}GhhSWOrhuBbqi+g{k-pXX~k?E`!K zk3(0!$N&8_eXe~&{+)ljSDv3=_phAYQk)^-ZV3O2Ag`TYp0MA${QVp|LxP4ci^
    `4wug(2^>WTcab@h4A z%d2d+-{z_R9-rqM`+sYE{lY1tdGk)cQ-4wo`N*S>WB_w1Lw zUspYwao47N*X!E+MfD%W>jJzqpYE^!b1?4R{oD!npBl?s-#*XCFsB&Y)>*Ny@m*-zL)h# zv^fLAGH?P|Fq8L7`2Vl67o&EHj7_xJ1d`6KJ}$9-0l12wEljZub*r4 z=UGSFTiNaW_v3Q#{QF=3F8-hQT3P*j$op6AQoEvl36+jsYTEhB^3nOnKAtQp|@mDi+n>o*-U28Y+z z@<6^f4ZZJh-)>jv*NrXU)IakTW2~3v^m%_8J~J{zaLeIs86CK`D!=06p{4Ds_o=^r z{;#=O`~O`ghKxo~LB~?{+gS_LY^=RoJ@NXxJyj82n&;!{&ixei0k`j%7~FjGRrF@Id%LP*yfmMpd2ZY9T^~13pMU%H>wDdM%AQ|! zetzA$zUcdp4aaseFgR}nCDeg$Pa<3ZkEo?nJ{<}6U;pdcn#S$#*LS7$cPqbM`1$qR{5!UGYYO!k4g`RcxdJGy4-#U-)>qPT zdkS7AYA&_;c;)iyeWuwD*6u#ObV|@W`yJaq-PdJk5W4}Xverx6SO46(ci+3cC$h`q zY)k6)epWB<=g*$Q&0rt`&WWTqi)p7LXYH>xas8O89j~f4)uH-@s9K+BMF-k`Ny@g} Uwb_*NY&uAjr>mdKI;Vst0P-~fd;kCd literal 0 HcmV?d00001 diff --git a/assets/images/hud_fg.aseprite b/assets/images/hud_fg.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..e015cb2d8e59dfea9d5eb662aa2041647552b0dc GIT binary patch literal 2513 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 literal 0 HcmV?d00001 diff --git a/assets/images/inventory.aseprite b/assets/images/inventory.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..5698dd0fe5f24c89ff401c8192e16814c3afe771 GIT binary patch literal 2546 zcmeyw$-wYnDI>!LrU#4)42%p63@HrAK!Je~B*-AZAb>1PHFgmv1H-S6EMQw%7#JAD z7#JAX6~I;^*^$M-z`z1>odN>`gOY-YtA+S`{HBs4|mJxrecKmP2K)3^Wp|NnpX+U+fM?U`9wkzqk)PF-t^ zjBbV}U$5TPKi#V<-#{K@lL9l6-Ty%@V(>|)!P|QKiB{MVNLZeZGAuPUCtNz zS>Df&m&@J{d?!=;ZtdsC(x=N?tT&(AYiGZ2W6hm|$LCht?XzFdTwvne znU()UnfEo;xbHN6{73rl54KNlU!J@6GoAImT}|~a;fH^|Z+y7BFF(J0N!||SJA&^# zrWLsFP`)!!=V2d+5qU`VVP8nG`lsy5ACLBlS)ur~z3RQtz8&xX&Z->sDI!j<@A&-a z{8K;sec5+DmM%A!FaJ=u{9fLo&xP#r<*(kVSy4&AwJiPF&v2g1pJ|5oj+aldHa@rS zVt>BE{dw|_1|SL+&%<~{^5PR z`6bAsT%56kk`eQh`2dsy~iUrEt(`Tu`@`SsaVfB)z8?$5{G{XTbN zx7WRUa`)~V2>H+F-J18Wg1+4g-Tu$-UC+PT8++$lSC*UahLHA~t+(&o+pij3zV+OT z&t>oTpIyIt_hg-SduH6+e}31uV)gg$(!T1i+w=3N`tq7@ck?S(INy1BIsW9_+cF=2 z+}rzqj`8a~tDHCAo%r|i!MR_5C-(2Uy|^kr?0lg9Q;l@FR|4fbCZC-9^?bD5zw_bp zcTV2h9aHyrx&QooCjd35G^`Te!( zr=Pp;{=Dg&PUWX3Cp8z#MwM;4o_uZTy`KBhPx#8$l|G+iTwndV*Lvn#p7I@s=k9s8 zMt*f)*|VFQ)0evxKl|DLe(t90#m~Io{rU6HeR)B<_0&5vpPsQSKWAAo#kgDh|KF$Y zm+Q!{-&|9-#W}m&?$`Ct!8;Dm?RvLnzV!RIXHHv{Y%zW=WBL83>SCFw-NHY28kO(( z98i8u*4?`JX*vh-_y(7Ux9Mk!#-G+nHFt>b?;t0_nWu-^}X}5fA3s8Ki8)G`d*OA#q+Hz->s_#sdTq5 z|8ytzJ4j_;-Mc3{cfSLvl>K|>WO4c3-{=OXm!w-Zf8i=mS!Nx(Kdqb=R$zDg{EKs+ z@?6rsGjZm-lE1U=QsxWnR$vdFdbjdv#vbbn+|zbl{+eAzY(SA>1&$byygTvU!#*1m M{=(<=ok7Vl04ySz_Vy(P2~E*?50hvAk3ak5^zA?Y|No!8c6&=*duCQvWLQv{Q`Z_J zqnqK$*Q{dQ;QS~JDC_5SV4|pU|{&q1XBeHyoxzT zr(63m8w#|oRElTl^5EdSc%daA;DAsEiz&wvJ*5W6mbU~G@am+ zHn-k*+wtEgZXLXI!p~;r`Tsv=PMx~U_2q`8I$rs)sf$YI_X&OCEq|Q1ApHI>%kEn# zSB{l`{OMJ0^}Z*d;eJB^6b~ymf~z`w>~T^li8>BOnA{k=Us+4gfWi_N(&waFq&+x@On%1Y=xHGn!?%rrQJ8NaV;oE<^^e+Fku3qk8cXsRa?7UNX z%l696o}qbj;a_GmohOIS&(7Lre|_Ozwz`b`;~^n7uErkFMRmdeZl;7x^A%vs*s=8?*WMvhP1HpBHC8 z`oHUsufg9l$tLFZ>tu`1ox6X<*nZVp^KG5(Rh9dG&P_hQ*|6&H!~Lt|FCS_3H*AZv zt^a>Bo#+1kci;2Q3C_R1(Dd0Sm9t-O+Ssjkc765zzRTGgxn3Q)-Xd^tt$Q z-0P1wFYTP}_PeHc`r5}KZ>*{{jH*-4>~oK}Blvw{aq>^ogjqBG^i9}#Y<)_e%zXjx mKFvGAy0vQyPt@)xzhiYo?63Du(=YDZH*FGmCf_>u)@%UO$cGjH literal 0 HcmV?d00001 diff --git a/assets/images/komodo.png b/assets/images/komodo.png new file mode 100644 index 0000000000000000000000000000000000000000..4a2621b71f9519847a27f381639ae20f33cff924 GIT binary patch literal 1150 zcmeAS@N?(olHy`uVBq!ia0y~yV322EU@+idV_;x-8M5#e0|NtNage(c!@6@aFBuqE zEIeHtLn2!DM)+n(yNmn_`_wkI*F{A|IxN#=<*glzlGe*Y4;;Lf*uRZwXRBOQf%A!@ zJDvpb>{Yw|sGI-e)fMSCmoI3NzS59vu(0WZTGIsQ(vHyMp$F}CN{VL8{X286?Axd^ z`-p3^Du3^Lzqj&p+I21^XXS&%>RuxL6MGnYMc(^xr}rIu{KBS6(9?mX$SbGsXo1OZ z{VQQyuh?g?7_U8`^L4?tpouIkD%Vde^*-?OrJ-B>yrzY#GfI3`tQAq!|H{Ddzvef) zpMU>Cjm`k3q{NQy@?{%i<{e89Kc4VHi{Ze9&>2hR&IFy8OE%kit787G1ssBws`gU- z)g3MwB|gy+x$CZ){}PzU{A|YkGbfh+N#5n9fAG%Cldea)kKSis*t&CC`f!z;FL<7U|LJA37Nh2wGaz6Ct_b?Q1pLay=+rHCUvA`E-BU7Y2&pyBGb z;7O}}g=$l$UGVxA_v@6-$*XE7vwmxG2ESzHo-t$9gw~&~Oc~;yZ^Rigo_zaz-0Wwo z@aIf6lcMaO)zJ;bbB#Byd(Cp;#GyY*J8S=MoSS9Gd`7S5dcoCo=?1gTD$d^7bL<n={Eu-gDW~i|lUi!tUO) zn{*w-_8Ju3jB;<)`DHx!IY)NBPWrh$&m0rhPHkWDqUOmWt@`_S zzb_SHaPbytnV53R{Mgc|_q%o8O3j?Jih<-kO|NobjVQ+*bii9_t z2Y;Wf(QG|c`J~CceYLrBqw)=Ao#hPnJ^!chxpw<6RiB2%>#CS{?sor{{CjKSf_>?l zwx@4*ew}lA{q^P9XSsh`%-zz%^>fGhyJyb$N=!BC@wyq8AAA15iYbPcg}tj8d)I7x zt?}fsMBOxzUp>`%3J5T%e{)YS_;j^~<49Ax#39J-+@MPZC8u)vytzl`X^V;k10Qu Z!)|>}2?)>r#K6G7;OXk;vd$@?2>>RsIP?Gj literal 0 HcmV?d00001 diff --git a/assets/images/landmine.aseprite b/assets/images/landmine.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..089c55f1dc403060a8817a40d06e2eafbe0c71e1 GIT binary patch literal 526 zcmd;MVqkc%l#xM%L4-kpfsuiMA%y`MC@?UB1Q`Sv1dxTP#_nTeVEFZs1#Bw|0|SE? z0|NuQ0@z9!?S*WGMo7{AZwq2 z`IJ2GO#L8l6(bu(#sZr+anf8_0ufQeZ~1`#;D<3_gjKsYMEg_Zb)%SQQu;7#Wxt{xiW;2{8yV zRLogA*_!LHfq+YCi^(zf0`Y>W*L=0aSGvwxQZiBci9lv?v&^Lh22GrYm7*u6NKBY^ z_4fL?xBu^QmS4~lJ*{xw`oOI^M(<7=@3}WWOYGjAKdL(X@{y)8-%`9)d7dP+-YL@Q zJsuU&|IIUUTKe-vJ*5_|4|&)ucgP=qd3;mx#hM51cITcLPdj(<+|g^T-zVHU(dhf! zLo9cV;??w`2mDK`mi*ipcKYV);x2Z+WT&X$mwa2_EWF!#?Z0>A8&6*6$o~fWmTr`r F0{|KQoRI(k literal 0 HcmV?d00001 diff --git a/assets/images/landmine.png b/assets/images/landmine.png new file mode 100644 index 0000000000000000000000000000000000000000..3b6d28a87ae7f5e67e1cc38e5d5f78674c530b71 GIT binary patch literal 453 zcmeAS@N?(olHy`uVBq!ia0y~yU=RUe4mJh`hTcwDUIqpR#^NA%Cx&(BWL`2bFxGpz zIEGmC-koBbeJDVrOWOtRkY~Rrl z%sSo4Yq!XjrWFDu6O>*lEODOgy+}=~;pkBdDx5YHQsW(;Gp1OWTmFGwZioJ+CQB4Ctv$|#{cQ5CCt`V z`=eI5E)gp?H;vDz5PWjroF1R+zT26X-)>>Nae98&Ev@gW=4akM%b6#-!D};T!^hPZ zI3ura{AHV=+{3l`LE6VB5iz_6UVS$xvh0cBTYW#-?0nbZpqD(;mkMoK)osSvT9+p# z(mVZ_F~h2Dr*qz|t>1IM+Dg1^R`fcP8GP!C!q1-H5D*z)!qs&wXWrMCn{s;%W&LdU z?>(Nh`=G1y!tYz1wdQ`*C~D}ttg}P*M@~wpx}M z0pH{G>}}S`3N+SN8fbP0l+XkKZOq86 literal 0 HcmV?d00001 diff --git a/assets/images/memory.aseprite b/assets/images/memory.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..3994b93384c852d64661a98ac1e61f8c87d484f7 GIT binary patch literal 517 zcmZQ&Vqkc%l#xMzL4ZMlfsuiMA%y`MC^9gD1i|vi;?!cdGBPmy`p5#dm4$(UL5zWc zfn5P?C6W~n7#J8N6%?T&3Q!tO{byiMQZV_?z~HK3^Phns&C>8c14Dn9^?wG2`-N)% z85nLZNc+#g@b2)e{|pSjKbie!V3_$d;6DRH);@#(3=HN|^8Pb0cxUSWXJ82OR{77s zU}R(YpMk;NzT`gxgF!;me+Gu=yodi87?NlI|Ife>fA-0L28PqO|NLiQ`2QayGJEaz z{|pQ*b?yHd7&5c6{xdK{h6Vj+U?_9y`p>|y#>fcdhVbP73=G$+cY)kC-RnOCLsve? zc91XR85kH?6qrFwDEBVYgDpZqH#v*_yI$d#>A)m&wLck44ofy`glX=O&z)<4p z;uvDlo1Cyfz+e&goSF~c?G=5Wah;L)6~HsWxbW})?@R2$r0z1QeUkM*(D0wXE_rFE zSwlpFkc7JYpTF`_j~c@dzcRHBV47H#=wR01`gpbf|3CwuIomFJ_$Vx57K;)3e*f>{ oGYmzopr02Q)DKL7v# literal 0 HcmV?d00001 diff --git a/assets/images/menu_bg.png b/assets/images/menu_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..91a1f551a750672d4329334d303f572ba726f198 GIT binary patch literal 2647 zcmeAS@N?(olHy`uVBq!ia0y~yV7kD-!1#cJje&uo=V;U#1_lPk;vjb?hIQv;UNSIn z33|FXhE&XXd-q`BBL@M8z#B@;n$AoMWsb;k9;(!2|Mp&IUGy1_uXb1_y-&j0_3_7Z?}>I5HR*I9N;=7+9F37#Nrs zc^McQhmNuxXXFxEa&M>gf7o=bbnd6!?!VKN&)>ZHx9>CagY7vac=E$=w)(&w_BFSB}3vsm_JZU2{b>koWqu#l4`A1Q|paM39B4#$GJK!0_uM3)of`1_lN( z1_lOp1+bM!c5pE=Fi0wBKt{n=G66{fnkl25y%bU$^RJ`u2=5@xox`Fe+GuG ze30!73=IFlKKakUuuvN0aw|?jhzJ7%gS>*~e+C9oF`54i3^p^%{>w8kFt8{vgEc@1 zkWzLApTx@4A_c<`VFm_P1qKEt1`dY*Ob`VO3?&Q}j1_a<&h?%2WCw5SYa!#+k++Lk z+;1G8xixvu%Dw#)4R_!BzH9x1=&9D(E_^JFDs^(ItAsmFyjXMYN%(t4k)>zEF6QTd z`*HjJ|DO*frfp=ks*k%@d`J80rXQ}AZqLu3)_(l&_x^hSi<_hNU8r0C?4r%|!V^(( zZ`Yg3JuO#_tNg!T?tQ)8zCZtdKJS0zpA@?O$otO^pDKP9e^^^pz3>0;$IIu}-?ZH$ zr!D+~|ETlt*v{+H(UP}cNBL_1oc*@kG*0rql((dZ?X=Gac;s%q*V-HGL zXY617Nu$}mZp-=djQy)WzA9I}RB<5v+v>laIt%XKE%)saePsE?bSsOE-)ZsF!5-2l z-uE5f$=54bdv}kut>4+{2H#_EO{sH#z2n5>S1YebtUaE``}?o`+l^06`}ZGwTq1XC zy}`49^Bn#w`{(!9{rxI`w$dS9dfi-{_C z-TYbfAxn4Z*NM+_R{!_7uULP`^6I4h>-M?XzMApAKW-i0zir=Ff4aI$&*+Esllleg zk7obMjp^_EQ}+4#%H^Hcck)fCIB(y+GijEJu5*3g&x>E4KUw*A)2wX~FaC7yIhfue zmOtZvE^o$5i>vn+EQ^}ExGKxy#h-;0?NU>clV-p7{+WH{${FY1u^rp1^`raUvv0`$ znft6&3e^#5*f9-tw>zT*2AK!PV6MwpPnR=z&z5LsY_w4_= z*=$ld?|bua620;-z3zRr`FHDuVb_zjM^B$X1`X}cI+ok3wcNH(x zE#6af-^lAa&%INhbC3SHSj&3lGuIx`&sUSyP1=9hPVo7?6~-0XPgWn_ZB^gC@yR`H z|MSb_&c7~PTEiPD{Mr1`$6D4)f4i^QB`eP}&DVZ<`my-j{c`JH`mSh<;-9rWU|-nz z_^;Qc_wUU+B|d3+LhPw%jr!Fu1uK=WUES0A$vmchPgU5t|2mU1PlyOh# z=Pu`eCeIU2d|lkUe3#t^nfVL;EdINwVsGW^e>Lonc>iihsB#^ZqZ+ze#_3`pl;%=T8R5OKW5&Sej?ruA8DSetOn^hJ16myY(D* z?>}#hd|S8uTy_8VTHdOvDSKjh)qYyHZV&i(D(>sW zJByAVc>e$WqPP0OPhw~4^X5nWllvQ2VjFjJ^}1bhcaQb{%34sd`BUqo$iLI>SG)Zy zd+q%=@m>7F56d2|vAVzR%>54c>te6ktBqefe)f_*6?Jmm^0PK2|HMz;e|YD5Mc$?6 zXH(CsE^T}B^Xboco%)pApBXmhtdq7U?MwT``pNFzwHMoqI$S>=P2TzkluT~cznEI7 z{q4MGT+jBZ|8t%kt&l_q`a)0b?9cz}@3Ci|yPsqIox>jg{R3VWPMar|J+;PDC*_}j zOi_br_I=j@X}HKlrud+*{NzfZ3F^>g~K4R5~AeA}P3$0$Cw_uwDb%Do$> zZruBB<;$!8(mr4K+5PFqpXtZMk4aYLoppYG{^0803^#71U$B4w>b=Ok3HB!b(f!wc z*zc>m{kL-3yw#stpDp~n{OQGy#?N1e{@Y^H5?}J~&g-`?YBqfY6+LoR^^W&mUa!um zfALze*{7k)0baa_D?-lZ8Qey)7VKT*GqFUC>c zs(#P1D7m}q?_5g$Xz_`Cy}D97W9^GSoi^WMw%(s7|Jbizx&E5%wASmZm$zDa-@o!l zvLgLuF3X{d|1WwWo^Xy*n8XVStr^uX@{IY$;mUp@D9>}$ARbWY>7$=lnHE(XsKI)A!KwN@kU;f3~4?@!Yn)$aLwRk+pe zyW6LAPuDJc?*71aMqKTze;U&kJhs0VH~IKXjapl|P4#}C`W`RcKl`ln_uZcNqW1J) zfY>R&!^58LjhgbO_ub@tTf6GUnvM6j_dLn06K`^@41d-AWc|M7cbGTdT^)3+GOP26 z>)*S*;wR&B+??f6Kgd{2Vqj-?(C4)rF6H*nLd?XWWzgtbE8>o`2q- zmCwb0RYWQLFQ2o2@t><-|6hE6H_2{(LuL8g{J1!k`t3XJIiENG@>Re?UaS6!dVPew z_M*z*XR`w?i#}Vwy4bW=-A~?s_$__jw`#_Gj`xyV+MP=3J|ufys@S>D z;oqXad2Ke1{o9Z8C)IUi3v8GdSbOo0XOzHKmrrg#H+?Y^{5{d&S?Dh={Ym?8Uo}2p zedV)bl_cx0(+j?c%B`6<_n)1f?V5bPCG(h!W6me+SD3QzQ1_Gdzp7<^-!o@7 zzej%jWbyfG#h>T*c1*2$zNJFB{^VrI)nCtf#`R2}hztrdEB?vqC&yL14t!VX@Y<0z zD*oX@+r<@exv6uW|Bt>_u-IqIzMFM{GHdN-JvZWesXI%5%Rc#=`IZ0Q&70c({=bv^ zi-{3m>h`SIclWPl%k#bWmm5s1+xvCFcjm6&fqQ=+bolB1diGQCQX7{qUypsVeH;~g zq9*fe*Rn1DChs+z{)Bt&d-m_uzrM~?jL)cD|7g0=kH@dh zcW2aQ+{>za4Qa$AdF;DUcjZL-x7$BWU#w1M|IMo_H}PlItKw@VtM0eCady+3OQJ`z~R> zC4OA};vn$X>ea%N`*+r#(}`QW%%Jwkob4^~?C%YZpE5ju?!209`O}HTH)1PyKc4et zN9UJ|ku%kMug=lewfXfu{Nsl}d%v-jB`z5BeS z_j}X&RP%M?+>imAjZHPH(KFUXa!>ymw9JcaycE7?-5c{&>+j5N|6r*c?@^m^ucxx{ zg8TdJOH2#j-(`9xzn1^(?7}_nUuD0|wfJ^P;p?20`ln{w*023FtGZ*`N5TFRH4DF- z{nj0I-{#j3JJy%~Tvs2TCztJe@mJapQCsWh`62Pf-<@Owes21)=9kDB?TWiHO8u-$ z_syzrbr0S*;dAnmvy1sp&fZvO@!D|dJmY7_Pko)Uai8n!&Ux!!{4Biov7~r^mTml- zzr}vD{~K3??N48yFIy|s|Lk>Oz`l@QJlFLu|7m)r?-lPa{^oPi6wV~YO543@mJr6jL-93=4b6?sFQ21pPz8W{P6n@?zO4z^Q+$8mw5J`uhREqx%xG`7aX-= za!-sWeBC{{;r`tpyT3iIG}8UmP**;WeZKwK&*B&BS1jy&Umm{puJNyr9>3(?Yt>%< zleKTQLjQ*QZFb*(1olgw6Ml8_H?za%`xne_zn}6ilC8EhZ|dHkYfT?J{^Hd9EBo!g zvEEgyKjlml_G#L<|7$YF~wJXZwUv;TXtGnI%59gi3 zUo0*zn(_bmj(RaK`PlBvzdvIPo@US9-d=ir_Ww^iml)hA4!50Hw^HrOpVC*4pKrQW zJXvm&*vGGmhn!zKe_COG^7qVNC!+pbtjObv+kHseN1uN!|H*3Qe=P6AUue&YvX5^5 z|IXk&@0xtI{worPgzM(n?mPbYuhk-(m(R5;C+%PTW$UlSALege_;$O{(}iDMD;L)p zZ~m(I>+YSI%b(uwY=2iidCyi{O|80lyN|u9Q~q_zw*AY0mZ@?XXT@qgc#Z$8Th@Qi z{a59G`Mu9C>?mLHA?(xjB&9#=R^^xHfB0@_|M!n~ZN;~|v!Ax_UVp~&{)9S9?m`=v zuV0Ua9s9Ox>9W;7K;66Ant$GpxgYoaOZy$`8^1Syes3+?-_Ps*ZK+fII%frdUp$#rn?|2JN%ed9^;$KY5>9-P-E>>aRNcmDlv!hQHlD{h0Lky~rd=f&r~>(;yX-eHygqItJ0#pm+3$87u;e@FUk^U?TJaXriQ%HN3| zb-uggit;BP{(5-9_D;9?yXW1O`x{_;y5gZ#Rx_!D^r8Jc`OcSuSAeKe4LpPgLaEs-QBL_?0Wa{VA6?FW*=7t>W72g$ws3 z*e-v~{M>(WVfnqquj`)t7Fu_*LT=|=yH4?o;vcGC%ys^Cr{asqvLl~2`CUnWw(#r1 zidJX&FkKbCH46M|-|Nhq&3VDSzTSgdvj0)xvDk_ZnNQCbFZgRL@K3}4(zAz!&({2L zukzhuQ#~`Rc5j`AoNkRySNo;wmu|l16nHsBw6Rm`_I?>*-}^VOXSdbr zaO}ULUSF~9ZhDPe!z)h6gUMu$G-1R)}Q`S}Sli`<*f9G)&=2ZQWdsh8;r)U3^fnPn3Tfe-Yc_aaX)V! z%HFu8ZfT6-XI0tN*XEgiv5}0mKT_wYlYViY#HF8+t2b`4syn~x`STgMy_r`xUHkm- zO{DOg{3QDs$9M0N?>Un7O+l-Y;fs9HRpT4?+l_wwvAS~kg8#ymf}!U<_!qU`3HC~g z{#yCx)8pS8!X)|@ACjKy9BbQgU@ z8WS3CQ2)ic@9vEZd%1JPHQQF(l*RFVzECHADf}AC%KN>`+qq|b-}ZH%<5%NZ3;1v4 zb1u}c=1+aE$jW{6anJ4#0+vtCyC1Ahk2u~RAt)Cm&)X^f()8k|o(C6yUVNnZd)tEQ zZ4177-Vrpu<==jt;ppp^yI19(le`zTX7e+d|GPE(SHFL=&+^`5%}U;T{}~xJy_x_3 DeOo&V literal 0 HcmV?d00001 diff --git a/assets/images/player.png b/assets/images/player.png new file mode 100644 index 0000000000000000000000000000000000000000..1f3e6317cd4a2b3b913213dcabc5aaa13a6f599b GIT binary patch literal 7276 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 Y_Q0G)37z{H7#J8lUHx3vIVCg!0KjHi(f|Me literal 0 HcmV?d00001 diff --git a/assets/images/quantum_bow.aseprite b/assets/images/quantum_bow.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..a03261df56accbcfb571d745e3fda8c391c62ee2 GIT binary patch literal 496 zcmeys$iVPmDI)_P10RC|10w?iLka^jP+(vL2{H&U2p|hnja|UV!0_uM3)of`1_lN( z1_lOp1+bM!c4RRyFtC7Jr@+9#prl~ps$r96Y1kiTeZNrc=7O|$hiCo%WH$3@K-NA3 z^C@}WnfgKADn>Sz_Vy(P2~E*?50hvAk3ak5^zA?Y|No!8c6&=*duCQvWLQv{Q`Z_J zqnqK$*Q{dQ;QS~4>B+?u!0=Hz`*dI38o4ZcolPm zMFsxra~{HC~?tchpYHy+M8^M84y zRMXMFmv8@9KmV_Pq1f!(6TUWmX*Ae%yYMCRO4n`EC&V6^dc zaSYLzSUbtqgV|A_b-%T%$M)9Kol{RWa+ozKoH7;LsrJA>Sv5j~%WS1d(aOckjC~a@ z9&^(<8pM6@)}^4uxgD+t{3dKHJhxZkQ^M-X&-ZT6{J4gbWx?g#YMzEqQ(iMqS-sWg zpz3SyM-x=|GOvD)5bj9#Rd|}k+u*g*WBKiGAu65D3}x5UX5QL=ywqo^`6AYvv2%p8 znIi96PkdV}cs;N(y*GQKnn1j$+`RiQ`Fc0l8S@=DemQfM-G~3Lo>+aqc9r))dH3Sa z?mN!?Wn1uD6w8HdExhxd&?M882+HeDgRv>U;In zI}=wIUw+oXC3yMj+WkMSv3#oBIs&_MzId%WEY^tA_8y t%#a=8Pu!N3Xx5g$j61eKbjJ_I&x*^6*2V^UF)%PNc)I$ztaD0e0stl;tZe`Q literal 0 HcmV?d00001 diff --git a/assets/images/radar.aseprite b/assets/images/radar.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..824f0ab92f56427b2000801c7edd67482f490248 GIT binary patch literal 655 zcmeBYVqkc%l#wBTL4-kpfsuiMA%y`MC@?UB1Q`Sv1dxTP#^z^YVEFZs1#Bw|0|SE? z0|NuQ0@z9!?S*WGMo7{AZwq2 z`IJ2GO#L8l6(bu(#sZr+anf8_0ufQeZ~1`#;D<3_gjKsYMEg35*O3tRP1)FfsgRf~oRj5MZd7 z^K`l+Uz34=Yc-4cF=Yuew{W#JcXnKQz!)Rx*)#9p-u4fA^Ukoyrp%g|=;oDh)$FI{ zsrR3)x3JE+e7nSG^}mx6Y1=$1B`?bDUH&>Y)M1-QMctlb1+H;BjsAa)XHD^LJudO$ zV^3RgwQjazch%IGxi`U9Qzok;w@IUzBmYf} zZH3u#0iMABpEtjn-W_vzW96+XiBqpnJhv>--Tc+>pZZq@_ek!QAAHU|{FHGw`h)dL z{*$7T{!bg^pDnqPv~}hAuV+OS`wI*HwspMY4_nt!nKQpdE~{tnob48Twz0qSj@W!V z!}I(ibMk|IOnS!8o^>UEcYVHw?%zB5{^T(|@PaJS*- literal 0 HcmV?d00001 diff --git a/assets/images/radar.png b/assets/images/radar.png new file mode 100644 index 0000000000000000000000000000000000000000..a7a21f321b8d944e63273b5b1696c2c55ae597c1 GIT binary patch literal 574 zcmeAS@N?(olHy`uVBq!ia0y~yUJoIHBa zS5vk$c+<^|79w+8H2yiT7H00c$@lVd%%tw9rL6k#94Ghg{=T#P{W~GO2HoiYn(XTj zDgWHJMkc+R^Ln_7oaAAt=o896UolKL_rxc$H>xst!l4f`QV|@ld<-_63SisAuq1k(Es^sp+cVP_8chj5|Ffy7#xXJHrBC1XFXo@l*{lPHTt8zFN?^RlWbu_NSJKOIPYuUjBNC zjp6yz#Y=Zft`8|$86LX#{b^;jjHfaawx6w-$ucjVW#*|Ig`H9xuBAQQ+`0Vv!U_|` z_9wZ*_c^EYCG0EN+|ZC^U0lb&Fl{}5*gK~WJ2>a)-wj~P4=DL|GJ_-L?i-OChM##j zW%hh$WYG6_U3YoM{n)KrZz(B#n8Ct#@Ar!>(I40kOMKt`f&FmDF(I~u#bIss5^l@y zGEFeL(tRY^PKia?HJNkHJyQmT)SaE1?xf7Q9#GtQz&Ix{YTc6a&rKKSz_Vy(P2~E*?50hvAk3ak5^zA?Y|No!8c6&=*duCQvWLQv{Q`Z_J zqnqK$*Q{dQ;QS~&oD4BuqrSxFfuSP{AYrx5@HZ! zsF;(SaDd;1^~nEb1IARAvlom@7?O+5GQBMR+V^k%jL6URht7y5m|3PN&SpOJWCk<0 zaLf!5quk*gLG72-oD$nkPmN$rZ2Rf5EXg$I^rxE^ zysjUOigJ{sw_S0SI(NYwE2?-6CPb6k0 zygj6_az&?*jSo-8#+PrOb6@{ZyOrO$NZXj1nVETx8=Fy47muf;BoEs)y#i;$B+2j` zzfH9bk4j@+MM^AatbX`n-hUn*j`wzzCG#EK`~=UPw_ekr!yA71z=4KIW)Ce{6E#+` zr8r+a*t)p-K~-W>#bjQcrVG4Br{}zwykR1*s;T~CQ-PZSY+DX2=a>*A7bMHTFhQ+t VujA^*Wef}q44$rjF6*2UngD7Ilq&!L literal 0 HcmV?d00001 diff --git a/assets/images/torch.aseprite b/assets/images/torch.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..95d1329ce95c773866950fceb8574a8fe71edd89 GIT binary patch literal 637 zcmbSz_Vy(P2~E*?50hvAk3ak5^zA?Y|No!8c6&=*duCQvWLQv{Q`Z_J zqnqK$*Q{dQ;QS~{TLY-SV4|pU}X5u1XC5jAjD8H z=k0ZGu0sX_Y!BoP_I}}bbSYFYcIUf8q5Tg+W2(6PpOm(on&R`zc&^Q_2CvDD@`h^j zJ$)C8xyID;hf3cpof=hDC%i{FeZO|JYXJI zzkVvNI$N=|)1*1>_s;0g^0qO>2c#oJa`XB>pWJphUFqxs_3PHxm9sthpC3;Beey(VmDEcnmtoEN+P)z*v zZr@JE{kIOfyo>>$w=KRfB5CP!|DqejGp3f| zw8C#&PDrku%5Ahlzu#2-)p8r9wKJb4-G91KW~RSnKaawtT^Co)uzc5YY*}@C^tMlj zwA1=pgH~2dNxT{SFmR{b;tv+BPgIwL_V9gU7xB0)ShB%>!Lwxttz+sQSKL|P_T=Cu z=E|6BF_DiZ%}nBA2)KWH*-szCuUjQ|7)IZ@pVs1@RA=*)OT=HLDr5hpzI}@$pK4xz ze)Z>O6T53-jSI`wc`vQ`WW($c7n+kUabsoi_eVQ!L@8q7(jm89dr2Tx<3I=gc1Fe8-^oKZ6$vG-}2LKR!5_S3zWT j&@zSM!l!3N!{kMcpBM3=9ln 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 literal 0 HcmV?d00001 diff --git a/assets/images/walls.png b/assets/images/walls.png new file mode 100644 index 0000000000000000000000000000000000000000..d0193ffda930d4df16b07f8f9c2432a43bfacaac GIT binary patch literal 4677 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 literal 0 HcmV?d00001 diff --git a/assets/shaders/fog.frag b/assets/shaders/fog.frag new file mode 100644 index 0000000..b208536 --- /dev/null +++ b/assets/shaders/fog.frag @@ -0,0 +1,99 @@ +#version 120 + +uniform vec2 resolution; // 720x480 +uniform vec2 offset; // padding in pixels +uniform float scale; // scale factor +uniform vec2 torch_lights[32]; +uniform int num_torches; +uniform float time_sec; +uniform float lorentz_field; + +// External snippet +// Simplex 2D noise +// Source: https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83 +vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); } + +float snoise(vec2 v){ + const vec4 C = vec4(0.211324865405187, 0.366025403784439, + -0.577350269189626, 0.024390243902439); + vec2 i = floor(v + dot(v, C.yy) ); + vec2 x0 = v - i + dot(i, C.xx); + vec2 i1; + i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); + vec4 x12 = x0.xyxy + C.xxzz; + x12.xy -= i1; + i = mod(i, 289.0); + vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) + + i.x + vec3(0.0, i1.x, 1.0 )); + vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), + dot(x12.zw,x12.zw)), 0.0); + m = m*m ; + m = m*m ; + vec3 x = 2.0 * fract(p * C.www) - 1.0; + vec3 h = abs(x) - 0.5; + vec3 ox = floor(x + 0.5); + vec3 a0 = x - ox; + m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h ); + vec3 g; + g.x = a0.x * x0.x + h.x * x0.y; + g.yz = a0.yz * x12.xz + h.yz * x12.yw; + return 130.0 * dot(m, g); +} +// End of external snippet. + +void main() { + vec2 virtual_coord = (gl_FragCoord.xy - offset) / scale; + + if (virtual_coord.x < 0.0 || virtual_coord.x > resolution.x || + virtual_coord.y < 0.0 || virtual_coord.y > resolution.y) { + return; + } + + // tint + vec3 lf_color = vec3(3.0/255.0, 9.0/255.0, 42.0/255.0); + vec3 tint = mix(vec3(0.0), lf_color, lorentz_field); + + // radii + float p_i_radius = mix(100.0, 120.0, lorentz_field); + float p_o_radius = mix(340.0, 235.0, lorentz_field); + + float t_i_radius = 10.0; + float t_o_radius = 160.0; + + // player is always at screen center in virtual coords + vec2 player_screen = vec2(resolution.x / 2.0, resolution.y / 2.0); + + float light_strength = 0.0; + + // Player light + float d = distance(virtual_coord, player_screen); + if (d < p_i_radius) { + light_strength = 1.0; + } else if (d < p_o_radius) { + float t = (d - p_i_radius) / (p_o_radius - p_i_radius); + light_strength = 1.0 - t; + } + + // Torch lights + for (int i = 0; i < num_torches; i++) { + float td = distance(virtual_coord, torch_lights[i]); + float strength = 0.0; + if (td < t_i_radius) { + strength = 1.0; + } else if (td < t_o_radius) { + float t = (td - t_i_radius) / (t_o_radius - t_i_radius); + strength = 1.0 - t; + } + light_strength += strength; + } + + light_strength = clamp(light_strength, 0.0, 1.0); + + // Noise + float noise = (snoise(virtual_coord / resolution * 3.0 + vec2(time_sec * 0.25, 0)) + 1.0) / 2.0; + + float t = 1.0 - light_strength; + float alpha = clamp(t * (1.0 + noise), 0.0, 1.0); + + gl_FragColor = vec4(tint, alpha); +} \ No newline at end of file diff --git a/assets/shaders/fog.vert b/assets/shaders/fog.vert new file mode 100644 index 0000000..7a7123e --- /dev/null +++ b/assets/shaders/fog.vert @@ -0,0 +1,5 @@ +#version 120 + +void main() { + gl_Position = gl_Vertex; +} \ No newline at end of file diff --git a/include/main.h b/include/main.h new file mode 100644 index 0000000..e69de29 diff --git a/include/pch.h b/include/pch.h new file mode 100644 index 0000000..2e86570 --- /dev/null +++ b/include/pch.h @@ -0,0 +1,15 @@ +#ifndef PCH_H +#define PCH_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#endif \ No newline at end of file diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 0000000..ecfbfb8 --- /dev/null +++ b/include/utils.h @@ -0,0 +1,145 @@ +#ifndef UTILS_H +#define UTILS_H + +#include "pch.h" + +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +struct Color { + uint8_t r, g, b, a; +}; + +template +struct Vec2 { + T x, y; + + template + Vec2 operator*(U s) const { + return {x * s, y * s}; + } + + template + Vec2 operator/(U s) const { + return {x / s, y / s}; + } + + Vec2 operator+(const Vec2 &other) const { + return {x + other.x, y + other.y}; + } + + Vec2 operator-(const Vec2 &other) const { + return {x - other.x, y - other.y}; + } + + Vec2 operator-() const { + return {-x, -y}; + } + + T dot(const Vec2 &o) const { + return x * o.x + y * o.y; + } + + float dist(const Vec2 &other) const { + float dx = other.x - x; + float dy = other.y - y; + return std::sqrt(dx * dx + dy * dy); + } + + Vec2 normalized() const { + float length = std::sqrt(x * x + y * y); + if (length == 0) + return {0, 0}; + return {x / length, y / length}; + } + + float angle_to(const Vec2 &other) const { + float dx = other.x - x; + float dy = other.y - y; + return std::atan2(dy, dx); + } +}; + +struct Line { + Vec2 start; + Vec2 end; + + Line(Vec2 start, Vec2 end) : start(start), end(end) {} + + bool intersects(const Line &other) const { + float denom = (other.end.y - other.start.y) * (end.x - start.x) - + (other.end.x - other.start.x) * (end.y - start.y); + + if (std::abs(denom) < 1e-6f) // Lines are parallel (or very close to it) + return false; + + float ua = ((other.end.x - other.start.x) * (start.y - other.start.y) - + (other.end.y - other.start.y) * (start.x - other.start.x)) / + denom; + float ub = ((end.x - start.x) * (start.y - other.start.y) - + (end.y - start.y) * (start.x - other.start.x)) / + denom; + + return ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1; + } +}; + +struct Rect { + Line diagonal; // from top-left to bottom-right + + Rect(Vec2 a, Vec2 b) : diagonal(a, b) { + diagonal.start = { + MIN(a.x, b.x), + MIN(a.y, b.y) + }; + diagonal.end = { + MAX(a.x, b.x), + MAX(a.y, b.y) + }; + } + + bool contains(const Vec2 &point) const { + return point.x >= diagonal.start.x && point.x <= diagonal.end.x && + point.y >= diagonal.start.y && point.y <= diagonal.end.y; + } + + bool intersects(const Rect &other) const { + return !(diagonal.start.x > other.diagonal.end.x || diagonal.end.x < other.diagonal.start.x || diagonal.start.y > other.diagonal.end.y || diagonal.end.y < other.diagonal.start.y); + } + + bool intersects(const Line &line) const { + if (contains(line.start) || contains(line.end)) + return true; + + Vec2 rect_points[4] = { + diagonal.start, + {diagonal.end.x, diagonal.start.y}, + {diagonal.start.x, diagonal.end.y}, + diagonal.end + }; + + for (int i = 0; i < 4; i++) { + Vec2 edge_start = rect_points[i]; + Vec2 edge_end = rect_points[(i + 1) % 4]; + if (Line{edge_start, edge_end}.intersects(line)) + return true; + } + + return false; + } + + float x() const { + return diagonal.start.x; + } + float y() const { + return diagonal.start.y; + } + float w() const { + return diagonal.end.x - diagonal.start.x; + } + float h() const { + return diagonal.end.y - diagonal.start.y; + } +}; + +#endif \ No newline at end of file diff --git a/include/window/window.h b/include/window/window.h new file mode 100644 index 0000000..518bc1c --- /dev/null +++ b/include/window/window.h @@ -0,0 +1,174 @@ +#ifndef WINDOW_H +#define WINDOW_H + +#include "../pch.h" +#include "../utils.h" + +using ImageID = uint32_t; +using FontID = uint32_t; +using TextID = uint32_t; + +struct Font { + TTF_Font *font; + bool pixel_art; +}; + +struct Event { + enum Type { + NONE, + QUIT, + KEY_UP, + KEY_DOWN, + TEXT_INPUT, + SCROLL, + MOUSE_BUTTON_UP, + MOUSE_BUTTON_DOWN, + MOUSE_MOVE + } type; + + union { + struct { + uint32_t key; + } key; + + struct { + char *text; + uint32_t length; + } text_input; + + struct { + float x, y; + } scroll; + + struct { + uint8_t button; + Vec2 position; + } mouse_button; + + struct { + Vec2 position; + Vec2 delta; + } mouse_move; + }; +}; + +struct Window { +public: + // title can be free'd by the caller after the window is created, as it's copied internally. + Window(Vec2 size, const char *title); + + ~Window(); + + // Rendering functions + + ImageID load_image(const char *path, bool pixel_art = false); + void unload_image(ImageID image_id); + + FontID load_font(const char *path, int font_size, bool bold = false, bool italic = false, bool underline = false, bool pixel_art = false); + void unload_font(FontID font_id); + + // text is copied internally, so it can be free'd by the caller after the text is created. + TextID create_text(FontID font_id, const char *text, uint32_t length, Color color); + void unload_text(TextID text_id); + + bool write(TextID text_id, Vec2 position, float z, float angle = 0.0f, Vec2 scale = {1.0f, 1.0f}); + + bool draw(ImageID image_id, Vec2 position, float z, float angle = 0.0f, Vec2 scale = {1.0f, 1.0f}, float alpha = 1.0f); + + bool draw_rect(Rect rect, Color color, float z, float angle = 0.0f); + + void render(); // the rest of the calls only build the instruction list, this actually renders everything added to the screen (following z order) + + void clear(); // clears the instruction list + + // Event handling + + bool poll(Event &event); // returns true if an event was polled, false otherwise + + // consumes an event, (mostly for text input, to free the text input buffer) + // but also so the user can decide when they're done with the event explicitly. + void consume(Event &event); + + // Text input + // Starts accepting Unicode text input events, and stops acceptong normal key down/up events for those keys. + + void start_text_input(); + void stop_text_input(); + + // Clipboard get + + // To be free'd by the caller after use. + const char *paste(); + + // Time + + uint64_t get_time(); // returns the time in milliseconds since the window was created + uint64_t delta_time(); // returns the time in milliseconds since the last render call + +private: + // internal data and functions + SDL_Window *window; + SDL_Renderer *renderer; + + Vec2 real_size; + Vec2 virtual_size; + Vec2 offset; + float scale; + + // White pixel texture for drawing colored rectangles + SDL_Texture *white_tex; + + // Image pool + std::vector images; + std::vector free_image_indices; + + // Font pool + std::vector fonts; + std::vector free_font_indices; + + // Text pool + std::vector texts; + std::vector free_text_indices; + + struct RenderInstruction { + enum Type { + DRAW_IMAGE, + DRAW_TEXT, + DRAW_RECT + } type; + + float z; + float angle; + Vec2 scale; + + union { + struct { + ImageID image_id; + Vec2 position; + float alpha; + } draw_image; + + struct { + TextID text_id; + Vec2 position; + } draw_text; + + struct { + Rect rect; + Color color; + } draw_rect; + }; + }; + + std::vector render_instructions; + + bool text_input_active; + + void compute_transform(); + + bool mouse_in_window(const Vec2 real_position, Vec2 &virtual_position); + + void draw_letterbox(); +}; + +#endif diff --git a/notes.md b/notes.md new file mode 100644 index 0000000..bd44b72 --- /dev/null +++ b/notes.md @@ -0,0 +1,110 @@ +Game Engine - With Mist a maze game + +# What? + +### The engine + +- A high performance - low level game engine that provides abstactions for 2d games to be built on. + +- A scene system + - A scene is a set of scene components used to create it, + - For example, a scene for menu, for game over screen, and most importantly, for the main game scene. + - A scene component can be one of, + - A basic geometric shape + - A text, with localization maybe? + - An image + - A game world + - A block, which allows merging other components into a single unit. (can be used to implement HUD systems) + - each component has coordinates on screen and z ordering. + - And they can handle clicks / keyboard events and have scripts attached to each. + +- The bus + - any event can be fired on the bus by any object and then used by other objects for actions like enemy attacking player or something, + - as the game "world" captures input, the bus can be used to forward it to other objects, or just propogated through "update" loop functions + - they can be scoped with lifetime rules for tighter control and avoiding a event mess + - they need to be predefined in a centralized table for making debugging easier, but this can be optional. + +- A game world is just the game world (everything is coordinates in the world map relative, and this object controls the camera), \ + and it can be defined as topdown or side (for z ordering rules). + - everything is an object (defined later) + - with specail object for tiling spritesheets where a set of tiles can be defined and the engine will place the right kind of tile based on the scenario \ + (for example in a maze if i define a spritesheet with tiles for corner / edge peices and then tell the maze as a grid of cells with 1 for wall and 0 \ + for empty it will automatically place the correct tile from that list.) + - and support for maze generation and solving algorithms, which can be used with tiling object to create maze maps but also just usable for anything. + - pixel perfect or anti-aliased modes + +- Every object is just a set of components added to it to form something in a game "world" + - An object joins together components to create an entity + - Some components could be + 1. Body + - Shape (Dynamic or static) + - light level + - Mass, physics interactions + - Coordinates + 2. Spritesheet (state based or singleton)/(animated or static) + - State based is for like player sprite can switch between states like facing direction etc. + - It will have an api to load and manage game sprites with animations handled automatically. + - The developer can define sprite locations for static and for dynamic a list of files or pixel \ + areas within a bitmap image for each animation frame and the fps of the animation. + 3. Render, only for those that dont use a Spritesheet + - Can be used for rendering a box or other simple shapes / text for fine grained control, this is automatically handled if a spritesheet exists. + 4. Update + - Can be used to connect a script to run on every game loop for this object (delta based) + 5. Event Subscription + - Can be used to subscribe to global / scoped events in the game event bus. (input can be handled through this or in update) + - In debug mode the collisions will be visible and other info like coords etc. logged for that game "world" + +- It will have a physics engine for physics calculations like collisions, velocity, friction, forces, los, etc. attached to the game world + - It applies to all objects with collision bodies + - It has a center property that explains where the objects center is relative to the shape, and the size of the shape + - it has a mass which shows its inertia + - And they can be static or dynamic, rigid is used for walls and dynamic is for player / enemies etc. \ + with an api to simulate a force on a dynamic object, with collision handling with other static objects to stop it and with other dynamic \ + objects for firing collision events (for exmple checking if player collides with enemy) + - it also has setting for adding gravity (added for side view games.), or defining friction force relative to a coefficient defined per object for texturing. + - Calculations for interacting these objects in the world and updating their positions based on velocity / forces each game loop. + - Calculations for raycasting and testing if objects have line of sight. + - See if i can GPU accelerate los/raycasting. (and maybe use haskell for that bit, would be fun to show cross language c abi) + +- Lighting support for fog based systems and shaders for common scenarios like clouds / fog and api to create custom shaders + - A light system to render torches/light systems + - With api to implement shaders for special effects + - These are GPU accelerated shaders + - owned by world + +- An api for game "world" persistence. and generic data store. + +- Normal classes / logic can be written outside and reused in the system. + +- The scripting is done in mruby with most common ruby gems available. (the actual project will be in c++) +- The scene can be defined and setup in a markup-style custom ruby DSL. +- Then components in the scene have scripts for components connected to it (like linking it to a do end block or class or smthn) + +- Maybe use ffi or libruby instead of mruby? + +- And special predefined scenes for menu / settings types with settings having predefined settings for keybinds used. +- And the godot style project settings where we can define key value pairs with datatype for value that can be used as globals. \ + and interfaced through autogenerated settings scene, or hidden but used as globals internally. + +- And as a sample game implemented using it I will make mist. + +### Mist - A maze game + +- The main focus of the game is to show off all features of this engine. +- It also focuses on maze generation and solving with choices to select different types of maze generation algorithms and solving ai for npc's +- The main game has 4 modes + 1. _Classic_ - A finite maze is generated on world creation and player has to fight enemies, collect resources / weapons and find the exit to win + 2. _Tutorial_ - A smaller map with basic elements and npc's for player to inteact with and tooltips for helping them learn how the game works + 3. _Infinite_ - A maze is procedurally generated as the player walks using predefined seeds and memory optimizations to allow storing such information, \ + the goal is to get as far from spawn as possible without dying. + 4. _Editor Mode_ - A mode in which teh user controls the map itself and can test out various maze generation algorithms and place npcs / rooms and tweak \ + game rules to create custom game scenarios. + +# Who? + +- This game engine is targeted towards basic to intermediate indie developers trying out 2d game development for 2d open world like scenarios with \ + physics etc. (in both topdown or side view mode) + - They can use the scene editor to make scene setups for all the visible stuff in teh game and then write scripts for behaviour of it using ruby syntax, \ + which is a high level OOP interpreted langauge with a low learning curve due to its simple to learn syntax, and tons of helper functions for rapid development. +- It could be used by educators for teaching game development basics to students and getting hands on experience. +- This engine also indirectly effects the gamers, the end users as they are the ones to feel any performance costs when enjoying the games. diff --git a/samples/main.rb b/samples/main.rb new file mode 100644 index 0000000..aef04fe --- /dev/null +++ b/samples/main.rb @@ -0,0 +1,185 @@ +# Sample ruby DSL to create a game window. + +width, height = 720, 480 +image = Image.new "assets/loading.png" + +window = Window.new width, height, title: "My Game", loading_bg: image + +window.map_key :forward, "w" +window.map_key :backward, "s" +window.map_key :left, "a" +window.map_key :right, "d" + +menu_scene = Scene.new + +default_font = Font.new "assets/DejaVuSans.ttf", 12, italic: true + +start_button = ElementText.new "Start Game", font: default_font, position: {x: 100, y: 100} + +start_button.on_click do + window.start :game_scene +end + +menu_scene.add_element start_button + +game_scene = Scene.new + +world = ElementWorld.new + +# world.cam # camera object to control the view of the world, has position, zoom, rotation + +# can define one of 5 types of components connectable to any entity + +world.create_body, :sample_body do |entity| + # these properties exist in the dsl, and are used by the physics engine, but you can also add any custom properties you want to the body and use them in your controller code or whatever + shape = :rectangle + size = {width: 50, height: 50} + angle = 0 + mass = 1 # this can be set to 0 for static bodies that should not be affected by physics, these bodies will not move but can still collide with other bodies (velocity will be ignored for static bodies) + velocity = {x: 1, y: 0} + collision = true # whether this body should collide with other bodies, if false it will not collide with anything and will not be affected by physics, but can still be moved by setting the position directly or by using a controller, useful for things like ghosts or whatever +end + +world.create_sprite, :sample_sprite do |entity| + tiles = Tiles.from "assets/sprite.png", tile_width: 50, tile_height: 50 + animation1 = {tiles: 5..6, frame_rate: 10, loop: true} + animations = {idle: animation1} + current_animation = :idle +end + +world.create_light, :sample_light do |entity| + color = {r: 255, g: 255, b: 255, a: 255} + intensity = 1.0 + radius = 100 + + # useful if using inbuilt fog light shader only + # mostly cuz i need it though most engines wouldnt have such a feature explicitly (it is dumb addition but i added it cuz i want to) + # it could be achived using other methods / custom properties but this is just a convenient way to do it +end + +world.create_render, :sample_render do |entity| + # for extra rendering options like speech bubbles, health bars, etc + z_index = 0 # by default all are y-sorted, but can override with z_index for more control in here + renderer do |entity| + draw_rect 0, 0, 50, 50, {r: 255, g: 0, b: 0, a: 255} # example of custom rendering code for this entity (0,0 is the position of the entity) + draw_text "Hello World", default_font, 0, 0, {r: 255, g: 255, b: 255, a: 255} + end + # offset from the position of the entity, useful for things like speech bubbles or health bars that should be above the entity, etc + offset = {x: 0, y: 0} +end + +world.create_controller, :sample_controller do |entity| + # event subscriptions for this entity + events = [:damage] # custom events that can be triggered on this entity, useful for things like taking damage, opening a chest, etc + + # these are defined in the window and mapped to the actual keys, so you can use whatever keys you want and just map them to these actions in the window + # teh user can also remap them in the settings of the game + keyboard_inputs = [ + :forward, :backward, :left, :right + ] + # direct means only clicks on this entity will trigger the mouse inputs, useful for like chests or doors or whatever + # global means mouse inputs will be triggered as long as the mouse is within the window, useful for like aiming or whatever + # none means mouse inputs will not be triggered for this entity, useful for when the entity should not be interactable with the mouse + # on direct and global mouse mode, you can also get the mouse position relative to the entity or the world + mouse_input_mode = :direct + mouse_position_mode = :relative + mouse_inputs = [:left, :right, :hover] + # when multiple entities match the mouse input, the one with the highest priority will receive the input, useful for when you have multiple entities on top of each other and you want to control which one should receive the mouse input + mouse_priority = 100 + # doesnt work without a body component (as they have no size) + + update do |delta_time| + # example of custom controller code for this entity, delta_time is the time since the last update in ms, useful for making movement frame rate independent + if key_down?(:forward) + # move the entity forward, you can access the body component of the entity to move it with physics, or you can set the position directly for more arcade style movement, etc + # if the entity has a body component, you can also apply forces or impulses to it for more realistic physics interactions + # if the entity has a sprite component, you can also change the current animation based on the input or whatever + entity.body&.velocity.y = -1 + elsif key_down?(:backward) + entity.body&.velocity.y = 1 + else + entity.body&.velocity.y = 0 + end + + if colliding_with?([:enemies]) # list of groups to check collisions with + puts "Colliding with an enemy!" + end + + if mouse_down?(:left) + # example of getting the mouse position relative to the entity or the world + puts "Mouse position relative to entity: #{mouse_position}" + end + end + + on_event :damage do |amount| + # example of custom event handling code for this entity, amount is the damage amount passed when the event is triggered, useful for things like taking damage, opening a chest, etc + puts "Entity took #{amount} damage!" + entity.store :health, entity.fetch(:health, 100) - amount # example of using the entity's store to keep track of custom properties like health, etc + # these store properties cane be used in any components or at entity creation + end +end + +world.add_entity "Enemy1" do |entity| # Name is optional (as entities can be referenced by group or name) + groups = [:enemies] + + position = {x: 100, y: 100} # entities always have a position, even if they don't have a body component, this is useful for rendering and for entities that don't need physics but still need to be positioned in the world + body = :sample_body + sprite = :sample_sprite + render = :sample_render + controller = :sample_controller + + entity.store :health, 100 +end + +world.add_entity "Trap Preview" do |entity| + groups = [:trap_previews] + + position = {x: 0, y: 0} + sprite = :trap_sprite + + disabled = true # this entity will not be updated or rendered normally +end + +# systems are called on the world every update frame + +world.add_system :physics # default basic physics + collisions system + +world.add_system :trap_placement do |dt| # example from teh game mist trying to be made with this engine + # do trap placement logic when a trap is held + if world.fetch(:holding_trap, false) # example of using the world's store to keep track of global state like whether the player is currently holding a trap or not + if mouse_down?(:left) + # place a trap at the mouse position, you can use the mouse position relative to the world for this + world.add_entity "Trap" do |entity| + groups = [:traps] + + position = mouse_position_world + body = :trap_body + sprite = :trap_sprite + render = :trap_render + controller = :trap_controller + end + + world.store :holding_trap, false # stop placing traps until the player picks up another one + else + # show a preview of the trap at the mouse position, you can use the mouse position relative to the world for this + world.entity("Trap Preview").update do |entity| + position = mouse_position_world + disabled = false # enable the trap preview entity to show it at the mouse position + end + end + else + # hide the trap preview when not holding a trap + world.entity("Trap Preview").update do |entity| + disabled = true + end + end +end + +# this is used by teh engine like so +# when a bunch of enities reuse the same components, you can define the components once and then just add them to the entities, which is more efficient than defining the components for each entity separately +# and also for example all the entites that have a body component can be packed and calculated together in the physics engine, all the entities that have a sprite component can be packed and rendered together in the rendering engine, etc. + +window.add_scene :menu_scene => menu_scene +window.add_scene :game_scene => game_scene + +window.start :menu_scene # first scene to show when the game starts stops the loading bg diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000..46321d1 --- /dev/null +++ b/src/main.cc @@ -0,0 +1,70 @@ +#include "../include/window/window.h" + +int main() { + printf("Starting engine...\n"); + + Window w({800, 600}, "Render Test"); + + ImageID img = w.load_image("./assets/images/arrow.png", true); + FontID font = w.load_font( + "./assets/fonts/charybdis.ttf", + 24, + false, + false, + false, + true + ); + + TextID text = w.create_text(font, "Hello Engine", 12, {255, 255, 255, 255}); + + float t = 0.0f; + + bool running = true; + + printf("Entering main loop...\n"); + + int i = 0; + + while (running) { + Event e; + while (w.poll(e)) { + printf("Event polled: %d\n", e.type); + switch (e.type) { + case Event::QUIT: + printf("Exiting\n"); + running = false; + break; + case Event::KEY_DOWN: + printf("Key down: %d\n", e.key.key); + if (e.key.key == 41) { + printf("Exiting\n"); + running = false; + } + break; + default: + break; + } + w.consume(e); + } + + w.clear(); + + // animated position + t += 0.016f; + float x = 200 + std::sin(t) * 100; + float y = 150 + std::cos(t) * 100; + + // background rect (low z) + w.draw_rect({{0, 0}, {800, 600}}, {20, 20, 30, 255}, 0, 20.0f); + + // image (mid z) + w.draw(img, {x, y}, 1.0f, t * 50.0f, {2.0f, 2.0f}, 1.0f); + + // text (top z) + w.write(text, {0, 0}, 2.0f, 0.0f, {1.0f, 1.0f}); + + w.render(); + } + + return 0; +} \ No newline at end of file diff --git a/src/test.c++ b/src/test.c++ new file mode 100644 index 0000000..9a3cb0d --- /dev/null +++ b/src/test.c++ @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include + +int main() { + SDL_Init(SDL_INIT_VIDEO); + TTF_Init(); + + SDL_Window* window = + SDL_CreateWindow("SDL3 Demo", 800, 600, SDL_WINDOW_RESIZABLE); + + SDL_Renderer* renderer = + SDL_CreateRenderer(window, NULL); + + // log window size + int w, h; + SDL_GetWindowSize(window, &w, &h); + SDL_StartTextInput(window); + printf("Window size: %d x %d\n", w, h); + + // ---------------- LOAD IMAGE ---------------- + SDL_Texture* image = IMG_LoadTexture(renderer, "./assets/images/arrow.png"); + + SDL_SetTextureScaleMode(image, SDL_SCALEMODE_NEAREST); + + // ---------------- LOAD FONT ---------------- + TTF_Font* font = TTF_OpenFont("./assets/fonts/charybdis.ttf", 24); + + SDL_Color white = {255, 255, 255, 255}; + + SDL_Surface* text_surface = + TTF_RenderText_Solid(font, "Hello SDL3 + TTF!", 17, white); + + SDL_Texture* text_tex = + SDL_CreateTextureFromSurface(renderer, text_surface); + + SDL_SetTextureScaleMode(text_tex, SDL_SCALEMODE_NEAREST); + + int running = 1; + float angle = 0.0f; + + while (running) { + SDL_Event e; + + while (SDL_PollEvent(&e)) { + if (e.type == SDL_EVENT_QUIT) { + running = 0; + } + + if (e.type == SDL_EVENT_TEXT_INPUT) { + printf("Text input: %s\n", e.text.text); + } + + if (e.type == SDL_EVENT_KEY_DOWN) { + printf("key down: %d\n", e.key.key); + } + } + + float mx, my; + SDL_GetMouseState(&mx, &my); + + // ---------------- UPDATE ---------------- + angle += 1.0f; + + // ---------------- RENDER ---------------- + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + + // ---- draw mouse box ---- + SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); + SDL_FRect mouse_rect = { mx, my, 10, 10 }; + SDL_RenderFillRect(renderer, &mouse_rect); + + // ---- draw image (scaled + rotated) ---- + SDL_FRect dst_img = { 200, 150, 200, 200 }; + + SDL_RenderTextureRotated( + renderer, + image, + NULL, // full texture + &dst_img, + angle, // rotation degrees + NULL, // center (NULL = center of rect) + SDL_FLIP_NONE + ); + + // ---- draw text ---- + SDL_FRect dst_text = { 50, 50, 400, 50 }; + SDL_RenderTexture(renderer, text_tex, NULL, &dst_text); + + SDL_RenderPresent(renderer); + + SDL_Delay(16); + } + + // ---------------- CLEANUP ---------------- + SDL_DestroyTexture(text_tex); + SDL_DestroyTexture(image); + TTF_CloseFont(font); + + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + + TTF_Quit(); + SDL_Quit(); + + return 0; +} \ No newline at end of file diff --git a/src/window/window.cc b/src/window/window.cc new file mode 100644 index 0000000..5c0f707 --- /dev/null +++ b/src/window/window.cc @@ -0,0 +1,534 @@ +#include "../../include/window/window.h" + +Window::Window(Vec2 size, const char *title) { + SDL_Init(SDL_INIT_VIDEO); + TTF_Init(); + + virtual_size = size; + + window = SDL_CreateWindow(title, size.x, size.y, SDL_WINDOW_RESIZABLE); + renderer = SDL_CreateRenderer(window, NULL); + + int w, h; + SDL_GetWindowSize(window, &w, &h); + real_size = {(float)w, (float)h}; + + compute_transform(); + + images.reserve(100); + free_image_indices.reserve(100); + fonts.reserve(100); + free_font_indices.reserve(100); + texts.reserve(100); + free_text_indices.reserve(100); + + text_input_active = false; + + white_tex = SDL_CreateTexture( + renderer, + SDL_PIXELFORMAT_RGBA8888, + SDL_TEXTUREACCESS_STATIC, + 1, + 1 + ); + + uint32_t pixel = 0xFFFFFFFF; + SDL_UpdateTexture(white_tex, nullptr, &pixel, sizeof(pixel)); +}; + +Window::~Window() { + for (auto tex : images) + if (tex) + SDL_DestroyTexture(tex); + + for (auto tex : texts) + if (tex) + SDL_DestroyTexture(tex); + + for (auto &f : fonts) + if (f.font) + TTF_CloseFont(f.font); + + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + TTF_Quit(); + SDL_Quit(); +} + +ImageID Window::load_image(const char *path, bool pixel_art) { + SDL_Texture *texture = IMG_LoadTexture(renderer, path); + + if (pixel_art) { + SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST); + } + + if (texture == nullptr) { + fprintf(stderr, "Failed to load image: %s\n", SDL_GetError()); + return UINT32_MAX; + } + + if (!free_image_indices.empty()) { + ImageID id = free_image_indices.back(); + free_image_indices.pop_back(); + images[id] = texture; + return id; + } else { + images.push_back(texture); + return images.size() - 1; + } + + return UINT32_MAX; +} + +void Window::unload_image(ImageID image_id) { + if (image_id >= images.size() || images[image_id] == nullptr) { + fprintf(stderr, "Invalid image ID: %d\n", image_id); + return; + } + + SDL_DestroyTexture(images[image_id]); + images[image_id] = nullptr; + free_image_indices.push_back(image_id); +} + +FontID Window::load_font(const char *path, int font_size, bool bold, bool italic, bool underline, bool pixel_art) { + TTF_Font *font = TTF_OpenFont(path, font_size); + + if (font == nullptr) { + fprintf(stderr, "Failed to load font: %s\n", SDL_GetError()); + return UINT32_MAX; + } + + int style = TTF_STYLE_NORMAL; + if (bold) + style |= TTF_STYLE_BOLD; + if (italic) + style |= TTF_STYLE_ITALIC; + if (underline) + style |= TTF_STYLE_UNDERLINE; + + TTF_SetFontStyle(font, style); + + if (!free_font_indices.empty()) { + FontID id = free_font_indices.back(); + free_font_indices.pop_back(); + fonts[id] = {font, pixel_art}; + return id; + } else { + fonts.push_back({font, pixel_art}); + return fonts.size() - 1; + } + + return UINT32_MAX; +} + +void Window::unload_font(FontID font_id) { + if (font_id >= fonts.size() || fonts[font_id].font == nullptr) { + fprintf(stderr, "Invalid font ID: %d\n", font_id); + return; + } + + TTF_CloseFont(fonts[font_id].font); + fonts[font_id].font = nullptr; + free_font_indices.push_back(font_id); +} + +TextID Window::create_text(FontID font_id, const char *text, uint32_t length, Color color) { + if (font_id >= fonts.size() || fonts[font_id].font == nullptr) { + fprintf(stderr, "Invalid font ID: %d\n", font_id); + return UINT32_MAX; + } + + SDL_Color sdl_color = {color.r, color.g, color.b, color.a}; + SDL_Surface *surface = TTF_RenderText_Solid(fonts[font_id].font, text, length, sdl_color); + + if (surface == nullptr) { + fprintf(stderr, "Failed to create text surface: %s\n", SDL_GetError()); + return UINT32_MAX; + } + + SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface); + SDL_DestroySurface(surface); + + if (texture == nullptr) { + fprintf(stderr, "Failed to create text texture: %s\n", SDL_GetError()); + return UINT32_MAX; + } + + if (fonts[font_id].pixel_art) + SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST); + + if (!free_text_indices.empty()) { + TextID id = free_text_indices.back(); + free_text_indices.pop_back(); + texts[id] = texture; + return id; + } else { + texts.push_back(texture); + return texts.size() - 1; + } + + return UINT32_MAX; +} + +void Window::unload_text(TextID text_id) { + if (text_id >= texts.size() || texts[text_id] == nullptr) { + fprintf(stderr, "Invalid text ID: %d\n", text_id); + return; + } + + SDL_DestroyTexture(texts[text_id]); + texts[text_id] = nullptr; + free_text_indices.push_back(text_id); +} + +bool Window::write(TextID text_id, Vec2 position, float z, float angle, Vec2 scale) { + if (text_id >= texts.size() || texts[text_id] == nullptr) { + fprintf(stderr, "Invalid text ID: %d\n", text_id); + return false; + } + + render_instructions.push_back( + {.type = RenderInstruction::DRAW_TEXT, + .z = z, + .angle = angle, + .scale = scale, + .draw_text = { + .text_id = text_id, + .position = position, + }} + ); + + return true; +}; + +bool Window::draw(ImageID image_id, Vec2 position, float z, float angle, Vec2 scale, float alpha) { + if (image_id >= images.size() || images[image_id] == nullptr) { + fprintf(stderr, "Invalid image ID: %d\n", image_id); + return false; + } + + render_instructions.push_back( + {.type = RenderInstruction::DRAW_IMAGE, + .z = z, + .angle = angle, + .scale = scale, + .draw_image = { + .image_id = image_id, + .position = position, + .alpha = alpha, + }} + ); + + return true; +}; + +bool Window::draw_rect(Rect rect, Color color, float z, float angle) { + render_instructions.push_back( + {.type = RenderInstruction::DRAW_RECT, + .z = z, + .angle = angle, + .scale = {1.0f, 1.0f}, + .draw_rect = { + .rect = rect, + .color = color, + }} + ); + + return true; +}; + +void Window::draw_letterbox() { + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + + // left bar + SDL_FRect left = {0, 0, offset.x, real_size.y}; + SDL_RenderFillRect(renderer, &left); + + // right bar + SDL_FRect right = { + real_size.x - offset.x, + 0, + offset.x, + real_size.y + }; + SDL_RenderFillRect(renderer, &right); + + // top bar + SDL_FRect top = {0, 0, real_size.x, offset.y}; + SDL_RenderFillRect(renderer, &top); + + // bottom bar + SDL_FRect bottom = { + 0, + real_size.y - offset.y, + real_size.x, + offset.y + }; + SDL_RenderFillRect(renderer, &bottom); +} + +void Window::render() { + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + + // sort render instructions by z value + std::stable_sort( + render_instructions.begin(), + render_instructions.end(), + [](const RenderInstruction &a, const RenderInstruction &b) { + return a.z < b.z; + } + ); + + for (const RenderInstruction &instruction : render_instructions) { + switch (instruction.type) { + case RenderInstruction::DRAW_TEXT: { + SDL_Texture *texture = texts[instruction.draw_text.text_id]; + + SDL_FlipMode flip = SDL_FLIP_NONE; + + if (instruction.scale.x < 0) + flip = (SDL_FlipMode)(flip | SDL_FLIP_HORIZONTAL); + if (instruction.scale.y < 0) + flip = (SDL_FlipMode)(flip | SDL_FLIP_VERTICAL); + + float scale_x = std::abs(instruction.scale.x) * scale; + float scale_y = std::abs(instruction.scale.y) * scale; + + float w, h; + SDL_GetTextureSize(texture, &w, &h); + + SDL_FRect dst_rect; + dst_rect.x = instruction.draw_text.position.x * scale + offset.x; + dst_rect.y = instruction.draw_text.position.y * scale + offset.y; + dst_rect.w = scale_x * w; + dst_rect.h = scale_y * h; + + SDL_RenderTextureRotated( + renderer, + texture, + nullptr, + &dst_rect, + instruction.angle, + nullptr, + flip + ); + } break; + + case RenderInstruction::DRAW_IMAGE: { + SDL_Texture *texture = images[instruction.draw_image.image_id]; + + float w, h; + SDL_GetTextureSize(texture, &w, &h); + + SDL_FlipMode flip = SDL_FLIP_NONE; + + if (instruction.scale.x < 0) + flip = (SDL_FlipMode)(flip | SDL_FLIP_HORIZONTAL); + if (instruction.scale.y < 0) + flip = (SDL_FlipMode)(flip | SDL_FLIP_VERTICAL); + + float scale_x = std::abs(instruction.scale.x) * scale; + float scale_y = std::abs(instruction.scale.y) * scale; + + SDL_FRect dst_rect; + dst_rect.x = instruction.draw_image.position.x * scale + offset.x; + dst_rect.y = instruction.draw_image.position.y * scale + offset.y; + dst_rect.w = scale_x * w; + dst_rect.h = scale_y * h; + + SDL_SetTextureAlphaMod(texture, static_cast(instruction.draw_image.alpha * 255)); + + SDL_RenderTextureRotated( + renderer, + texture, + nullptr, + &dst_rect, + instruction.angle, + nullptr, + flip + ); + } break; + + case RenderInstruction::DRAW_RECT: { + SDL_FRect dst_rect; + dst_rect.x = instruction.draw_rect.rect.x() * scale + offset.x; + dst_rect.y = instruction.draw_rect.rect.y() * scale + offset.y; + dst_rect.w = instruction.draw_rect.rect.w() * scale; + dst_rect.h = instruction.draw_rect.rect.h() * scale; + + SDL_SetTextureColorMod( + white_tex, + instruction.draw_rect.color.r, + instruction.draw_rect.color.g, + instruction.draw_rect.color.b + ); + + SDL_SetTextureAlphaMod(white_tex, instruction.draw_rect.color.a); + + SDL_RenderTextureRotated( + renderer, + white_tex, + nullptr, + &dst_rect, + instruction.angle, + nullptr, + SDL_FLIP_NONE + ); + } break; + } + } + + draw_letterbox(); + + SDL_RenderPresent(renderer); +} + +void Window::clear() { + render_instructions.clear(); +} + +bool Window::poll(Event &event) { + SDL_Event sdl_event; + if (SDL_PollEvent(&sdl_event)) { + switch (sdl_event.type) { + case SDL_EVENT_QUIT: + event.type = Event::QUIT; + break; + + case SDL_EVENT_KEY_DOWN: + if (text_input_active && sdl_event.key.key > '0' && sdl_event.key.key < 'z') + return false; + event.type = Event::KEY_DOWN; + event.key.key = sdl_event.key.scancode; + break; + + case SDL_EVENT_KEY_UP: + if (text_input_active && sdl_event.key.key > '0' && sdl_event.key.key < 'z') + return false; + event.type = Event::KEY_UP; + event.key.key = sdl_event.key.scancode; + break; + + case SDL_EVENT_TEXT_INPUT: + if (!text_input_active) + return false; + event.type = Event::TEXT_INPUT; + event.text_input.length = strlen(sdl_event.text.text); + event.text_input.text = (char *)malloc(event.text_input.length + 1); + strcpy(event.text_input.text, sdl_event.text.text); + event.text_input.text[event.text_input.length] = '\0'; + break; + + case SDL_EVENT_MOUSE_BUTTON_DOWN: { + event.type = Event::MOUSE_BUTTON_DOWN; + event.mouse_button.button = sdl_event.button.button; + Vec2 virtual_position; + if (mouse_in_window({sdl_event.button.x, sdl_event.button.y}, virtual_position)) + event.mouse_button.position = virtual_position; + else + return false; + } break; + + case SDL_EVENT_MOUSE_BUTTON_UP: { + event.type = Event::MOUSE_BUTTON_UP; + event.mouse_button.button = sdl_event.button.button; + Vec2 virtual_position; + if (mouse_in_window({sdl_event.button.x, sdl_event.button.y}, virtual_position)) + event.mouse_button.position = virtual_position; + else + return false; + } break; + + case SDL_EVENT_MOUSE_MOTION: { + event.type = Event::MOUSE_MOVE; + event.mouse_move.delta = {sdl_event.motion.xrel, sdl_event.motion.yrel}; + Vec2 virtual_position; + if (mouse_in_window({sdl_event.button.x, sdl_event.button.y}, virtual_position)) + event.mouse_button.position = virtual_position; + else + return false; + } break; + + case SDL_EVENT_MOUSE_WHEEL: + event.type = Event::SCROLL; + event.scroll.x = sdl_event.wheel.x; + event.scroll.y = sdl_event.wheel.y; + break; + + case SDL_EVENT_WINDOW_RESIZED: + sdl_event.window.data1 = MAX(sdl_event.window.data1, 1); + sdl_event.window.data2 = MAX(sdl_event.window.data2, 1); + real_size = {(float)sdl_event.window.data1, (float)sdl_event.window.data2}; + compute_transform(); + break; + + default: + return false; + } + + return true; + } + return false; +} + +void Window::consume(Event &event) { + if (event.type == Event::TEXT_INPUT) { + free(event.text_input.text); + event.text_input.text = nullptr; + event.text_input.length = 0; + } + + event.type = Event::NONE; +} + +void Window::start_text_input() { + text_input_active = true; + SDL_StartTextInput(window); +} + +void Window::stop_text_input() { + text_input_active = false; + SDL_StopTextInput(window); +} + +const char *Window::paste() { + const char *clipboard_text = SDL_GetClipboardText(); + if (clipboard_text == nullptr) { + fprintf(stderr, "Failed to get clipboard text: %s\n", SDL_GetError()); + return nullptr; + } + const char *copy = strdup(clipboard_text); + SDL_free((void *)clipboard_text); + return copy; +} + +uint64_t Window::get_time() { + return SDL_GetTicks(); +} + +uint64_t Window::delta_time() { + static uint64_t last_time = 0; + uint64_t current_time = SDL_GetTicks(); + uint64_t delta = current_time - last_time; + last_time = current_time; + return delta; +} + +void Window::compute_transform() { + float scale_factor_x = real_size.x / virtual_size.x; + float scale_factor_y = real_size.y / virtual_size.y; + + scale = MIN(scale_factor_x, scale_factor_y); + + offset.x = (real_size.x - virtual_size.x * scale) / 2.0f; + offset.y = (real_size.y - virtual_size.y * scale) / 2.0f; +} + +bool Window::mouse_in_window(const Vec2 real_position, Vec2 &virtual_position) { + virtual_position = (real_position - offset) / scale; + + return virtual_position.x >= 0 && virtual_position.x <= virtual_size.x && + virtual_position.y >= 0 && virtual_position.y <= virtual_size.y; +} \ No newline at end of file