From 5d0e71c629a633dc76a36d199a4e9c8c4a91a690 Mon Sep 17 00:00:00 2001 From: Mikhail Romanko Date: Thu, 17 Jul 2025 21:52:37 +0300 Subject: [PATCH] Initial commit --- .gitignore | 56 +++ Credits.txt | 3 + Data/Audio/explosion1.wav | Bin 0 -> 6342 bytes Data/Audio/explosion2.wav | Bin 0 -> 8863 bytes Data/Audio/explosion3.wav | Bin 0 -> 14101 bytes Data/Audio/jump.wav | Bin 0 -> 2705 bytes Data/Audio/laser.wav | Bin 0 -> 2158 bytes Data/Audio/pickup.wav | Bin 0 -> 6625 bytes Data/Audio/powerUp.wav | Bin 0 -> 11354 bytes Data/Audio/select.wav | Bin 0 -> 2389 bytes Data/Audio/shoot.wav | Bin 0 -> 2015 bytes Data/Credits.txt | 3 + Data/Fonts/Blue.txt | 101 ++++ Data/Fonts/Gold.txt | 101 ++++ Data/Fonts/Gray.txt | 101 ++++ Data/Fonts/Green.txt | 101 ++++ Data/Fonts/LightBlue.txt | 101 ++++ Data/Fonts/Purple.txt | 101 ++++ Data/Fonts/Red.txt | 101 ++++ Data/Fonts/Swamp.txt | 101 ++++ Data/Fonts/White.txt | 101 ++++ Data/Sprites/Arrow.txt | 5 + Data/Sprites/Blood.txt | 2 + Data/Sprites/BloodSplat.txt | 5 + Data/Sprites/Button.txt | 8 + Data/Sprites/Digits.txt | 23 + Data/Sprites/Flash.txt | 3 + Data/Sprites/Hero.txt | 7 + Data/Sprites/Item.txt | 7 + Data/Sprites/Ladder.txt | 3 + Data/Sprites/Light.txt | 2 + Data/Sprites/Mine.txt | 2 + Data/Sprites/Passage.txt | 3 + Data/Sprites/Plank.txt | 3 + Data/Sprites/Possum.txt | 1 + Data/Sprites/PushRock.txt | 2 + Data/Sprites/Spike.txt | 3 + Data/Sprites/Stone.txt | 2 + Data/Sprites/Wall.txt | 4 + Data/Templates/Any.txt | 64 +++ Data/Templates/LR.txt | 15 + Data/Templates/LRD.txt | 15 + Data/Templates/LRU.txt | 15 + Data/Templates/LRUD.txt | 15 + Data/Texture/Digits.bmp | Bin 0 -> 7530 bytes Data/Texture/Font.bmp | Bin 0 -> 216138 bytes Data/Texture/Main.bmp | Bin 0 -> 65674 bytes Makefile | 28 ++ Makefile.emcc | 30 ++ Makefile.srcs | 19 + makepak.py | 43 ++ src/Alarm.cpp | 60 +++ src/Alarm.h | 30 ++ src/App.cpp | 116 +++++ src/App.h | 64 +++ src/Common.cpp | 81 ++++ src/Common.h | 174 +++++++ src/DigitManager.cpp | 36 ++ src/DigitManager.h | 22 + src/Entity.cpp | 113 +++++ src/Entity.h | 112 +++++ src/EntityManager.cpp | 144 ++++++ src/EntityManager.h | 66 +++ src/FileManager.cpp | 99 ++++ src/FileManager.h | 27 ++ src/FontManager.cpp | 35 ++ src/FontManager.h | 35 ++ src/Gridmap.cpp | 58 +++ src/Gridmap.h | 29 ++ src/LRU.h | 80 ++++ src/Main.cpp | 65 +++ src/RenderManager.cpp | 348 ++++++++++++++ src/RenderManager.h | 119 +++++ src/RoomEntity.cpp | 897 ++++++++++++++++++++++++++++++++++++ src/RoomEntity.h | 272 +++++++++++ src/SpriteManager.cpp | 119 +++++ src/SpriteManager.h | 38 ++ src/State.cpp | 39 ++ src/State.h | 36 ++ src/SurfaceManager.cpp | 32 ++ src/SurfaceManager.h | 25 + src/Variable.cpp | 83 ++++ src/Variable.h | 33 ++ 83 files changed, 4787 insertions(+) create mode 100644 .gitignore create mode 100644 Credits.txt create mode 100644 Data/Audio/explosion1.wav create mode 100644 Data/Audio/explosion2.wav create mode 100644 Data/Audio/explosion3.wav create mode 100644 Data/Audio/jump.wav create mode 100644 Data/Audio/laser.wav create mode 100644 Data/Audio/pickup.wav create mode 100644 Data/Audio/powerUp.wav create mode 100644 Data/Audio/select.wav create mode 100644 Data/Audio/shoot.wav create mode 100644 Data/Credits.txt create mode 100644 Data/Fonts/Blue.txt create mode 100644 Data/Fonts/Gold.txt create mode 100644 Data/Fonts/Gray.txt create mode 100644 Data/Fonts/Green.txt create mode 100644 Data/Fonts/LightBlue.txt create mode 100644 Data/Fonts/Purple.txt create mode 100644 Data/Fonts/Red.txt create mode 100644 Data/Fonts/Swamp.txt create mode 100644 Data/Fonts/White.txt create mode 100644 Data/Sprites/Arrow.txt create mode 100644 Data/Sprites/Blood.txt create mode 100644 Data/Sprites/BloodSplat.txt create mode 100644 Data/Sprites/Button.txt create mode 100644 Data/Sprites/Digits.txt create mode 100644 Data/Sprites/Flash.txt create mode 100644 Data/Sprites/Hero.txt create mode 100644 Data/Sprites/Item.txt create mode 100644 Data/Sprites/Ladder.txt create mode 100644 Data/Sprites/Light.txt create mode 100644 Data/Sprites/Mine.txt create mode 100644 Data/Sprites/Passage.txt create mode 100644 Data/Sprites/Plank.txt create mode 100644 Data/Sprites/Possum.txt create mode 100644 Data/Sprites/PushRock.txt create mode 100644 Data/Sprites/Spike.txt create mode 100644 Data/Sprites/Stone.txt create mode 100644 Data/Sprites/Wall.txt create mode 100644 Data/Templates/Any.txt create mode 100644 Data/Templates/LR.txt create mode 100644 Data/Templates/LRD.txt create mode 100644 Data/Templates/LRU.txt create mode 100644 Data/Templates/LRUD.txt create mode 100644 Data/Texture/Digits.bmp create mode 100644 Data/Texture/Font.bmp create mode 100644 Data/Texture/Main.bmp create mode 100644 Makefile create mode 100644 Makefile.emcc create mode 100644 Makefile.srcs create mode 100755 makepak.py create mode 100644 src/Alarm.cpp create mode 100644 src/Alarm.h create mode 100644 src/App.cpp create mode 100644 src/App.h create mode 100644 src/Common.cpp create mode 100644 src/Common.h create mode 100644 src/DigitManager.cpp create mode 100644 src/DigitManager.h create mode 100644 src/Entity.cpp create mode 100644 src/Entity.h create mode 100644 src/EntityManager.cpp create mode 100644 src/EntityManager.h create mode 100644 src/FileManager.cpp create mode 100644 src/FileManager.h create mode 100644 src/FontManager.cpp create mode 100644 src/FontManager.h create mode 100644 src/Gridmap.cpp create mode 100644 src/Gridmap.h create mode 100644 src/LRU.h create mode 100644 src/Main.cpp create mode 100644 src/RenderManager.cpp create mode 100644 src/RenderManager.h create mode 100644 src/RoomEntity.cpp create mode 100644 src/RoomEntity.h create mode 100644 src/SpriteManager.cpp create mode 100644 src/SpriteManager.h create mode 100644 src/State.cpp create mode 100644 src/State.h create mode 100644 src/SurfaceManager.cpp create mode 100644 src/SurfaceManager.h create mode 100644 src/Variable.cpp create mode 100644 src/Variable.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e52b303 --- /dev/null +++ b/.gitignore @@ -0,0 +1,56 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Linker files +*.ilk + +# Debugger Files +*.pdb + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# debug information files +*.dwo + +# Binary archives +[Dd]ata.pak + +# Misc +[Oo]utdated +[Jj]unk +[Tt]rash +[Oo]ld + +# Binaries +eulamadness +eulamadness.html +eulamadness.wasm +eulamandess.js \ No newline at end of file diff --git a/Credits.txt b/Credits.txt new file mode 100644 index 0000000..896c4e3 --- /dev/null +++ b/Credits.txt @@ -0,0 +1,3 @@ +Bitmap font by gnsh +https://opengameart.org/content/bitmap-font-0 + diff --git a/Data/Audio/explosion1.wav b/Data/Audio/explosion1.wav new file mode 100644 index 0000000000000000000000000000000000000000..48d4128943fc7a330553e9354f0b1eb5896bff03 GIT binary patch literal 6342 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 literal 0 HcmV?d00001 diff --git a/Data/Audio/explosion3.wav b/Data/Audio/explosion3.wav new file mode 100644 index 0000000000000000000000000000000000000000..f486c76003ed11e7a4f91dba93fb80be4a3982ff GIT binary patch literal 14101 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 Z+dF|s^dxbS&J{|fd%#^^oE^f)`CmqNBGdo? literal 0 HcmV?d00001 diff --git a/Data/Audio/pickup.wav b/Data/Audio/pickup.wav new file mode 100644 index 0000000000000000000000000000000000000000..d493606fd862c6111c329fb833e77da405d0c1a2 GIT binary patch literal 6625 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 X2m49qKirSn|Ly#c`}6J5Ta*6*TGrNV literal 0 HcmV?d00001 diff --git a/Data/Audio/powerUp.wav b/Data/Audio/powerUp.wav new file mode 100644 index 0000000000000000000000000000000000000000..37ba69286dddb4f482ec400499b496e3bce65c98 GIT binary patch literal 11354 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 literal 0 HcmV?d00001 diff --git a/Data/Audio/shoot.wav b/Data/Audio/shoot.wav new file mode 100644 index 0000000000000000000000000000000000000000..45b9651dc0efa58d447bddd30e2ec114e13d7cde GIT binary patch literal 2015 zcmajeX;T_!8~||ps(smy(CJL3W^%N#Y0^nNq8N-(qN0Kd@j$=@VdcJ;Lj{&=1r|h( ziCPdQUr@ew#jv?R026?QduQv$M1N{Eugs-c6W=J`WX^d^enTk;BFF%o*+o91_ z9xb`*Lop~Q?a5uZB`nj5o`H{Lw717!xRg8!TfMC0d98jy$&nMX=A|XfV3BTZ4#NxS zf|@f<&E$D#*m9oeG$Nm;6lWa{g+bP@kr}3DVR+Cm?e3loKt`{&d9G+gO#y6r#l4LKl)6;3?%O2#GZ{4`0W5vZ@;QK1M`GPmP4T> zj?Bf<58Urps9{bjgVV2FyU9-!@Vz`29(wK-;3r3JN%lC*7=n!}$!gMzEJPzwyT)Uc zV<=+Jq)Y2YH!fISmw4A~7Q>9E8l4%W^^V!2a$a;JWnjv+F1I_5D|G6_VKqtUN3OS^ z)veHKI)kT*l{M$=41>vh&dsR$a3h(i;iQH8EpiIyl_k=_os_){fIbUC>o(+8q<9%6FF zI^r6imM(!Uawm^I%9rDjf?_N4{aH03){HH#Cw|VKmNJ-8VQ>q)Qau)2KR7wXaznVm zh(bobS?N@dP`YmjS_dY@*y%wvo5;GqiGKgObnM<-o)7b$ci(;pwYa5N;Y|f|Dn7$H z#s^s%S13xLcu`PFhAn<+v-?JK%LB%QDx*1$ijQK2!+d>Y#$qRi{Sq|NJE zWsoytl(@onm5e*g9vOjHoqQ+zzLIyBK_AoG%}$$4IyXH!8*y`2OCei)HM4ho{*}kG zB&YLbyr(0mROG6r*S7MB;@;8Qh&fWxHqFipsVo&Vt3rg6Q(PA9SvR%4eGnE>nB0U| zmU{jA$6vlaKa02=;=X}CY8R~^^7#;ZByNH^ee@}ZUTZ2A^TANQ9GKJFIZs+?{j`pr z>8bvXp`iyKwK16-G!l$wF_A#R>Fw&E(L)Xgl*?6!<#6Tl^5FEek|cFHXzl)pVh- zX1Ci3f`}|EErnt}pC`Auxn@ET9gRxu>g(z0;mGB3d#O|^UR+*YrYsf<(h0QPxzp5i z^JAc;4dCnbdSi4R;F2M^18i^Pa@j1Q7KucHM|Yc=n(kbm2G_dXt#Y}X_W~J!P=Qww z34osf=|wOHB~UGM4NfPbn^ZLi4iJJ=u1CIA2c literal 0 HcmV?d00001 diff --git a/Data/Credits.txt b/Data/Credits.txt new file mode 100644 index 0000000..896c4e3 --- /dev/null +++ b/Data/Credits.txt @@ -0,0 +1,3 @@ +Bitmap font by gnsh +https://opengameart.org/content/bitmap-font-0 + diff --git a/Data/Fonts/Blue.txt b/Data/Fonts/Blue.txt new file mode 100644 index 0000000..663d6f0 --- /dev/null +++ b/Data/Fonts/Blue.txt @@ -0,0 +1,101 @@ +Texture Texture/Font.bmp +Frame 200 60 5 12 +Frame 205 60 5 12 +Frame 210 60 5 12 +Frame 215 60 5 12 +Frame 220 60 5 12 +Frame 225 60 5 12 +Frame 230 60 5 12 +Frame 235 60 5 12 +Frame 240 60 5 12 +Frame 245 60 5 12 +Frame 250 60 5 12 +Frame 255 60 5 12 +Frame 260 60 5 12 +Frame 265 60 5 12 +Frame 270 60 5 12 +Frame 275 60 5 12 +Frame 280 60 5 12 +Frame 285 60 5 12 +Frame 290 60 5 12 +Frame 295 60 5 12 +Frame 200 72 5 12 +Frame 205 72 5 12 +Frame 210 72 5 12 +Frame 215 72 5 12 +Frame 220 72 5 12 +Frame 225 72 5 12 +Frame 230 72 5 12 +Frame 235 72 5 12 +Frame 240 72 5 12 +Frame 245 72 5 12 +Frame 250 72 5 12 +Frame 255 72 5 12 +Frame 260 72 5 12 +Frame 265 72 5 12 +Frame 270 72 5 12 +Frame 275 72 5 12 +Frame 280 72 5 12 +Frame 285 72 5 12 +Frame 290 72 5 12 +Frame 295 72 5 12 +Frame 200 84 5 12 +Frame 205 84 5 12 +Frame 210 84 5 12 +Frame 215 84 5 12 +Frame 220 84 5 12 +Frame 225 84 5 12 +Frame 230 84 5 12 +Frame 235 84 5 12 +Frame 240 84 5 12 +Frame 245 84 5 12 +Frame 250 84 5 12 +Frame 255 84 5 12 +Frame 260 84 5 12 +Frame 265 84 5 12 +Frame 270 84 5 12 +Frame 275 84 5 12 +Frame 280 84 5 12 +Frame 285 84 5 12 +Frame 290 84 5 12 +Frame 295 84 5 12 +Frame 200 96 5 12 +Frame 205 96 5 12 +Frame 210 96 5 12 +Frame 215 96 5 12 +Frame 220 96 5 12 +Frame 225 96 5 12 +Frame 230 96 5 12 +Frame 235 96 5 12 +Frame 240 96 5 12 +Frame 245 96 5 12 +Frame 250 96 5 12 +Frame 255 96 5 12 +Frame 260 96 5 12 +Frame 265 96 5 12 +Frame 270 96 5 12 +Frame 275 96 5 12 +Frame 280 96 5 12 +Frame 285 96 5 12 +Frame 290 96 5 12 +Frame 295 96 5 12 +Frame 200 108 5 12 +Frame 205 108 5 12 +Frame 210 108 5 12 +Frame 215 108 5 12 +Frame 220 108 5 12 +Frame 225 108 5 12 +Frame 230 108 5 12 +Frame 235 108 5 12 +Frame 240 108 5 12 +Frame 245 108 5 12 +Frame 250 108 5 12 +Frame 255 108 5 12 +Frame 260 108 5 12 +Frame 265 108 5 12 +Frame 270 108 5 12 +Frame 275 108 5 12 +Frame 280 108 5 12 +Frame 285 108 5 12 +Frame 290 108 5 12 +Frame 295 108 5 12 diff --git a/Data/Fonts/Gold.txt b/Data/Fonts/Gold.txt new file mode 100644 index 0000000..812a9ed --- /dev/null +++ b/Data/Fonts/Gold.txt @@ -0,0 +1,101 @@ +Texture Texture/Font.bmp +Frame 0 0 5 12 +Frame 5 0 5 12 +Frame 10 0 5 12 +Frame 15 0 5 12 +Frame 20 0 5 12 +Frame 25 0 5 12 +Frame 30 0 5 12 +Frame 35 0 5 12 +Frame 40 0 5 12 +Frame 45 0 5 12 +Frame 50 0 5 12 +Frame 55 0 5 12 +Frame 60 0 5 12 +Frame 65 0 5 12 +Frame 70 0 5 12 +Frame 75 0 5 12 +Frame 80 0 5 12 +Frame 85 0 5 12 +Frame 90 0 5 12 +Frame 95 0 5 12 +Frame 0 12 5 12 +Frame 5 12 5 12 +Frame 10 12 5 12 +Frame 15 12 5 12 +Frame 20 12 5 12 +Frame 25 12 5 12 +Frame 30 12 5 12 +Frame 35 12 5 12 +Frame 40 12 5 12 +Frame 45 12 5 12 +Frame 50 12 5 12 +Frame 55 12 5 12 +Frame 60 12 5 12 +Frame 65 12 5 12 +Frame 70 12 5 12 +Frame 75 12 5 12 +Frame 80 12 5 12 +Frame 85 12 5 12 +Frame 90 12 5 12 +Frame 95 12 5 12 +Frame 0 24 5 12 +Frame 5 24 5 12 +Frame 10 24 5 12 +Frame 15 24 5 12 +Frame 20 24 5 12 +Frame 25 24 5 12 +Frame 30 24 5 12 +Frame 35 24 5 12 +Frame 40 24 5 12 +Frame 45 24 5 12 +Frame 50 24 5 12 +Frame 55 24 5 12 +Frame 60 24 5 12 +Frame 65 24 5 12 +Frame 70 24 5 12 +Frame 75 24 5 12 +Frame 80 24 5 12 +Frame 85 24 5 12 +Frame 90 24 5 12 +Frame 95 24 5 12 +Frame 0 36 5 12 +Frame 5 36 5 12 +Frame 10 36 5 12 +Frame 15 36 5 12 +Frame 20 36 5 12 +Frame 25 36 5 12 +Frame 30 36 5 12 +Frame 35 36 5 12 +Frame 40 36 5 12 +Frame 45 36 5 12 +Frame 50 36 5 12 +Frame 55 36 5 12 +Frame 60 36 5 12 +Frame 65 36 5 12 +Frame 70 36 5 12 +Frame 75 36 5 12 +Frame 80 36 5 12 +Frame 85 36 5 12 +Frame 90 36 5 12 +Frame 95 36 5 12 +Frame 0 48 5 12 +Frame 5 48 5 12 +Frame 10 48 5 12 +Frame 15 48 5 12 +Frame 20 48 5 12 +Frame 25 48 5 12 +Frame 30 48 5 12 +Frame 35 48 5 12 +Frame 40 48 5 12 +Frame 45 48 5 12 +Frame 50 48 5 12 +Frame 55 48 5 12 +Frame 60 48 5 12 +Frame 65 48 5 12 +Frame 70 48 5 12 +Frame 75 48 5 12 +Frame 80 48 5 12 +Frame 85 48 5 12 +Frame 90 48 5 12 +Frame 95 48 5 12 \ No newline at end of file diff --git a/Data/Fonts/Gray.txt b/Data/Fonts/Gray.txt new file mode 100644 index 0000000..2393d37 --- /dev/null +++ b/Data/Fonts/Gray.txt @@ -0,0 +1,101 @@ +Texture Texture/Font.bmp +Frame 0 120 5 12 +Frame 5 120 5 12 +Frame 10 120 5 12 +Frame 15 120 5 12 +Frame 20 120 5 12 +Frame 25 120 5 12 +Frame 30 120 5 12 +Frame 35 120 5 12 +Frame 40 120 5 12 +Frame 45 120 5 12 +Frame 50 120 5 12 +Frame 55 120 5 12 +Frame 60 120 5 12 +Frame 65 120 5 12 +Frame 70 120 5 12 +Frame 75 120 5 12 +Frame 80 120 5 12 +Frame 85 120 5 12 +Frame 90 120 5 12 +Frame 95 120 5 12 +Frame 0 132 5 12 +Frame 5 132 5 12 +Frame 10 132 5 12 +Frame 15 132 5 12 +Frame 20 132 5 12 +Frame 25 132 5 12 +Frame 30 132 5 12 +Frame 35 132 5 12 +Frame 40 132 5 12 +Frame 45 132 5 12 +Frame 50 132 5 12 +Frame 55 132 5 12 +Frame 60 132 5 12 +Frame 65 132 5 12 +Frame 70 132 5 12 +Frame 75 132 5 12 +Frame 80 132 5 12 +Frame 85 132 5 12 +Frame 90 132 5 12 +Frame 95 132 5 12 +Frame 0 144 5 12 +Frame 5 144 5 12 +Frame 10 144 5 12 +Frame 15 144 5 12 +Frame 20 144 5 12 +Frame 25 144 5 12 +Frame 30 144 5 12 +Frame 35 144 5 12 +Frame 40 144 5 12 +Frame 45 144 5 12 +Frame 50 144 5 12 +Frame 55 144 5 12 +Frame 60 144 5 12 +Frame 65 144 5 12 +Frame 70 144 5 12 +Frame 75 144 5 12 +Frame 80 144 5 12 +Frame 85 144 5 12 +Frame 90 144 5 12 +Frame 95 144 5 12 +Frame 0 156 5 12 +Frame 5 156 5 12 +Frame 10 156 5 12 +Frame 15 156 5 12 +Frame 20 156 5 12 +Frame 25 156 5 12 +Frame 30 156 5 12 +Frame 35 156 5 12 +Frame 40 156 5 12 +Frame 45 156 5 12 +Frame 50 156 5 12 +Frame 55 156 5 12 +Frame 60 156 5 12 +Frame 65 156 5 12 +Frame 70 156 5 12 +Frame 75 156 5 12 +Frame 80 156 5 12 +Frame 85 156 5 12 +Frame 90 156 5 12 +Frame 95 156 5 12 +Frame 0 168 5 12 +Frame 5 168 5 12 +Frame 10 168 5 12 +Frame 15 168 5 12 +Frame 20 168 5 12 +Frame 25 168 5 12 +Frame 30 168 5 12 +Frame 35 168 5 12 +Frame 40 168 5 12 +Frame 45 168 5 12 +Frame 50 168 5 12 +Frame 55 168 5 12 +Frame 60 168 5 12 +Frame 65 168 5 12 +Frame 70 168 5 12 +Frame 75 168 5 12 +Frame 80 168 5 12 +Frame 85 168 5 12 +Frame 90 168 5 12 +Frame 95 168 5 12 diff --git a/Data/Fonts/Green.txt b/Data/Fonts/Green.txt new file mode 100644 index 0000000..19d04b2 --- /dev/null +++ b/Data/Fonts/Green.txt @@ -0,0 +1,101 @@ +Texture Texture/Font.bmp +Frame 200 0 5 12 +Frame 205 0 5 12 +Frame 210 0 5 12 +Frame 215 0 5 12 +Frame 220 0 5 12 +Frame 225 0 5 12 +Frame 230 0 5 12 +Frame 235 0 5 12 +Frame 240 0 5 12 +Frame 245 0 5 12 +Frame 250 0 5 12 +Frame 255 0 5 12 +Frame 260 0 5 12 +Frame 265 0 5 12 +Frame 270 0 5 12 +Frame 275 0 5 12 +Frame 280 0 5 12 +Frame 285 0 5 12 +Frame 290 0 5 12 +Frame 295 0 5 12 +Frame 200 12 5 12 +Frame 205 12 5 12 +Frame 210 12 5 12 +Frame 215 12 5 12 +Frame 220 12 5 12 +Frame 225 12 5 12 +Frame 230 12 5 12 +Frame 235 12 5 12 +Frame 240 12 5 12 +Frame 245 12 5 12 +Frame 250 12 5 12 +Frame 255 12 5 12 +Frame 260 12 5 12 +Frame 265 12 5 12 +Frame 270 12 5 12 +Frame 275 12 5 12 +Frame 280 12 5 12 +Frame 285 12 5 12 +Frame 290 12 5 12 +Frame 295 12 5 12 +Frame 200 24 5 12 +Frame 205 24 5 12 +Frame 210 24 5 12 +Frame 215 24 5 12 +Frame 220 24 5 12 +Frame 225 24 5 12 +Frame 230 24 5 12 +Frame 235 24 5 12 +Frame 240 24 5 12 +Frame 245 24 5 12 +Frame 250 24 5 12 +Frame 255 24 5 12 +Frame 260 24 5 12 +Frame 265 24 5 12 +Frame 270 24 5 12 +Frame 275 24 5 12 +Frame 280 24 5 12 +Frame 285 24 5 12 +Frame 290 24 5 12 +Frame 295 24 5 12 +Frame 200 36 5 12 +Frame 205 36 5 12 +Frame 210 36 5 12 +Frame 215 36 5 12 +Frame 220 36 5 12 +Frame 225 36 5 12 +Frame 230 36 5 12 +Frame 235 36 5 12 +Frame 240 36 5 12 +Frame 245 36 5 12 +Frame 250 36 5 12 +Frame 255 36 5 12 +Frame 260 36 5 12 +Frame 265 36 5 12 +Frame 270 36 5 12 +Frame 275 36 5 12 +Frame 280 36 5 12 +Frame 285 36 5 12 +Frame 290 36 5 12 +Frame 295 36 5 12 +Frame 200 48 5 12 +Frame 205 48 5 12 +Frame 210 48 5 12 +Frame 215 48 5 12 +Frame 220 48 5 12 +Frame 225 48 5 12 +Frame 230 48 5 12 +Frame 235 48 5 12 +Frame 240 48 5 12 +Frame 245 48 5 12 +Frame 250 48 5 12 +Frame 255 48 5 12 +Frame 260 48 5 12 +Frame 265 48 5 12 +Frame 270 48 5 12 +Frame 275 48 5 12 +Frame 280 48 5 12 +Frame 285 48 5 12 +Frame 290 48 5 12 +Frame 295 48 5 12 diff --git a/Data/Fonts/LightBlue.txt b/Data/Fonts/LightBlue.txt new file mode 100644 index 0000000..550654a --- /dev/null +++ b/Data/Fonts/LightBlue.txt @@ -0,0 +1,101 @@ +Texture Texture/Font.bmp +Frame 0 60 5 12 +Frame 5 60 5 12 +Frame 10 60 5 12 +Frame 15 60 5 12 +Frame 20 60 5 12 +Frame 25 60 5 12 +Frame 30 60 5 12 +Frame 35 60 5 12 +Frame 40 60 5 12 +Frame 45 60 5 12 +Frame 50 60 5 12 +Frame 55 60 5 12 +Frame 60 60 5 12 +Frame 65 60 5 12 +Frame 70 60 5 12 +Frame 75 60 5 12 +Frame 80 60 5 12 +Frame 85 60 5 12 +Frame 90 60 5 12 +Frame 95 60 5 12 +Frame 0 72 5 12 +Frame 5 72 5 12 +Frame 10 72 5 12 +Frame 15 72 5 12 +Frame 20 72 5 12 +Frame 25 72 5 12 +Frame 30 72 5 12 +Frame 35 72 5 12 +Frame 40 72 5 12 +Frame 45 72 5 12 +Frame 50 72 5 12 +Frame 55 72 5 12 +Frame 60 72 5 12 +Frame 65 72 5 12 +Frame 70 72 5 12 +Frame 75 72 5 12 +Frame 80 72 5 12 +Frame 85 72 5 12 +Frame 90 72 5 12 +Frame 95 72 5 12 +Frame 0 84 5 12 +Frame 5 84 5 12 +Frame 10 84 5 12 +Frame 15 84 5 12 +Frame 20 84 5 12 +Frame 25 84 5 12 +Frame 30 84 5 12 +Frame 35 84 5 12 +Frame 40 84 5 12 +Frame 45 84 5 12 +Frame 50 84 5 12 +Frame 55 84 5 12 +Frame 60 84 5 12 +Frame 65 84 5 12 +Frame 70 84 5 12 +Frame 75 84 5 12 +Frame 80 84 5 12 +Frame 85 84 5 12 +Frame 90 84 5 12 +Frame 95 84 5 12 +Frame 0 96 5 12 +Frame 5 96 5 12 +Frame 10 96 5 12 +Frame 15 96 5 12 +Frame 20 96 5 12 +Frame 25 96 5 12 +Frame 30 96 5 12 +Frame 35 96 5 12 +Frame 40 96 5 12 +Frame 45 96 5 12 +Frame 50 96 5 12 +Frame 55 96 5 12 +Frame 60 96 5 12 +Frame 65 96 5 12 +Frame 70 96 5 12 +Frame 75 96 5 12 +Frame 80 96 5 12 +Frame 85 96 5 12 +Frame 90 96 5 12 +Frame 95 96 5 12 +Frame 0 108 5 12 +Frame 5 108 5 12 +Frame 10 108 5 12 +Frame 15 108 5 12 +Frame 20 108 5 12 +Frame 25 108 5 12 +Frame 30 108 5 12 +Frame 35 108 5 12 +Frame 40 108 5 12 +Frame 45 108 5 12 +Frame 50 108 5 12 +Frame 55 108 5 12 +Frame 60 108 5 12 +Frame 65 108 5 12 +Frame 70 108 5 12 +Frame 75 108 5 12 +Frame 80 108 5 12 +Frame 85 108 5 12 +Frame 90 108 5 12 +Frame 95 108 5 12 diff --git a/Data/Fonts/Purple.txt b/Data/Fonts/Purple.txt new file mode 100644 index 0000000..47e2a54 --- /dev/null +++ b/Data/Fonts/Purple.txt @@ -0,0 +1,101 @@ +Texture Texture/Font.bmp +Frame 100 60 5 12 +Frame 105 60 5 12 +Frame 110 60 5 12 +Frame 115 60 5 12 +Frame 120 60 5 12 +Frame 125 60 5 12 +Frame 130 60 5 12 +Frame 135 60 5 12 +Frame 140 60 5 12 +Frame 145 60 5 12 +Frame 150 60 5 12 +Frame 155 60 5 12 +Frame 160 60 5 12 +Frame 165 60 5 12 +Frame 170 60 5 12 +Frame 175 60 5 12 +Frame 180 60 5 12 +Frame 185 60 5 12 +Frame 190 60 5 12 +Frame 195 60 5 12 +Frame 100 72 5 12 +Frame 105 72 5 12 +Frame 110 72 5 12 +Frame 115 72 5 12 +Frame 120 72 5 12 +Frame 125 72 5 12 +Frame 130 72 5 12 +Frame 135 72 5 12 +Frame 140 72 5 12 +Frame 145 72 5 12 +Frame 150 72 5 12 +Frame 155 72 5 12 +Frame 160 72 5 12 +Frame 165 72 5 12 +Frame 170 72 5 12 +Frame 175 72 5 12 +Frame 180 72 5 12 +Frame 185 72 5 12 +Frame 190 72 5 12 +Frame 195 72 5 12 +Frame 100 84 5 12 +Frame 105 84 5 12 +Frame 110 84 5 12 +Frame 115 84 5 12 +Frame 120 84 5 12 +Frame 125 84 5 12 +Frame 130 84 5 12 +Frame 135 84 5 12 +Frame 140 84 5 12 +Frame 145 84 5 12 +Frame 150 84 5 12 +Frame 155 84 5 12 +Frame 160 84 5 12 +Frame 165 84 5 12 +Frame 170 84 5 12 +Frame 175 84 5 12 +Frame 180 84 5 12 +Frame 185 84 5 12 +Frame 190 84 5 12 +Frame 195 84 5 12 +Frame 100 96 5 12 +Frame 105 96 5 12 +Frame 110 96 5 12 +Frame 115 96 5 12 +Frame 120 96 5 12 +Frame 125 96 5 12 +Frame 130 96 5 12 +Frame 135 96 5 12 +Frame 140 96 5 12 +Frame 145 96 5 12 +Frame 150 96 5 12 +Frame 155 96 5 12 +Frame 160 96 5 12 +Frame 165 96 5 12 +Frame 170 96 5 12 +Frame 175 96 5 12 +Frame 180 96 5 12 +Frame 185 96 5 12 +Frame 190 96 5 12 +Frame 195 96 5 12 +Frame 100 108 5 12 +Frame 105 108 5 12 +Frame 110 108 5 12 +Frame 115 108 5 12 +Frame 120 108 5 12 +Frame 125 108 5 12 +Frame 130 108 5 12 +Frame 135 108 5 12 +Frame 140 108 5 12 +Frame 145 108 5 12 +Frame 150 108 5 12 +Frame 155 108 5 12 +Frame 160 108 5 12 +Frame 165 108 5 12 +Frame 170 108 5 12 +Frame 175 108 5 12 +Frame 180 108 5 12 +Frame 185 108 5 12 +Frame 190 108 5 12 +Frame 195 108 5 12 diff --git a/Data/Fonts/Red.txt b/Data/Fonts/Red.txt new file mode 100644 index 0000000..a77ad87 --- /dev/null +++ b/Data/Fonts/Red.txt @@ -0,0 +1,101 @@ +Texture Texture/Font.bmp +Frame 100 0 5 12 +Frame 105 0 5 12 +Frame 110 0 5 12 +Frame 115 0 5 12 +Frame 120 0 5 12 +Frame 125 0 5 12 +Frame 130 0 5 12 +Frame 135 0 5 12 +Frame 140 0 5 12 +Frame 145 0 5 12 +Frame 150 0 5 12 +Frame 155 0 5 12 +Frame 160 0 5 12 +Frame 165 0 5 12 +Frame 170 0 5 12 +Frame 175 0 5 12 +Frame 180 0 5 12 +Frame 185 0 5 12 +Frame 190 0 5 12 +Frame 195 0 5 12 +Frame 100 12 5 12 +Frame 105 12 5 12 +Frame 110 12 5 12 +Frame 115 12 5 12 +Frame 120 12 5 12 +Frame 125 12 5 12 +Frame 130 12 5 12 +Frame 135 12 5 12 +Frame 140 12 5 12 +Frame 145 12 5 12 +Frame 150 12 5 12 +Frame 155 12 5 12 +Frame 160 12 5 12 +Frame 165 12 5 12 +Frame 170 12 5 12 +Frame 175 12 5 12 +Frame 180 12 5 12 +Frame 185 12 5 12 +Frame 190 12 5 12 +Frame 195 12 5 12 +Frame 100 24 5 12 +Frame 105 24 5 12 +Frame 110 24 5 12 +Frame 115 24 5 12 +Frame 120 24 5 12 +Frame 125 24 5 12 +Frame 130 24 5 12 +Frame 135 24 5 12 +Frame 140 24 5 12 +Frame 145 24 5 12 +Frame 150 24 5 12 +Frame 155 24 5 12 +Frame 160 24 5 12 +Frame 165 24 5 12 +Frame 170 24 5 12 +Frame 175 24 5 12 +Frame 180 24 5 12 +Frame 185 24 5 12 +Frame 190 24 5 12 +Frame 195 24 5 12 +Frame 100 36 5 12 +Frame 105 36 5 12 +Frame 110 36 5 12 +Frame 115 36 5 12 +Frame 120 36 5 12 +Frame 125 36 5 12 +Frame 130 36 5 12 +Frame 135 36 5 12 +Frame 140 36 5 12 +Frame 145 36 5 12 +Frame 150 36 5 12 +Frame 155 36 5 12 +Frame 160 36 5 12 +Frame 165 36 5 12 +Frame 170 36 5 12 +Frame 175 36 5 12 +Frame 180 36 5 12 +Frame 185 36 5 12 +Frame 190 36 5 12 +Frame 195 36 5 12 +Frame 100 48 5 12 +Frame 105 48 5 12 +Frame 110 48 5 12 +Frame 115 48 5 12 +Frame 120 48 5 12 +Frame 125 48 5 12 +Frame 130 48 5 12 +Frame 135 48 5 12 +Frame 140 48 5 12 +Frame 145 48 5 12 +Frame 150 48 5 12 +Frame 155 48 5 12 +Frame 160 48 5 12 +Frame 165 48 5 12 +Frame 170 48 5 12 +Frame 175 48 5 12 +Frame 180 48 5 12 +Frame 185 48 5 12 +Frame 190 48 5 12 +Frame 195 48 5 12 diff --git a/Data/Fonts/Swamp.txt b/Data/Fonts/Swamp.txt new file mode 100644 index 0000000..fd42736 --- /dev/null +++ b/Data/Fonts/Swamp.txt @@ -0,0 +1,101 @@ +Texture Texture/Font.bmp +Frame 200 120 5 12 +Frame 205 120 5 12 +Frame 210 120 5 12 +Frame 215 120 5 12 +Frame 220 120 5 12 +Frame 225 120 5 12 +Frame 230 120 5 12 +Frame 235 120 5 12 +Frame 240 120 5 12 +Frame 245 120 5 12 +Frame 250 120 5 12 +Frame 255 120 5 12 +Frame 260 120 5 12 +Frame 265 120 5 12 +Frame 270 120 5 12 +Frame 275 120 5 12 +Frame 280 120 5 12 +Frame 285 120 5 12 +Frame 290 120 5 12 +Frame 295 120 5 12 +Frame 200 132 5 12 +Frame 205 132 5 12 +Frame 210 132 5 12 +Frame 215 132 5 12 +Frame 220 132 5 12 +Frame 225 132 5 12 +Frame 230 132 5 12 +Frame 235 132 5 12 +Frame 240 132 5 12 +Frame 245 132 5 12 +Frame 250 132 5 12 +Frame 255 132 5 12 +Frame 260 132 5 12 +Frame 265 132 5 12 +Frame 270 132 5 12 +Frame 275 132 5 12 +Frame 280 132 5 12 +Frame 285 132 5 12 +Frame 290 132 5 12 +Frame 295 132 5 12 +Frame 200 144 5 12 +Frame 205 144 5 12 +Frame 210 144 5 12 +Frame 215 144 5 12 +Frame 220 144 5 12 +Frame 225 144 5 12 +Frame 230 144 5 12 +Frame 235 144 5 12 +Frame 240 144 5 12 +Frame 245 144 5 12 +Frame 250 144 5 12 +Frame 255 144 5 12 +Frame 260 144 5 12 +Frame 265 144 5 12 +Frame 270 144 5 12 +Frame 275 144 5 12 +Frame 280 144 5 12 +Frame 285 144 5 12 +Frame 290 144 5 12 +Frame 295 144 5 12 +Frame 200 156 5 12 +Frame 205 156 5 12 +Frame 210 156 5 12 +Frame 215 156 5 12 +Frame 220 156 5 12 +Frame 225 156 5 12 +Frame 230 156 5 12 +Frame 235 156 5 12 +Frame 240 156 5 12 +Frame 245 156 5 12 +Frame 250 156 5 12 +Frame 255 156 5 12 +Frame 260 156 5 12 +Frame 265 156 5 12 +Frame 270 156 5 12 +Frame 275 156 5 12 +Frame 280 156 5 12 +Frame 285 156 5 12 +Frame 290 156 5 12 +Frame 295 156 5 12 +Frame 200 168 5 12 +Frame 205 168 5 12 +Frame 210 168 5 12 +Frame 215 168 5 12 +Frame 220 168 5 12 +Frame 225 168 5 12 +Frame 230 168 5 12 +Frame 235 168 5 12 +Frame 240 168 5 12 +Frame 245 168 5 12 +Frame 250 168 5 12 +Frame 255 168 5 12 +Frame 260 168 5 12 +Frame 265 168 5 12 +Frame 270 168 5 12 +Frame 275 168 5 12 +Frame 280 168 5 12 +Frame 285 168 5 12 +Frame 290 168 5 12 +Frame 295 168 5 12 diff --git a/Data/Fonts/White.txt b/Data/Fonts/White.txt new file mode 100644 index 0000000..acaa198 --- /dev/null +++ b/Data/Fonts/White.txt @@ -0,0 +1,101 @@ +Texture Texture/Font.bmp +Frame 100 120 5 12 +Frame 105 120 5 12 +Frame 110 120 5 12 +Frame 115 120 5 12 +Frame 120 120 5 12 +Frame 125 120 5 12 +Frame 130 120 5 12 +Frame 135 120 5 12 +Frame 140 120 5 12 +Frame 145 120 5 12 +Frame 150 120 5 12 +Frame 155 120 5 12 +Frame 160 120 5 12 +Frame 165 120 5 12 +Frame 170 120 5 12 +Frame 175 120 5 12 +Frame 180 120 5 12 +Frame 185 120 5 12 +Frame 190 120 5 12 +Frame 195 120 5 12 +Frame 100 132 5 12 +Frame 105 132 5 12 +Frame 110 132 5 12 +Frame 115 132 5 12 +Frame 120 132 5 12 +Frame 125 132 5 12 +Frame 130 132 5 12 +Frame 135 132 5 12 +Frame 140 132 5 12 +Frame 145 132 5 12 +Frame 150 132 5 12 +Frame 155 132 5 12 +Frame 160 132 5 12 +Frame 165 132 5 12 +Frame 170 132 5 12 +Frame 175 132 5 12 +Frame 180 132 5 12 +Frame 185 132 5 12 +Frame 190 132 5 12 +Frame 195 132 5 12 +Frame 100 144 5 12 +Frame 105 144 5 12 +Frame 110 144 5 12 +Frame 115 144 5 12 +Frame 120 144 5 12 +Frame 125 144 5 12 +Frame 130 144 5 12 +Frame 135 144 5 12 +Frame 140 144 5 12 +Frame 145 144 5 12 +Frame 150 144 5 12 +Frame 155 144 5 12 +Frame 160 144 5 12 +Frame 165 144 5 12 +Frame 170 144 5 12 +Frame 175 144 5 12 +Frame 180 144 5 12 +Frame 185 144 5 12 +Frame 190 144 5 12 +Frame 195 144 5 12 +Frame 100 156 5 12 +Frame 105 156 5 12 +Frame 110 156 5 12 +Frame 115 156 5 12 +Frame 120 156 5 12 +Frame 125 156 5 12 +Frame 130 156 5 12 +Frame 135 156 5 12 +Frame 140 156 5 12 +Frame 145 156 5 12 +Frame 150 156 5 12 +Frame 155 156 5 12 +Frame 160 156 5 12 +Frame 165 156 5 12 +Frame 170 156 5 12 +Frame 175 156 5 12 +Frame 180 156 5 12 +Frame 185 156 5 12 +Frame 190 156 5 12 +Frame 195 156 5 12 +Frame 100 168 5 12 +Frame 105 168 5 12 +Frame 110 168 5 12 +Frame 115 168 5 12 +Frame 120 168 5 12 +Frame 125 168 5 12 +Frame 130 168 5 12 +Frame 135 168 5 12 +Frame 140 168 5 12 +Frame 145 168 5 12 +Frame 150 168 5 12 +Frame 155 168 5 12 +Frame 160 168 5 12 +Frame 165 168 5 12 +Frame 170 168 5 12 +Frame 175 168 5 12 +Frame 180 168 5 12 +Frame 185 168 5 12 +Frame 190 168 5 12 +Frame 195 168 5 12 diff --git a/Data/Sprites/Arrow.txt b/Data/Sprites/Arrow.txt new file mode 100644 index 0000000..2d7cc31 --- /dev/null +++ b/Data/Sprites/Arrow.txt @@ -0,0 +1,5 @@ +Texture Texture/Main.bmp +Frame 0 48 16 16 Right +Frame 16 48 16 16 Up +Frame 32 48 16 16 Left +Frame 48 48 16 16 Down diff --git a/Data/Sprites/Blood.txt b/Data/Sprites/Blood.txt new file mode 100644 index 0000000..9ee276b --- /dev/null +++ b/Data/Sprites/Blood.txt @@ -0,0 +1,2 @@ +Texture Texture/Main.bmp +Frame 101 21 6 6 diff --git a/Data/Sprites/BloodSplat.txt b/Data/Sprites/BloodSplat.txt new file mode 100644 index 0000000..b8c0508 --- /dev/null +++ b/Data/Sprites/BloodSplat.txt @@ -0,0 +1,5 @@ +Texture Texture/Main.bmp +Frame 64 48 16 16 Up +Frame 80 48 16 16 Left +Frame 64 64 16 16 Down +Frame 80 64 16 16 Right diff --git a/Data/Sprites/Button.txt b/Data/Sprites/Button.txt new file mode 100644 index 0000000..88f9ec2 --- /dev/null +++ b/Data/Sprites/Button.txt @@ -0,0 +1,8 @@ +Texture Texture/Main.bmp +Frame 0 0 16 16 Z +Frame 16 0 16 16 X +Frame 32 0 16 16 C +Frame 48 0 16 16 Right +Frame 64 0 16 16 Up +Frame 80 0 16 16 Left +Frame 96 0 16 16 Down diff --git a/Data/Sprites/Digits.txt b/Data/Sprites/Digits.txt new file mode 100644 index 0000000..11bafa0 --- /dev/null +++ b/Data/Sprites/Digits.txt @@ -0,0 +1,23 @@ +Texture Texture/Digits.bmp +Frame 0 0 7 12 +Frame 7 0 7 12 +Frame 14 0 7 12 +Frame 21 0 7 12 +Frame 28 0 7 12 +Frame 35 0 7 12 +Frame 42 0 7 12 +Frame 49 0 7 12 +Frame 56 0 7 12 +Frame 63 0 7 12 +Frame 70 0 7 12 +Frame 0 12 7 12 +Frame 7 12 7 12 +Frame 14 12 7 12 +Frame 21 12 7 12 +Frame 28 12 7 12 +Frame 35 12 7 12 +Frame 42 12 7 12 +Frame 49 12 7 12 +Frame 56 12 7 12 +Frame 63 12 7 12 +Frame 70 12 7 12 diff --git a/Data/Sprites/Flash.txt b/Data/Sprites/Flash.txt new file mode 100644 index 0000000..c10a98b --- /dev/null +++ b/Data/Sprites/Flash.txt @@ -0,0 +1,3 @@ +Texture Texture/Main.bmp +Frame 0 96 16 16 +Frame 112 0 16 16 diff --git a/Data/Sprites/Hero.txt b/Data/Sprites/Hero.txt new file mode 100644 index 0000000..15e033a --- /dev/null +++ b/Data/Sprites/Hero.txt @@ -0,0 +1,7 @@ +Texture Texture/Main.bmp +Frame 0 80 16 16 Stand +Frame 16 80 16 16 Hold +Frame 32 80 16 16 Jump +Frame 48 80 16 16 Run +Frame 64 80 16 16 Dead +Frame 32 96 16 16 Climb \ No newline at end of file diff --git a/Data/Sprites/Item.txt b/Data/Sprites/Item.txt new file mode 100644 index 0000000..ffc622c --- /dev/null +++ b/Data/Sprites/Item.txt @@ -0,0 +1,7 @@ +Texture Texture/Main.bmp +Frame 0 64 16 16 Time +Frame 16 64 16 16 Bullet +Frame 32 64 16 16 Torch +Frame 48 64 16 16 Key +Frame 80 16 16 16 Rope +Frame 16 96 16 16 Rock \ No newline at end of file diff --git a/Data/Sprites/Ladder.txt b/Data/Sprites/Ladder.txt new file mode 100644 index 0000000..48ba187 --- /dev/null +++ b/Data/Sprites/Ladder.txt @@ -0,0 +1,3 @@ +Texture Texture/Main.bmp +Frame 64 16 16 16 Ladder +Frame 80 16 16 16 Rope diff --git a/Data/Sprites/Light.txt b/Data/Sprites/Light.txt new file mode 100644 index 0000000..43f0815 --- /dev/null +++ b/Data/Sprites/Light.txt @@ -0,0 +1,2 @@ +Texture Texture/Main.bmp +Frame 80 80 48 48 \ No newline at end of file diff --git a/Data/Sprites/Mine.txt b/Data/Sprites/Mine.txt new file mode 100644 index 0000000..a39a6db --- /dev/null +++ b/Data/Sprites/Mine.txt @@ -0,0 +1,2 @@ +Texture Texture/Main.bmp +Frame 80 32 16 16 diff --git a/Data/Sprites/Passage.txt b/Data/Sprites/Passage.txt new file mode 100644 index 0000000..bfd085b --- /dev/null +++ b/Data/Sprites/Passage.txt @@ -0,0 +1,3 @@ +Texture Texture/Main.bmp +Frame 32 32 16 16 Closed +Frame 48 32 16 16 Open diff --git a/Data/Sprites/Plank.txt b/Data/Sprites/Plank.txt new file mode 100644 index 0000000..409b325 --- /dev/null +++ b/Data/Sprites/Plank.txt @@ -0,0 +1,3 @@ +Texture Texture/Main.bmp +Frame 96 32 16 16 + diff --git a/Data/Sprites/Possum.txt b/Data/Sprites/Possum.txt new file mode 100644 index 0000000..a7c6215 --- /dev/null +++ b/Data/Sprites/Possum.txt @@ -0,0 +1 @@ +Texture Texture/possum.bmp diff --git a/Data/Sprites/PushRock.txt b/Data/Sprites/PushRock.txt new file mode 100644 index 0000000..41ee549 --- /dev/null +++ b/Data/Sprites/PushRock.txt @@ -0,0 +1,2 @@ +Texture Texture/Main.bmp +Frame 48 16 16 16 diff --git a/Data/Sprites/Spike.txt b/Data/Sprites/Spike.txt new file mode 100644 index 0000000..dcf418f --- /dev/null +++ b/Data/Sprites/Spike.txt @@ -0,0 +1,3 @@ +Texture Texture/Main.bmp +Frame 0 32 16 16 Clean +Frame 16 32 16 16 Bloody diff --git a/Data/Sprites/Stone.txt b/Data/Sprites/Stone.txt new file mode 100644 index 0000000..18c7089 --- /dev/null +++ b/Data/Sprites/Stone.txt @@ -0,0 +1,2 @@ +Texture Texture/Main.bmp +Frame 20 100 8 8 diff --git a/Data/Sprites/Wall.txt b/Data/Sprites/Wall.txt new file mode 100644 index 0000000..ffb9381 --- /dev/null +++ b/Data/Sprites/Wall.txt @@ -0,0 +1,4 @@ +Texture Texture/Main.bmp +Frame 0 16 16 16 Backdrop +Frame 16 16 16 16 Dirt +Frame 32 16 16 16 Grass diff --git a/Data/Templates/Any.txt b/Data/Templates/Any.txt new file mode 100644 index 0000000..419a5a9 --- /dev/null +++ b/Data/Templates/Any.txt @@ -0,0 +1,64 @@ +Room +gggggggggg +g8ggg8g8gg +g8ggg8g8gg +g888g858gg +g8g8g8g8gg +g858g8g8gg + +Room +gggggggggg +gggggggggg +gggggggggg +gggggggggg +gggggggggg +gggggggggg + +Room +0000gggggg +0000gggggg +0000gggggg +0000gggggg +0000gggggg +gggggggggg + +Room +gggggg0000 +gggggg0000 +gggggg0000 +gggggg0000 +gggggg0000 +gggggggggg + +Room +000000000g +000000000g +000000000g +000000000g +000000000g +gggggggggg + +Room +g000000000 +g000000000 +g000000000 +g000000000 +g000000000 +gggggggggg + +Room +0000l00000 +0000l00000 +7775557757 +5557775575 +0000000000 +gggggggggg + +Room +g000l0000g +g000l0000g +g000l0000g +g000l0000g +gDDDlDDDDg +gggggggggg + diff --git a/Data/Templates/LR.txt b/Data/Templates/LR.txt new file mode 100644 index 0000000..3f62b67 --- /dev/null +++ b/Data/Templates/LR.txt @@ -0,0 +1,15 @@ +Room +5555555555 +0000000000 +0000000000 +0000000000 +0000000000 +GGGGGGGGGG + +Room +5555555555 +0000000000 +000lGGGGl0 +000lggggl0 +000lggggl0 +GGGGggggGG diff --git a/Data/Templates/LRD.txt b/Data/Templates/LRD.txt new file mode 100644 index 0000000..9f920b5 --- /dev/null +++ b/Data/Templates/LRD.txt @@ -0,0 +1,15 @@ +Room +0000000000 +0000000000 +0000000000 +0000000000 +0000000000 +GGGGPPPPGG + +Room +0000000000 +0000000000 +0000000000 +0lgggggggl +0l000000gl +GGGGPPPPgG diff --git a/Data/Templates/LRU.txt b/Data/Templates/LRU.txt new file mode 100644 index 0000000..c022d32 --- /dev/null +++ b/Data/Templates/LRU.txt @@ -0,0 +1,15 @@ +Room +0000l00000 +0000l00000 +0000l00000 +0000l00000 +0000l00000 +GGGGGGGGGG + +Room +0000l00000 +0000l00000 +0lggggggl0 +0l000000l0 +0l000000l0 +GGGGGGGGGG \ No newline at end of file diff --git a/Data/Templates/LRUD.txt b/Data/Templates/LRUD.txt new file mode 100644 index 0000000..58220b2 --- /dev/null +++ b/Data/Templates/LRUD.txt @@ -0,0 +1,15 @@ +Room +0000l00000 +0000l00000 +0000l00000 +0000l00000 +0000l00000 +GGPPlPPPGG + +Room +0000l00000 +0000l00000 +ggggggglgg +0000000l00 +0000l00l00 +GGPPlGGGGG diff --git a/Data/Texture/Digits.bmp b/Data/Texture/Digits.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c60bf52c24dddc4db34e5a3fc59e91a77bd2172b GIT binary patch literal 7530 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 literal 0 HcmV?d00001 diff --git a/Data/Texture/Main.bmp b/Data/Texture/Main.bmp new file mode 100644 index 0000000000000000000000000000000000000000..9fddf22bfc6892894c618ec07fd6806a42a590d2 GIT binary patch literal 65674 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 literal 0 HcmV?d00001 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3386234 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +# GNU makefile + +# User configuration +CXXFLAGS ?= -Og -std=c++17 -Iinclude +LDFLAGS ?= -lSDL2 +TARGET = eulamadness + +# System configuration +include Makefile.srcs + +EXE = $(TARGET) +OBJS = $(SRCS:.cpp=.o) + +# Targets +all: $(EXE) + +dist: $(EXE) + mkdir -p dist + cp $(EXE) dist/ + +$(EXE): $(OBJS) + $(CXX) -o $@ $(OBJS) $(LDFLAGS) + +.cpp.o: + $(CXX) $(CXXFLAGS) -c $< -o $@ + +clean: + -rm -rf $(OBJS) $(EXE) dist diff --git a/Makefile.emcc b/Makefile.emcc new file mode 100644 index 0000000..7764b63 --- /dev/null +++ b/Makefile.emcc @@ -0,0 +1,30 @@ +# GNU makefile + +# User configuration +CC = emcc +CXX = emcc +CXXFLAGS ?= -sUSE_SDL=2 -std=c++17 -Iinclude -fexceptions +LDFLAGS ?= -sUSE_SDL=2 -fexceptions --embed-file=data.pak@data.pak -sMAX_WEBGL_VERSION=2 +TARGET = eulamadness.html + +# System configuration +include Makefile.srcs + +EXE = $(TARGET) +OBJS = $(SRCS:.cpp=.o) + +# Targets +all: $(EXE) + +dist: $(EXE) + mkdir -p dist + cp $(EXE) dist/ + +$(EXE): $(OBJS) + $(CXX) -o $@ $(OBJS) $(LDFLAGS) + +.cpp.o: + $(CXX) $(CXXFLAGS) -c $< -o $@ + +clean: + -rm -rf $(OBJS) $(EXE) eulamadness.js eulamadness.wasm eulamadness.data dist diff --git a/Makefile.srcs b/Makefile.srcs new file mode 100644 index 0000000..0bc92c9 --- /dev/null +++ b/Makefile.srcs @@ -0,0 +1,19 @@ +# Source files + +SRCS = \ + src/Alarm.cpp \ + src/App.cpp \ + src/Common.cpp \ + src/Entity.cpp \ + src/EntityManager.cpp \ + src/FileManager.cpp \ + src/Gridmap.cpp \ + src/Main.cpp \ + src/RenderManager.cpp \ + src/SpriteManager.cpp \ + src/State.cpp \ + src/SurfaceManager.cpp \ + src/FontManager.cpp \ + src/DigitManager.cpp \ + src/RoomEntity.cpp \ + src/Variable.cpp \ No newline at end of file diff --git a/makepak.py b/makepak.py new file mode 100755 index 0000000..b5ac5bd --- /dev/null +++ b/makepak.py @@ -0,0 +1,43 @@ +import sys +import struct +import os + +#dummy class for stuffing the file headers into +class FileEntry: + pass + +#arguments are source directory, then target filename e.g. "pak1.pak" +rootdir = sys.argv[1] +pakfilename = sys.argv[2] + +pakfile = open(pakfilename,"wb") + +#write a dummy header to start with +pakfile.write(struct.Struct("<4s2l").pack(b"PACK",0,0)) + +#walk the directory recursively, add the files and record the file entries +offset = 12 +fileentries = [] +for root, subFolders, files in os.walk(rootdir): + for file in files: + entry = FileEntry() + impfilename = os.path.join(root,file) + entry.filename = os.path.relpath(impfilename,rootdir).replace("\\","/") + with open(impfilename, "rb") as importfile: + pakfile.write(importfile.read()) + entry.offset = offset + entry.length = importfile.tell() + offset = offset + entry.length + fileentries.append(entry) +tablesize = 0 + +#after all the file data, write the list of entries +for entry in fileentries: + pakfile.write(struct.Struct("<56s").pack(entry.filename.encode("ascii"))) + pakfile.write(struct.Struct("second; + if (alarm.Target) { + alarm.Current += delta; + } + + if (alarm.Current >= alarm.Target) { + DueAlarms_.push(it->first); + } + } +} + +bool TAlarm::Next(TId& id) { + bool repeat; + + do { + repeat = false; + + if (DueAlarms_.empty()) + return false; + + id = DueAlarms_.front(); + DueAlarms_.pop(); + + auto& alarm = Alarms_[id]; + if (alarm.Target) { + if (alarm.Current >= alarm.Target) { + alarm.Current -= alarm.Target; + } + + if (alarm.Current >= alarm.Target) { + DueAlarms_.push(id); + } + } else { + alarm.Current = 0; + repeat = true; + } + } while (repeat); + + return true; +} + +} // namespace NGame diff --git a/src/Alarm.h b/src/Alarm.h new file mode 100644 index 0000000..730e2d5 --- /dev/null +++ b/src/Alarm.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Common.h" + +#include +#include +#include + +namespace NGame { + +class TAlarm { +public: + using TId = int; + + void Set(TId id, std::uint32_t millis); + void Unset(TId id); + void Update(std::uint32_t delta); + bool Next(TId& id); + +private: + struct TData { + std::uint32_t Target; + std::uint32_t Current; + }; + + std::unordered_map Alarms_; + std::queue DueAlarms_; +}; + +} \ No newline at end of file diff --git a/src/App.cpp b/src/App.cpp new file mode 100644 index 0000000..e88dd45 --- /dev/null +++ b/src/App.cpp @@ -0,0 +1,116 @@ +#include "App.h" + +#include + +#ifdef __EMSCRIPTEN__ +#include +#endif + +namespace NGame { + +TApp* TApp::Instance() { + static TApp* instance = nullptr; + + if (!instance) { + instance = new TApp(); + } + + return instance; +} + +TApp::TApp() { + FileManager_ = std::make_unique("Data", "data.pak"); + SurfaceManager_ = std::make_unique(*FileManager_); + RenderManager_ = std::make_unique(*SurfaceManager_, 320, 240, "SDL Game"); + SpriteManager_ = std::make_unique(*FileManager_, *RenderManager_); + EntityManager_ = std::make_unique(*RenderManager_, 1000 / 60); + FontManager_ = std::make_unique(*SpriteManager_); + DigitManager_ = std::make_unique(*SpriteManager_); +} + +TApp::~TApp() { +} + +void TApp::Process() { + if (!RenderManager_->IsRunning()) { + #ifdef __EMSCRIPTEN__ + emscripten_cancel_main_loop(); /* this should "kill" the app. */ + #else + exit(0); + #endif + } + + EntityManager_->Run(); + RenderManager_->Run(); +} + +int TApp::Run() { + Process(); + return 0; +} + +TState& TApp::State() { + return State_; +} + +const TState& TApp::State() const { + return State_; +} + +TFileManager& TApp::FileManager() { + return *FileManager_; +} + +TRenderManager& TApp::RenderManager() { + return *RenderManager_; +} + +TEntityManager& TApp::EntityManager() { + return *EntityManager_; +} + +TSurfaceManager& TApp::SurfaceManager() { + return *SurfaceManager_; +} + +TSpriteManager& TApp::SpriteManager() { + return *SpriteManager_; +} + +TFontManager& TApp::FontManager() { + return *FontManager_; +} + +TDigitManager& TApp::DigitManager() { + return *DigitManager_; +} + +const TFileManager& TApp::FileManager() const { + return *FileManager_; +} + +const TRenderManager& TApp::RenderManager() const { + return *RenderManager_; +} + +const TEntityManager& TApp::EntityManager() const { + return *EntityManager_; +} + +const TSurfaceManager& TApp::SurfaceManager() const { + return *SurfaceManager_; +} + +const TSpriteManager& TApp::SpriteManager() const { + return *SpriteManager_; +} + +const TFontManager& TApp::FontManager() const { + return *FontManager_; +} + +const TDigitManager& TApp::DigitManager() const { + return *DigitManager_; +} + +} // namespace NGame \ No newline at end of file diff --git a/src/App.h b/src/App.h new file mode 100644 index 0000000..88566e2 --- /dev/null +++ b/src/App.h @@ -0,0 +1,64 @@ +#pragma once + +#include "Common.h" + +#include "DigitManager.h" +#include "EntityManager.h" +#include "FileManager.h" +#include "FontManager.h" +#include "RenderManager.h" +#include "SpriteManager.h" +#include "State.h" +#include "SurfaceManager.h" + +#include +#include +#include +#include +#include + + +namespace NGame { + +class TApp { +public: + static TApp* Instance(); + int Run(); + + TState& State(); + const TState& State() const; + + TFileManager& FileManager(); + TRenderManager& RenderManager(); + TEntityManager& EntityManager(); + TSurfaceManager& SurfaceManager(); + TSpriteManager& SpriteManager(); + TFontManager& FontManager(); + TDigitManager& DigitManager(); + + const TFileManager& FileManager() const; + const TRenderManager& RenderManager() const; + const TEntityManager& EntityManager() const; + const TSurfaceManager& SurfaceManager() const; + const TSpriteManager& SpriteManager() const; + const TFontManager& FontManager() const; + const TDigitManager& DigitManager() const; + +private: + TApp(); + ~TApp(); + void Process(); + +private: + std::unique_ptr FileManager_; + std::unique_ptr RenderManager_; + std::unique_ptr EntityManager_; + std::unique_ptr SurfaceManager_; + std::unique_ptr SpriteManager_; + std::unique_ptr FontManager_; + std::unique_ptr DigitManager_; + TState State_; +}; + +} // namespace NGame + diff --git a/src/Common.cpp b/src/Common.cpp new file mode 100644 index 0000000..c0cae9a --- /dev/null +++ b/src/Common.cpp @@ -0,0 +1,81 @@ +#include "Common.h" + +#include +#include +#include +#include + +namespace NGame { + +std::string ToUpper(const std::string_view& value) { + std::string result; + result.resize(value.size()); + + std::transform(value.begin(), value.end(), result.begin(), ::toupper); + return result; +} + +std::string ToLower(const std::string_view& value) { + std::string result; + result.resize(value.size()); + + std::transform(value.begin(), value.end(), result.begin(), ::tolower); + return result; +} + +std::string_view LeftTrim(const std::string_view& value) { + auto firstNonSpace = std::find_if(value.begin(), value.end(), [](char symbol) { + return !std::isspace(symbol); + }); + + auto length = std::distance(firstNonSpace, value.end()); + return std::string_view(&*firstNonSpace, length); +} + +std::string_view RightTrim(const std::string_view& value) { + auto lastNonSpace = std::find_if(value.rbegin(), value.rend(), [](char symbol) { + return !std::isspace(symbol); + }); + + auto index = value.size() - std::distance(value.rbegin(), lastNonSpace); + auto length = std::distance(value.begin(), value.begin() + index); + return std::string_view(&*value.begin(), length); +} + +std::string_view Trim(const std::string_view& value) { + return LeftTrim(RightTrim(value)); +} + +std::string_view NextToken(std::string_view& value, const std::string_view& token) { + auto position = value.find(token); + auto result = value.substr(0, position); + + if (position == std::string_view::npos) { + value = {}; + } else { + value = value.substr(position + token.size()); + } + + return result; +} + +bool RectContains(const Vec2i& position, const Vec2i& size, const Vec2i& point) { + if (point.X < position.X || point.X - position.X > size.X || + point.Y < position.Y || point.Y - position.Y > size.Y) { + return false; + } + + return true; +} + +bool RectOverlaps(const Vec2i& positionA, const Vec2i& sizeA, const Vec2i& positionB, const Vec2i& sizeB) { + if (positionB.X + sizeB.X <= positionA.X || + positionA.X + sizeA.X <= positionB.X || + positionB.Y + sizeB.Y <= positionA.Y || + positionA.Y + sizeA.Y <= positionB.Y) { + return false; + } + return true; +} + +} // namespace NGame diff --git a/src/Common.h b/src/Common.h new file mode 100644 index 0000000..ccbddf8 --- /dev/null +++ b/src/Common.h @@ -0,0 +1,174 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#define GAME_UNUSED(x) (void)(x) + +#define DELETE_COPY(NAME) \ + NAME(const NAME&) = delete; \ + NAME& operator=(const NAME&) = delete; + +namespace NGame { + +template +struct TOverload : Ts... { using Ts::operator()...; }; + +template +TOverload(Ts...) -> TOverload; + +template +struct Vec2 { + using TType = T; + + Vec2() + : X(), Y() { + } + + Vec2(T value) + : X(value), Y(value) { + } + Vec2(T x, T y) + : X(x), Y(y) { + } + + Vec2 operator+(const Vec2& other) const { + Vec2 result(X + other.X, Y + other.Y); + return result; + } + + Vec2& operator+=(const Vec2& other) { + X += other.X; + Y += other.Y; + return *this; + } + + Vec2 operator-(const Vec2& other) const { + Vec2 result(X - other.X, Y - other.Y); + return result; + } + + Vec2& operator-=(const Vec2& other) { + X -= other.X; + Y -= other.Y; + return *this; + } + + template + Vec2 operator*(const Vec2& other) const { + Vec2 result(X * other.X, Y * other.Y); + return result; + } + + template + Vec2& operator*=(const Vec2& other) { + X *= other.X; + Y *= other.Y; + return *this; + } + + template + Vec2 operator*(const Q& other) const { + Vec2 result(X * other, Y * other); + return result; + } + + template + Vec2& operator*=(const Q& other) { + X *= other; + Y *= other; + return *this; + } + + template + Vec2 operator/(const Vec2& other) const { + Vec2 result(X / other.X, Y / other.Y); + return result; + } + + template + Vec2& operator/=(const Vec2& other) { + X /= other.X; + Y /= other.Y; + return *this; + } + + template + Vec2 operator/(const Q& other) const { + Vec2 result(X / other, Y / other); + return result; + } + + template + Vec2& operator/=(const Q& other) { + X /= other; + Y /= other; + return *this; + } + + template::value>::type> + Q Length() const { + return std::sqrt(X * X + Y * Y); + } + + template::value>::type> + Vec2 Normilize() const { + return *this / Length(); + } + + template::value>::type> + float Length() const { + return std::sqrt(static_cast(X * X + Y * Y)); + } + + bool operator==(const Vec2& other) const { + return X == other.X && Y == other.Y; + } + + bool operator!=(const Vec2& other) const { + return X != other.X || Y != other.Y; + } + + union { + struct { + T X; + T Y; + }; + T Data[2]; + }; +}; + +using Vec2f = Vec2; +using Vec2i = Vec2; + +std::string ToUpper(const std::string_view& value); +std::string ToLower(const std::string_view& value); +std::string_view LeftTrim(const std::string_view& value); +std::string_view RightTrim(const std::string_view& value); +std::string_view Trim(const std::string_view& value); +std::string_view NextToken(std::string_view& value, const std::string_view& token); + +bool RectContains(const Vec2i& position, const Vec2i& size, const Vec2i& point); +bool RectOverlaps(const Vec2i& positionA, const Vec2i& sizeA, const Vec2i& positionB, const Vec2i& sizeB); + +} // namespace NGame + +template<> +struct std::hash { + std::size_t operator()(const NGame::Vec2i& key) const { + return ((static_cast(key.X) * 73856093) ^ + (static_cast(key.Y) * 19349663)); + } +}; + +template<> +struct std::hash { + std::size_t operator()(const NGame::Vec2f& key) const { + return ((static_cast(key.X) * 73856093) ^ + (static_cast(key.Y) * 19349663)); + } +}; \ No newline at end of file diff --git a/src/DigitManager.cpp b/src/DigitManager.cpp new file mode 100644 index 0000000..85328a5 --- /dev/null +++ b/src/DigitManager.cpp @@ -0,0 +1,36 @@ +#include "DigitManager.h" + +namespace NGame { + +TDigitManager::TDigitManager(TSpriteManager& spriteManager) + : SpriteManager_(spriteManager) { + Digits_ = SpriteManager_.Get("Sprites/Digits.txt"); +} + +void TDigitManager::Draw(const Vec2i position, int value, int length, int point) { + Vec2i currentPosition = {position.X + length * GlyphSize.X, position.Y}; + + for (size_t remainder = length; remainder; --remainder, --point) { + if (value) { + auto digit = value % 10; + value /= 10; + + if (point == 0) { + SpriteManager_.Draw(Digits_, digit + 11, currentPosition); + } else { + SpriteManager_.Draw(Digits_, digit, currentPosition); + } + } else { + if (point == 0) { + SpriteManager_.Draw(Digits_, 21, currentPosition); + } else { + SpriteManager_.Draw(Digits_, 10, currentPosition); + } + } + + currentPosition.X -= GlyphSize.X; + } + +} + +} // namespace NGame \ No newline at end of file diff --git a/src/DigitManager.h b/src/DigitManager.h new file mode 100644 index 0000000..4b42ad2 --- /dev/null +++ b/src/DigitManager.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Common.h" + +#include "SpriteManager.h" + +namespace NGame { + +class TDigitManager { +public: + TDigitManager(TSpriteManager& spriteManager); + DELETE_COPY(TDigitManager) + + void Draw(const Vec2i position, int value, int length, int point); + +private: + TSpriteManager& SpriteManager_; + const Vec2i GlyphSize = {7, 12}; + std::shared_ptr Digits_; +}; + +} // namespace NGame \ No newline at end of file diff --git a/src/Entity.cpp b/src/Entity.cpp new file mode 100644 index 0000000..3948f69 --- /dev/null +++ b/src/Entity.cpp @@ -0,0 +1,113 @@ +#include "Entity.h" + +namespace NGame { + +TEntity::TEntity(TEntity::TId id) + : Id_(id) { + +} + +void TEntity::Tick(std::uint32_t delta) { + Alarm_.Update(delta); + + TAlarm::TId alarmId; + while (Alarm_.Next(alarmId)) { + Alarm(alarmId); + } + Update(delta); +} + +void TEntity::Input(SDL_Event* event) { + GAME_UNUSED(event); +} + +void TEntity::Update(std::uint32_t delta) { + GAME_UNUSED(delta); +} + +void TEntity::Alarm(TAlarm::TId id) { + GAME_UNUSED(id); +} + +void TEntity::Draw() const { +} + +void TEntity::SetPosition(Vec2i value) { + Position_ = value; +} + +void TEntity::SetSize(Vec2i value) { + Size_ = value; +} + +const Vec2i& TEntity::Position() const { + return Position_; +} + +const Vec2i& TEntity::Size() const { + return Size_; +} + +const Vec2i& TEntity::AckPosition() const { + return AckPosition_; +} + +const Vec2i& TEntity::AckSize() const { + return AckSize_; +} + +bool TEntity::HasChanges() const { + if (AckPosition_ != Position_ || AckSize_ != Size_) + return true; + + return false; +} + +void TEntity::AckChanges() { + AckPosition_ = Position_; + AckSize_ = Size_; +} + +TEntity::TCollisionGroup TEntity::CollisionGroup() const { + return CollisionGroup_; +} + +void TEntity::SetCollisionGroup(TCollisionGroup group) { + CollisionGroup_ = group; +} + +float TEntity::MovementPerTick(std::uint32_t delta, float speed) { + return speed * (delta / 1000.0f); +} + +Vec2f TEntity::MovementPerTick(std::uint32_t delta, Vec2f speed) { + return speed * (delta / 1000.0f); +} + +std::pair TEntity::NextMovement(float speed, float fraction) { + std::pair result; + + result.second = speed + fraction; + result.first = static_cast(result.second); + result.second -= result.first; + + return result; +} + +TEntity::TId TEntity::Id() const { + return Id_; +} + +void TEntity::Remove() { + IsRemoved_ = true; +} + +bool TEntity::IsRemoved() const { + return IsRemoved_; +} + +bool TEntity::IsPersistent() const { + return IsPersistent_; +} + +} // namespace NGame diff --git a/src/Entity.h b/src/Entity.h new file mode 100644 index 0000000..24a9245 --- /dev/null +++ b/src/Entity.h @@ -0,0 +1,112 @@ +#pragma once + +#include "Common.h" + +#include "Alarm.h" +#include "State.h" + +#include +#include +#include +#include + +namespace NGame { + +class TEntity { +public: + using TId = std::uint32_t; + using TCollisionGroup = std::bitset<32>; + + TEntity(TEntity::TId id); + virtual ~TEntity() = default; + void Tick(std::uint32_t delta); + virtual void Input(SDL_Event* event); + virtual void Update(std::uint32_t delta); + virtual void Alarm(TAlarm::TId id); + virtual void Draw() const; + + void SetPosition(Vec2i value); + void SetSize(Vec2i value); + const Vec2i& Position() const; + const Vec2i& Size() const; + const Vec2i& AckPosition() const; + const Vec2i& AckSize() const; + bool HasChanges() const; + void AckChanges(); + + TCollisionGroup CollisionGroup() const; + void SetCollisionGroup(TCollisionGroup group); + static float MovementPerTick(std::uint32_t delta, float speed); + static Vec2f MovementPerTick(std::uint32_t delta, Vec2f speed); + std::pair NextMovement(float speed, float fraction); + + template + std::pair> MoveWithCondition(Vec2f speed, Vec2f fraction, UnaryPredicate p) { + auto distanceX = NextMovement(speed.X, fraction.X); + auto distanceY = NextMovement(speed.Y, fraction.Y); + auto directionFlags = std::make_pair(true, true); + + Vec2f resultFraction(distanceX.second, distanceY.second); + Vec2i resultDistance(distanceX.first, distanceY.first); + + Vec2i newPosition = Position() + resultDistance; + if (p(newPosition)) { + // There is no problem with the new position, we are done + SetPosition(newPosition); + } else { + // There might be some problem, move 1px at a time + Vec2i direction(1, 1); + + if (resultDistance.X < 0) { + direction.X = -1; + } + if (resultDistance.Y < 0) { + direction.Y = -1; + } + resultDistance.X = abs(resultDistance.X); + resultDistance.Y = abs(resultDistance.Y); + + for (Vec2i::TType i = 0; i < resultDistance.X; ++i) { + newPosition = Position() + Vec2i(direction.X, 0); + if (!p(newPosition)) { + resultFraction.X = 0; + directionFlags.first = false; + break; + } + SetPosition(newPosition); + } + + for (Vec2i::TType i = 0; i < resultDistance.Y; ++i) { + newPosition = Position() + Vec2i(0, direction.Y); + if (!p(newPosition)) { + resultFraction.Y = 0; + directionFlags.second = false; + break; + } + SetPosition(newPosition); + } + } + + return std::make_pair(resultFraction, directionFlags); + } + + TEntity::TId Id() const; + void Remove(); + bool IsRemoved() const; + bool IsPersistent() const; + +protected: + TAlarm Alarm_; + +private: + bool IsPersistent_ = false; + bool IsRemoved_ = false; + TEntity::TId Id_; + TCollisionGroup CollisionGroup_; + Vec2i Position_; + Vec2i Size_; + Vec2i AckPosition_; + Vec2i AckSize_; +}; + +} // namespace NGame \ No newline at end of file diff --git a/src/EntityManager.cpp b/src/EntityManager.cpp new file mode 100644 index 0000000..3ff2747 --- /dev/null +++ b/src/EntityManager.cpp @@ -0,0 +1,144 @@ +#include "EntityManager.h" + +#include +#include + +namespace NGame { + +TEntityManager::TEntityManager(TRenderManager& renderManager, std::size_t timestep) + : RenderManager_(renderManager), Timestep_(timestep) { + LastUpdateTick_ = SDL_GetTicks(); +} + +void TEntityManager::Run() { + Input(); + Update(); + Draw(); +} + +std::shared_ptr TEntityManager::Entity(TEntity::TId id) { + auto it = Entities_.find(id); + if (it != Entities_.end()) { + return it->second; + } + throw std::runtime_error("Could not find entity with id: " + std::to_string(id)); +} + +void TEntityManager::UpdateCollision(TEntity::TId id) { + auto entity = Entity(id); + UpdateCollision(entity); +} + +void TEntityManager::UpdateCollision(std::shared_ptr entity) { + if (entity->HasChanges()) { + Gridmap_.Remove(entity->AckPosition(), entity->AckSize(), entity->Id()); + Gridmap_.Add(entity->Position(), entity->Size(), entity->Id()); + entity->AckChanges(); + } +} + +std::shared_ptr TEntityManager::MakeEntityByName(const std::string& name) { + if (EntityFactories_.find(name) == EntityFactories_.end()) { + throw std::runtime_error("can't create entity with name " + name); + } + + auto entity = EntityFactories_[name](++IdCounter_); + Entities_[entity->Id()] = entity; + + // Explicilty ack changes in entity colision boxes + Gridmap_.Add(entity->Position(), entity->Size(), entity->Id()); + entity->AckChanges(); + return entity; +} + +bool TEntityManager::IsPlaceEmpty(const Vec2i& position, const Vec2i& size, TEntity::TCollisionGroup group, TEntity::TId ignoreId) { + auto query = Gridmap_.Query(position, size); + + return !std::any_of(query.begin(), query.end(), [&](auto id) { + auto entity = Entity(id); + if (id == ignoreId) + return false; + + if (!(entity->CollisionGroup() & group).any()) + return false; + + return RectOverlaps(position, size, entity->Position(), entity->Size()); + }); +} + +std::unordered_set TEntityManager::CollisionList(const Vec2i& position, const Vec2i& size, TEntity::TCollisionGroup group, TEntity::TId ignoreId) { + auto query = Gridmap_.Query(position, size); + + for (auto it = query.begin(); it != query.end(); ) { + auto entity = Entity(*it); + + if (entity->Id() == ignoreId || !(entity->CollisionGroup() & group).any()) { + it = query.erase(it); + } else { + if (RectOverlaps(position, size, entity->Position(), entity->Size())) { + ++it; + } else { + it = query.erase(it); + } + } + } + return query; +} + +void TEntityManager::Input() { + SDL_Event event; + while (RenderManager_.Input(&event)) { + std::for_each(Entities_.begin(), Entities_.end(), [&](auto& entity) { + entity.second->Input(&event); + }); + } +} + +void TEntityManager::Update() { + std::uint32_t currentTick = SDL_GetTicks(); + std::size_t attempts = 5; + + while (--attempts && (currentTick - LastUpdateTick_ > Timestep_)) { + LastUpdateTick_ += Timestep_; + + std::queue idForRemoval; + + // Update each enitity + std::for_each(Entities_.begin(), Entities_.end(), [&](auto& pair) { + auto& entity = pair.second; + + if (!entity->IsRemoved()) { + entity->Tick(Timestep_); + } + + if (entity->IsRemoved()) { + // Explicitly remove collision + Gridmap_.Remove(entity->AckPosition(), entity->AckSize(), entity->Id()); + idForRemoval.emplace(pair.first); + } else { + UpdateCollision(entity); + } + }); + + // Remove removed entities + while (!idForRemoval.empty()) { + Entities_.erase(idForRemoval.front()); + idForRemoval.pop(); + } + } + + // Failsafe when we run out of attempts + if (!attempts) { + LastUpdateTick_ = currentTick; + } +} + +void TEntityManager::Draw() { + std::for_each(Entities_.begin(), Entities_.end(), [&](auto& pair) { + auto& entity = pair.second; + entity->Draw(); + }); +} + + +} // namespace NGame diff --git a/src/EntityManager.h b/src/EntityManager.h new file mode 100644 index 0000000..6d62e5f --- /dev/null +++ b/src/EntityManager.h @@ -0,0 +1,66 @@ +#pragma once + +#include "Common.h" + +#include "Entity.h" +#include "Gridmap.h" +#include "RenderManager.h" + +namespace NGame { + +class TEntityManager { +public: + TEntityManager(TRenderManager& renderManager, const std::size_t timestep = 1000 / 30); + DELETE_COPY(TEntityManager) + + void Run(); + std::shared_ptr Entity(TEntity::TId id); + void UpdateCollision(TEntity::TId id); + void UpdateCollision(std::shared_ptr entity); + + template + void RegisterEntity(const std::string& name) { + if (EntityFactories_.find(name) != EntityFactories_.end()) { + throw std::runtime_error("can't register duplicate entity with name " + name); + } + EntityFactories_[name] = &MakeEntityFactory; + } + + template + std::shared_ptr MakeEntity(Args&&... args) { + auto entity = std::make_shared(++IdCounter_, std::forward(args)...); + Entities_[entity->Id()] = entity; + + // Explicilty ack changes in entity colision boxes + Gridmap_.Add(entity->Position(), entity->Size(), entity->Id()); + entity->AckChanges(); + return entity; + } + + std::shared_ptr MakeEntityByName(const std::string& name); + bool IsPlaceEmpty(const Vec2i& position, const Vec2i& size, TEntity::TCollisionGroup group, TEntity::TId ignoreId = {}); + std::unordered_set CollisionList(const Vec2i& position, const Vec2i& size, TEntity::TCollisionGroup group, TEntity::TId ignoreId = {}); + +private: + using TFactoryMethod = std::shared_ptr(*)(TEntity::TId); + + void Input(); + void Update(); + void Draw(); + + template + static std::shared_ptr MakeEntityFactory(TEntity::TId id) { + return std::make_shared(id); + } + +private: + TRenderManager& RenderManager_; + std::unordered_map> Entities_; + std::unordered_map EntityFactories_; + std::uint32_t LastUpdateTick_; + TEntity::TId IdCounter_; + std::size_t Timestep_; + TGridmap Gridmap_; +}; + +} // namespace NGame \ No newline at end of file diff --git a/src/FileManager.cpp b/src/FileManager.cpp new file mode 100644 index 0000000..9f8a32c --- /dev/null +++ b/src/FileManager.cpp @@ -0,0 +1,99 @@ +#include "FileManager.h" + +#include +#include +#include +#include +#include + +namespace NGame { + +TFileManager::TFileManager(const std::string& dataDirectory, const std::string& archivePath, std::size_t cacheSize) + : DataDirectory_(dataDirectory), ArchivePath_(archivePath), Cache_(cacheSize) { + +} + +std::string TFileManager::Get(const std::string& path) { + if (Cache_.Contains(path)) { + return Cache_.Get(path); + } + + auto getAttempt = FromFilesystem(path); + if (getAttempt.second) { + Cache_.Set(path, getAttempt.first); + return getAttempt.first; + } + + getAttempt = FromArchive(path); + if (getAttempt.second) { + Cache_.Set(path, getAttempt.first); + return getAttempt.first; + } + + throw std::runtime_error("can't find file " + path); +} + +std::pair TFileManager::FromFilesystem(const std::string& path) { + auto finalPath = DataDirectory_ + "/" + path; + + std::ifstream fileStream(finalPath, std::ios::in | std::ios::binary); + if (!fileStream.is_open()) { + return std::make_pair({}, false); + } + + std::stringstream fileStringStream; + fileStringStream << fileStream.rdbuf(); + return std::make_pair(fileStringStream.str(), true); +} + +std::pair TFileManager::FromArchive(const std::string& path) { + std::ifstream archiveStream(ArchivePath_, std::ios::in | std::ios::binary); + if (!archiveStream.is_open()) { + return std::make_pair({}, false); + } + + // Identify and validate PAK file + char identifier[4]; + archiveStream.read(identifier, 4); + if (std::memcmp(identifier, "PACK", 4)) { + return std::make_pair({}, false); + } + + std::uint32_t tocOffset = ReadInt32(archiveStream); + std::uint32_t tocSize = ReadInt32(archiveStream) / 64; + + if (!tocSize || tocOffset < 12) { + return std::make_pair({}, false); + } + + // Find requested file + archiveStream.seekg(tocOffset); + + for (std::uint32_t index = 0; index < tocSize; ++index) { + char name[56]; + + archiveStream.read(name, 56); + std::uint32_t fileOffset = ReadInt32(archiveStream); + std::uint32_t fileSize = ReadInt32(archiveStream); + + if (std::strncmp(name, path.c_str(), 56)) { + continue; + } + + archiveStream.seekg(fileOffset); + std::string result(fileSize, 0); + archiveStream.read(result.data(), result.size()); + + return std::make_pair(result, true); + } + + return std::make_pair({}, false); +} + +std::uint32_t TFileManager::ReadInt32(std::ifstream& stream) { + unsigned char bytes[4]; + stream.read(reinterpret_cast(bytes), 4); + return (bytes[0] << 0) | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24); +} + +} // namespace NGame \ No newline at end of file diff --git a/src/FileManager.h b/src/FileManager.h new file mode 100644 index 0000000..78c5ad2 --- /dev/null +++ b/src/FileManager.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Common.h" + +#include "LRU.h" + +namespace NGame { + +class TFileManager { +public: + TFileManager(const std::string& dataDirectory, const std::string& archivePath, std::size_t cacheSize = 4); + DELETE_COPY(TFileManager) + + std::string Get(const std::string& path); + +private: + std::pair FromFilesystem(const std::string& path); + std::pair FromArchive(const std::string& path); + std::uint32_t ReadInt32(std::ifstream& stream); + +private: + const std::string DataDirectory_; + const std::string ArchivePath_; + TLRU Cache_; +}; + +} // namespace NGame \ No newline at end of file diff --git a/src/FontManager.cpp b/src/FontManager.cpp new file mode 100644 index 0000000..67e3fcf --- /dev/null +++ b/src/FontManager.cpp @@ -0,0 +1,35 @@ +#include "FontManager.h" + +#include + +namespace NGame { + +TFontManager::TFontManager(TSpriteManager& spriteManager) + : SpriteManager_(spriteManager) { + Fonts_[White] = SpriteManager_.Get("Fonts/White.txt"); + Fonts_[Gray] = SpriteManager_.Get("Fonts/Gray.txt"); + Fonts_[Red] = SpriteManager_.Get("Fonts/Red.txt"); + Fonts_[Green] = SpriteManager_.Get("Fonts/Green.txt"); + Fonts_[Blue] = SpriteManager_.Get("Fonts/Blue.txt"); + Fonts_[Purple] = SpriteManager_.Get("Fonts/Purple.txt"); + Fonts_[Gold] = SpriteManager_.Get("Fonts/Gold.txt"); + Fonts_[Swamp] = SpriteManager_.Get("Fonts/Swamp.txt"); + Fonts_[LightBlue] = SpriteManager_.Get("Fonts/LightBlue.txt"); +} + +void TFontManager::Draw(EColor color, const Vec2i position, const std::string& text) { + Vec2i currentPosition = position; + + for (const auto& symbol : text) { + if (symbol >= ' ' && symbol <= '~') { + SpriteManager_.Draw(Fonts_[color], (unsigned char)symbol - ' ', currentPosition); + currentPosition += Vec2i(GlyphSize.X, 0); + } else if (symbol == '\n') { + currentPosition = Vec2i(position.X, currentPosition.Y + GlyphSize.Y); + } else if (symbol == '\t') { + currentPosition += Vec2i(GlyphSize.X * 4, 0); + } + } +} + +} // namespace NGame \ No newline at end of file diff --git a/src/FontManager.h b/src/FontManager.h new file mode 100644 index 0000000..ba61aa2 --- /dev/null +++ b/src/FontManager.h @@ -0,0 +1,35 @@ +#pragma once + +#include "Common.h" + +#include "SpriteManager.h" + +namespace NGame { + +class TFontManager { +public: + enum EColor { + White, + Gray, + Red, + Green, + Blue, + Purple, + Gold, + Swamp, + LightBlue, + SentinelMax + }; + + TFontManager(TSpriteManager& spriteManager); + DELETE_COPY(TFontManager) + + void Draw(EColor color, const Vec2i position, const std::string& text); + +private: + TSpriteManager& SpriteManager_; + const Vec2i GlyphSize = {5, 12}; + std::array, SentinelMax> Fonts_; +}; + +} // namespace NGame \ No newline at end of file diff --git a/src/Gridmap.cpp b/src/Gridmap.cpp new file mode 100644 index 0000000..7365e8a --- /dev/null +++ b/src/Gridmap.cpp @@ -0,0 +1,58 @@ +#include "Gridmap.h" + +#include +#include + +namespace NGame { + +std::unordered_set TGridmap::Query(const Vec2i& position, const Vec2i& size) const { + Vec2i cellPosition = position / CellSize_; + Vec2i cellEnd = cellPosition + ((size + (CellSize_ - 1)) / CellSize_); + + std::unordered_set result; + for (Vec2i::TType i = cellPosition.X; i <= cellEnd.X; ++i) { + for (Vec2i::TType j = cellPosition.Y; j <= cellEnd.Y; ++j) { + TId gridId = GridId(Vec2i(i, j)); + const auto& cell = Grid_[gridId]; + for (auto id : cell) { + result.insert(id); + } + } + } + + return result; +} + +void TGridmap::Remove(const Vec2i& position, const Vec2i& size, TEntity::TId id) { + Vec2i cellPosition = position / CellSize_; + Vec2i cellEnd = cellPosition + ((size + (CellSize_ - 1)) / CellSize_); + + std::unordered_set result; + for (Vec2i::TType i = cellPosition.X; i <= cellEnd.X; ++i) { + for (Vec2i::TType j = cellPosition.Y; j <= cellEnd.Y; ++j) { + TId gridId = GridId(Vec2i(i, j)); + auto& cell = Grid_[gridId]; + auto it = std::remove(cell.begin(), cell.end(), id); + cell.erase(it, cell.end()); + } + } +} + +void TGridmap::Add(const Vec2i& position, const Vec2i& size, TEntity::TId id) { + Vec2i cellPosition = position / CellSize_; + Vec2i cellEnd = cellPosition + ((size + (CellSize_ - 1)) / CellSize_); + + std::unordered_set result; + for (Vec2i::TType i = cellPosition.X; i <= cellEnd.X; ++i) { + for (Vec2i::TType j = cellPosition.Y; j <= cellEnd.Y; ++j) { + TId gridId = GridId(Vec2i(i, j)); + Grid_[gridId].push_back(id); + } + } +} + +TGridmap::TId TGridmap::GridId(const Vec2i& point) const { + return std::hash{}(point) % Grid_.size(); +} + +} // namespace NGame \ No newline at end of file diff --git a/src/Gridmap.h b/src/Gridmap.h new file mode 100644 index 0000000..33f2799 --- /dev/null +++ b/src/Gridmap.h @@ -0,0 +1,29 @@ +#pragma once + +#include "Common.h" + +#include "Entity.h" + +#include +#include +#include +#include + +namespace NGame { + +class TGridmap { +public: + std::unordered_set Query(const Vec2i& position, const Vec2i& size) const; + void Remove(const Vec2i& position, const Vec2i& size, TEntity::TId id); + void Add(const Vec2i& position, const Vec2i& size, TEntity::TId id); + +private: + using TId = size_t; + TId GridId(const Vec2i& point) const; + +private: + std::array, 32768> Grid_; + const std::uint32_t CellSize_ = 128; +}; + +} // namespace NGame \ No newline at end of file diff --git a/src/LRU.h b/src/LRU.h new file mode 100644 index 0000000..219c45a --- /dev/null +++ b/src/LRU.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include +#include +#include + +namespace NGame { + +template +class TLRU { +public: + using TPair = std::pair; + using TKey = Key; + using TValue = Value; + + TLRU(std::size_t capacity) + : Capacity_(capacity) { + } + + const Value& Get(const Key& key) const { + auto it = Map_.find(key); + if (it == Map_.end()) { + throw std::runtime_error("lru cache miss"); + } + + Cache_.splice(Cache_.begin(), Cache_, it->second); + return it->second->second; + } + + void Set(const Key& key, const Value& value) { + Set(key, value, [](auto& value){}); + } + + template + void Set(const Key& key, const Value& value, UnaryPredicate p) { + auto it = Map_.find(key); + if (it != Map_.end()) { + p(*it->second); + Cache_.erase(it->second); + Map_.erase(it); + } + + Cache_.push_front(std::make_pair(key, value)); + Map_[key] = Cache_.begin(); + + while (Map_.size() > Capacity_) { + auto lastElement = --(Cache_.end()); + p(*lastElement); + Map_.erase(lastElement->first); + Cache_.erase(lastElement); + } + } + + bool Contains(const Key& key) const { + return Map_.find(key) != Map_.end(); + } + + std::size_t Size() const { + return Map_.size(); + } + + std::size_t Capacity() const { + return Capacity_; + } + + void Clear() { + Cache_.clear(); + Map_.clear(); + } + +private: + using TIterator = typename std::list::iterator; + + mutable std::list Cache_; + mutable std::unordered_map Map_; + std::size_t Capacity_; +}; + +} // namespace NGame diff --git a/src/Main.cpp b/src/Main.cpp new file mode 100644 index 0000000..7c02441 --- /dev/null +++ b/src/Main.cpp @@ -0,0 +1,65 @@ +#include "App.h" + +#include +#include "RoomEntity.h" +#include +#include +#include + +#ifdef __EMSCRIPTEN__ +#include +#endif + +static NGame::TApp *app = nullptr; + +static void InitApp(void) { + app = NGame::TApp::Instance(); + + app->EntityManager().RegisterEntity("Hero"); + app->EntityManager().RegisterEntity("DirtEntity"); + app->EntityManager().RegisterEntity("GrassEntity"); + app->EntityManager().RegisterEntity("PlankEntity"); + app->EntityManager().RegisterEntity("StoneEntity"); + app->EntityManager().RegisterEntity("LadderEntity"); + app->EntityManager().RegisterEntity("ExplosionEntity"); + app->EntityManager().RegisterEntity("KeyEntity"); + app->EntityManager().RegisterEntity("MineEntity"); + app->EntityManager().RegisterEntity("CurseEntity"); + app->EntityManager().RegisterEntity("SpikeEntity"); + app->EntityManager().RegisterEntity("EntranceEntity"); + app->EntityManager().RegisterEntity("ExitEntity"); + app->EntityManager().RegisterEntity("FloatingTextEntity"); + app->EntityManager().RegisterEntity("BackgroundTiler"); + app->EntityManager().RegisterEntity("RoomEntity"); + + app->EntityManager().MakeEntityByName("RoomEntity"); + app->EntityManager().MakeEntityByName("BackgroundTiler"); + + auto h = app->EntityManager().MakeEntityByName("Hero"); + h->SetPosition({0, -64}); + app->EntityManager().UpdateCollision(h); +} + +static void MainLoop(void) { + if (app) { + app->Run(); + } else { + InitApp(); + } +} + +int main(int argc, char** argv) +{ + SDL_Init(SDL_INIT_VIDEO); + + srand(time(NULL)); + + #ifdef __EMSCRIPTEN__ + emscripten_set_main_loop(MainLoop, -1, true); + #else + while (true) { MainLoop(); } + #endif + + return 0; +} + diff --git a/src/RenderManager.cpp b/src/RenderManager.cpp new file mode 100644 index 0000000..0f29686 --- /dev/null +++ b/src/RenderManager.cpp @@ -0,0 +1,348 @@ +#include "RenderManager.h" + +#include +#include +#include +#include + +namespace NGame { + +TRenderManager::TRenderManager(TSurfaceManager& surfaceManager, int width, int height, const std::string& title, std::size_t cacheSize) + : SurfaceManager_(surfaceManager), Cache_(cacheSize), Size_(width, height) { + Window_ = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_RESIZABLE); + if (!Window_) { + throw std::runtime_error("Can't create window"); + } + + Renderer_ = SDL_CreateRenderer(Window_, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + if (!Renderer_) { + SDL_DestroyWindow(Window_); + throw std::runtime_error("Can't create renderer"); + } + + ScreenTexture_ = SDL_CreateTexture(Renderer_, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, width, height); + if (!ScreenTexture_) { + SDL_DestroyRenderer(Renderer_); + SDL_DestroyWindow(Window_); + throw std::runtime_error("Can't create screen texture"); + } + + LightTexture_ = SDL_CreateTexture(Renderer_, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, width, height); + if (!LightTexture_) { + SDL_DestroyTexture(ScreenTexture_); + SDL_DestroyRenderer(Renderer_); + SDL_DestroyWindow(Window_); + throw std::runtime_error("Can't create light texture"); + } + SDL_SetTextureBlendMode(LightTexture_, SDL_BLENDMODE_MOD); + + // Make window bigger + if (width < 640 && height < 480) { + SDL_SetWindowSize(Window_, 640, 480); + } + + Reset(); +} + +std::shared_ptr TRenderManager::Get(const std::string& path) { + if (Cache_.Contains(path)) { + return Cache_.Get(path); + } + + std::shared_ptr surface = SurfaceManager_.Get(path); + SDL_Texture* texture = SDL_CreateTextureFromSurface(Renderer_, surface.get()); + if (!texture) { + std::runtime_error("Can't create texture from surface " + path); + } + + std::shared_ptr result(texture, [](SDL_Texture* texture) { SDL_DestroyTexture(texture); }); + Cache_.Set(path, result); + return result; +} + +void TRenderManager::SetLayer(ELayer layer) { + Layer_ = layer; +} + +void TRenderManager::SetColor(std::uint8_t red, std::uint8_t green, std::uint8_t blue, std::uint8_t alpha) { + Commands_[Layer_].push_back(TColorCommand{red, green, blue, alpha}); +} + +void TRenderManager::SetTexture(const std::shared_ptr& texture) { + if (ActiveTextures_[Layer_] != texture) { + Commands_[Layer_].push_back(TTextureCommand{texture}); + ActiveTextures_[Layer_] = texture; + } +} + +void TRenderManager::DrawRect(const Vec2i& position, const Vec2i& size, bool filled) { + Commands_[Layer_].push_back(TRectCommand{position, size, filled}); +} + +void TRenderManager::DrawLine(const Vec2i& start, const Vec2i& end) { + Commands_[Layer_].push_back(TLineCommand{start, end}); +} + +void TRenderManager::DrawSprite(const Vec2i& texturePosition, const Vec2i& textureSize, const Vec2i& targetPosition, const Vec2i& targetSize, SDL_RendererFlip flip) { + if (!ActiveTextures_[Layer_]) { + throw std::runtime_error("layer has not active texture"); + } + Commands_[Layer_].push_back(TSpriteCommand{texturePosition, textureSize, targetPosition, targetSize, flip}); +} + +void TRenderManager::DrawGeometry(const SDL_Vertex* vertices, std::size_t verticesCount, const int* indices, std::size_t indicesCount) { + if (!ActiveTextures_[Layer_]) { + throw std::runtime_error("layer has not active texture"); + } + + Commands_[Layer_].push_back(TGeometryCommand{vertices, verticesCount, indices, indicesCount}); +} + +void TRenderManager::SetCamera(const Vec2i& position) { + Camera_ = position; +} + +const Vec2i& TRenderManager::Camera() const { + return Camera_; +} + +const Vec2i& TRenderManager::Size() const { + return Size_; +} + +bool TRenderManager::IsLightEnabled() const { + return LightEnabled_; +} + +void TRenderManager::EnableLight(bool value) { + LightEnabled_ = value; +} + +void TRenderManager::SetDefaultLightColor(std::uint8_t red, std::uint8_t green, std::uint8_t blue) { + DefaultLightColor_ = {red, green, blue, 255}; +} + +std::uint8_t TRenderManager::DefaultLightRed() const { + return DefaultLightColor_.Red; +} + +std::uint8_t TRenderManager::DefaultLightGreen() const { + return DefaultLightColor_.Green; +} + +std::uint8_t TRenderManager::DefaultLightBlue() const { + return DefaultLightColor_.Blue; +} + + +void TRenderManager::Run() { + if (!IsRunning()) { + return; + } + + for (size_t layer = 0; layer < SentinelMax; ++layer) { + switch (layer) { + case ELayer::BackgroundGlow: + case ELayer::ForegroundGlow: + case ELayer::EffectsGlow: + if (SDL_SetRenderDrawBlendMode(Renderer_, SDL_BlendMode::SDL_BLENDMODE_ADD)) { + std::runtime_error("can't set blendmode"); + } + if (SDL_SetRenderTarget(Renderer_, ScreenTexture_)) { + std::runtime_error("can't set render target"); + } + break; + + case ELayer::Light: + if (!IsLightEnabled()) { + continue; + } + if (SDL_SetRenderDrawBlendMode(Renderer_, SDL_BlendMode::SDL_BLENDMODE_BLEND)) { + std::runtime_error("can't set blendmode"); + } + if (SDL_SetRenderTarget(Renderer_, LightTexture_)) { + std::runtime_error("can't set render target"); + } + break; + + default: + if (SDL_SetRenderDrawBlendMode(Renderer_, SDL_BlendMode::SDL_BLENDMODE_BLEND)) { + std::runtime_error("can't set blendmode"); + } + if (SDL_SetRenderTarget(Renderer_, ScreenTexture_)) { + std::runtime_error("can't set render target"); + } + break; + } + + SDL_Texture* activeTexture = nullptr; + + for (const auto& command : Commands_[layer]) { + auto drawOperation = TOverload { + [&](const TColorCommand& value) { + SDL_SetRenderDrawColor(Renderer_, value.Red, value.Green, value.Blue, value.Alpha); + }, + [&](const TRectCommand& value) { + SDL_Rect rect; + rect.x = value.Position.X; + rect.y = value.Position.Y; + rect.w = value.Size.X; + rect.h = value.Size.Y; + + if (layer != ELayer::Interface) { + rect.x -= Camera_.X; + rect.y -= Camera_.Y; + } + + if (value.ShouldFill) { + SDL_RenderFillRect(Renderer_, &rect); + } else { + SDL_RenderDrawRect(Renderer_, &rect); + } + }, + [&](const TLineCommand& value) { + if (layer != ELayer::Interface) { + SDL_RenderDrawLine(Renderer_, value.Start.X - Camera_.X, value.Start.Y - Camera_.Y, value.End.X - Camera_.X, value.End.Y - Camera_.Y); + } else { + SDL_RenderDrawLine(Renderer_, value.Start.X, value.Start.Y, value.End.X, value.End.Y); + } + }, + [&](const TTextureCommand& value) { + activeTexture = value.Texture.get(); + + switch (layer) { + case ELayer::BackgroundGlow: + case ELayer::ForegroundGlow: + case ELayer::EffectsGlow: + if (SDL_SetTextureBlendMode(activeTexture, SDL_BlendMode::SDL_BLENDMODE_ADD)) { + std::runtime_error("can't set texture blend mode"); + } + break; + + default: + if (SDL_SetTextureBlendMode(activeTexture, SDL_BlendMode::SDL_BLENDMODE_BLEND)) { + std::runtime_error("can't set texture blend mode"); + } + break; + } + }, + [&](const TSpriteCommand& value) { + SDL_Rect sourceRect; + sourceRect.x = value.TexturePosition.X; + sourceRect.y = value.TexturePosition.Y; + sourceRect.w = value.TextureSize.X; + sourceRect.h = value.TextureSize.Y; + + SDL_Rect destinationRect; + destinationRect.x = value.TargetPosition.X; + destinationRect.y = value.TargetPosition.Y; + destinationRect.w = value.TargetSize.X; + destinationRect.h = value.TargetSize.Y; + + if (layer != ELayer::Interface) { + destinationRect.x -= Camera_.X; + destinationRect.y -= Camera_.Y; + } + + SDL_RenderCopyEx(Renderer_, activeTexture, &sourceRect, &destinationRect, 0, NULL, value.Flip); + }, + [&](const TGeometryCommand& value) { + if (layer == ELayer::Interface) { + SDL_RenderGeometry(Renderer_, activeTexture, value.Vertices, value.VerticesCount, value.Indices, value.IndicesCount); + } else { + VerticesBuffer_.clear(); + for (std::size_t i = 0; i < value.VerticesCount; ++i) { + SDL_Vertex convertedVertex = value.Vertices[i]; + convertedVertex.position.x -= Camera_.X; + convertedVertex.position.y -= Camera_.Y; + VerticesBuffer_.push_back(convertedVertex); + } + + SDL_RenderGeometry(Renderer_, activeTexture, VerticesBuffer_.data(), VerticesBuffer_.size(), value.Indices, value.IndicesCount); + } + + } + }; + + std::visit(drawOperation, command); + } + + // Apply light + if (layer == ELayer::Light) { + SDL_SetRenderTarget(Renderer_, ScreenTexture_); + SDL_RenderCopy(Renderer_, LightTexture_, NULL, NULL); + } + } + + // Keep logical resolution the same + int windowWidth, windowHeight; + SDL_GetWindowSize(Window_, &windowWidth, &windowHeight); + Vec2f scaleFactors = {float(windowWidth) / Size_.X, float(windowHeight) / Size_.Y}; + float targetFactor = std::min(scaleFactors.X, scaleFactors.Y); + + SDL_Rect destinationRect; + destinationRect.w = targetFactor * Size_.X; + destinationRect.h = targetFactor * Size_.Y; + destinationRect.x = (windowWidth - destinationRect.w) / 2; + destinationRect.y = (windowHeight - destinationRect.h) / 2; + + SDL_SetRenderTarget(Renderer_, NULL); + SDL_RenderCopy(Renderer_, ScreenTexture_, NULL, &destinationRect); + SDL_RenderPresent(Renderer_); + Reset(); +} + +bool TRenderManager::Input(SDL_Event* event) { + bool result = SDL_PollEvent(event); + + if (result && event->type == SDL_QUIT) { + Shutdown(); + } + + return result; +} + +bool TRenderManager::IsRunning() { + return Window_ != nullptr; +} + +void TRenderManager::Shutdown() { + if (IsRunning()) { + Reset(); + SDL_DestroyTexture(LightTexture_); + SDL_DestroyTexture(ScreenTexture_); + SDL_DestroyRenderer(Renderer_); + SDL_DestroyWindow(Window_); + ScreenTexture_ = nullptr; + Renderer_ = nullptr; + Window_ = nullptr; + } +} + +void TRenderManager::Reset() { + Layer_ = Tile; + ActiveTextures_.fill(nullptr); + for (auto& queue : Commands_) { + queue.clear(); + } + + SDL_Rect a; + SDL_RenderGetViewport(Renderer_, &a); + + SDL_SetRenderDrawColor(Renderer_, 0, 0, 0, 0); + SDL_SetRenderTarget(Renderer_, NULL); + SDL_RenderClear(Renderer_); + + SDL_SetRenderDrawColor(Renderer_, 0, 0, 0, 0); + SDL_SetRenderTarget(Renderer_, ScreenTexture_); + SDL_RenderClear(Renderer_); + + if (IsLightEnabled()) { + SDL_SetRenderDrawColor(Renderer_, DefaultLightColor_.Red, DefaultLightColor_.Green, DefaultLightColor_.Blue, DefaultLightColor_.Alpha); + SDL_SetRenderTarget(Renderer_, LightTexture_); + SDL_RenderClear(Renderer_); + } +} + +} // namespace NGame \ No newline at end of file diff --git a/src/RenderManager.h b/src/RenderManager.h new file mode 100644 index 0000000..7b2a4af --- /dev/null +++ b/src/RenderManager.h @@ -0,0 +1,119 @@ +#pragma once + +#include "Common.h" + +#include "LRU.h" +#include "SurfaceManager.h" + +#include +#include +#include +#include +#include +#include + +namespace NGame { + +class TRenderManager { +public: + enum ELayer { + Tile, + Background, + BackgroundGlow, + Foreground, + ForegroundGlow, + Effects, + EffectsGlow, + Light, + Interface, + SentinelMax + }; + + struct TColorCommand { + std::uint8_t Red; + std::uint8_t Green; + std::uint8_t Blue; + std::uint8_t Alpha; + }; + + struct TRectCommand { + Vec2i Position; + Vec2i Size; + bool ShouldFill; + }; + + struct TLineCommand { + Vec2i Start; + Vec2i End; + }; + + struct TTextureCommand { + std::shared_ptr Texture; + }; + + struct TSpriteCommand { + Vec2i TexturePosition; + Vec2i TextureSize; + Vec2i TargetPosition; + Vec2i TargetSize; + SDL_RendererFlip Flip; + }; + + struct TGeometryCommand { + const SDL_Vertex* Vertices; + std::size_t VerticesCount; + const int* Indices; + std::size_t IndicesCount; + }; + + using TCommand = std::variant; + + TRenderManager(TSurfaceManager& surfaceManager, int width = 640, int height = 480, const std::string& title = "Game", std::size_t cacheSize = 4); + DELETE_COPY(TRenderManager) + + std::shared_ptr Get(const std::string& path); + void SetLayer(ELayer layer); + void SetColor(std::uint8_t red, std::uint8_t green, std::uint8_t blue, std::uint8_t alpha = 255); + void SetTexture(const std::shared_ptr& texture); + void DrawRect(const Vec2i& position, const Vec2i& size, bool filled = false); + void DrawLine(const Vec2i& start, const Vec2i& end); + void DrawSprite(const Vec2i& texturePosition, const Vec2i& textureSize, const Vec2i& targetPosition, const Vec2i& targetSize, SDL_RendererFlip flip = SDL_FLIP_NONE); + void DrawGeometry(const SDL_Vertex* vertices, std::size_t verticesCount, const int* indices, std::size_t indicesCount); + + void SetCamera(const Vec2i& position); + const Vec2i& Camera() const; + const Vec2i& Size() const; + + bool IsLightEnabled() const; + void EnableLight(bool value); + void SetDefaultLightColor(std::uint8_t red, std::uint8_t green, std::uint8_t blue); + std::uint8_t DefaultLightRed() const; + std::uint8_t DefaultLightGreen() const; + std::uint8_t DefaultLightBlue() const; + + void Run(); + bool Input(SDL_Event* event); + bool IsRunning(); + void Shutdown(); + +private: + void Reset(); + +private: + SDL_Window* Window_; + SDL_Renderer* Renderer_; + TSurfaceManager& SurfaceManager_; + TLRU> Cache_; + std::array, SentinelMax> Commands_; + std::array, SentinelMax> ActiveTextures_; + SDL_Texture* ScreenTexture_; + SDL_Texture* LightTexture_; + TColorCommand DefaultLightColor_; + bool LightEnabled_ = false; + ELayer Layer_; + Vec2i Camera_; + Vec2i Size_; + std::vector VerticesBuffer_; +}; + +} // namespace NGame \ No newline at end of file diff --git a/src/RoomEntity.cpp b/src/RoomEntity.cpp new file mode 100644 index 0000000..4d13477 --- /dev/null +++ b/src/RoomEntity.cpp @@ -0,0 +1,897 @@ +#include "RoomEntity.h" +#include +#include +#include +#include +#include + +#define ROOM_WIDTH 6 +#define ROOM_HEIGHT 6 + +TDirtEntity::TDirtEntity(NGame::TEntity::TId id) + : NGame::TEntity(id) { + SetCollisionGroup(TERRAIN_GROUP); + SetSize({16, 16}); + Sprite_ = NGame::TApp::Instance()->SpriteManager().Get("Sprites/Wall.txt"); +} + +void TDirtEntity::Update(std::uint32_t delta) { + +} + +void TDirtEntity::Draw() const { + NGame::TApp::Instance()->RenderManager().SetLayer(NGame::TRenderManager::Background); + NGame::TApp::Instance()->SpriteManager().Draw(Sprite_, 1, Position()); +} + +TGrassEntity::TGrassEntity(NGame::TEntity::TId id) + : NGame::TEntity(id) { + SetCollisionGroup(TERRAIN_GROUP); + SetSize({16, 16}); + Sprite_ = NGame::TApp::Instance()->SpriteManager().Get("Sprites/Wall.txt"); +} + +void TGrassEntity::Update(std::uint32_t delta) { + +} + +void TGrassEntity::Draw() const { + NGame::TApp::Instance()->RenderManager().SetLayer(NGame::TRenderManager::Background); + NGame::TApp::Instance()->SpriteManager().Draw(Sprite_, 2, Position()); +} + +TPlankEntity::TPlankEntity(NGame::TEntity::TId id) + : NGame::TEntity(id) { + SetCollisionGroup(TERRAIN_GROUP); + SetSize({16, 2}); + Sprite_ = NGame::TApp::Instance()->SpriteManager().Get("Sprites/Plank.txt"); +} + +void TPlankEntity::Update(std::uint32_t delta) { + +} + +void TPlankEntity::Draw() const { + NGame::TApp::Instance()->RenderManager().SetLayer(NGame::TRenderManager::Background); + NGame::TApp::Instance()->SpriteManager().Draw(Sprite_, 0, Position()); +} + +TStoneEntity::TStoneEntity(NGame::TEntity::TId id) + : NGame::TEntity(id) { + SetCollisionGroup(TERRAIN_GROUP); + SetSize({16, 16}); + Sprite_ = NGame::TApp::Instance()->SpriteManager().Get("Sprites/Wall.txt"); +} + +void TStoneEntity::Update(std::uint32_t delta) { + +} + +void TStoneEntity::Draw() const { + NGame::TApp::Instance()->RenderManager().SetLayer(NGame::TRenderManager::Background); + NGame::TApp::Instance()->SpriteManager().Draw(Sprite_, 0, Position()); +} + +TLadderEntity::TLadderEntity(NGame::TEntity::TId id) + : NGame::TEntity(id) { + SetCollisionGroup(LADDER_GROUP); + SetSize({16, 16}); + Sprite_ = NGame::TApp::Instance()->SpriteManager().Get("Sprites/Ladder.txt"); +} + +void TLadderEntity::Update(std::uint32_t delta) { + +} + +void TLadderEntity::Draw() const { + NGame::TApp::Instance()->RenderManager().SetLayer(NGame::TRenderManager::Background); + NGame::TApp::Instance()->SpriteManager().Draw(Sprite_, 0, Position()); + + NGame::TApp::Instance()->RenderManager().SetLayer(NGame::TRenderManager::Light); + auto light = NGame::TApp::Instance()->SpriteManager().Get("Sprites/Light.txt"); + NGame::TApp::Instance()->SpriteManager().Draw(light, 0, Position() - (light->Frames[0].Size * 1 / 2), {1, 1}); +} + +TExplosionEntity::TExplosionEntity(NGame::TEntity::TId id) + : NGame::TEntity(id) { + SetCollisionGroup(DAMAGE_GROUP); + SetSize({16, 16}); + Sprite_ = NGame::TApp::Instance()->SpriteManager().Get("Sprites/Wall.txt"); +} + +void TExplosionEntity::Update(std::uint32_t delta) { + +} + +void TExplosionEntity::Draw() const { + NGame::TApp::Instance()->RenderManager().SetLayer(NGame::TRenderManager::Background); + NGame::TApp::Instance()->SpriteManager().Draw(Sprite_, 0, Position()); +} + +void TExplosionEntity::Alarm(NGame::TAlarm::TId id) { + +} + +TKeyEntity::TKeyEntity(NGame::TEntity::TId id) + : NGame::TEntity(id) { + SetCollisionGroup(ITEM_GROUP); + SetSize({16, 16}); + Sprite_ = NGame::TApp::Instance()->SpriteManager().Get("Sprites/Item.txt"); +} + +void TKeyEntity::Update(std::uint32_t delta) { + +} + +void TKeyEntity::Draw() const { + NGame::TApp::Instance()->RenderManager().SetLayer(NGame::TRenderManager::Background); + NGame::TApp::Instance()->SpriteManager().Draw(Sprite_, 0, Position()); +} + +TMineEntity::TMineEntity(NGame::TEntity::TId id) + : NGame::TEntity(id) { + SetSize({16, 16}); + Sprite_ = NGame::TApp::Instance()->SpriteManager().Get("Sprites/Mine.txt"); +} + +void TMineEntity::Update(std::uint32_t delta) { + +} + +void TMineEntity::Draw() const { + NGame::TApp::Instance()->RenderManager().SetLayer(NGame::TRenderManager::Background); + NGame::TApp::Instance()->SpriteManager().Draw(Sprite_, 0, Position()); +} + +void TMineEntity::Alarm(NGame::TAlarm::TId id) { + +} + +TCurseEntity::TCurseEntity(NGame::TEntity::TId id) + : NGame::TEntity(id) { + SetCollisionGroup(ITEM_GROUP); + SetSize({16, 16}); + Sprite_ = NGame::TApp::Instance()->SpriteManager().Get("Sprites/Item.txt"); +} + +void TCurseEntity::Update(std::uint32_t delta) { + +} + +void TCurseEntity::Draw() const { + NGame::TApp::Instance()->RenderManager().SetLayer(NGame::TRenderManager::Background); + NGame::TApp::Instance()->SpriteManager().Draw(Sprite_, 0, Position()); +} + +TSpikeEntity::TSpikeEntity(NGame::TEntity::TId id) + : NGame::TEntity(id) { + SetCollisionGroup(DAMAGE_GROUP); + SetSize({16, 16}); + Sprite_ = NGame::TApp::Instance()->SpriteManager().Get("Sprites/Spike.txt"); +} + +void TSpikeEntity::Update(std::uint32_t delta) { + +} + +void TSpikeEntity::Draw() const { + NGame::TApp::Instance()->RenderManager().SetLayer(NGame::TRenderManager::Background); + NGame::TApp::Instance()->SpriteManager().Draw(Sprite_, 0, Position()); +} + +TEntranceEntity::TEntranceEntity(NGame::TEntity::TId id) + : NGame::TEntity(id) { + SetCollisionGroup(PASSAGE_GROUP); + SetSize({16, 16}); + Sprite_ = NGame::TApp::Instance()->SpriteManager().Get("Sprites/Passage.txt"); +} + +void TEntranceEntity::Update(std::uint32_t delta) { + +} + +void TEntranceEntity::Draw() const { + NGame::TApp::Instance()->RenderManager().SetLayer(NGame::TRenderManager::Background); + NGame::TApp::Instance()->SpriteManager().Draw(Sprite_, 0, Position()); +} + +TExitEntity::TExitEntity(NGame::TEntity::TId id) + : NGame::TEntity(id) { + SetCollisionGroup(PASSAGE_GROUP); + SetSize({16, 16}); + Sprite_ = NGame::TApp::Instance()->SpriteManager().Get("Sprites/Passage.txt"); +} + +void TExitEntity::Update(std::uint32_t delta) { + +} + +void TExitEntity::Draw() const { + NGame::TApp::Instance()->RenderManager().SetLayer(NGame::TRenderManager::Background); + NGame::TApp::Instance()->SpriteManager().Draw(Sprite_, 0, Position()); +} + +TFloatingTextEntity::TFloatingTextEntity(NGame::TEntity::TId id) + : NGame::TEntity(id) { + Alarm_.Set(0, 1000); +} + +void TFloatingTextEntity::SetText(const std::string& text) { + Text_ = text; +} + +void TFloatingTextEntity::SetColor(NGame::TFontManager::EColor color) { + Color_ = color; +} + +void TFloatingTextEntity::Update(std::uint32_t delta) { + SetPosition({Position().X, Position().Y - 1}); +} + +void TFloatingTextEntity::Draw() const { + auto& renderManager = NGame::TApp::Instance()->RenderManager(); + auto& fontManager = NGame::TApp::Instance()->FontManager(); + + renderManager.SetLayer(NGame::TRenderManager::Effects); + fontManager.Draw(Color_, Position(), Text_); +} + +void TFloatingTextEntity::Alarm(NGame::TAlarm::TId id) { + Remove(); +} + +TRoomEntity::TRoomEntity(NGame::TEntity::TId id) + : NGame::TEntity(id) { + LoadTemplates(); + GenerateLayout(); + Remove(); +} + +void TRoomEntity::LoadTemplates() { + auto& fileManager = NGame::TApp::Instance()->FileManager(); + ParseRooms(fileManager.Get("Templates/LR.txt"), LRRooms_); + ParseRooms(fileManager.Get("Templates/LRD.txt"), LRDRooms_); + ParseRooms(fileManager.Get("Templates/LRU.txt"), LRURooms_); + ParseRooms(fileManager.Get("Templates/LRUD.txt"), LRUDRooms_); + ParseRooms(fileManager.Get("Templates/Any.txt"), AnyRooms_); +} + +void TRoomEntity::ParseRooms(const std::string& data, std::vector& rooms) { + std::istringstream inputStream(data); + + TRoom currentRoom; + while (ParseRoom(inputStream, currentRoom)) { + rooms.push_back(currentRoom); + } +} + +bool TRoomEntity::ParseRoom(std::istringstream& stream, TRoom& room) { + for (std::string line; std::getline(stream, line); ) { + auto action = NGame::Trim(std::string_view(line)); + + if (action == "Room") { + std::string rows[6]; + std::getline(stream, rows[0]); + std::getline(stream, rows[1]); + std::getline(stream, rows[2]); + std::getline(stream, rows[3]); + std::getline(stream, rows[4]); + std::getline(stream, rows[5]); + + std::memset(room.data, Empty, sizeof(room)); + FillRow(rows[0], 0, room); + FillRow(rows[1], 1, room); + FillRow(rows[2], 2, room); + FillRow(rows[3], 3, room); + FillRow(rows[4], 4, room); + FillRow(rows[5], 5, room); + + return true; + } + } + + return false; +} + +void TRoomEntity::FillRow(const std::string& data, std::size_t row, TRoom& room) { + for (std::size_t i = 0; i < 10 || i < data.size(); i++) { + switch (data[i]) { + case 'g': room.data[row * 10 + i] = Dirt; break; + case '1': case '2': + case '3': room.data[row * 10 + i] = LowDirt; break; + case '4': case '5': + case '6': room.data[row * 10 + i] = MidDirt; break; + case '7': case '8': + case '9': room.data[row * 10 + i] = HighDirt; break; + case 'G': room.data[row * 10 + i] = Grass; break; + case 'l': room.data[row * 10 + i] = Ladder; break; + case 'd': room.data[row * 10 + i] = Mine; break; + case 'D': room.data[row * 10 + i] = Spike; break; + case 'p': room.data[row * 10 + i] = Passage; break; + case 'P': room.data[row * 10 + i] = Plank; break; + case 'c': room.data[row * 10 + i] = Curse; break; + case 'b': room.data[row * 10 + i] = Baddy; break; + case 'u': room.data[row * 10 + i] = Utility; break; + default: break; + } + } +} + +bool TRoomEntity::GoLeft(int* maze, NGame::Vec2i& position) { + if (position.X > 0 && maze[ROOM_WIDTH * position.Y + position.X - 1] != 0) { + return true; + } + + if (position.X <= 0) { + maze[ROOM_WIDTH * position.Y + position.X] |= Down; + if (!GoDown(maze, position)) { + return false; + } + return GoRight(maze, position); + } else { + position.X--; + if (maze[ROOM_WIDTH * position.Y + position.X]) { + position.X++; + } else { + maze[ROOM_WIDTH * position.Y + position.X] = LeftRight; + } + } + + return true; +} + +bool TRoomEntity::GoRight(int* maze, NGame::Vec2i& position) { + if (position.X + 1 >= ROOM_WIDTH) { + maze[ROOM_WIDTH * position.Y + position.X] |= Down; + if (!GoDown(maze, position)) { + return false; + } + return GoLeft(maze, position); + } else { + position.X++; + if (maze[ROOM_WIDTH * position.Y + position.X]) { + position.X--; + } else { + maze[ROOM_WIDTH * position.Y + position.X] = LeftRight; + } + } + + return true; +} + +bool TRoomEntity::GoDown(int* maze, NGame::Vec2i& position) { + if (position.Y + 1 >= ROOM_HEIGHT) { + maze[ROOM_WIDTH * position.Y + position.X] |= Exit; + return false; + } else { + maze[ROOM_WIDTH * position.Y + position.X] |= Down; + position.Y++; + maze[ROOM_WIDTH * position.Y + position.X] = LeftRightUp; + } + + return true; +} + +void TRoomEntity::GenerateLayout() { + int maze[ROOM_HEIGHT * ROOM_WIDTH]; + + NGame::Vec2i position = {0, 0}; + std::memset(maze, None, sizeof(maze)); + + position.X = rand() % ROOM_WIDTH; + maze[ROOM_WIDTH * position.Y + position.X] = LeftRight | Start; + + // Generate rooms + while (true) { + int choice = rand() % 100; + + if (choice < 33) { + if (!GoLeft(maze, position)) { + break; + } + } else if (choice < 66) { + if (!GoRight(maze, position)) { + break; + } + } else { + if (!GoDown(maze, position)) { + break; + } + } + } + + for (int i = 0; i < ROOM_HEIGHT; i++) { + for (int j = 0; j < ROOM_WIDTH; j++) { + auto item = maze[i * ROOM_WIDTH + j]; + GenerateRoom(maze, NGame::Vec2i(j, i)); + } + } +} + +void TRoomEntity::GenerateRoom(int* maze, const NGame::Vec2i& position) { + + auto roomType = maze[ROOM_WIDTH * position.Y + position.X]; + TRoom sourceRoom; + int randomRoomChance = rand() % 100; + + SelectRoom(roomType & LeftRightUpDown, sourceRoom); + for (int i = 0; i < 6; i++) { + for (int j = 0; j < 10; j++) { + auto tile = sourceRoom.data[i * 10 + j]; + NGame::Vec2i entityPosition = {j * 16, i * 16}; + entityPosition += NGame::Vec2i(position.X * 160, position.Y * 96); + std::shared_ptr entity; + + switch (tile) { + default: + case Empty: + break; + + case LowDirt: + if (rand() % 100 < 25) { + entity = NGame::TApp::Instance()->EntityManager().MakeEntityByName("DirtEntity"); + } + break; + + case MidDirt: + if (rand() % 100 < 50) { + entity = NGame::TApp::Instance()->EntityManager().MakeEntityByName("DirtEntity"); + } + break; + + case HighDirt: + if (rand() % 100 < 75) { + entity = NGame::TApp::Instance()->EntityManager().MakeEntityByName("DirtEntity"); + } + break; + + case Dirt: + entity = NGame::TApp::Instance()->EntityManager().MakeEntityByName("DirtEntity"); + break; + + case Grass: + entity = NGame::TApp::Instance()->EntityManager().MakeEntityByName("GrassEntity"); + break; + + case Ladder: + entity = NGame::TApp::Instance()->EntityManager().MakeEntityByName("LadderEntity"); + break; + + case Plank: + entity = NGame::TApp::Instance()->EntityManager().MakeEntityByName("PlankEntity"); + break; + + case Mine: + if (NGame::TApp::Instance()->State().Variable("MoreMines").Bool()) { + if (rand() % 100 < 50) { + entity = NGame::TApp::Instance()->EntityManager().MakeEntityByName("MineEntity"); + } + } else { + if (rand() % 100 < 25) { + entity = NGame::TApp::Instance()->EntityManager().MakeEntityByName("MineEntity"); + } + } + break; + + case Passage: + entity = NGame::TApp::Instance()->EntityManager().MakeEntityByName("EntranceEntity"); + break; + + case Curse: + if (rand() % 100 < 25) { + entity = NGame::TApp::Instance()->EntityManager().MakeEntityByName("CurseEntity"); + } + break; + + case Spike: + if (rand() % 100 < 75) { + entity = NGame::TApp::Instance()->EntityManager().MakeEntityByName("SpikeEntity"); + } + break; + } + + if (!entity) { + continue; + } + + entity->SetPosition(entityPosition); + NGame::TApp::Instance()->EntityManager().UpdateCollision(entity); + } + } +} + +void TRoomEntity::SelectRoom(int type, TRoom& room) { + std::vector selection; + + if (type == LeftRight) { + selection.insert(selection.end(), LRRooms_.begin(), LRRooms_.end()); + selection.insert(selection.end(), LRDRooms_.begin(), LRDRooms_.end()); + selection.insert(selection.end(), LRURooms_.begin(), LRURooms_.end()); + selection.insert(selection.end(), LRUDRooms_.begin(), LRUDRooms_.end()); + room = selection[rand() % selection.size()]; + } else if (type == LeftRightUp) { + selection.insert(selection.end(), LRURooms_.begin(), LRURooms_.end()); + selection.insert(selection.end(), LRUDRooms_.begin(), LRUDRooms_.end()); + room = selection[rand() % selection.size()]; + } else if (type == LeftRightDown) { + selection.insert(selection.end(), LRDRooms_.begin(), LRDRooms_.end()); + selection.insert(selection.end(), LRUDRooms_.begin(), LRUDRooms_.end()); + room = selection[rand() % selection.size()]; + } else if (type == LeftRightUpDown) { + room = LRUDRooms_[rand() % LRUDRooms_.size()]; + } else { + selection.insert(selection.end(), AnyRooms_.begin(), AnyRooms_.end()); + selection.insert(selection.end(), LRRooms_.begin(), LRRooms_.end()); + selection.insert(selection.end(), LRDRooms_.begin(), LRDRooms_.end()); + selection.insert(selection.end(), LRURooms_.begin(), LRURooms_.end()); + selection.insert(selection.end(), LRUDRooms_.begin(), LRUDRooms_.end()); + room = selection[rand() % selection.size()]; + } +} + + +THero::THero(NGame::TEntity::TId id) + : NGame::TEntity(id) { + SetSize(NGame::Vec2i(4, 12)); + SetPosition(NGame::Vec2i(0, 0)); + NGame::TApp::Instance()->RenderManager().EnableLight(true); + NGame::TApp::Instance()->RenderManager().SetDefaultLightColor(0, 0, 0); + + Alarm_.Set(0, 100); +} + +void THero::Input(SDL_Event* event) { + switch (event->type) { + case SDL_KEYDOWN: + if (event->key.keysym.sym == SDLK_UP || event->key.keysym.sym == 'w') { + KeysHeld_[static_cast(EKeys::Up)] = true; + KeysPressed_[static_cast(EKeys::Up)] = true; + } + if (event->key.keysym.sym == SDLK_DOWN || event->key.keysym.sym == 's') { + KeysHeld_[static_cast(EKeys::Down)] = true; + KeysPressed_[static_cast(EKeys::Down)] = true; + } + if (event->key.keysym.sym == SDLK_LEFT || event->key.keysym.sym == 'a') { + KeysHeld_[static_cast(EKeys::Left)] = true; + KeysPressed_[static_cast(EKeys::Left)] = true; + } + if (event->key.keysym.sym == SDLK_RIGHT || event->key.keysym.sym == 'd') { + KeysHeld_[static_cast(EKeys::Right)] = true; + KeysPressed_[static_cast(EKeys::Right)] = true; + } + if (event->key.keysym.sym == 'z') { + KeysHeld_[static_cast(EKeys::Z)] = true; + KeysPressed_[static_cast(EKeys::Z)] = true; + } + if (event->key.keysym.sym == 'x') { + KeysHeld_[static_cast(EKeys::X)] = true; + KeysPressed_[static_cast(EKeys::X)] = true; + } + if (event->key.keysym.sym == 'c') { + KeysHeld_[static_cast(EKeys::C)] = true; + KeysPressed_[static_cast(EKeys::C)] = true; + } + if (event->key.keysym.sym == SDLK_LSHIFT) { + KeysHeld_[static_cast(EKeys::Shift)] = true; + KeysPressed_[static_cast(EKeys::Shift)] = true; + } + if (event->key.keysym.sym == SDLK_ESCAPE) { + KeysHeld_[static_cast(EKeys::Escape)] = true; + KeysPressed_[static_cast(EKeys::Escape)] = true; + } + break; + + case SDL_KEYUP: + if (event->key.keysym.sym == SDLK_UP || event->key.keysym.sym == 'w') { + KeysHeld_[static_cast(EKeys::Up)] = false; + } + if (event->key.keysym.sym == SDLK_DOWN || event->key.keysym.sym == 's') { + KeysHeld_[static_cast(EKeys::Down)] = false; + } + if (event->key.keysym.sym == SDLK_LEFT || event->key.keysym.sym == 'a') { + KeysHeld_[static_cast(EKeys::Left)] = false; + } + if (event->key.keysym.sym == SDLK_RIGHT || event->key.keysym.sym == 'd') { + KeysHeld_[static_cast(EKeys::Right)] = false; + } + if (event->key.keysym.sym == 'z') { + KeysHeld_[static_cast(EKeys::Z)] = false; + } + if (event->key.keysym.sym == 'x') { + KeysHeld_[static_cast(EKeys::X)] = false; + } + if (event->key.keysym.sym == 'c') { + KeysHeld_[static_cast(EKeys::C)] = false; + } + if (event->key.keysym.sym == SDLK_LSHIFT) { + KeysHeld_[static_cast(EKeys::Shift)] = false; + } + if (event->key.keysym.sym == SDLK_ESCAPE) { + KeysHeld_[static_cast(EKeys::Escape)] = false; + } + break; + } +} + + + +void THero::Update(std::uint32_t delta) { + auto app = NGame::TApp::Instance(); + + for (bool repeatState = true; repeatState; ) { + repeatState = false; + + switch (State_) { + case EState::Normal: + Speed_.X = 0; + Speed_.Y += MovementPerTick(delta, Gravity_); + + if (KeysHeld_[static_cast(EKeys::Right)]) { + Speed_.X = 100; + } + if (KeysHeld_[static_cast(EKeys::Left)]) { + Speed_.X = -100; + } + if (KeysPressed_[static_cast(EKeys::X)]) { + auto entity = app->EntityManager().MakeEntityByName("FloatingTextEntity"); + auto targetEntity = dynamic_cast(entity.get()); + targetEntity->SetText("You died, LMAO"); + targetEntity->SetColor(NGame::TFontManager::Red); + targetEntity->SetPosition(Position()); + } + + if (!app->EntityManager().IsPlaceEmpty(Position(), Size(), LADDER_GROUP)) { + if (KeysHeld_[static_cast(EKeys::Up)] || KeysHeld_[static_cast(EKeys::Down)]) { + State_ = EState::Climb; + continue; + } + } + if (!app->EntityManager().IsPlaceEmpty(Position() + NGame::Vec2i(-2, Size().Y), NGame::Vec2i(Size().X + 4, 1), TERRAIN_GROUP, Id())) { + if (KeysHeld_[static_cast(EKeys::Down)]) { + if (KeysPressed_[static_cast(EKeys::Z)]) { + IgnorePlanks_ = true; + Speed_.Y = 80; + State_ = EState::Fall; + continue; + } + } else { + if (KeysPressed_[static_cast(EKeys::Z)]) { + Speed_.Y = -std::sqrt(2 * Gravity_ * (16 + (16 - Size().Y))); + State_ = EState::Jump; + continue; + } + } + } + if (MovementPerTick(delta, Speed_.Y) >= 1.0) { + State_ = EState::Fall; + continue; + } + + break; + + case EState::Jump: + Speed_.X = 0; + Speed_.Y += MovementPerTick(delta, Gravity_); + + if (!app->EntityManager().IsPlaceEmpty(Position(), Size(), LADDER_GROUP)) { + if (KeysHeld_[static_cast(EKeys::Up)] || KeysHeld_[static_cast(EKeys::Down)]) { + State_ = EState::Climb; + continue; + } + } + if (KeysHeld_[static_cast(EKeys::Right)]) { + Speed_.X = 100; + } + if (KeysHeld_[static_cast(EKeys::Left)]) { + Speed_.X = -100; + } + if (Speed_.Y > 0) { + State_ = EState::Fall; + continue; + } + break; + + case EState::Fall: + Speed_.X = 0; + Speed_.Y += MovementPerTick(delta, Gravity_); + + if (!app->EntityManager().IsPlaceEmpty(Position(), Size(), LADDER_GROUP)) { + if (KeysHeld_[static_cast(EKeys::Up)] || KeysHeld_[static_cast(EKeys::Down)]) { + State_ = EState::Climb; + continue; + } + } + if (KeysHeld_[static_cast(EKeys::Right)]) { + Speed_.X = 100; + auto collisonIds = app->EntityManager().CollisionList(Position() + NGame::Vec2i(Size().X, 0), NGame::Vec2i(Size().X, 2), TERRAIN_GROUP, Id()); + auto containsWall = std::any_of(collisonIds.begin(), collisonIds.end(), [&](TEntity::TId id) { + auto other = app->EntityManager().Entity(id); + if (dynamic_cast(other.get())) { + return false; + } + return true; + }); + + if (containsWall) { + if (app->EntityManager().IsPlaceEmpty(Position() + NGame::Vec2i(0, -Size().Y), NGame::Vec2i(Size().X * 2, Size().Y), TERRAIN_GROUP, Id())) { + State_ = EState::Hold; + continue; + } + } + } + if (KeysHeld_[static_cast(EKeys::Left)]) { + Speed_.X = -100; + auto collisonIds = app->EntityManager().CollisionList(Position() - NGame::Vec2i(Size().X, 0), NGame::Vec2i(Size().X, 2), TERRAIN_GROUP, Id()); + auto containsWall = std::any_of(collisonIds.begin(), collisonIds.end(), [&](TEntity::TId id) { + auto other = app->EntityManager().Entity(id); + if (dynamic_cast(other.get())) { + return false; + } + return true; + }); + + if (containsWall) { + if (app->EntityManager().IsPlaceEmpty(Position() - NGame::Vec2i(Size().X, Size().Y), NGame::Vec2i(Size().X * 2, Size().Y), TERRAIN_GROUP, Id())) { + State_ = EState::Hold; + continue; + } + } + } + if (!app->EntityManager().IsPlaceEmpty(Position() + NGame::Vec2i(0, Size().Y), NGame::Vec2i(Size().X, 1), TERRAIN_GROUP, Id())) { + State_ = EState::Normal; + continue; + } + break; + + case EState::Climb: + if (app->EntityManager().IsPlaceEmpty(Position(), Size(), LADDER_GROUP)) { + State_ = EState::Normal; + repeatState = true; + continue; + } + + Speed_ = {0, 0}; + if (KeysHeld_[static_cast(EKeys::Right)]) { + Speed_.X = 100; + } + if (KeysHeld_[static_cast(EKeys::Left)]) { + Speed_.X = -100; + } + if (KeysHeld_[static_cast(EKeys::Up)]) { + Speed_.Y = -100; + } + if (KeysHeld_[static_cast(EKeys::Down)]) { + Speed_.Y = 100; + } + break; + + case EState::Hold: + Speed_ = 0; + if (KeysPressed_[static_cast(EKeys::Down)]) { + State_ = EState::Fall; + continue; + } + if (KeysPressed_[static_cast(EKeys::Z)]) { + Speed_.Y = -std::sqrt(2 * Gravity_ * (16 + (16 - Size().Y))); + State_ = EState::Jump; + continue; + } + break; + } + } + + // Check if we need to flip sprite + if (Speed_.X < 0) { + FaceLeft_ = 1; + } else if (Speed_.X > 0) { + FaceLeft_ = 0; + + } + // Perform movement and collision + NGame::Vec2f resultSpeed = MovementPerTick(delta, Speed_); + auto moveResult = MoveWithCondition(resultSpeed, Fraction_, [&](const NGame::Vec2i& position) { + auto collisionList = app->EntityManager().CollisionList(position, Size(), TERRAIN_GROUP, Id()); + if (collisionList.empty()) { + return true; + } + + // Check collision for planks + for (auto& otherId : collisionList) { + auto other = app->EntityManager().Entity(otherId); + if (dynamic_cast(other.get())) { + // Are we ignoring planks, because we are dropping down? + if (IgnorePlanks_) { + continue; + } + + // Ignore planks if we moving up + if (Speed_.Y < 0) { + continue; + } + + // Ignore planks, that are already inside of us + if (NGame::RectOverlaps(Position(), Size(), other->Position(), other->Size())) { + continue; + } + + // If all fails - we should be on top of the plank + return false; + } else { + return false; + } + } + + return true; + }); + + Fraction_ = moveResult.first; + if (!moveResult.second.first) { + Speed_.X = 0; + } + if (!moveResult.second.second) { + Speed_.Y = 0; + } + + // Set camera to track hero + app->RenderManager().SetCamera(Position() - app->RenderManager().Size() / 2); + + // Reset pressed keys + IgnorePlanks_ = false; + std::memset(KeysPressed_, false, sizeof(KeysPressed_)); +} + +void THero::Draw() const { + auto& renderManager = NGame::TApp::Instance()->RenderManager(); + auto& spriteManager = NGame::TApp::Instance()->SpriteManager(); + + renderManager.SetLayer(NGame::TRenderManager::Foreground); + auto hero = spriteManager.Get("Sprites/Hero.txt"); + int spriteIndex = 0; + + + switch (State_) { + case EState::Normal: spriteIndex = 0; break; + case EState::Jump: spriteIndex = 2; break; + case EState::Fall: spriteIndex = 0; break; + case EState::Climb: spriteIndex = 5; break; + case EState::Hold: spriteIndex = 1; break; + } + + if (State_ == EState::Normal && Speed_.X != 0 && AlternateRun_) { + spriteIndex = 3; + } + + spriteManager.Draw(hero, spriteIndex, Position() - NGame::Vec2i((16 - Size().X) / 2, 16 - Size().Y), {(FaceLeft_?-1.0f:1.0f), 1.0f}); + + renderManager.SetLayer(NGame::TRenderManager::Light); + auto light = spriteManager.Get("Sprites/Light.txt"); + spriteManager.Draw(light, 0, Position() - (light->Frames[0].Size * 4 / 2), {4, 4}); +} + +void THero::Alarm(NGame::TAlarm::TId id) { + if (id == 0) { + AlternateRun_ = !AlternateRun_; + } +} + +TBackgroundTiler::TBackgroundTiler(NGame::TEntity::TId id) + : NGame::TEntity(id) { + Background_ = NGame::TApp::Instance()->SpriteManager().Get("Sprites/Wall.txt"); +} +void TBackgroundTiler::Draw() const { + auto& renderManager = NGame::TApp::Instance()->RenderManager(); + auto& spriteManager = NGame::TApp::Instance()->SpriteManager(); + + auto cameraPosition = renderManager.Camera(); + auto tileOffset = cameraPosition; + tileOffset.X %= 16; + tileOffset.Y %= 16; + + + renderManager.SetLayer(NGame::TRenderManager::Tile); + for (int i = -1; i <= 320 / 16; ++i) { + for (int j = -1; j <= 240 / 16; ++j) { + spriteManager.Draw(Background_, 0, NGame::Vec2i(i * 16, j * 16) + cameraPosition - tileOffset); + } + } +} \ No newline at end of file diff --git a/src/RoomEntity.h b/src/RoomEntity.h new file mode 100644 index 0000000..27eb3ad --- /dev/null +++ b/src/RoomEntity.h @@ -0,0 +1,272 @@ +#pragma once + +#include "App.h" + +#define TERRAIN_GROUP 0x01 +#define LADDER_GROUP 0x02 +#define DAMAGE_GROUP 0x04 +#define ITEM_GROUP 0x08 +#define PASSAGE_GROUP 0x10 +#define PLANK_GROUP 0x20 + +class TDirtEntity : public NGame::TEntity { +public: + TDirtEntity(NGame::TEntity::TId id); + + virtual void Update(std::uint32_t delta) override; + virtual void Draw() const override; + +private: + std::shared_ptr Sprite_; +}; + +class TGrassEntity : public NGame::TEntity { +public: + TGrassEntity(NGame::TEntity::TId id); + + virtual void Update(std::uint32_t delta) override; + virtual void Draw() const override; + +private: + std::shared_ptr Sprite_; +}; + +class TPlankEntity : public NGame::TEntity { +public: + TPlankEntity(NGame::TEntity::TId id); + + virtual void Update(std::uint32_t delta) override; + virtual void Draw() const override; + +private: + std::shared_ptr Sprite_; +}; + +class TStoneEntity : public NGame::TEntity { +public: + TStoneEntity(NGame::TEntity::TId id); + + virtual void Update(std::uint32_t delta) override; + virtual void Draw() const override; + +private: + std::shared_ptr Sprite_; +}; + +class TLadderEntity : public NGame::TEntity { +public: + TLadderEntity(NGame::TEntity::TId id); + + virtual void Update(std::uint32_t delta) override; + virtual void Draw() const override; + +private: + std::shared_ptr Sprite_; +}; + +class TExplosionEntity : public NGame::TEntity { +public: + TExplosionEntity(NGame::TEntity::TId id); + + virtual void Update(std::uint32_t delta) override; + virtual void Draw() const override; + virtual void Alarm(NGame::TAlarm::TId id) override; + +private: + std::shared_ptr Sprite_; +}; + +class TKeyEntity : public NGame::TEntity { +public: + TKeyEntity(NGame::TEntity::TId id); + + virtual void Update(std::uint32_t delta) override; + virtual void Draw() const override; + +private: + std::shared_ptr Sprite_; +}; + +class TMineEntity : public NGame::TEntity { +public: + TMineEntity(NGame::TEntity::TId id); + + virtual void Update(std::uint32_t delta) override; + virtual void Draw() const override; + virtual void Alarm(NGame::TAlarm::TId id) override; + +private: + std::shared_ptr Sprite_; +}; + +class TCurseEntity : public NGame::TEntity { +public: + TCurseEntity(NGame::TEntity::TId id); + + virtual void Update(std::uint32_t delta) override; + virtual void Draw() const override; + +private: + std::shared_ptr Sprite_; +}; + +class TSpikeEntity : public NGame::TEntity { +public: + TSpikeEntity(NGame::TEntity::TId id); + + virtual void Update(std::uint32_t delta) override; + virtual void Draw() const override; + +private: + std::shared_ptr Sprite_; +}; + +class TEntranceEntity : public NGame::TEntity { +public: + TEntranceEntity(NGame::TEntity::TId id); + + virtual void Update(std::uint32_t delta) override; + virtual void Draw() const override; + +private: + std::shared_ptr Sprite_; +}; + +class TExitEntity : public NGame::TEntity { +public: + TExitEntity(NGame::TEntity::TId id); + + virtual void Update(std::uint32_t delta) override; + virtual void Draw() const override; + +private: + std::shared_ptr Sprite_; +}; + +class TFloatingTextEntity : public NGame::TEntity { +public: + TFloatingTextEntity(NGame::TEntity::TId id); + + void SetText(const std::string& text); + void SetColor(NGame::TFontManager::EColor color); + + virtual void Update(std::uint32_t delta) override; + virtual void Draw() const override; + virtual void Alarm(NGame::TAlarm::TId id) override; + +private: + std::string Text_; + NGame::TFontManager::EColor Color_; +}; + +class TRoomEntity : public NGame::TEntity { +public: + TRoomEntity(NGame::TEntity::TId id); + +private: + enum EType { + None, + Left = 0x0001, + Right = 0x0002, + Up = 0x0004, + Down = 0x0008, + Start = 0x0010, + Exit = 0x0020, + LeftRight = Left | Right, + LeftRightDown = Left | Right | Down, + LeftRightUp = Left | Right | Up, + LeftRightUpDown = Left | Right | Up | Down, + }; + + enum ETile { + Empty, + Baddy, + Curse, + Dirt, + Grass, + HighDirt, + Ladder, + LowDirt, + MidDirt, + Mine, + Passage, + Plank, + Spike, + Utility, + }; + + struct TRoom { + ETile data[60]; + }; + + void LoadTemplates(); + void ParseRooms(const std::string& data, std::vector& rooms); + bool ParseRoom(std::istringstream& stream, TRoom& room); + void FillRow(const std::string& data, std::size_t row, TRoom& room); + bool GoLeft(int* maze, NGame::Vec2i& position); + bool GoRight(int* maze, NGame::Vec2i& position); + bool GoDown(int* maze, NGame::Vec2i& position); + void GenerateLayout(); + void GenerateRoom(int* maze, const NGame::Vec2i& position); + void SelectRoom(int type, TRoom& room); + +private: + std::vector LRRooms_; + std::vector LRURooms_; + std::vector LRDRooms_; + std::vector LRUDRooms_; + std::vector AnyRooms_; +}; + +class THero : public NGame::TEntity { +public: + THero(NGame::TEntity::TId id); + + enum class EKeys { + Left, + Right, + Up, + Down, + X, + Z, + C, + Shift, + Escape, + MaxSentinel + }; + + enum class EState { + Normal, + Jump, + Fall, + Climb, + Hold, + }; + + virtual void Input(SDL_Event* event) override; + virtual void Update(std::uint32_t delta) override; + virtual void Draw() const override; + virtual void Alarm(NGame::TAlarm::TId id) override; + +private: + bool KeysPressed_[static_cast(EKeys::MaxSentinel)] = {}; + bool KeysHeld_[static_cast(EKeys::MaxSentinel)] = {}; + bool FaceLeft_ = false; + bool IgnorePlanks_ = false; + bool AlternateRun_ = false; + EState State_ = EState::Normal; + + const float Gravity_ = 500.0; + NGame::Vec2f Speed_ = NGame::Vec2f(0.0, 0.0); + NGame::Vec2f Fraction_ = NGame::Vec2f(0.0, 0.0); + NGame::Vec2i Want_; +}; + +class TBackgroundTiler : public NGame::TEntity { +public: + TBackgroundTiler(NGame::TEntity::TId id); + virtual void Draw() const override; + +private: + std::shared_ptr Background_; +}; \ No newline at end of file diff --git a/src/SpriteManager.cpp b/src/SpriteManager.cpp new file mode 100644 index 0000000..80bc509 --- /dev/null +++ b/src/SpriteManager.cpp @@ -0,0 +1,119 @@ +#include "SpriteManager.h" + +#include +#include + +namespace NGame { + +TSpriteManager::TSpriteManager(TFileManager& fileManager, TRenderManager& renderManager, std::size_t cacheSize) + : FileManager_(fileManager), RenderManager_(renderManager), Cache_(cacheSize) { +} + +std::shared_ptr TSpriteManager::Get(const std::string& path) { + if (Cache_.Contains(path)) { + return Cache_.Get(path); + } + + auto result = std::make_shared(ParseInfoFile(path)); + Cache_.Set(path, result); + return result; +} + +void TSpriteManager::Draw(const std::shared_ptr& sprite, size_t frame, const Vec2i& position, const Vec2f& scale) { + const auto& frameData = sprite->Frames[frame]; + + int flip = 0; + Vec2i size = frameData.Size * scale; + + if (size.X < 0) { + size.X = -size.X; + flip |= SDL_FLIP_HORIZONTAL; + } + if (size.Y < 0) { + size.Y = -size.Y; + flip |= SDL_FLIP_VERTICAL; + } + + RenderManager_.SetTexture(sprite->Texture); + RenderManager_.DrawSprite(frameData.Position, frameData.Size, position, size, static_cast(flip)); +} + +TSpriteManager::TSprite TSpriteManager::ParseInfoFile(const std::string& path) { + std::string data = FileManager_.Get(path); + std::istringstream inputStream(data); + + std::shared_ptr currentTexture; + std::vector currentFrames; + + for (std::string line; std::getline(inputStream, line); ) { + auto remainder = Trim(std::string_view(line)); + + auto action = RightTrim(NextToken(remainder, " ")); + if (action == "Texture") { + auto textureString = Trim(NextToken(remainder, " ")); + if (textureString.empty()) { + throw std::runtime_error("texture name should not be empty for " + path); + } + currentTexture = RenderManager_.Get(std::string(textureString)); + } else if (action == "Frame") { + auto xString = Trim(NextToken(remainder, " ")); + auto yString = Trim(NextToken(remainder, " ")); + auto widthString = Trim(NextToken(remainder, " ")); + auto heightString = Trim(NextToken(remainder, " ")); + + + TFrame frame; + + if (xString.empty() && yString.empty()) { + frame.Position.X = 0; + frame.Position.Y = 0; + } else { + frame.Position.X = std::stoi(std::string(xString)); + frame.Position.Y = std::stoi(std::string(yString)); + } + + if (widthString.empty() && heightString.empty()) { + int width, height; + + if (SDL_QueryTexture(currentTexture.get(), NULL, NULL, &width, &height)) { + throw std::runtime_error("can't query texture information for " + path); + } + + frame.Size.X = width; + frame.Size.Y = height; + } else { + frame.Size.X = std::stoi(std::string(widthString)); + frame.Size.Y = std::stoi(std::string(heightString)); + } + + if (xString.empty() || yString.empty() || widthString.empty() || heightString.empty()) { + throw std::runtime_error("frame should have x, y, width, height parameters for " + path); + } + + currentFrames.push_back(frame); + } + } + + if (!currentTexture) { + std::runtime_error("no texture for " + path); + } + + if (currentFrames.empty()) { + TFrame frame; + int width, height; + if (SDL_QueryTexture(currentTexture.get(), NULL, NULL, &width, &height)) { + throw std::runtime_error("can't query texture information for " + path); + } + + frame.Position.X = 0; + frame.Position.Y = 0; + frame.Size.X = width; + frame.Size.Y = height; + currentFrames.push_back(frame); + } + + return {currentTexture, currentFrames}; +} + + +} // namespace NGame diff --git a/src/SpriteManager.h b/src/SpriteManager.h new file mode 100644 index 0000000..96ca002 --- /dev/null +++ b/src/SpriteManager.h @@ -0,0 +1,38 @@ +#pragma once + +#include "Common.h" + +#include "FileManager.h" +#include "LRU.h" +#include "RenderManager.h" + +namespace NGame { + +class TSpriteManager { +public: + struct TFrame { + Vec2i Position; + Vec2i Size; + }; + + struct TSprite { + std::shared_ptr Texture; + std::vector Frames; + }; + + TSpriteManager(TFileManager& fileManager, TRenderManager& renderManager, std::size_t cacheSize = 4); + DELETE_COPY(TSpriteManager) + + std::shared_ptr Get(const std::string& path); + void Draw(const std::shared_ptr& sprite, size_t frame, const Vec2i& position, const Vec2f& scale = {1.0f, 1.0f}); + +private: + TSprite ParseInfoFile(const std::string& path); + +private: + TFileManager& FileManager_; + TRenderManager& RenderManager_; + TLRU> Cache_; +}; + +} // namespace NGame \ No newline at end of file diff --git a/src/State.cpp b/src/State.cpp new file mode 100644 index 0000000..4c4e772 --- /dev/null +++ b/src/State.cpp @@ -0,0 +1,39 @@ +#include "State.h" + +#include + +namespace NGame { + +TVariable& TState::Variable(const std::string_view& name) { + auto stringName = std::string(name); + auto iterator = Variables_.find(stringName); + + if (iterator != Variables_.end()) { + return iterator->second; + } else { + return SetVariable(name, 0); + } +} + +const TVariable& TState::Variable(const std::string_view& name) const { + auto stringName = std::string(name); + auto iterator = Variables_.find(stringName); + + if (iterator != Variables_.end()) { + return iterator->second; + } + + throw std::runtime_error("Attempting to access non-existing variable"); +} + +bool TState::ContainsVariable(const std::string_view& name) const { + auto stringName = std::string(name); + auto iterator = Variables_.find(stringName); + + if (iterator != Variables_.end()) { + return true; + } + return false; +} + +} // namespace NGame diff --git a/src/State.h b/src/State.h new file mode 100644 index 0000000..74401e8 --- /dev/null +++ b/src/State.h @@ -0,0 +1,36 @@ +#pragma once + +#include "Common.h" + +#include "Variable.h" + +#include +#include +#include +#include +#include + +namespace NGame { + +class TState { +public: + TVariable& Variable(const std::string_view& name); + const TVariable& Variable(const std::string_view& name) const; + bool ContainsVariable(const std::string_view& name) const; + + template + TVariable& SetVariable(const std::string_view& name, const T& value) { + auto stringName = std::string(name); + auto result = Variables_.emplace(std::make_pair(stringName, value)); + if (result.second) { + return result.first->second; + } + + throw std::runtime_error("Failed to set variable"); + } + +private: + std::unordered_map Variables_; +}; + +} // namespace NGame \ No newline at end of file diff --git a/src/SurfaceManager.cpp b/src/SurfaceManager.cpp new file mode 100644 index 0000000..259f650 --- /dev/null +++ b/src/SurfaceManager.cpp @@ -0,0 +1,32 @@ +#include "SurfaceManager.h" + +#include +#include +#include +#include + +namespace NGame { + +TSurfaceManager::TSurfaceManager(TFileManager& fileManager, std::size_t cacheSize) + : FileManager_(fileManager), Cache_(cacheSize) { +} + +std::shared_ptr TSurfaceManager::Get(const std::string& path) { + if (Cache_.Contains(path)) { + return Cache_.Get(path); + } + + std::string data = FileManager_.Get(path); + SDL_RWops* rw = SDL_RWFromConstMem(data.data(), data.size()); + SDL_Surface* surface = SDL_LoadBMP_RW(rw, 1); + + if (!surface) { + std::runtime_error("Can't create surface from file " + path); + } + + std::shared_ptr result(surface, [](SDL_Surface* surface) { SDL_FreeSurface(surface); }); + Cache_.Set(path, result); + return result; +} + +} // namespace NGame \ No newline at end of file diff --git a/src/SurfaceManager.h b/src/SurfaceManager.h new file mode 100644 index 0000000..b1290b2 --- /dev/null +++ b/src/SurfaceManager.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Common.h" + +#include "LRU.h" +#include "FileManager.h" + +#include +#include + +namespace NGame { + +class TSurfaceManager { +public: + TSurfaceManager(TFileManager& fileManager, std::size_t cacheSize = 4); + DELETE_COPY(TSurfaceManager) + + std::shared_ptr Get(const std::string& path); + +private: + TFileManager& FileManager_; + TLRU> Cache_; +}; + +} // namespace NGame \ No newline at end of file diff --git a/src/Variable.cpp b/src/Variable.cpp new file mode 100644 index 0000000..67c4a65 --- /dev/null +++ b/src/Variable.cpp @@ -0,0 +1,83 @@ +#include "Variable.h" + +namespace NGame { + +void TVariable::SetDouble(double value) { + Data_ = value; +} + +void TVariable::SetString(const std::string& value) { + Data_ = value; +} + +void TVariable::SetInt(int value) { + Data_ = value; +} + +void TVariable::SetBool(bool value) { + Data_ = value; +} + +double TVariable::Double() const { + const auto asDouble = TOverload { + [](int value) { return double(value); }, + [](double value) { return value; }, + [](const std::string value) { + try { + return std::stod(value); + } catch (...) { + return 0.0; + } + }, + [](bool value) { return double(value); } + }; + + return std::visit(asDouble, Data_); +} + +std::string TVariable::String() const { + const auto asString = TOverload { + [](int value) { return std::to_string(value); }, + [](double value) { return std::to_string(value); }, + [](const std::string value) { return value; }, + [](bool value) { return std::to_string(value); } + }; + + return std::visit(asString, Data_); +} + +int TVariable::Int() const { + const auto asInt = TOverload { + [](int value) { return value; }, + [](double value) { return int(value); }, + [](const std::string value) { + try { + return std::stoi(value); + } catch (...) { + return 0; + } + }, + [](bool value) { return int(value); } + }; + + return std::visit(asInt, Data_); +} + +bool TVariable::Bool() const { + const auto asBool = TOverload { + [](int value) { return value != 0; }, + [](double value) { return value != 0; }, + [](const std::string value) { + std::string caseFolded = ToUpper(Trim(value)); + if (caseFolded == "TRUE") { + return true; + } + return false; + }, + [](bool value) { return value; } + }; + + return std::visit(asBool, Data_); +} + +} // namespace NGame diff --git a/src/Variable.h b/src/Variable.h new file mode 100644 index 0000000..ea0ef0c --- /dev/null +++ b/src/Variable.h @@ -0,0 +1,33 @@ +#pragma once + +#include "Common.h" + +#include +#include + +namespace NGame { + +class TVariable { +public: + TVariable() = default; + + template + TVariable(const T& value) + : Data_(value) { + } + + void SetDouble(double value); + void SetString(const std::string& value); + void SetInt(int value); + void SetBool(bool value); + + double Double() const; + std::string String() const; + int Int() const; + bool Bool() const; + +private: + std::variant Data_; +}; + +} // namespace NGame \ No newline at end of file