diff --git a/.gitattributes b/.gitattributes index bba9ac7..e8a6909 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ *.bmp filter=lfs diff=lfs merge=lfs -text *.ttf filter=lfs diff=lfs merge=lfs -text +*.psd filter=lfs diff=lfs merge=lfs -text diff --git a/2dgk_zad3/readme.txt b/2dgk_zad3/readme.txt index 2bceeec..a875c3b 100644 --- a/2dgk_zad3/readme.txt +++ b/2dgk_zad3/readme.txt @@ -4,4 +4,13 @@ Biblioteki: SDL2, SDL2_ttf, nlohmann-json Przetestowane pady: DualSense, DualShock 3 (ze sterownikiem/wrapperem XInputowym ScpToolkit), telefon Android (ze sterownikiem/wrapperem XInputowym HandyGamePad). +Sterowanie (możliwa zmiana w config.json): +AD/lewa gałka pada - ruch poziomy +Spacja/A na padzie - skok + +F1 - tryb deweloperski +F2 (w trybie deweloperskim) - wł./wył. kolizji +F3/F4 - (w trybie deweloperskim) - zwiększ/zmiejsz maksymalną wysokość skoku. +F5/F6 - (w trybie deweloperskim) - zwiększ/zmiejsz maksymalną odległość poziomą skoku + Uwaga: Do pobrania bibliotek projekt wykorzystuje vcpkg, menedżer bibliotek C++, w trybie Manifest. Należy go pobrać i skonfigurować zgodnie z instrukcją: https://github.com/microsoft/vcpkg#getting-started diff --git a/2dkg_zad5/2dgk_zad5.sln b/2dkg_zad5/2dgk_zad5.sln new file mode 100644 index 0000000..f23dcb2 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31729.503 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{423520C9-0301-4445-99D4-078B2842C600}") = "2dgk_zad5", "2dgk_zad5\2dgk_zad5.vcxproj", "{2696A0B3-E7C4-4876-9DF4-E7D3EC6C06F8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2696A0B3-E7C4-4876-9DF4-E7D3EC6C06F8}.Debug|x64.ActiveCfg = Debug|x64 + {2696A0B3-E7C4-4876-9DF4-E7D3EC6C06F8}.Debug|x64.Build.0 = Debug|x64 + {2696A0B3-E7C4-4876-9DF4-E7D3EC6C06F8}.Debug|x86.ActiveCfg = Debug|Win32 + {2696A0B3-E7C4-4876-9DF4-E7D3EC6C06F8}.Debug|x86.Build.0 = Debug|Win32 + {2696A0B3-E7C4-4876-9DF4-E7D3EC6C06F8}.Release|x64.ActiveCfg = Release|x64 + {2696A0B3-E7C4-4876-9DF4-E7D3EC6C06F8}.Release|x64.Build.0 = Release|x64 + {2696A0B3-E7C4-4876-9DF4-E7D3EC6C06F8}.Release|x86.ActiveCfg = Release|Win32 + {2696A0B3-E7C4-4876-9DF4-E7D3EC6C06F8}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2292BE73-514E-4923-9A1A-5A991B0DE0DB} + EndGlobalSection +EndGlobal diff --git a/2dkg_zad5/2dgk_zad5/2dgk_zad5.rc b/2dkg_zad5/2dgk_zad5/2dgk_zad5.rc new file mode 100644 index 0000000..4a3e13d --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/2dgk_zad5.rc @@ -0,0 +1,101 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) + +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Polish (Poland) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_PLK) +LANGUAGE LANG_POLISH, SUBLANG_DEFAULT + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "041504b0" + BEGIN + VALUE "CompanyName", "Michał Leśniak" + VALUE "FileDescription", "A simple game" + VALUE "FileVersion", "1.0.0.1" + VALUE "InternalName", "2dgk_zad5.exe" + VALUE "LegalCopyright", "Copyright (C) 2022 Michał Leśniak" + VALUE "OriginalFilename", "2dgk_zad5.exe" + VALUE "ProductName", "2DKG_ZAD5" + VALUE "ProductVersion", "1.0.0.1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x415, 1200 + END +END + +#endif // Polish (Poland) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/2dkg_zad5/2dgk_zad5/2dgk_zad5.vcxproj b/2dkg_zad5/2dgk_zad5/2dgk_zad5.vcxproj new file mode 100644 index 0000000..e68d24a --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/2dgk_zad5.vcxproj @@ -0,0 +1,252 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {2696a0b3-e7c4-4876-9df4-e7d3ec6c06f8} + My2dgk_zad5 + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + true + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + vcpkg_installed\x86-windows\x86-windows\include\SDL2 + + + Console + true + vcpkg_installed\x86-windows\x86-windows\debug\lib\manual-link;%(AdditionalLibraryDirectories) + SDL2maind.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + vcpkg_installed\x86-windows\x86-windows\include\SDL2 + + + Console + true + true + true + vcpkg_installed\x86-windows\x86-windows\lib\manual-link;%(AdditionalLibraryDirectories) + SDL2main.lib;%(AdditionalDependencies) + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + vcpkg_installed\x64-windows\x64-windows\include\SDL2 + stdcpp17 + + + Console + true + vcpkg_installed\x64-windows\x64-windows\debug\lib\manual-link;%(AdditionalLibraryDirectories) + SDL2maind.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + vcpkg_installed\x64-windows\x64-windows\include\SDL2 + stdcpp17 + + + Console + true + true + true + vcpkg_installed\x64-windows\x64-windows\lib\manual-link;%(AdditionalLibraryDirectories) + SDL2main.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + + + true + + + + + true + + + true + + + true + + + true + + + true + + + + + + + + + + + \ No newline at end of file diff --git a/2dkg_zad5/2dgk_zad5/2dgk_zad5.vcxproj.filters b/2dkg_zad5/2dgk_zad5/2dgk_zad5.vcxproj.filters new file mode 100644 index 0000000..eddf0cd --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/2dgk_zad5.vcxproj.filters @@ -0,0 +1,183 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + + + + + + + + \ No newline at end of file diff --git a/2dkg_zad5/2dgk_zad5/Constants.h b/2dkg_zad5/2dgk_zad5/Constants.h new file mode 100644 index 0000000..53c4848 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/Constants.h @@ -0,0 +1,24 @@ +#pragma once + +namespace KapitanGame::Constants +{ + constexpr const char* WINDOW_TITLE = "2DGK - Zadanie 5 (Lab 14)"; + //Analog joystick dead zone + constexpr int JOYSTICK_DEAD_ZONE = 8000; + //Screen dimension constants + constexpr int WINDOW_DEAD_ZONE = 100; + constexpr int SCREEN_WIDTH = 640; + constexpr int SCREEN_HEIGHT = 480; + constexpr float SCREEN_RATIO = static_cast(SCREEN_WIDTH) / SCREEN_HEIGHT; + constexpr int TILE_WIDTH = 32; + constexpr int TILE_HEIGHT = 32; + constexpr int TILE_COUNT = 8; + constexpr int BG_LAYER_COUNT = 3; + constexpr int FG_LAYER_COUNT = 1; + constexpr float SPEED = 200.f; + constexpr float SMOOTH = 0.4f; + constexpr float JUMP_SPEED = 300.f; + constexpr float HORIZONTAL_DISTANCE_TO_MAX_JUMP_HEIGHT = TILE_WIDTH * 4.f; + constexpr float MAX_JUMP_HEIGHT = TILE_HEIGHT * 4.f; + constexpr float MAX_GRAVITY = 1000.f; +} diff --git a/2dkg_zad5/2dgk_zad5/Controllers.h b/2dkg_zad5/2dgk_zad5/Controllers.h new file mode 100644 index 0000000..6df735f --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/Controllers.h @@ -0,0 +1,9 @@ +#pragma once +namespace KapitanGame { + enum class Controllers : int { + Controller1, + Controller2, + Controller3, + Controller4 + }; +} diff --git a/2dkg_zad5/2dgk_zad5/GamePad.h b/2dkg_zad5/2dgk_zad5/GamePad.h new file mode 100644 index 0000000..4d8990f --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/GamePad.h @@ -0,0 +1,8 @@ +#pragma once +#include +namespace KapitanGame { + struct GamePad { + bool Buttons[SDL_CONTROLLER_BUTTON_MAX]; + int Axis[SDL_CONTROLLER_AXIS_MAX]; + }; +} diff --git a/2dkg_zad5/2dgk_zad5/KActionBind.h b/2dkg_zad5/2dgk_zad5/KActionBind.h new file mode 100644 index 0000000..f2cb2de --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KActionBind.h @@ -0,0 +1,31 @@ +#pragma once +#include "KPlayer.h" +#include "KPlayerController.h" + +namespace KapitanGame { + ; + enum class InputState : int { + Pressed, + Released, + Hold + }; + struct KActionBind { + KActionBind(std::string name, const InputState expectedInputState, const std::shared_ptr& controllerObject, const KPlayerCommand command, const KPlayer player) : + Name(std::move(name)), ExpectedInputState(expectedInputState), ControllerObject(controllerObject), Command(command), Player(player) {} + std::string Name; + InputState ExpectedInputState; + std::weak_ptr ControllerObject; + KPlayerCommand Command; + KPlayer Player; + }; + struct KAxisBind + { + KAxisBind(std::string name, const std::shared_ptr& controllerObject, const KPlayerAxisCommand command, const KPlayer player) : + Name(std::move(name)), ControllerObject(controllerObject), AxisCommand(command), Player(player) {} + std::string Name; + std::weak_ptr ControllerObject; + KPlayerAxisCommand AxisCommand; + KPlayer Player; + }; +} + diff --git a/2dkg_zad5/2dgk_zad5/KCamera.cpp b/2dkg_zad5/2dgk_zad5/KCamera.cpp new file mode 100644 index 0000000..3d719c6 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KCamera.cpp @@ -0,0 +1,46 @@ +#include "KCamera.h" + +#include "KPawn.h" +#include "Utils.h" + +namespace KapitanGame +{ + const SDL_FRect& KCamera::GetViewport() const + { + return Viewport; + } + + float KCamera::GetScale() const + { + return Scale; + } + + const KVector2D& KCamera::GetFocusPoint() const + { + return FocusPoint; + } + + void KCamera::Update(const std::vector>& pawns, const SDL_FRect& map) + { + FocusPoint = { 0.f,0.f }; + for(const auto& pawn : pawns) { + FocusPoint += pawn->GetPosition(); + } + FocusPoint *= 1.f / static_cast(pawns.size()); + Viewport.x = Utils::Clamp(FocusPoint.X - Viewport.w / 2.f, 0.f, map.w - Viewport.w); + Viewport.y = Utils::Clamp(FocusPoint.Y - Viewport.h / 2.f, 0.f, map.h - Viewport.h); + PlayArea.x = Utils::Clamp(FocusPoint.X - PlayArea.w / 2.f, static_cast(-Constants::WINDOW_DEAD_ZONE), map.w + static_cast(Constants::WINDOW_DEAD_ZONE)); + PlayArea.y = Utils::Clamp(FocusPoint.Y - PlayArea.h / 2.f, static_cast(-Constants::WINDOW_DEAD_ZONE), map.h + static_cast(Constants::WINDOW_DEAD_ZONE)); + } + + void KCamera::SetDebug(const SDL_FRect& map) + { + Viewport = { 0,0,Constants::SCREEN_WIDTH, Constants::SCREEN_HEIGHT }; + Scale = std::min(Constants::SCREEN_HEIGHT * 1.f / map.h, Constants::SCREEN_WIDTH * 1.f / map.w); + } + + const SDL_FRect& KCamera::GetPlayArea() const + { + return PlayArea; + } +} diff --git a/2dkg_zad5/2dgk_zad5/KCamera.h b/2dkg_zad5/2dgk_zad5/KCamera.h new file mode 100644 index 0000000..357223a --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KCamera.h @@ -0,0 +1,43 @@ +#pragma once +#include +#include +#include + +#include "Constants.h" +#include "KVector2d.h" + +namespace KapitanGame +{ + class KPawn; + + class KCamera + { + public: + explicit KCamera(const SDL_FRect& map) + : Viewport({ + (map.w - Constants::SCREEN_WIDTH) / 2.f, (map.h - Constants::SCREEN_HEIGHT) / 2.f, + Constants::SCREEN_WIDTH * 1.f, + Constants::SCREEN_HEIGHT * 1.f + }), PlayArea({ + 0, 0, + Constants::SCREEN_WIDTH - Constants::WINDOW_DEAD_ZONE, + Constants::SCREEN_HEIGHT - Constants::WINDOW_DEAD_ZONE + }), FocusPoint(Viewport.x + Viewport.w / 2, Viewport.y + Viewport.h / 2), + Scale(1.f) + { + } + + const SDL_FRect& GetViewport() const; + float GetScale() const; + const KVector2D& GetFocusPoint() const; + void Update(const std::vector>& pawns, const SDL_FRect& map); + void SetDebug(const SDL_FRect& map); + const SDL_FRect& GetPlayArea() const; + private: + SDL_FRect Viewport; + SDL_FRect PlayArea; + KVector2D FocusPoint; + float Scale; + }; +} + diff --git a/2dkg_zad5/2dgk_zad5/KCircle.cpp b/2dkg_zad5/2dgk_zad5/KCircle.cpp new file mode 100644 index 0000000..0554446 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KCircle.cpp @@ -0,0 +1,108 @@ +#include "KCircle.h" + +#include +#include + +#include "KRect.h" +#include "Utils.h" + +namespace KapitanGame { + + bool KCircle::IsCollision(const KCircle* other) const { + return (GetParent()->GetPosition() - other->GetParent()->GetPosition()).Length() < Radius + other->Radius; + } + + bool KCircle::IsCollision(const KCollider* other) const + { + if (const auto circle = dynamic_cast(other); circle != nullptr) + return IsCollision(circle); + + + if (const auto rect = dynamic_cast(other); rect != nullptr) + return IsCollision(rect); + + throw std::runtime_error("unsupported shape"); + } + + KVector2D KCircle::GetSeparationVector(const KCollider* other) const + { + if (const auto circle = dynamic_cast(other); circle != nullptr) + return GetSeparationVector(circle); + + if (const auto rect = dynamic_cast(other); rect != nullptr) + return GetSeparationVector(rect); + + throw std::runtime_error("unsupported shape"); + } + + bool KCircle::IsCollision(const KRect* other) const { + const auto f = KVector2D(Utils::Clamp(GetParent()->GetPosition().X, other->GetParent()->GetPosition().X - other->GetWidth() / 2, other->GetParent()->GetPosition().X + other->GetWidth() / 2), + Utils::Clamp(GetParent()->GetPosition().Y, other->GetParent()->GetPosition().Y - other->GetHeight() / 2, other->GetParent()->GetPosition().Y + other->GetHeight() / 2)); + + return (GetParent()->GetPosition() - f).Length() < Radius; + } + + KVector2D KCircle::GetSeparationVector(const KCircle* other) const { + const KVector2D centerDiff = GetParent()->GetPosition() - other->GetParent()->GetPosition(); + const float centerDiffLength = centerDiff.Length(); + return centerDiff / centerDiffLength * (Radius + other->Radius - centerDiffLength); + } + + KVector2D KCircle::GetSeparationVector(const KRect* other) const { + const float l = other->GetParent()->GetPosition().X - other->GetWidth() / 2; + const float r = other->GetParent()->GetPosition().X + other->GetWidth() / 2; + const float t = other->GetParent()->GetPosition().Y - other->GetHeight() / 2; + const float b = other->GetParent()->GetPosition().Y + other->GetHeight() / 2; + const auto f = KVector2D(Utils::Clamp(GetParent()->GetPosition().X, l, r), + Utils::Clamp(GetParent()->GetPosition().Y, t, b)); + + if (GetParent()->GetPosition() == f) { + const auto left = GetParent()->GetPosition().X - l + Radius; + const auto right = r - GetParent()->GetPosition().X + Radius; + const auto top = GetParent()->GetPosition().Y - t + Radius; + const auto bottom = b - GetParent()->GetPosition().Y + Radius; + return KRect::GetSeparationVector(left, right, top, bottom); + } + return (GetParent()->GetPosition() - f) / (GetParent()->GetPosition() - f).Length() * (Radius - (GetParent()->GetPosition() - f).Length()); + } + + KVector2D KCircle::GetSeparationVector(const SDL_FRect& map) const { + KVector2D separationVector{ 0.f,0.f }; + if (GetParent()->GetPosition().X - map.x - Radius < 0) + { + separationVector.X += -(GetParent()->GetPosition().X - map.x - Radius); + } + if (GetParent()->GetPosition().X + Radius - (map.x + map.w) > 0) + { + separationVector.X += -(GetParent()->GetPosition().X - (map.x+map.w) + Radius); + } + if (GetParent()->GetPosition().Y - Radius - map.y < 0) { + separationVector.Y += -(GetParent()->GetPosition().Y - map.y - Radius); + } + if (GetParent()->GetPosition().Y + Radius - (map.y + map.h) > 0) { + separationVector.Y += -(GetParent()->GetPosition().Y - (map.y + map.h) + Radius); + } + return separationVector; + } + + float KCircle::GetWidth() const + { + return Radius * 2; + } + + float KCircle::GetHeight() const + { + return Radius * 2; + } + + KCircle::KCircle(KSolid* parent, const float radius): KCollider(parent), + Radius(radius) + { + } + + float KCircle::GetRadius() const { + return Radius; + } + +} + diff --git a/2dkg_zad5/2dgk_zad5/KCircle.h b/2dkg_zad5/2dgk_zad5/KCircle.h new file mode 100644 index 0000000..3c40436 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KCircle.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include "KCollider.h" +#include "KVector2d.h" + +namespace KapitanGame { + class KRect; + + class KCircle final : public KCollider + { + public: + KCircle(KSolid* parent, float radius); + [[nodiscard]] float GetRadius() const; + private: + KVector2D GetSeparationVector(const KCircle* other) const; + KVector2D GetSeparationVector(const KRect* other) const; + bool IsCollision(const KCircle* other) const; + bool IsCollision(const KRect* other) const; + public: + bool IsCollision(const KCollider* other) const override; + KVector2D GetSeparationVector(const KCollider* other) const override; + [[nodiscard]] KVector2D GetSeparationVector(const SDL_FRect& map) const override; + [[nodiscard]] float GetWidth() const override; + [[nodiscard]] float GetHeight() const override; + private: + float Radius{ 1.f }; + }; +} + + diff --git a/2dkg_zad5/2dgk_zad5/KCirclePawn.cpp b/2dkg_zad5/2dgk_zad5/KCirclePawn.cpp new file mode 100644 index 0000000..cdd80c6 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KCirclePawn.cpp @@ -0,0 +1,15 @@ +#include "KCirclePawn.h" + +namespace KapitanGame +{ + KCirclePawn::KCirclePawn(const KVector2D& position, const KTexture& texture, + const std::shared_ptr& playerController, KSettings* settings) : KPawn(position, texture, playerController, settings), + Collider(this, static_cast(texture.GetWidth())/2.0f) + { + } + + const KCollider* KCirclePawn::GetCollider() const + { + return &Collider; + } +} diff --git a/2dkg_zad5/2dgk_zad5/KCirclePawn.h b/2dkg_zad5/2dgk_zad5/KCirclePawn.h new file mode 100644 index 0000000..b454faf --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KCirclePawn.h @@ -0,0 +1,18 @@ +#pragma once +#include "KCircle.h" +#include "KPawn.h" + +namespace KapitanGame +{ + class KCirclePawn final :public KPawn + { + public: + KCirclePawn(const KVector2D& position, const KTexture& texture, + const std::shared_ptr& playerController, KSettings* settings); + + const KCollider* GetCollider() const override; + private: + KCircle Collider; + }; +} + diff --git a/2dkg_zad5/2dgk_zad5/KCollider.cpp b/2dkg_zad5/2dgk_zad5/KCollider.cpp new file mode 100644 index 0000000..ed908e8 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KCollider.cpp @@ -0,0 +1,34 @@ +#include "KCollider.h" + +namespace KapitanGame { + KCollider::KCollider(const KCollider& other) = default; + + KCollider::KCollider(KCollider&& other) noexcept: Parent(other.Parent) + { + } + + KCollider& KCollider::operator=(const KCollider& other) + { + if (this == &other) + return *this; + Parent = other.Parent; + return *this; + } + + KCollider& KCollider::operator=(KCollider&& other) noexcept + { + if (this == &other) + return *this; + Parent = other.Parent; + return *this; + } + + KCollider::KCollider(KSolid* parent): Parent(parent) + { + } + + KSolid* KCollider::GetParent() const + { + return Parent; + } +} diff --git a/2dkg_zad5/2dgk_zad5/KCollider.h b/2dkg_zad5/2dgk_zad5/KCollider.h new file mode 100644 index 0000000..0e9c30a --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KCollider.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include "KSolid.h" +#include "KVector2d.h" + +namespace KapitanGame { + + class KCollider + { + public: + KCollider(const KCollider& other); + + KCollider(KCollider&& other) noexcept; + + KCollider& operator=(const KCollider& other); + + KCollider& operator=(KCollider&& other) noexcept; + + explicit KCollider(KSolid* parent); + + virtual ~KCollider() = default; + virtual bool IsCollision(const KCollider* other) const = 0; + virtual KVector2D GetSeparationVector(const KCollider* other) const = 0; + [[nodiscard]] virtual KVector2D GetSeparationVector(const SDL_FRect& map) const = 0; + [[nodiscard]] virtual float GetWidth() const = 0; + [[nodiscard]] virtual float GetHeight() const = 0; + [[nodiscard]] KSolid* GetParent() const; + private: + KSolid* Parent; + }; +} + + diff --git a/2dkg_zad5/2dgk_zad5/KDrawable.cpp b/2dkg_zad5/2dgk_zad5/KDrawable.cpp new file mode 100644 index 0000000..231e1f6 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KDrawable.cpp @@ -0,0 +1,26 @@ +#include "KDrawable.h" + +#include "KCamera.h" + +namespace KapitanGame +{ + std::atomic KDrawable::IdCounter{ 0 }; + + KDrawable::KDrawable(const KVector2D& position, const KTexture& texture, SDL_Rect* tileClip, const SDL_RendererFlip flip) : + Id(++IdCounter), Position(position), Texture(texture), TileClip(tileClip), Flip(flip) + { + } + + void KDrawable::Render(SDL_Renderer* renderer, const KCamera& camera, const float parallaxFactor) const + { + if (TileClip != nullptr) + Texture.RenderEx(renderer, Position.X - Constants::TILE_WIDTH / 2.f - camera.GetViewport().x * parallaxFactor, Position.Y - Constants::TILE_HEIGHT / 2.f - camera.GetViewport().y, 0, TileClip, camera.GetScale(), Flip); // NOLINT(clang-diagnostic-implicit-int-float-conversion, bugprone-narrowing-conversions, cppcoreguidelines-narrowing-conversions) + else + Texture.Render(renderer, Position.X - Texture.GetWidth() / 2.f - camera.GetViewport().x * parallaxFactor, Position.Y - Texture.GetHeight() / 2.f - camera.GetViewport().y, nullptr, camera.GetScale()); // NOLINT(clang-diagnostic-implicit-int-float-conversion, bugprone-narrowing-conversions, cppcoreguidelines-narrowing-conversions) + } + + KVector2D KDrawable::GetPosition() const + { + return Position; + } +} diff --git a/2dkg_zad5/2dgk_zad5/KDrawable.h b/2dkg_zad5/2dgk_zad5/KDrawable.h new file mode 100644 index 0000000..c89b045 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KDrawable.h @@ -0,0 +1,28 @@ +#pragma once +#include + +#include "KTexture.h" +#include "KVector2d.h" + +namespace KapitanGame +{ + class KCamera; + + class KDrawable + { + public: + KDrawable(const KVector2D& position, const KTexture& texture, SDL_Rect* tileClip, SDL_RendererFlip flip = SDL_FLIP_NONE); + virtual ~KDrawable() = default; + void Render(SDL_Renderer* renderer, const KCamera& camera, float parallaxFactor = 1.f) const; + [[nodiscard]] KVector2D GetPosition() const; + const int Id; + static std::atomic IdCounter; + protected: + KVector2D Position{ 0.f, 0.f }; + private: + const KTexture& Texture; + SDL_Rect* TileClip; + const SDL_RendererFlip Flip; + }; +} + diff --git a/2dkg_zad5/2dgk_zad5/KFont.cpp b/2dkg_zad5/2dgk_zad5/KFont.cpp new file mode 100644 index 0000000..16dea77 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KFont.cpp @@ -0,0 +1,132 @@ +#include "KFont.h" + +#include + +#include "KTexture.h" + +namespace KapitanGame +{ + KFont::KFont() + = default; + + KFont::~KFont() + { + Free(); + } + + KFont::KFont(KFont&& other) noexcept : Font(other.Font) + { + other.Font = nullptr; + } + + KFont& KFont::operator=(KFont&& other) noexcept + { + Font = other.Font; + other.Font = nullptr; + return *this; + } + + bool KFont::LoadFromFile(const std::string& path, const int ptSize) + { + bool success = true; + Font = TTF_OpenFont(path.c_str(), ptSize); + if (Font == nullptr) + { + printf("Failed to load %s font! SDL_ttf Error: %s\n", path.c_str(), TTF_GetError()); + success = false; + } + return success; + } + + void KFont::Free() + { + if (Font != nullptr) + { + TTF_CloseFont(Font); + Font = nullptr; + } + } + + KTexture KFont::GetTextTexture(const std::string& text, const SDL_Color textColor, SDL_Renderer* renderer) const + { + KTexture texture; + constexpr int spacing = 1; + int width, height = width = 0; + std::istringstream inputText; + inputText.str(text); + for(std::string line; std::getline(inputText, line);) + { + int w, h; + TTF_SizeText(Font, line.c_str(), &w, &h); + if (w > width) width = w; + height += w+spacing; + } + if (height > 0) + height -= spacing; + inputText.clear(); + inputText.seekg(0); + SDL_Surface* textureSurface = SDL_CreateRGBSurfaceWithFormat(0, width, height, 8, SDL_PIXELFORMAT_INDEX8); + { + const SDL_Palette* palette = textureSurface->format->palette; + palette->colors[0].r = 255 - textColor.r; + palette->colors[0].g = 255 - textColor.g; + palette->colors[0].b = 255 - textColor.b; + palette->colors[1].r = textColor.r; + palette->colors[1].g = textColor.g; + palette->colors[1].b = textColor.b; + palette->colors[1].a = textColor.a; + } + SDL_SetColorKey(textureSurface, SDL_TRUE, 0); + int y = 0; + for (std::string line; std::getline(inputText, line);) { + if (SDL_Surface* textSurface = TTF_RenderText_Solid(Font, line.c_str(), textColor); textSurface == nullptr) + { + printf("Unable to render text surface! SDL_ttf Error: %s\n", TTF_GetError()); + } + else + { + SDL_Rect destRect{0, y, textSurface->w, textSurface->h}; + SDL_BlitSurface(textSurface, nullptr, textureSurface, &destRect); + y += textSurface->h + spacing; + SDL_FreeSurface(textSurface); + } + } + texture.LoadFromSurface(textureSurface, renderer); + SDL_FreeSurface(textureSurface); + return texture; + } + + KTexture KFont::GetTextWithOutlineTexture(const std::string& text, const SDL_Color fgColor, const SDL_Color bgColor, + const int outlineSize, SDL_Renderer* renderer) const + { + KTexture texture; + const int originalOutlineSize = TTF_GetFontOutline(Font); + + if (SDL_Surface* fgSurface = TTF_RenderText_Blended(Font, text.c_str(), fgColor); fgSurface == nullptr) + { + printf("Unable to render text surface! SDL_ttf Error: %s\n", TTF_GetError()); + } + else + { + TTF_SetFontOutline(Font, outlineSize); + SDL_Surface* bgSurface = TTF_RenderText_Blended(Font, text.c_str(), bgColor); + TTF_SetFontOutline(Font, originalOutlineSize); + if (bgSurface == nullptr) + { + printf("Unable to render text surface! SDL_ttf Error: %s\n", TTF_GetError()); + } + else + { + SDL_Rect rect = { outlineSize, outlineSize, fgSurface->w, fgSurface->h }; + + /* blit text onto its outline */ + SDL_SetSurfaceBlendMode(fgSurface, SDL_BLENDMODE_BLEND); + SDL_BlitSurface(fgSurface, nullptr, bgSurface, &rect); + texture.LoadFromSurface(bgSurface, renderer); + SDL_FreeSurface(bgSurface); + } + SDL_FreeSurface(fgSurface); + } + return texture; + } +} diff --git a/2dkg_zad5/2dgk_zad5/KFont.h b/2dkg_zad5/2dgk_zad5/KFont.h new file mode 100644 index 0000000..20e8e92 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KFont.h @@ -0,0 +1,37 @@ +#pragma once +#include +#include + +namespace KapitanGame { + //Font wrapper class + class KTexture; + class KFont { + public: + KFont(); + + ~KFont(); + + KFont(const KFont& other) = delete; + + KFont(KFont&& other) noexcept; + + KFont& operator=(const KFont& other) = delete; + + KFont& operator=(KFont&& other) noexcept; + + //Loads image at specified path + bool LoadFromFile(const std::string& path, int ptSize); + + //Deallocates Font + void Free(); + + KTexture GetTextTexture(const std::string& text, SDL_Color textColor, SDL_Renderer* renderer) const; + KTexture GetTextWithOutlineTexture(const std::string& text, SDL_Color fgColor, SDL_Color bgColor, int outlineSize, SDL_Renderer* renderer) const; + + private: + //The actual hardware font + TTF_Font* Font{}; + }; + +} + diff --git a/2dkg_zad5/2dgk_zad5/KGame.cpp b/2dkg_zad5/2dgk_zad5/KGame.cpp new file mode 100644 index 0000000..5ef6b84 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KGame.cpp @@ -0,0 +1,473 @@ +#include "KGame.h" +#include "SDL.h" +#include +#include +#include +#include +#include +#include +#include +#include "Utils.h" +#include "Constants.h" +#include "KCamera.h" +#include "KCirclePawn.h" +#include "KTexture.h" +#include "KTile.h" +#include "KVector2d.h" +#include "KSolidTile.h" + +namespace KapitanGame { + KGame::KGame() : Time(0), PreviousTime(0), Map(), Settings(Constants::MAX_JUMP_HEIGHT, Constants::HORIZONTAL_DISTANCE_TO_MAX_JUMP_HEIGHT) + { + //Initialize SDL + if (SDL_Init(SDL_INIT_VIDEO) < 0) + { + throw std::runtime_error(Utils::StringFormat("SDL could not initialize! SDL_Error: %s", SDL_GetError())); + } + +#ifndef NDEBUG + SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE); +#endif + + + ////Set texture filtering to linear + //if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) + //{ + // printf("Warning: Linear texture filtering not enabled!\n"); + //} + + Input.Init(); + + if (TTF_Init() < 0) + { + throw std::runtime_error(Utils::StringFormat("SDL_TTF could not initialize! TTF_Error: %s", TTF_GetError())); + } + + if (IMG_Init(IMG_INIT_PNG) < 0) + { + throw std::runtime_error(Utils::StringFormat("SDL_IMG could not initialize! IMG_Error: %s", IMG_GetError())); + } + //Create window + Window = SDL_CreateWindow(Constants::WINDOW_TITLE, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + Constants::SCREEN_WIDTH, Constants::SCREEN_HEIGHT, SDL_WINDOW_SHOWN); + if (Window == nullptr) + { + throw std::runtime_error(Utils::StringFormat("Window could not be created! SDL_Error: %s", SDL_GetError())); + } + //Create renderer for window + Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_ACCELERATED); + if (Renderer == nullptr) + { + throw std::runtime_error( + Utils::StringFormat("Renderer could not be created! SDL Error: %s", SDL_GetError())); + } + //Initialize renderer color + SDL_SetRenderDrawColor(Renderer, 0, 0xFF, 0, 0xFF); + SDL_Log("KGame initialized..."); + } + + KGame::~KGame() { + //Free loaded images + Textures.clear(); + for (auto& layer : BackgroundLayers) + { + layer.clear(); + } + Objects.clear(); + for (auto& layer : ForegroundLayers) + { + layer.clear(); + } + Pawns.clear(); + PlayerControllers.clear(); + Fonts.clear(); + TTF_Quit(); + IMG_Quit(); + Input.Free(); + + //Destroy window + SDL_DestroyRenderer(Renderer); + SDL_DestroyWindow(Window); + Window = nullptr; + Renderer = nullptr; + + //Quit SDL subsystems + SDL_Quit(); + } + + bool KGame::LoadLevel() + { + bool success = true; + Objects.clear(); + Pawns.clear(); + std::ifstream levelFile; + levelFile.open("assets/levels/level" + std::to_string(LvlCounter) + ".txt"); + if (levelFile.fail()) + { + printf("Failed to load assets/levels/level%d.txt!\n", LvlCounter); + success = false; + } + else + { + int y = 0; + int layer = 0; + float mapWidth = 0, mapHeight = 0; + KVector2D startPosition{ 0.f, 0.f }; + std::string line; + while (std::getline(levelFile, line)) { + if (line.length() > 0 && line[0] == '#') + { + std::string mapSizes = line.substr(1); + const auto delimPos = mapSizes.find(','); + mapWidth = std::stof(mapSizes.substr(0, delimPos)) * Constants::TILE_WIDTH; + mapHeight = std::stof(mapSizes.substr(delimPos + 1)) * Constants::TILE_HEIGHT; + continue; + } + if (line.length() > 0 && line[0] == '!') + { + layer = std::stoi(line.substr(1)); + y = 0; + continue; + } + for (auto i = 0ull; i < line.length(); ++i) { + KVector2D position{ + static_cast(i * Constants::TILE_WIDTH + Constants::TILE_WIDTH / 2), // NOLINT(bugprone-integer-division) + static_cast(y * Constants::TILE_HEIGHT + Constants::TILE_HEIGHT / 2) // NOLINT(bugprone-integer-division) + }; + if (layer == 3) { + switch (line[i]) + { + case 'P': + startPosition = position; + break; + default: + auto clip = -1; + SDL_RendererFlip flip = SDL_FLIP_NONE; + if (std::isdigit(line[i])) + { + clip = line[i] - '0'; + } + else if (line[i] >= 'a' && line[i] <= 'z') + { + clip = line[i] - 'a'; + flip = SDL_FLIP_HORIZONTAL; + } + if (clip >= 0) + Objects.emplace(std::make_pair(static_cast(i), y), std::make_shared(position, Textures["tiles"], &TileClips[clip], flip)); + + break; + } + } + else if (layer < 3) + { + auto clip = -1; + SDL_RendererFlip flip = SDL_FLIP_NONE; + if (std::isdigit(line[i])) + { + clip = line[i] - '0'; + } + else if (line[i] >= 'a' && line[i] <= 'z') + { + clip = line[i] - 'a'; + flip = SDL_FLIP_HORIZONTAL; + } + if (clip >= 0) + BackgroundLayers[layer].emplace(std::make_pair(static_cast(i), y), std::make_shared(position, Textures["tiles"], &TileClips[clip], flip)); + } + else + { + const auto index = layer - 4; + auto clip = -1; + SDL_RendererFlip flip = SDL_FLIP_NONE; + if (std::isdigit(line[i])) + { + clip = line[i] - '0'; + } + else if (line[i] >= 'a' && line[i] <= 'z') + { + clip = line[i] - 'a'; + flip = SDL_FLIP_HORIZONTAL; + } + if (clip >= 0) + ForegroundLayers[index].emplace(std::make_pair(static_cast(i), y), std::make_shared(position, Textures["tiles"], &TileClips[clip], flip)); + + } + } + ++y; + } + + levelFile.close(); + Map = { 0,0,mapWidth,mapHeight }; + Pawns.emplace_back(std::make_shared(startPosition, Textures["P1.bmp"], PlayerControllers[static_cast(KPlayer::Player1)], &Settings)); + PlayerControllers[static_cast(KPlayer::Player1)]->Possess(Pawns.back().get()); + } + return success; + } + + bool KGame::LoadMedia() + { + //Loading success flag + bool success = true; + + for (auto player : Utils::KPlayerIterator()) + { + std::string filename = "P" + std::to_string(static_cast(player) + 1) + ".bmp"; + Textures.emplace(filename, KTexture()); + if (!Textures[filename].LoadFromFile("assets/textures/" + filename, Renderer)) + { + Textures.erase(filename); + printf("Failed to load player texture image!\n"); + success = false; + break; + } + + PlayerControllers.emplace_back(std::make_shared(player)); + PlayerControllers.back()->SetupInputBindings(Input); + } + Textures.emplace("tiles", KTexture()); + if (!Textures["tiles"].LoadFromFile("assets/textures/maptiles.png", Renderer)) + { + printf("Failed to load map tiles texture image!\n"); + Textures.erase("tiles"); + success = false; + } + else + { + for (int i = 0; i < Constants::TILE_COUNT; ++i) + { + TileClips[i].x = (i % 4) * Constants::TILE_WIDTH; + TileClips[i].y = (i / 4) * Constants::TILE_HEIGHT; + TileClips[i].w = Constants::TILE_WIDTH; + TileClips[i].h = Constants::TILE_HEIGHT; + } + } + + Fonts.emplace("PressStart2P-Regular", KFont()); + if (!Fonts["PressStart2P-Regular"].LoadFromFile("assets/fonts/PressStart2P-Regular.ttf", 8)) + { + printf("Failed to load PressStart2P-Regular font!\n"); + Fonts.erase("PressStart2P-Regular"); + success = false; + } + + if (std::ifstream configFile("config.json"); configFile.fail()) { + printf("Failed to load config.json!\n"); + success = false; + } + else { + nlohmann::json configJson; + configFile >> configJson; + configFile.close(); + for (auto& player : configJson["Input"].items()) { + const auto playerEnum = Utils::GetPlayerFromString(player.key()); + for (auto& axisMapping : player.value()["AxisMappings"]) { + Input.AddAxisMapping(axisMapping["AxisName"].get(), axisMapping["Scale"].get(), axisMapping["Key"].get(), playerEnum); + } + for (auto& actionMapping : player.value()["ActionMappings"]) { + Input.AddActionMapping(actionMapping["ActionName"].get(), actionMapping["Key"].get(), playerEnum); + } + } + } + if (!LoadLevel()) + success = false; + return success; + } + + int KGame::Run(int argc, char* args[]) + { + //Load media + if (!LoadMedia()) + { + printf("Failed to load media!\n"); + } + else + { + //Main loop flag + bool quit = false; + + //Event handler + SDL_Event e; + + KCamera camera(Map); + KCamera debugCamera(Map); + debugCamera.SetDebug(Map); + + char devMode = 0; + + camera.Update(Pawns, Map); + + SettingsTextTextureDirty = true; + + Time = PreviousTime = SDL_GetTicks(); + printf("\n"); + + //While application is running + while (!quit) + { + PreviousTime = Time; + Time = SDL_GetTicks(); + float deltaTime = static_cast(Time - PreviousTime) * 0.001f; + + Input.HandleInputPreEvents(); + + //Handle events on queue + while (SDL_PollEvent(&e)) + { + //User requests quit + if (e.type == SDL_QUIT) + { + quit = true; + continue; + } + Input.HandleEvent(e); + } + + Input.HandleInputPostEvents(Playing); + + if (Input.IsKeyboardButtonPressed(SDL_SCANCODE_F1)) + { + if (++devMode > 2) devMode = 0; + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Changed devMode to %d\n", devMode); + } + if (devMode >= 1) { + if (Input.IsKeyboardButtonPressed(SDL_SCANCODE_F2)) + { + Settings.CollisionEnabled = !Settings.CollisionEnabled; + } + if (Input.IsKeyboardButtonHeld(SDL_SCANCODE_F3)) + { + Settings.MaxJumpHeight -= 0.1f; + SettingsTextTextureDirty = true; + } + if (Input.IsKeyboardButtonHeld(SDL_SCANCODE_F4)) + { + Settings.MaxJumpHeight += 0.1f; + SettingsTextTextureDirty = true; + } + if (Input.IsKeyboardButtonHeld(SDL_SCANCODE_F5)) + { + Settings.HorizontalDistanceToMaxJumpHeight -= 0.1f; + SettingsTextTextureDirty = true; + } + if (Input.IsKeyboardButtonHeld(SDL_SCANCODE_F6)) + { + Settings.HorizontalDistanceToMaxJumpHeight += 0.1f; + SettingsTextTextureDirty = true; + } + } + + if (Playing) { + for (const auto& pc : PlayerControllers) { + pc->Update(deltaTime); + } + for (const auto& pawn : Pawns) { + if (!Playing) break; + pawn->MovementStep(deltaTime); + } + if (Settings.CollisionEnabled) { + for (const auto& pawn : Pawns) { + if (!Playing) break; + pawn->CollisionDetectionStep(Objects); + } + } + for (const auto& pawn : Pawns) { + if (!Playing) break; + pawn->CollisionDetectionWithMapStep(Map); + } + for (const auto& pawn : Pawns) { + if (!Playing) break; + pawn->CollisionDetectionWithMapStep(camera.GetPlayArea()); + } + + camera.Update(Pawns, Map); + + } + Textures.insert_or_assign("Text_Settings", Fonts["PressStart2P-Regular"].GetTextTexture( + Utils::StringFormat( + "Developer Mode (F1): %d\n" + "Collision Enabled (F2): %s\n" + "Max Jump Height (F3/F4): %f\n" + "Horizontal Distance to Max Jump Height (F5/F6): %f\n" + "Initial Jump Velocity: %f\n" + "Gravity: %f\n" + "Player Position X: %f\n" + "Player Position Y: %f", + static_cast(devMode), + Settings.CollisionEnabled ? "True" : "False", + static_cast(Settings.MaxJumpHeight), + static_cast(Settings.HorizontalDistanceToMaxJumpHeight), + static_cast(Settings.JumpInitialVelocity), + static_cast(Settings.Gravity), + Pawns.back()->GetPosition().X, + Pawns.back()->GetPosition().Y), { 0, 0, 0, 0xFF }, + Renderer)); + + + if (!Playing) + { + if (Time > RestartTick) + { + LoadLevel(); + debugCamera.SetDebug(Map); + camera.Update(Pawns, Map); + Playing = true; + continue; + } + } + + + //Clear screen + //SDL_SetRenderDrawColor(Renderer, 66, 135, 245, 0xFF); + SDL_SetRenderDrawColor(Renderer, 21, 36, 143, 0xFF); + SDL_RenderClear(Renderer); + + const auto& cameraInUse = devMode >= 2 ? debugCamera : camera; + + + if (Playing) { + for (int i = 0; i < Constants::BG_LAYER_COUNT; ++i) + { + for (const auto& [tile, drawable] : BackgroundLayers[i]) + drawable->Render(Renderer, cameraInUse, 1.f - (Constants::BG_LAYER_COUNT - i - 1) * .25f); //TODO: Add controls for changing parallax factor + } + for (const auto& [tile, obj] : Objects) + obj->Render(Renderer, cameraInUse); + for (const auto& pawn : Pawns) { + pawn->Render(Renderer, cameraInUse); + } + for (auto& layer : ForegroundLayers) + { + for (const auto& [tile, drawable] : layer) + drawable->Render(Renderer, cameraInUse); + } + } + + if (devMode >= 1) { + Textures["Text_Settings"].Render(Renderer, 10.f, 25.f); + } + if (devMode >= 2) + { + + SDL_RenderDrawPointF(Renderer, camera.GetFocusPoint().X * debugCamera.GetScale(), camera.GetFocusPoint().Y * debugCamera.GetScale()); + SDL_SetRenderDrawColor(Renderer, 0xFF, 0x00, 0x00, 0xFF); + SDL_FRect debugViewport = { camera.GetViewport().x * debugCamera.GetScale(), + camera.GetViewport().y * debugCamera.GetScale(), + camera.GetViewport().w * debugCamera.GetScale(), + camera.GetViewport().h * debugCamera.GetScale() }; + SDL_RenderDrawRectF(Renderer, &debugViewport); + SDL_FRect debug2Viewport = { camera.GetPlayArea().x * debugCamera.GetScale(), + camera.GetPlayArea().y * debugCamera.GetScale(), + camera.GetPlayArea().w * debugCamera.GetScale(), + camera.GetPlayArea().h * debugCamera.GetScale() }; + SDL_SetRenderDrawColor(Renderer, 0x00, 0xFF, 0x00, 0xFF); + SDL_RenderDrawRectF(Renderer, &debug2Viewport); + } + + //Update screen + SDL_RenderPresent(Renderer); + } + } + + return 0; + } +} diff --git a/2dkg_zad5/2dgk_zad5/KGame.h b/2dkg_zad5/2dgk_zad5/KGame.h new file mode 100644 index 0000000..fd51dd3 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KGame.h @@ -0,0 +1,59 @@ +#pragma once +#include + +#include "KTexture.h" +#include +#include + +#include "Constants.h" +#include "KFont.h" +#include "KInput.h" +#include "KSettings.h" +#include "Utils.h" + + +namespace KapitanGame { + class KExit; + class KSolid; + + class KGame + { + public: + KGame(); + ~KGame(); + KGame(const KGame& other) = delete; + KGame(KGame&& other) noexcept = delete; + KGame& operator=(const KGame& other) = delete; + KGame& operator=(KGame&& other) noexcept = delete; + int Run(int argc, char* args[]); + private: + SDL_Window* Window = nullptr; + + SDL_Renderer* Renderer = nullptr; + + std::unordered_map, std::shared_ptr, Utils::PairHash> BackgroundLayers[Constants::BG_LAYER_COUNT]; + std::unordered_map, std::shared_ptr, Utils::PairHash> ForegroundLayers[Constants::FG_LAYER_COUNT]; + std::unordered_map, std::shared_ptr, Utils::PairHash> Objects; + std::vector> Pawns; + std::vector> PlayerControllers; + std::unordered_map Textures; + std::unordered_map Fonts; + SDL_Rect TileClips[Constants::TILE_COUNT]; + KInput Input; + uint32_t Time; + uint32_t PreviousTime; + SDL_FRect Map; + std::weak_ptr Exit; + KSettings Settings; + bool SettingsTextTextureDirty = true; + + bool LoadLevel(); + bool LoadMedia(); + + int LvlCounter = 1; + bool Playing = true; + uint32_t RestartTick = -1; + }; +} + + diff --git a/2dkg_zad5/2dgk_zad5/KInput.cpp b/2dkg_zad5/2dgk_zad5/KInput.cpp new file mode 100644 index 0000000..a13b3c6 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KInput.cpp @@ -0,0 +1,307 @@ +#include "KInput.h" +#include "Constants.h" + +#include +#include + +namespace KapitanGame { + KInput::KInput() : GamePadsCount(0), Initialized(false) + { + } + + void KInput::Init() { + if (Initialized) return; + if (SDL_WasInit(SDL_INIT_GAMECONTROLLER) != 1) + SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); + + const int nJoysticks = SDL_NumJoysticks(); + GamePadsCount = 0; + + // Count how many controllers there are + for (int i = 0; i < nJoysticks; i++) + if (SDL_IsGameController(i)) + GamePadsCount++; + + // If we have some controllers attached + if (GamePadsCount > 0) + { + for (int i = 0; i < GamePadsCount; i++) + { + // Open the controller and add it to our list + SDL_GameController* pad = SDL_GameControllerOpen(i); + if (SDL_GameControllerGetAttached(pad) == 1) + ConnectedControllers.push_back(pad); + else + printf("SDL_GetError() = %s\n", SDL_GetError()); + } + SDL_GameControllerEventState(SDL_ENABLE); + } + + // Vectors are empty to begin with, this sets their size + ControllerInputs.resize(GamePadsCount); + LastControllerInputs.resize(GamePadsCount); + + // Set the status of the controllers to "nothing is happening" + for (int i = 0; i < GamePadsCount; i++) { + for (int a = 0; a < SDL_CONTROLLER_AXIS_MAX; a++) { + ControllerInputs[i].Axis[a] = 0; + LastControllerInputs[i].Axis[a] = 0; + } + for (int b = 0; b < SDL_CONTROLLER_BUTTON_MAX; b++) { + ControllerInputs[i].Buttons[b] = false; + LastControllerInputs[i].Buttons[b] = false; + } + } + Initialized = true; + } + + void KInput::Free() { + for (const auto& pad : ConnectedControllers) { + SDL_GameControllerClose(pad); + } + ConnectedControllers.clear(); + Actions.clear(); + } + + void KInput::HandleInputPreEvents() { + for (int i = 0; i < GamePadsCount; i++) { + for (int a = 0; a < SDL_CONTROLLER_AXIS_MAX; a++) { + LastControllerInputs[i].Axis[a] = ControllerInputs[i].Axis[a]; + } + for (int b = 0; b < SDL_CONTROLLER_BUTTON_MAX; b++) { + LastControllerInputs[i].Buttons[b] = ControllerInputs[i].Buttons[b]; + } + } + LastKeyboardInputs = KeyboardInputs; + } + void KInput::HandleInputPostEvents(const bool processInput) { + int size = 0; + const Uint8* currentKeyStates = SDL_GetKeyboardState(&size); + KeyboardInputs = std::vector(currentKeyStates, currentKeyStates + size); + if (processInput) { + ProcessActions(); + ProcessAxes(); + } + } + void KInput::HandleEvent(const SDL_Event& event) { + switch (event.type) { + // This happens when a device is added + // A future improvement is to cope with new controllers being plugged in + // when the game is running + case SDL_CONTROLLERDEVICEADDED: + printf("DEVICEADDED cdevice.which = %d\n", event.cdevice.which); + break; + + // If a controller button is pressed + case SDL_CONTROLLERBUTTONDOWN: + // Cycle through the controllers + for (int i = 0; i < GamePadsCount; i++) { + // Looking for the button that was pressed + if (event.cbutton.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(ConnectedControllers[i]))) { + // So the relevant state can be updated + ControllerInputs[i].Buttons[event.cbutton.button] = true; + } + } + break; + + // Do the same for releasing a button + case SDL_CONTROLLERBUTTONUP: + for (int i = 0; i < GamePadsCount; i++) { + if (event.cbutton.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(ConnectedControllers[i]))) { + ControllerInputs[i].Buttons[event.cbutton.button] = false; + } + } + break; + + // And something similar for axis motion + case SDL_CONTROLLERAXISMOTION: + for (int i = 0; i < GamePadsCount; i++) { + if (event.cbutton.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(ConnectedControllers[i]))) { + ControllerInputs[i].Axis[event.caxis.axis] = event.caxis.value; + } + } + break; + default: break; + } + } + + void KInput::BindAction(const std::string& name, InputState expectedInputState, + const std::shared_ptr& controllerObject, + KPlayerCommand command, KPlayer player) { + Actions.emplace_back(name, expectedInputState, controllerObject, command, player); + } + + void KInput::BindAxis(const std::string& name, const std::shared_ptr& controllerObject, KPlayerAxisCommand command, KPlayer player) + { + Axes.emplace_back(name, controllerObject, command, player); + } + + bool KInput::CheckAction(const std::string& name, const InputState expectedInputState, KPlayer player) { + const auto keyBindRange = KeyBinds[static_cast(player)].equal_range(name); + for (auto keyBind = keyBindRange.first; keyBind != keyBindRange.second; ++keyBind) { + switch (expectedInputState) { + case InputState::Pressed: + if (IsKeyboardButtonPressed(keyBind->second)) { + return true; + } + break; + case InputState::Released: + if (IsKeyboardButtonReleased(keyBind->second)) { + return true; + } + break; + case InputState::Hold: + if (IsKeyboardButtonHeld(keyBind->second)) { + return true; + } + break; + } + } + + const auto buttonBindRange = ButtonBinds[static_cast(player)].equal_range(name); + for (auto buttonBind = buttonBindRange.first; buttonBind != buttonBindRange.second; ++buttonBind) { + switch (expectedInputState) { + case InputState::Pressed: + if (IsControllerButtonPressed(buttonBind->second.first, buttonBind->second.second)) { + return true; + } + break; + case InputState::Released: + if (IsControllerButtonReleased(buttonBind->second.first, buttonBind->second.second)) { + return true; + } + break; + case InputState::Hold: + if (IsControllerButtonHeld(buttonBind->second.first, buttonBind->second.second)) { + return true; + } + break; + } + } + return false; + } + + void KInput::ProcessActions() { + for (auto& action : Actions) { + if (CheckAction(action.Name, action.ExpectedInputState, action.Player)) { + if (auto obj = action.ControllerObject.lock()) + std::invoke(action.Command, obj); + } + } + } + + void KInput::ProcessAxes() + { + for (auto& axis : Axes) { + if (auto obj = axis.ControllerObject.lock()) + std::invoke(axis.AxisCommand, obj, GetAxisValue(axis.Name, axis.Player)); + } + } + + void KInput::AddAxisMapping(const std::string& name, const float& scale, const std::string& key, const KPlayer& player) + { + if (key.rfind("Gamepad", 0) == 0) { + const auto delimiterPosition = key.find('_', 7); + const auto controllerIndex = static_cast(std::stoi(key.substr(7, delimiterPosition))); + auto axis = key.substr(delimiterPosition + 1); + std::for_each(axis.begin(), axis.end(), [](char& c) { + c = ::tolower(c); + }); + ControllerAxisBinds[static_cast(player)].emplace(name, KControllerAxisMapping(controllerIndex, SDL_GameControllerGetAxisFromString(axis.c_str()), scale)); + } + else { + SDL_Scancode scanCode = SDL_GetScancodeFromName(key.c_str()); + if (scanCode != SDL_SCANCODE_UNKNOWN) { + KeyAxisBinds[static_cast(player)].emplace(name, std::make_pair(scanCode, scale)); + } + } + } + + void KInput::AddActionMapping(const std::string& name, const std::string& key, const KPlayer& player) + { + if (key.rfind("Gamepad", 0) == 0) { + const auto delimiterPosition = key.find('_', 7); + const auto controllerIndex = static_cast(std::stoi(key.substr(7, delimiterPosition))); + auto button = key.substr(delimiterPosition + 1); + std::for_each(button.begin(), button.end(), [](char& c) { + c = ::tolower(c); + }); + ButtonBinds[static_cast(player)].emplace(name, std::make_pair(controllerIndex, SDL_GameControllerGetButtonFromString(button.c_str()))); + } + else { + SDL_Scancode scanCode = SDL_GetScancodeFromName(key.c_str()); + if (scanCode != SDL_SCANCODE_UNKNOWN) { + KeyBinds[static_cast(player)].emplace(name, scanCode); + } + } + } + + bool KInput::IsControllerButtonPressed(const Controllers controllerId, const SDL_GameControllerButton button) const + { + if (controllerId < Controllers::Controller1 || controllerId > static_cast(GamePadsCount - 1)) return false; + + return ControllerInputs[static_cast(controllerId)].Buttons[button] && !LastControllerInputs[static_cast(controllerId)].Buttons[button]; + } + bool KInput::IsControllerButtonReleased(const Controllers controllerId, const SDL_GameControllerButton button) const + { + if (controllerId < Controllers::Controller1 || controllerId > static_cast(GamePadsCount - 1)) return false; + + return !ControllerInputs[static_cast(controllerId)].Buttons[button] && LastControllerInputs[static_cast(controllerId)].Buttons[button]; + } + + bool KInput::IsControllerButtonHeld(const Controllers controllerId, const SDL_GameControllerButton button) const + { + if (controllerId < Controllers::Controller1 || controllerId > static_cast(GamePadsCount - 1)) return false; + + return ControllerInputs[static_cast(controllerId)].Buttons[button] && LastControllerInputs[static_cast(controllerId)].Buttons[button]; + } + + float KInput::GetControllerAxis(const Controllers controllerId, const SDL_GameControllerAxis axis) const + { + if (controllerId static_cast(GamePadsCount - 1)) return 0.0; + + const int value = ControllerInputs[static_cast(controllerId)].Axis[axis]; + if (std::abs(value) < Constants::JOYSTICK_DEAD_ZONE) return 0.0; + + return static_cast(value) / 32768.0f; + } + + bool KInput::IsKeyboardButtonPressed(const Uint8 scanCode) const + { + if (scanCode > KeyboardInputs.size() || scanCode > LastKeyboardInputs.size()) + return false; + return KeyboardInputs[scanCode] && KeyboardInputs[scanCode] != LastKeyboardInputs[scanCode]; + } + + bool KInput::IsKeyboardButtonReleased(const Uint8 scanCode) const + { + if (scanCode > KeyboardInputs.size() || scanCode > LastKeyboardInputs.size()) + return false; + return !KeyboardInputs[scanCode] && KeyboardInputs[scanCode] != LastKeyboardInputs[scanCode]; + } + + float KInput::GetAxisValue(const std::string& name, const KPlayer& player) const + { + float value = 0.f; + const auto keyAxisBindRange = KeyAxisBinds[static_cast(player)].equal_range(name); + for (auto keyAxisBind = keyAxisBindRange.first; keyAxisBind != keyAxisBindRange.second; ++keyAxisBind) { + if (IsKeyboardButtonHeld(keyAxisBind->second.first)) { + value += 1.0f * keyAxisBind->second.second; + } + } + + const auto controllerAxisBindRange = ControllerAxisBinds[static_cast(player)].equal_range(name); + for (auto controllerAxisBind = controllerAxisBindRange.first; controllerAxisBind != controllerAxisBindRange.second; ++controllerAxisBind) { + value += GetControllerAxis(controllerAxisBind->second.Controller, controllerAxisBind->second.Axis) * controllerAxisBind->second.Scale; + } + return value; + } + + bool KInput::IsKeyboardButtonHeld(const Uint8 scanCode) const + { + if (scanCode > KeyboardInputs.size()) + return false; + return KeyboardInputs[scanCode]; + } +} + diff --git a/2dkg_zad5/2dgk_zad5/KInput.h b/2dkg_zad5/2dgk_zad5/KInput.h new file mode 100644 index 0000000..4678c94 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KInput.h @@ -0,0 +1,61 @@ +#pragma once +#include +#include +#include + +#include "Controllers.h" +#include "GamePad.h" +#include "KActionBind.h" +#include "KPlayer.h" + +namespace KapitanGame { + struct KControllerAxisMapping { + KControllerAxisMapping(const Controllers controller, const SDL_GameControllerAxis axis, const float scale) : Controller(controller), Axis(axis), Scale(scale) {} + Controllers Controller; + SDL_GameControllerAxis Axis; + float Scale; + }; + + class KInput { + std::vector ConnectedControllers; + std::vector ControllerInputs; + std::vector LastControllerInputs; + std::vector KeyboardInputs; + std::vector LastKeyboardInputs; + std::vector Actions; + std::vector Axes; + std::unordered_multimap KeyBinds[static_cast(KPlayer::Player2) + 1]; + std::unordered_multimap> ButtonBinds[static_cast(KPlayer::Player2) + 1]; + std::unordered_multimap ControllerAxisBinds[static_cast(KPlayer::Player2) + 1]; + std::unordered_multimap> KeyAxisBinds[static_cast(KPlayer::Player2) + 1]; + int GamePadsCount; + bool Initialized; + + void ProcessActions(); + void ProcessAxes(); + public: + KInput(); + void Init(); + void Free(); + void HandleInputPreEvents(); + void HandleInputPostEvents(bool processInput); + void HandleEvent(const SDL_Event& event); + void BindAction(const std::string& name, InputState expectedInputState, const std::shared_ptr& controllerObject, KPlayerCommand command, KPlayer player); + void BindAxis(const std::string& name, const std::shared_ptr& controllerObject, KPlayerAxisCommand command, KPlayer player); + bool CheckAction(const std::string& name, InputState expectedInputState, KPlayer player); + + void AddAxisMapping(const std::string& name, const float& scale, const std::string& key, const KPlayer& player); + void AddActionMapping(const std::string& name, const std::string& key, const KPlayer& player); + + bool IsControllerButtonPressed(Controllers controllerId, SDL_GameControllerButton button) const; + bool IsControllerButtonReleased(Controllers controllerId, SDL_GameControllerButton button) const; + bool IsControllerButtonHeld(Controllers controllerId, SDL_GameControllerButton button) const; + float GetControllerAxis(Controllers controllerId, SDL_GameControllerAxis axis) const; + bool IsKeyboardButtonHeld(Uint8 scanCode) const; + bool IsKeyboardButtonPressed(Uint8 scanCode) const; + bool IsKeyboardButtonReleased(Uint8 scanCode) const; + float GetAxisValue(const std::string& name, const KPlayer& player) const; + }; +} + + diff --git a/2dkg_zad5/2dgk_zad5/KPawn.cpp b/2dkg_zad5/2dgk_zad5/KPawn.cpp new file mode 100644 index 0000000..acbdc1d --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KPawn.cpp @@ -0,0 +1,106 @@ +#include "KPawn.h" + +#include +//#include + +#include "Constants.h" +#include "KCollider.h" +//#include "KPlayerController.h" +#include "KSettings.h" + +namespace KapitanGame +{ + void KPawn::CollisionDetectionStep(const std::unordered_map, std::shared_ptr, Utils::PairHash>& objects) + { + if (GetCollider() == nullptr) return; + + const auto xPos = static_cast(GetPosition().X / Constants::TILE_WIDTH); + const auto yPos = static_cast(GetPosition().Y / Constants::TILE_HEIGHT); + + for (const auto& [oX, oY] : std::initializer_list>{ {0, 0}, {-1,0}, {1,0}, {0,-1}, {0,1}, {-1,-1}, {1,1}, {1,-1}, {-1,1} }) + { + auto it = objects.find({ xPos + oX, yPos + oY }); + if (it == objects.end()) continue; + const auto& other = it->second; + if (other->GetCollider() == nullptr) continue; + if (other->Id == Id) continue; + if (GetCollider()->IsCollision(other->GetCollider())) { + //if (typeid(*other) == typeid(KExit)) { + // if (const auto pc = PlayerController.lock()) { + // //pc->NotifyWin(); + // } + // return; + //} + const auto separationVector = GetCollider()->GetSeparationVector(other->GetCollider()); + + Position += separationVector; + if (GetPosition().Y - GetCollider()->GetHeight() / 2.f >= other->GetPosition().Y + other->GetCollider()->GetHeight() / 2.f) + { + Velocity.Y = 0; + } + if (GetPosition().Y + GetCollider()->GetHeight() / 2.f <= other->GetPosition().Y - other->GetCollider()->GetHeight() / 2.f) + { + Velocity.Y = 0; + CanJump = true; + CanDoubleJump = true; + HasJumped = false; + } + } + } + } + + void KPawn::MovementStep(const float& timeStep) + { + const float gravityScale = Velocity.Y > 0.f ? 3.f : 1.f; + Position.X += Velocity.X * timeStep; + Position.Y += Velocity.Y * timeStep + 1.f / 2.f * gravityScale * Settings->Gravity * timeStep * timeStep; + Velocity.Y += gravityScale * Settings->Gravity * timeStep; + } + + void KPawn::CollisionDetectionWithMapStep(const SDL_FRect& map) + { + const auto separationVector = GetCollider()->GetSeparationVector(map); + Position += separationVector; + if (separationVector.Y < 0) + { + Velocity.Y = 0; + CanJump = true; + CanDoubleJump = true; + HasJumped = false; + } + if (separationVector.Y > 0) + { + Velocity.Y = 0; + } + } + + void KPawn::AddXMovementInput(const float& input) + { + Velocity.X = input * Constants::SPEED * (1 - Constants::SMOOTH) + Velocity.X * Constants::SMOOTH; + } + + void KPawn::StopJump() { + if (!CanJump) { + if (Velocity.Y < -Settings->ShortJumpVelocity) + Velocity.Y = -Settings->ShortJumpVelocity; + } + } + + void KPawn::StartJump() { + if (Velocity.Y != 0.f) + { + CanJump = false; + } + if (CanJump) { + CanJump = false; + HasJumped = true; + Velocity.Y = -Settings->JumpInitialVelocity; + } + else if (HasJumped && CanDoubleJump) + { + CanDoubleJump = false; + Velocity.Y = -Settings->JumpInitialVelocity; + } + } +} + diff --git a/2dkg_zad5/2dgk_zad5/KPawn.h b/2dkg_zad5/2dgk_zad5/KPawn.h new file mode 100644 index 0000000..26f83ba --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KPawn.h @@ -0,0 +1,39 @@ +#pragma once +#include +#include +#include + +#include "KSolid.h" +#include "Utils.h" + +namespace KapitanGame +{ + class KSettings; + class KPlayerController; + + class KPawn : public KSolid + { + public: + KPawn(const KVector2D& position, const KTexture& texture, + const std::shared_ptr& playerController, KSettings* settings) + : KSolid(position, texture, nullptr), + PlayerController(playerController), CanJump(false), CanDoubleJump(false), HasJumped(true), Settings(settings) + { + } + + void CollisionDetectionStep(const std::unordered_map, std::shared_ptr, Utils::PairHash>& objects); + void MovementStep(const float& timeStep); + void CollisionDetectionWithMapStep(const SDL_FRect& map); + void AddXMovementInput(const float& input); + void StopJump(); + void StartJump(); + private: + const std::weak_ptr PlayerController; + KVector2D Velocity{ 0.f, 0.f }; + bool CanJump; + bool CanDoubleJump; + bool HasJumped; + KSettings* Settings; + }; +} + diff --git a/2dkg_zad5/2dgk_zad5/KPlayer.h b/2dkg_zad5/2dgk_zad5/KPlayer.h new file mode 100644 index 0000000..f5af9a2 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KPlayer.h @@ -0,0 +1,8 @@ +#pragma once + +namespace KapitanGame { + enum class KPlayer : int { + Player1, + Player2 + }; +} diff --git a/2dkg_zad5/2dgk_zad5/KPlayerController.cpp b/2dkg_zad5/2dgk_zad5/KPlayerController.cpp new file mode 100644 index 0000000..06665ba --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KPlayerController.cpp @@ -0,0 +1,55 @@ +#include "KPlayerController.h" + +#include "KInput.h" + +namespace KapitanGame { + void KPlayerController::SetupInputBindings(KInput& input) + { + input.BindAxis("MoveX", shared_from_this(), &KPlayerController::MoveXAxis, Player); + input.BindAction("Jump", InputState::Pressed, shared_from_this(), &KPlayerController::StartJump, Player); + input.BindAction("Jump", InputState::Released, shared_from_this(), &KPlayerController::StopJump, Player); + } + + void KPlayerController::MoveXAxis(const float axis) + { + Input.X += axis; + } + + void KPlayerController::StartJump() { + InputStartJump = true; + } + + void KPlayerController::StopJump() { + InputStopJump = true; + } + + void KPlayerController::Update(float deltaTime) + { + Input.Normalize(); + if (Pawn != nullptr) { + Pawn->AddXMovementInput(Input.X); + if (InputStartJump) + Pawn->StartJump(); + if (InputStopJump) + Pawn->StopJump(); + } + Input *= 0; + InputStartJump = false; + InputStopJump = false; + } + + void KPlayerController::Possess(KPawn* pawn) + { + Pawn = pawn; + } + + void KPlayerController::UnPossess() + { + Pawn = nullptr; + } + + const KPlayer& KPlayerController::GetPlayer() const + { + return Player; + } +} diff --git a/2dkg_zad5/2dgk_zad5/KPlayerController.h b/2dkg_zad5/2dgk_zad5/KPlayerController.h new file mode 100644 index 0000000..9b3b7ad --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KPlayerController.h @@ -0,0 +1,36 @@ +#pragma once +#include "KPawn.h" +#include "KPlayer.h" +#include "KVector2d.h" + +namespace KapitanGame { + class KInput; + + class KPlayerController : public std::enable_shared_from_this + { + public: + explicit KPlayerController(const KPlayer player) + : Player(player) + { + } + + void SetupInputBindings(KInput& input); + void MoveXAxis(float axis); + void StartJump(); + void StopJump(); + void Update(float deltaTime); + void Possess(KPawn* pawn); + void UnPossess(); + const KPlayer& GetPlayer() const; + private: + KVector2D Input{ 0.f, 0.f }; + const KPlayer Player; + KPawn* Pawn{}; + bool InputStartJump{}; + bool InputStopJump{}; + }; + typedef void (KPlayerController::* KPlayerCommand)(); + typedef void (KPlayerController::* KPlayerAxisCommand)(float input); +} + + diff --git a/2dkg_zad5/2dgk_zad5/KRect.cpp b/2dkg_zad5/2dgk_zad5/KRect.cpp new file mode 100644 index 0000000..8a815f7 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KRect.cpp @@ -0,0 +1,134 @@ +#include "KRect.h" + +#include "KCircle.h" +#include "Utils.h" + +namespace KapitanGame { + + float KRect::GetWidth() const { + return Width; + } + + float KRect::GetHeight() const { + return Height; + } + + KVector2D KRect::GetSeparationVector(const float left, const float right, const float top, const float bottom) { + const auto x = left < right ? -left : right; + const auto y = top < bottom ? -top : bottom; + auto vX = x; + auto vY = y; + if (fabs(x) < fabs(y)) + vY = 0; + if (fabs(x) > fabs(y)) + vX = 0; + return { vX, vY }; + } + + KVector2D KRect::GetSeparationVector(const KCircle* other) const { + const float l = GetLeft(); + const float r = GetRight(); + const float t = GetTop(); + const float b = GetBottom(); + const auto f = KVector2D(Utils::Clamp(other->GetParent()->GetPosition().X, l, r), + Utils::Clamp(other->GetParent()->GetPosition().Y, t, b)); + + if (other->GetParent()->GetPosition() == f) { + const auto left = other->GetParent()->GetPosition().X - l + other->GetRadius(); + const auto right = r - other->GetParent()->GetPosition().X + other->GetRadius(); + const auto top = other->GetParent()->GetPosition().Y - t + other->GetRadius(); + const auto bottom = b - other->GetParent()->GetPosition().Y + other->GetRadius(); + return GetSeparationVector(left, right, top, bottom) * -1.f; + } + return (other->GetParent()->GetPosition() - f) / (other->GetParent()->GetPosition() - f).Length() * (other->GetRadius() - (other->GetParent()->GetPosition() - f).Length()) * 1.f; + } + + KVector2D KRect::GetSeparationVector(const KRect* other) const { + const auto left = GetRight() - other->GetLeft(); + const auto right = other->GetRight() - GetLeft(); + const auto top = GetBottom() - other->GetTop(); + const auto bottom = other->GetBottom() - GetTop(); + + return GetSeparationVector(left, right, top, bottom); + } + + bool KRect::IsCollision(const KCircle* other) const { + const auto f = KVector2D(Utils::Clamp(other->GetParent()->GetPosition().X, GetLeft(), GetRight()), + Utils::Clamp(other->GetParent()->GetPosition().Y, GetTop(), GetBottom())); + + return (other->GetParent()->GetPosition() - f).Length() < other->GetRadius(); + } + + float KRect::GetLeft() const + { + return GetParent()->GetPosition().X - Width / 2; + } + + float KRect::GetRight() const + { + return GetParent()->GetPosition().X + Width / 2; + } + + float KRect::GetBottom() const + { + return GetParent()->GetPosition().Y + Height / 2; + } + + float KRect::GetTop() const + { + return GetParent()->GetPosition().Y - Height / 2; + } + + bool KRect::IsCollision(const KRect* other) const { + return GetRight() >= other->GetLeft() + && other->GetRight() >= GetLeft() + && GetBottom() >= other->GetTop() + && other->GetBottom() >= GetTop(); + } + + bool KRect::IsCollision(const KCollider* other) const + { + const auto circle = dynamic_cast(other); + if (circle != nullptr) + return IsCollision(circle); + + const auto rect = dynamic_cast(other); + if (rect != nullptr) + return IsCollision(rect); + + throw std::runtime_error("unsupported shape"); + } + + KVector2D KRect::GetSeparationVector(const KCollider* other) const + { + const auto circle = dynamic_cast(other); + if (circle != nullptr) + return GetSeparationVector(circle); + + const auto rect = dynamic_cast(other); + if (rect != nullptr) + return GetSeparationVector(rect); + + throw std::runtime_error("unsupported shape"); + } + + KVector2D KRect::GetSeparationVector(const SDL_FRect& map) const + { + KVector2D separationVector{ 0.f,0.f }; + if (GetParent()->GetPosition().X - map.x - GetWidth() / 2 < 0) + { + separationVector.X += -(GetParent()->GetPosition().X - map.x - GetWidth() / 2); + } + if (GetParent()->GetPosition().X + GetWidth() / 2 - (map.x + map.w) > 0) + { + separationVector.X += -(GetParent()->GetPosition().X - (map.x + map.w) + GetWidth() / 2); + } + if (GetParent()->GetPosition().Y - GetHeight() / 2 - map.y < 0) { + separationVector.Y += -(GetParent()->GetPosition().Y - map.y - GetHeight() / 2); + } + if (GetParent()->GetPosition().Y + GetHeight() / 2 - (map.y + map.h) > 0) { + separationVector.Y += -(GetParent()->GetPosition().Y - (map.y + map.h) + GetHeight() / 2); + } + return separationVector; + } +} diff --git a/2dkg_zad5/2dgk_zad5/KRect.h b/2dkg_zad5/2dgk_zad5/KRect.h new file mode 100644 index 0000000..c2022a5 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KRect.h @@ -0,0 +1,39 @@ +#pragma once +#include + +#include "KCollider.h" + +namespace KapitanGame { + class KCircle; + + class KRect final : public KCollider + { + public: + KRect(KSolid* parent, const float width, const float height) + : KCollider(parent), + Width(width), + Height(height) + { + } + + [[nodiscard]] float GetWidth() const override; + [[nodiscard]] float GetHeight() const override; + static KVector2D GetSeparationVector(float left, float right, float top, float bottom); + bool IsCollision(const KCollider* other) const override; + KVector2D GetSeparationVector(const KCollider* other) const override; + [[nodiscard]] KVector2D GetSeparationVector(const SDL_FRect& map) const override; + private: + KVector2D GetSeparationVector(const KCircle* other) const; + KVector2D GetSeparationVector(const KRect* other) const; + bool IsCollision(const KCircle* other) const; + [[nodiscard]] float GetLeft() const; + [[nodiscard]] float GetRight() const; + [[nodiscard]] float GetBottom() const; + [[nodiscard]] float GetTop() const; + bool IsCollision(const KRect* other) const; + float Width; + float Height; + }; +} + + diff --git a/2dkg_zad5/2dgk_zad5/KRectPawn.cpp b/2dkg_zad5/2dgk_zad5/KRectPawn.cpp new file mode 100644 index 0000000..0794cf2 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KRectPawn.cpp @@ -0,0 +1,15 @@ +#include "KRectPawn.h" + +namespace KapitanGame +{ + KRectPawn::KRectPawn(const KVector2D& position, const KTexture& texture, + const std::shared_ptr& playerController, KSettings* settings) : KPawn(position, texture, playerController, settings), + Collider(this, texture.GetWidth(), texture.GetHeight()) + { + } + + const KCollider* KRectPawn::GetCollider() const + { + return &Collider; + } +} diff --git a/2dkg_zad5/2dgk_zad5/KRectPawn.h b/2dkg_zad5/2dgk_zad5/KRectPawn.h new file mode 100644 index 0000000..750a564 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KRectPawn.h @@ -0,0 +1,20 @@ +#pragma once +#include "KPawn.h" +#include "KRect.h" + +namespace KapitanGame +{ + class KRectPawn final : + public KPawn + { + public: + KRectPawn(const KVector2D& position, const KTexture& texture, + const std::shared_ptr& playerController, KSettings* settings); + + const KCollider* GetCollider() const override; + private: + KRect Collider; + }; + + +} diff --git a/2dkg_zad5/2dgk_zad5/KSettings.cpp b/2dkg_zad5/2dgk_zad5/KSettings.cpp new file mode 100644 index 0000000..89deca4 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KSettings.cpp @@ -0,0 +1,98 @@ +#include "KSettings.h" + +#include +#include +#include "Constants.h" + +namespace KapitanGame +{ + KSettings::KSettings(const float maxJumpHeight, const float horizontalDistanceToMaxJumpHeight) : MaxJumpHeightValue(maxJumpHeight), + HorizontalDistanceToMaxJumpHeightValue(horizontalDistanceToMaxJumpHeight), + TimeToMaxHeightValue(NAN), + GravityValue(NAN), + JumpInitialVelocityValue(NAN), + ShortJumpVelocityValue(NAN), + CollisionEnabledValue(true), + Dirty(true), + MaxJumpHeight(this, &KSettings::GetMaxJumpHeight, &KSettings::SetMaxJumpHeight), + HorizontalDistanceToMaxJumpHeight(this, &KSettings::GetHorizontalDistanceToMaxJumpHeight, &KSettings::SetHorizontalDistanceToMaxJumpHeight), + TimeToMaxHeight(this, &KSettings::GetTimeToMaxHeight), + Gravity(this, &KSettings::GetGravity), + JumpInitialVelocity(this, &KSettings::GetJumpInitialVelocity), + ShortJumpVelocity(this, &KSettings::GetShortJumpVelocity), + CollisionEnabled(this, &KSettings::GetCollisionEnabled, &KSettings::SetCollisionEnabled) + { + } + + void KSettings::SetCollisionEnabled(const bool value) + { + if (value != CollisionEnabledValue) + CollisionEnabledValue = value; + } + + void KSettings::SetMaxJumpHeight(const float h) + { + if (h <= 0.f && MaxJumpHeightValue <= 0.f) return; + if (MaxJumpHeightValue != h) { // NOLINT(clang-diagnostic-float-equal) + MaxJumpHeightValue = std::max(h, 0.f); + Dirty = true; + } + } + + void KSettings::SetHorizontalDistanceToMaxJumpHeight(const float xn) + { + if (xn <= 0.f && HorizontalDistanceToMaxJumpHeightValue <= 0.f) return; + if (HorizontalDistanceToMaxJumpHeightValue != xn) { // NOLINT(clang-diagnostic-float-equal) + HorizontalDistanceToMaxJumpHeightValue = std::max(xn, 0.f); + Dirty = true; + } + } + + // ReSharper disable once CppMemberFunctionMayBeConst + bool KSettings::GetCollisionEnabled() + { + return CollisionEnabledValue; + } + + float KSettings::GetTimeToMaxHeight() + { + if (Dirty) + TimeToMaxHeightValue = HorizontalDistanceToMaxJumpHeightValue / Constants::SPEED; + return TimeToMaxHeightValue; + } + + float KSettings::GetGravity() + { + if (Dirty) { + const auto timeToMaxHeight = GetTimeToMaxHeight(); + GravityValue = 2.0f * MaxJumpHeightValue / (timeToMaxHeight * timeToMaxHeight); + } + return GravityValue; + } + + float KSettings::GetJumpInitialVelocity() + { + if (Dirty) + JumpInitialVelocityValue = 2.0f * MaxJumpHeightValue / GetTimeToMaxHeight(); + return JumpInitialVelocityValue; + } + + float KSettings::GetShortJumpVelocity() + { + if (Dirty) + ShortJumpVelocityValue = GetJumpInitialVelocity() / 2.f; + return ShortJumpVelocityValue; + } + + // ReSharper disable once CppMemberFunctionMayBeConst + float KSettings::GetMaxJumpHeight() + { + return MaxJumpHeightValue; + } + + // ReSharper disable once CppMemberFunctionMayBeConst + float KSettings::GetHorizontalDistanceToMaxJumpHeight() + { + return HorizontalDistanceToMaxJumpHeightValue; + } +} diff --git a/2dkg_zad5/2dgk_zad5/KSettings.h b/2dkg_zad5/2dgk_zad5/KSettings.h new file mode 100644 index 0000000..2c160ac --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KSettings.h @@ -0,0 +1,37 @@ +#pragma once +#include "Property.h" + +namespace KapitanGame +{ + class KSettings final + { + float MaxJumpHeightValue; + float HorizontalDistanceToMaxJumpHeightValue; + float TimeToMaxHeightValue; + float GravityValue; + float JumpInitialVelocityValue; + float ShortJumpVelocityValue; + bool CollisionEnabledValue; + bool Dirty; + + public: + + KSettings(float maxJumpHeight, float horizontalDistanceToMaxJumpHeight); + const Property MaxJumpHeight, HorizontalDistanceToMaxJumpHeight; + const ReadOnlyProperty TimeToMaxHeight, Gravity, JumpInitialVelocity, ShortJumpVelocity; + const Property CollisionEnabled; + + private: + void SetCollisionEnabled(bool value); + void SetMaxJumpHeight(float h); + void SetHorizontalDistanceToMaxJumpHeight(float xn); + bool GetCollisionEnabled(); + float GetTimeToMaxHeight(); + float GetGravity(); + float GetJumpInitialVelocity(); + float GetShortJumpVelocity(); + float GetMaxJumpHeight(); + float GetHorizontalDistanceToMaxJumpHeight(); + }; +} + diff --git a/2dkg_zad5/2dgk_zad5/KSolid.cpp b/2dkg_zad5/2dgk_zad5/KSolid.cpp new file mode 100644 index 0000000..59f7230 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KSolid.cpp @@ -0,0 +1,10 @@ +#include "KSolid.h" + +#include "KCamera.h" + +namespace KapitanGame +{ + KSolid::KSolid(const KVector2D& position, const KTexture& texture, SDL_Rect* tileClip, const SDL_RendererFlip flip) : KDrawable(position, texture, tileClip, flip) + { + } +} diff --git a/2dkg_zad5/2dgk_zad5/KSolid.h b/2dkg_zad5/2dgk_zad5/KSolid.h new file mode 100644 index 0000000..2ed07a5 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KSolid.h @@ -0,0 +1,21 @@ +#pragma once +#include + +#include "KDrawable.h" +#include "KTexture.h" +#include "KVector2d.h" + +namespace KapitanGame +{ + class KCamera; + class KCollider; + + class KSolid : public KDrawable + { + public: + KSolid(const KVector2D& position, const KTexture& texture, SDL_Rect* tileClip, SDL_RendererFlip flip = SDL_FLIP_NONE); + ~KSolid() override = default; + [[nodiscard]] virtual const KCollider* GetCollider() const = 0; + }; +} + diff --git a/2dkg_zad5/2dgk_zad5/KSolidTile.cpp b/2dkg_zad5/2dgk_zad5/KSolidTile.cpp new file mode 100644 index 0000000..0125434 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KSolidTile.cpp @@ -0,0 +1,9 @@ +#include "KSolidTile.h" + +namespace KapitanGame +{ + const KCollider* KSolidTile::GetCollider() const + { + return &Collider; + } +} diff --git a/2dkg_zad5/2dgk_zad5/KSolidTile.h b/2dkg_zad5/2dgk_zad5/KSolidTile.h new file mode 100644 index 0000000..af13113 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KSolidTile.h @@ -0,0 +1,23 @@ +#pragma once +#include "Constants.h" +#include "KSolid.h" +#include "KRect.h" + +namespace KapitanGame +{ + class KSolidTile final : + public KSolid + { + public: + KSolidTile(const KVector2D& position, const KTexture& texture, SDL_Rect* tileClip, const SDL_RendererFlip flip = SDL_FLIP_NONE) + : KSolid(position, texture, tileClip, flip), + Collider(this, tileClip != nullptr ? Constants::TILE_WIDTH * 1.f : texture.GetWidth() * 1.f, tileClip != nullptr ? Constants::TILE_HEIGHT * 1.f : texture.GetHeight() * 1.f) + { + } + + const KCollider* GetCollider() const override; + private: + KRect Collider; + }; +} + diff --git a/2dkg_zad5/2dgk_zad5/KTexture.cpp b/2dkg_zad5/2dgk_zad5/KTexture.cpp new file mode 100644 index 0000000..26c047a --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KTexture.cpp @@ -0,0 +1,155 @@ +#include "KTexture.h" + +#include +#include + +namespace KapitanGame { + KTexture::KTexture() : Texture(nullptr), Width(0), Height(0) + { + } + + KTexture::KTexture(KTexture&& other) noexcept + : Texture(other.Texture), + Width(other.Width), + Height(other.Height) { + other.Texture = nullptr; + other.Width = 0; + other.Height = 0; + } + + KTexture& KTexture::operator=(KTexture&& other) noexcept { + if (this == &other) + return *this; + Free(); + Texture = other.Texture; + Width = other.Width; + Height = other.Height; + other.Texture = nullptr; + other.Width = 0; + other.Height = 0; + return *this; + } + + KTexture::~KTexture() + { + //Deallocate + Free(); + + } + bool KTexture::LoadFromFile(const std::string& path, SDL_Renderer* renderer) + { + //Get rid of preexisting texture + Free(); + //The final texture + SDL_Texture* newTexture = nullptr; + + //Load image at specified path + SDL_Surface* loadedSurface = IMG_Load(path.c_str()); + if (loadedSurface == nullptr) + { + printf("Unable to load image %s! SDL_image Error: %s\n", path.c_str(), IMG_GetError()); + } + else + { + //Color key image + SDL_SetColorKey(loadedSurface, SDL_TRUE, SDL_MapRGB(loadedSurface->format, 0xFF, 0, 0xFF)); + //Create texture from surface pixels + newTexture = SDL_CreateTextureFromSurface(renderer, loadedSurface); + if (newTexture == nullptr) + { + printf("Unable to create texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError()); + } + else + { + //Get image dimensions + Width = loadedSurface->w; + Height = loadedSurface->h; + } + + //Get rid of old loaded surface + SDL_FreeSurface(loadedSurface); + } + + //Return success + Texture = newTexture; + return Texture != nullptr; + } + + bool KTexture::LoadFromSurface(SDL_Surface* surface, SDL_Renderer* renderer) + { + Free(); + Texture = SDL_CreateTextureFromSurface(renderer, surface); + if (Texture == nullptr) + { + printf("Unable to create texture from surface! SDL Error: %s\n", SDL_GetError()); + } + else + { + Width = surface->w; + Height = surface->h; + } + return Texture != nullptr; + } + + void KTexture::Free() + { + //Free texture if it exists + if (Texture != nullptr) + { + SDL_DestroyTexture(Texture); + Texture = nullptr; + Width = 0; + Height = 0; + } + } + + void KTexture::Render(SDL_Renderer* renderer, const float x, const float y, SDL_Rect* clip, const float scale) const + { + if (Texture == nullptr) return; + //Set rendering space and render to screen + SDL_FRect renderQuad = { + x * scale, y * scale, + static_cast(Width) * scale, static_cast(Height) * scale + }; + + //Set clip rendering dimensions + if (clip != nullptr) + { + renderQuad.w = clip->w; + renderQuad.h = clip->h; + } + + //Render to screen + SDL_RenderCopyF(renderer, Texture, clip, &renderQuad); + } + + void KTexture::RenderEx(SDL_Renderer* renderer, const float x, const float y, const double degrees, SDL_Rect* clip, const float scale, const SDL_RendererFlip flip) const + { + if (Texture == nullptr) return; + //Set rendering space and render to screen + SDL_FRect renderQuad = { + x * scale, y * scale, + static_cast(Width) * scale, static_cast(Height) * scale + }; + + //Set clip rendering dimensions + if (clip != nullptr) + { + renderQuad.w = clip->w; + renderQuad.h = clip->h; + } + + //Render to screen + SDL_RenderCopyExF(renderer, Texture, clip, &renderQuad, degrees, nullptr, flip); + } + + int KTexture::GetWidth() const + { + return Width; + } + + int KTexture::GetHeight() const + { + return Height; + } +} diff --git a/2dkg_zad5/2dgk_zad5/KTexture.h b/2dkg_zad5/2dgk_zad5/KTexture.h new file mode 100644 index 0000000..0542686 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KTexture.h @@ -0,0 +1,48 @@ +#pragma once +#include +#include + +namespace KapitanGame { + //Texture wrapper class + class KTexture { + public: + KTexture(); + + ~KTexture(); + + + KTexture(const KTexture& other) = delete; + + KTexture(KTexture&& other) noexcept; + + KTexture& operator=(const KTexture& other) = delete; + + KTexture& operator=(KTexture&& other) noexcept; + + //Loads image at specified path + bool LoadFromFile(const std::string& path, SDL_Renderer* renderer); + + bool LoadFromSurface(SDL_Surface* surface, SDL_Renderer* renderer); + + //Deallocates texture + void Free(); + + //Renders texture at given point + void Render(SDL_Renderer* renderer, float x, float y, SDL_Rect* clip = nullptr, float scale = 1.f) const; + + void RenderEx(SDL_Renderer* renderer, float x, float y, double degrees = 0, SDL_Rect* clip = nullptr, float scale = 1.f, SDL_RendererFlip flip = SDL_FLIP_NONE) const; + + //Gets image dimensions + int GetWidth() const; + int GetHeight() const; + + private: + //The actual hardware texture + SDL_Texture* Texture; + + //Image dimensions + int Width; + int Height; + }; + +} diff --git a/2dkg_zad5/2dgk_zad5/KTile.cpp b/2dkg_zad5/2dgk_zad5/KTile.cpp new file mode 100644 index 0000000..9836c73 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KTile.cpp @@ -0,0 +1,33 @@ +#include "KTile.h" + +#include + +#include "Constants.h" +#include "KTexture.h" + +namespace KapitanGame { + + int KTile::GetType() const { + return static_cast(Type); + } + + SDL_Rect KTile::GetBox() const { + return Box; + } + + KTile::KTile(const int x, const int y, const TileType tileType) { + //Get the offsets + Box.x = x; + Box.y = y; + + Box.w = Constants::TILE_WIDTH; + Box.h = Constants::TILE_HEIGHT; + + //Get the tile type + Type = tileType; + } + + void KTile::Render(SDL_Renderer* renderer, KTexture tileTextures[], const SDL_Rect& camera, const float scale) const { + tileTextures[GetType()].Render(renderer, Box.x - camera.x, Box.y - camera.y, nullptr, scale); + } +} diff --git a/2dkg_zad5/2dgk_zad5/KTile.h b/2dkg_zad5/2dgk_zad5/KTile.h new file mode 100644 index 0000000..1c25567 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KTile.h @@ -0,0 +1,34 @@ +#pragma once +#include +#include +#include + +namespace KapitanGame +{ + class KTexture; + + enum class TileType : std::uint32_t { + Default = 0, + Wall = 1 + }; + class KTile { + public: + //Initializes position and type + KTile(int x, int y, TileType tileType); + + //Shows the tile + void Render(SDL_Renderer* renderer, KTexture tileTextures[], const SDL_Rect& camera, float scale = 1.f) const; + + //Get the tile type + int GetType() const; + + SDL_Rect GetBox() const; + + private: + //The attributes of the tile + SDL_Rect Box{}; + + //The tile type + TileType Type; + }; +} diff --git a/2dkg_zad5/2dgk_zad5/KVector2d.h b/2dkg_zad5/2dgk_zad5/KVector2d.h new file mode 100644 index 0000000..5150f64 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/KVector2d.h @@ -0,0 +1,82 @@ +#pragma once +#include +#define _USE_MATH_DEFINES +#include + +namespace KapitanGame { + struct KVector2D { + float X; + float Y; + + KVector2D(const float x, const float y) : X(x), Y(y) {} + + float Length() const { return sqrt(X * X + Y * Y); } + float Length2() const { return X * X + Y * Y; } + + bool operator==(const KVector2D& v2) const { + return (*this - v2).Length2() < FLT_EPSILON * FLT_EPSILON; + } + + KVector2D operator+(const KVector2D& v2) const { + return { X + v2.X, Y + v2.Y }; + } + + friend KVector2D& operator+=(KVector2D& v1, const KVector2D& v2) { + v1.X += v2.X; + v1.Y += v2.Y; + return v1; + } + + KVector2D operator*(const float scalar) const + { + return { X * scalar, Y * scalar }; + } + + KVector2D& operator*=(const float scalar) { + X *= scalar; + Y *= scalar; + return *this; + } + + KVector2D operator-(const KVector2D& v2) const { + return { X - v2.X, Y - v2.Y }; + } + friend KVector2D& operator-=(KVector2D& v1, const KVector2D& v2) { + v1.X -= v2.X; + v1.Y -= v2.Y; + return v1; + } + + KVector2D operator/(const float scalar) const + { + return { X / scalar, Y / scalar }; + } + KVector2D& operator/=(const float scalar) { + X /= scalar; + Y /= scalar; + return *this; + } + float DotProduct(const KVector2D& v2) const { + return DotProduct(*this, v2); + } + KVector2D Reflect(const KVector2D& normal) const { + return Reflect(normal, *this); + } + + void Normalize() { + const float l = Length(); + if (l > 1.f) { + (*this) *= 1 / l; + } + } + + static KVector2D Reflect(const KVector2D& normal, const KVector2D& vector) { + const float dn = 2 * vector.DotProduct(normal); + return vector - normal * dn; + } + static float DotProduct(const KVector2D& v1, const KVector2D& v2) { + return v1.X * v2.X + v1.Y * v2.Y ; + } + }; +} + diff --git a/2dkg_zad5/2dgk_zad5/Main.cpp b/2dkg_zad5/2dgk_zad5/Main.cpp new file mode 100644 index 0000000..2944d8f --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/Main.cpp @@ -0,0 +1,8 @@ +#include "KGame.h" + +int main(int argc, char* args[]) +{ + KapitanGame::KGame game; + + return game.Run(argc, args); +} diff --git a/2dkg_zad5/2dgk_zad5/Property.h b/2dkg_zad5/2dgk_zad5/Property.h new file mode 100644 index 0000000..f4c6a5e --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/Property.h @@ -0,0 +1,54 @@ +#pragma once +namespace KapitanGame +{ + template + class ReadOnlyProperty { + protected: + C* Owner; + T(C::* Getter)(); + public: + ReadOnlyProperty() : Owner(), Getter() {} + ReadOnlyProperty(C* owner, T(C::* getter)()) : Owner(owner), Getter(getter) {} + + [[nodiscard]] T Get() const { + return (Owner->*Getter)(); + } + + // ReSharper disable once CppNonExplicitConversionOperator + operator T() const { + return Get(); + } + }; + + template + class Property : public ReadOnlyProperty { + void(C::* Setter)(T); + public: + Property() : ReadOnlyProperty(), Setter() {} + Property(C* owner, T(C::* getter)(), void(C::* setter)(T)) : ReadOnlyProperty(owner,getter), Setter(setter) {} + void Set(T value) const { + (this->Owner->*Setter)(value); + } + + const Property& operator = (T value) const { // NOLINT(misc-unconventional-assign-operator) + Set(value); + return *this; + } + const Property& operator -= (T value) const { // NOLINT(misc-unconventional-assign-operator) + Set(this->Get() - value); + return *this; + } + const Property& operator += (T value) const { // NOLINT(misc-unconventional-assign-operator) + Set(this->Get() + value); + return *this; + } + const Property& operator *= (T value) const { // NOLINT(misc-unconventional-assign-operator) + Set(this->Get() * value); + return *this; + } + const Property& operator /= (T value) const { // NOLINT(misc-unconventional-assign-operator) + Set(this->Get() / value); + return *this; + } + }; +} diff --git a/2dkg_zad5/2dgk_zad5/Utils.cpp b/2dkg_zad5/2dgk_zad5/Utils.cpp new file mode 100644 index 0000000..b889ed7 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/Utils.cpp @@ -0,0 +1,7 @@ +#include "Utils.h" + +namespace KapitanGame { + namespace Utils { + + } +} diff --git a/2dkg_zad5/2dgk_zad5/Utils.h b/2dkg_zad5/2dgk_zad5/Utils.h new file mode 100644 index 0000000..e7a9d5e --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/Utils.h @@ -0,0 +1,109 @@ +#pragma once +#include +#include +#include +#include +#include + +#include "KPlayer.h" + +namespace KapitanGame { + namespace Utils { + + template + std::string StringFormat(const std::string& format, Args ... args) + { + const int sizeS = std::snprintf(nullptr, 0, format.c_str(), args ...) + 1; // Extra space for '\0' + if (sizeS <= 0) { throw std::runtime_error("Error during formatting."); } + const auto size = static_cast(sizeS); + const auto buf = std::make_unique(size); + std::snprintf(buf.get(), size, format.c_str(), args ...); + return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside + } + template + constexpr const T& Clamp(const T& x, const T& min, const T& max) + { + return std::max(std::min(max, x), min); + } + template + constexpr const T& Clamp01(const T& x) + { + return Clamp(x, 0.f, 1.f); + } + template + constexpr const T& Lerp(const T& a, const T& b, const float& t) + { + return a + (b - a) * Clamp01(t); + } + + static std::default_random_engine make_default_random_engine() + { + // This gets a source of actual, honest-to-god randomness + std::random_device source; + + // Here, we fill an array of random data from the source + unsigned int random_data[10]; + for (auto& elem : random_data) { + elem = source(); + } + + // this creates the random seed sequence out of the random data + std::seed_seq seq(random_data + 0, random_data + 10); + return std::default_random_engine{ seq }; + } + + inline std::default_random_engine& GetRandomEngine() + { + // Making rng static ensures that it stays the same + // Between different invocations of the function + static std::default_random_engine rng(make_default_random_engine()); + return rng; + } + + inline double RandomNumber() { + const std::uniform_real_distribution dist(0.0, 1.0); + return dist(GetRandomEngine()); + } + + template + class Iterator { + typedef std::underlying_type_t ValT; + int Value; + public: + explicit Iterator(const C& f) : Value(static_cast(f)) {} + Iterator() : Value(static_cast(BeginVal)) {} + Iterator operator++() { + ++Value; + return *this; + } + C operator*() { return static_cast(Value); } + Iterator begin() { return *this; } //default ctor is good + Iterator end() { + static const Iterator END_ITERATE = ++Iterator(EndVal); // cache it + return END_ITERATE; + } + bool operator!=(const Iterator& i) { return Value != i.Value; } + }; + + inline KPlayer GetPlayerFromString(const std::string& str) + { + static std::unordered_map const TABLE = { {"Player1",KPlayer::Player1},{"Player2",KPlayer::Player2} }; + const auto it = TABLE.find(str); + if (it != TABLE.end()) { + return it->second; + } + return KPlayer::Player1; + } + + typedef Iterator KPlayerIterator; + + struct PairHash { + template + std::size_t operator () (const std::pair& p) const { + auto h1 = std::hash{}(p.first); + auto h2 = std::hash{}(p.second); + return h1 ^ h2; + } + }; + } +} diff --git a/2dkg_zad5/2dgk_zad5/assets/fonts/LICENSE.txt b/2dkg_zad5/2dgk_zad5/assets/fonts/LICENSE.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/assets/fonts/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/2dkg_zad5/2dgk_zad5/assets/fonts/OFL.txt b/2dkg_zad5/2dgk_zad5/assets/fonts/OFL.txt new file mode 100644 index 0000000..c0f4383 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/assets/fonts/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2012 The Press Start 2P Project Authors (cody@zone38.net), with Reserved Font Name "Press Start 2P". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/2dkg_zad5/2dgk_zad5/assets/fonts/PressStart2P-Regular.ttf b/2dkg_zad5/2dgk_zad5/assets/fonts/PressStart2P-Regular.ttf new file mode 100644 index 0000000..d1b14d4 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/assets/fonts/PressStart2P-Regular.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1732cbf0b83525ca6769c3a58d15de73f38122ed8c056ca7e30a6076767ef3d6 +size 116008 diff --git a/2dkg_zad5/2dgk_zad5/assets/fonts/Roboto-Thin.ttf b/2dkg_zad5/2dgk_zad5/assets/fonts/Roboto-Thin.ttf new file mode 100644 index 0000000..b7afed0 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/assets/fonts/Roboto-Thin.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bf9760a4821688d544ec7dafba7b060ab1fa758360403537579bf06792d290f8 +size 168488 diff --git a/2dkg_zad5/2dgk_zad5/assets/levels/level1.txt b/2dkg_zad5/2dgk_zad5/assets/levels/level1.txt new file mode 100644 index 0000000..0fa2acb --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/assets/levels/level1.txt @@ -0,0 +1,79 @@ +#100,15 +!0 + + 3 3 3 + 3 3 3 + 3 3 +g +hg 6g +5hg 6g 67hg +55hg 67hg 6g 6755hg 6 +555hg 6755hg67hg 6g 675555hg 67 +5555hg 675555h755hg 67hg 67555555hg 675 +55555hg 675555555555hg 6755hg 675555555555555555555555555555555555555555555556755 +5555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555 +5555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555 +5555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555 +5555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555 +!1 + + + + + + + 44 +4 444 44 4 4 4 44 444 4 +44444 444 4 444 44 44 4444 4 4 4444 4 4 44 4444444444 4444 444 44 +44444 444444444444 4 4444 4444444444444 44444444 4 4444 444444444444 44444444444 4444 +4444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444 +4444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444 +4444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444 +4444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444 +!2 + + + + 2222 + 222222222222 + 22222222222222 + 2222222222222222 + 22222222222222222 + 22222222222222222 + 2222222222 22222222222222222 + 2222222222 22222222222222222 + 2222222222 22222222222222222 + 2222222222 22222222222222222 + + +!3 + + + + 0000 + 0000 000 + 0 000 00 + 000 + 000000 + 0000 0 + 0 00000 00 00 0 + 00 00 00 0000 0 + 00 00 00 00 + P 00 00 00 00 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +!4 + + + + 1111 + 1111 + 1 11 + 11 + 11111 + 1111 + 1 11111 1111111111 + 11 1111 + 11 + +1111111111111111111111111111 11111111 111111 111111 11111111111111111111111111111111111111111111 diff --git a/2dkg_zad5/2dgk_zad5/assets/levels/level1_bg.txt b/2dkg_zad5/2dgk_zad5/assets/levels/level1_bg.txt new file mode 100644 index 0000000..ede4de7 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/assets/levels/level1_bg.txt @@ -0,0 +1,15 @@ + + + + 3333 + 333344444111 + 3 33144444444411 + 3314444444444444 + 333311444444444444444 + 3333 14444444444444444 + 3 33333 3366666633 14444444444444444 + 33 1144444411 3333 14444444444444444 + 33 11 1144444411 44444444444444444 + P 11 11 1144444411 44444444444444444 +3333333333333333333333333333113333333311333333113333331133333333333333333333333333333333333333333333 +1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 \ No newline at end of file diff --git a/2dkg_zad5/2dgk_zad5/assets/textures/P1.bmp b/2dkg_zad5/2dgk_zad5/assets/textures/P1.bmp new file mode 100644 index 0000000..c469977 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/assets/textures/P1.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f8b4227d619ec92a996fabcb5a1308e5799382b4b7277c2f48feaef857e528d +size 1782 diff --git a/2dkg_zad5/2dgk_zad5/config.json b/2dkg_zad5/2dgk_zad5/config.json new file mode 100644 index 0000000..c9a79fb --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/config.json @@ -0,0 +1,33 @@ +{ + "Input": { + "Player1": { + "AxisMappings": [ + { + "AxisName": "MoveX", + "Scale": 1.000000, + "Key": "D" + }, + { + "AxisName": "MoveX", + "Scale": -1.000000, + "Key": "A" + }, + { + "AxisName": "MoveX", + "Scale": 1.000000, + "Key": "Gamepad0_LeftX" + } + ], + "ActionMappings": [ + { + "ActionName": "Jump", + "Key": "Space" + }, + { + "ActionName": "Jump", + "Key": "Gamepad0_A" + } + ] + } + } +} \ No newline at end of file diff --git a/2dkg_zad5/2dgk_zad5/resource.h b/2dkg_zad5/2dgk_zad5/resource.h new file mode 100644 index 0000000..9965af9 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by 2dgk_zad5.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/2dkg_zad5/2dgk_zad5/vcpkg.json b/2dkg_zad5/2dgk_zad5/vcpkg.json new file mode 100644 index 0000000..14bb9c1 --- /dev/null +++ b/2dkg_zad5/2dgk_zad5/vcpkg.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json", + "name": "dgk-zad5", + "version": "0.1.0", + "dependencies": [ + "sdl2", + "nlohmann-json", + "sdl2-ttf", + "sdl2-image" + ] +} \ No newline at end of file diff --git a/2dkg_zad5/assets_src/building.piskel b/2dkg_zad5/assets_src/building.piskel new file mode 100644 index 0000000..f487c35 --- /dev/null +++ b/2dkg_zad5/assets_src/building.piskel @@ -0,0 +1 @@ +{"modelVersion":2,"piskel":{"name":"building","description":"","fps":12,"height":32,"width":32,"layers":["{\"name\":\"Layer 1\",\"opacity\":1,\"frameCount\":1,\"chunks\":[{\"layout\":[[0]],\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAsUlEQVRYR2NsbGz8zzCAgHFQOeDOYzZwWKjI/gLThPgV7ivB6jp2hhOlHt08kCaUECBkIbo81R0wEElhaKUBSoOc4jRAcweMpgFSg5hU9QTTAKkGkqqeoANG0wCpRTGl6imuC6jugNE0QGmQkpotqZ4GKHbAaBqgdpOLUJoimAZIjVNCFo7WBdgSOUXNclKDHD1KCaYBUi0gpJ6gA0bLAVKDkNJyg+Q0QKhcICSPrRwAAKcaoHCrImDsAAAAAElFTkSuQmCC\"}]}"]}} \ No newline at end of file diff --git a/2dkg_zad5/assets_src/cloud.piskel b/2dkg_zad5/assets_src/cloud.piskel new file mode 100644 index 0000000..a1c6f07 --- /dev/null +++ b/2dkg_zad5/assets_src/cloud.piskel @@ -0,0 +1 @@ +{"modelVersion":2,"piskel":{"name":"cloud","description":"","fps":12,"height":32,"width":32,"layers":["{\"name\":\"Layer 1\",\"opacity\":1,\"frameCount\":1,\"chunks\":[{\"layout\":[[0]],\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAsklEQVRYR+2WwRGAIAwE5WcLVmTNVmQL/nR44ACSSY5g+MQ35HbukmBYwO86jzu/sm57AEsUx6HLtXis5ADaOMQRtOynskdiEQEg4glKCsEC9IgjsZAAWuE6HsqRD8BoYS6SF+AvYc6JYCVM9cV8gEg2w4XkiHkP1E1ZTIGlE2kszQDEe0DTE9L1m09EcxP2RjEMoPXKUVA9oqwDml8s9C77GqIF0fMO4A64A+6AOzDdgQdHX0u4c/gHsgAAAABJRU5ErkJggg==\"}]}"]}} \ No newline at end of file diff --git a/2dkg_zad5/assets_src/grass.piskel b/2dkg_zad5/assets_src/grass.piskel new file mode 100644 index 0000000..a06d158 --- /dev/null +++ b/2dkg_zad5/assets_src/grass.piskel @@ -0,0 +1 @@ +{"modelVersion":2,"piskel":{"name":"grass","description":"","fps":12,"height":32,"width":32,"layers":["{\"name\":\"Layer 1\",\"opacity\":1,\"frameCount\":1,\"chunks\":[{\"layout\":[[0]],\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAb0lEQVRYR+2T0QnAIAwFzRpO1VE7lWtYIlhE89FgJT/np5iXy6ly11xT4BIAwg3o9e9AXFJkJ+Mt1iAPSG88vl9vvZ5vAHOIBdP3rMZeiDFjAfjzR85GLPijAF+GAQADGMAABjCAAQxgAAMYCDfwANHJNNEYkYYOAAAAAElFTkSuQmCC\"}]}"]}} \ No newline at end of file diff --git a/2dkg_zad5/assets_src/ground.piskel b/2dkg_zad5/assets_src/ground.piskel new file mode 100644 index 0000000..0c546c7 --- /dev/null +++ b/2dkg_zad5/assets_src/ground.piskel @@ -0,0 +1 @@ +{"modelVersion":2,"piskel":{"name":"ground","description":"","fps":12,"height":32,"width":32,"layers":["{\"name\":\"Layer 1\",\"opacity\":1,\"frameCount\":1,\"chunks\":[{\"layout\":[[0]],\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABPElEQVRYR91W2xHCQAg0xWgFtmEF2kx6sQO7sRmdywwZ5JZnEl/+RQ5ugVuWYRzHx0787rer/Gv+Ppwu0Gb5kAPyHc7H/QSAjJFAKGDGj2cwA1BT3tgw8BZUs6hibFX/GABqeRgAf0BrVKoD4AWVL5ifbzbPX2PCXAEvAKIQ+WQYJOO4ADTeWw9PSwbOgcaCyuD5HwA0Ca2Msm2ItIDOhCbhTwHw2CQr3YlROyCDZCqQBoDkWILwAGQvfVFDDUAURPVyqAXWYrJkEZFxeSzIAm20WnqQkWQeP0RDTUiq5X/LRmSJV/cI18jEWjzJhujtqmGlt5qPCQANoMzlfLNGfqjK3U64FESGquoc0JBqfbQ4bvmUAUSBeO0zJ2FWjDIsUndCjvjrAHgc37wCFYqhN6BuxZYKetlnqIsAPAF+tP9L2HSFWAAAAABJRU5ErkJggg==\"}]}"]}} \ No newline at end of file diff --git a/2dkg_zad5/assets_src/ground2.piskel b/2dkg_zad5/assets_src/ground2.piskel new file mode 100644 index 0000000..0752f39 --- /dev/null +++ b/2dkg_zad5/assets_src/ground2.piskel @@ -0,0 +1 @@ +{"modelVersion":2,"piskel":{"name":"ground2","description":"","fps":12,"height":32,"width":32,"layers":["{\"name\":\"Layer 1\",\"opacity\":1,\"frameCount\":1,\"chunks\":[{\"layout\":[[0]],\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABN0lEQVRYR91WyRHDMAiUC/XPTaQCN5FfCk1GmcFD0HLazpWfg0ALaFmmeVnvTfxu14v8a/uelxXaLB9yQL5Ta+0JgIyRQChgxo9nsAFQUz7ZMPEWVLOoYuxV/xgAankYAH9AR1RqAOAFlS+Yn+82z19jwlYBLwCiEPlkGCTjuAA03lsPT0sGzoHOgsrg+R8ANAmtjLJtiLSAzoQm4U8B8NgkKz2IUT8gg2QqkAaA5FiC8ABkL31RQw1AFET1cqgF1mKyZxGRcXksyAJttFp6kJFkHj9EQ01IquV/y0ZkidfwCI/IxFo8yYbo7aphpbeajwkADaDM5XyzRn6oysNOuBdEhqrqHNCQan20OG75lAFEgXjtMydhVowyLFJ3Qo746wB4HD+9AhWKoTegbsWWCnrZZ6iLADwAcfPzMd769scAAAAASUVORK5CYII=\"}]}"]}} \ No newline at end of file diff --git a/2dkg_zad5/assets_src/maptiles.psd b/2dkg_zad5/assets_src/maptiles.psd new file mode 100644 index 0000000..9c4ec7f --- /dev/null +++ b/2dkg_zad5/assets_src/maptiles.psd @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5072ae961b542d4e6147106414358baecd7a5c098bc9ee226dc5cdffc6c99368 +size 65409 diff --git a/2dkg_zad5/assets_src/mountain.piskel b/2dkg_zad5/assets_src/mountain.piskel new file mode 100644 index 0000000..3638e2c --- /dev/null +++ b/2dkg_zad5/assets_src/mountain.piskel @@ -0,0 +1 @@ +{"modelVersion":2,"piskel":{"name":"mountain","description":"","fps":12,"height":32,"width":32,"layers":["{\"name\":\"Layer 1\",\"opacity\":1,\"frameCount\":1,\"chunks\":[{\"layout\":[[0]],\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAANElEQVRYR+3QQREAAAQAQcJooI3+OYjhs5fgZnO6Nh5LAwQIECBAgAABAgQIECBAgMC3wAGoHDaB0QshaAAAAABJRU5ErkJggg==\"}]}"]}} \ No newline at end of file diff --git a/2dkg_zad5/assets_src/mountain2.piskel b/2dkg_zad5/assets_src/mountain2.piskel new file mode 100644 index 0000000..f70c1e4 --- /dev/null +++ b/2dkg_zad5/assets_src/mountain2.piskel @@ -0,0 +1 @@ +{"modelVersion":2,"piskel":{"name":"mountain2","description":"","fps":12,"height":32,"width":32,"layers":["{\"name\":\"Layer 1\",\"opacity\":1,\"frameCount\":1,\"chunks\":[{\"layout\":[[0]],\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAzElEQVRYR+WXyQ2AIBBFpR3L8myhtmQ8aCAhMQjM+jFRC+D9ZVgM0+Bv3+bzjgyj+CU4c+ECWuB/CKDcxxSgFfQELOuRWoAJaMEzGDoDHOevCCjdQyrgRg9LoCag5hwiQOretQIN/DsCJNuuvPxcDiJt/C4VWOAwAb1t51qB1b0pAcvgmZ5k1B0viV+VgJdz8VFMOY8LSt2zE6DgGrAoAe/Y2UOIdE4mMALenIFR8IcACqyd9N7vX+BA8wKWaW+JYAtAwFMFnARQ8CjgApWjjZRhAQvBAAAAAElFTkSuQmCC\"}]}"]}} \ No newline at end of file diff --git a/2dkg_zad5/assets_src/mountain3.piskel b/2dkg_zad5/assets_src/mountain3.piskel new file mode 100644 index 0000000..3bbf914 --- /dev/null +++ b/2dkg_zad5/assets_src/mountain3.piskel @@ -0,0 +1 @@ +{"modelVersion":2,"piskel":{"name":"mountain3","description":"","fps":12,"height":32,"width":32,"layers":["{\"name\":\"Layer 1\",\"opacity\":1,\"frameCount\":1,\"chunks\":[{\"layout\":[[0]],\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAWklEQVRYR2P8eULrPwMBkJTzm5ASsuUZB7UDaOlzWJBhDQF6WIzTAfS0HOQIxmgTVYKJkOwURoTGUQeMhsBoCIyGwGgIjIbAaAiMhsBoCIyGwGgIjIbAaAgAAGaSRYF+p7PUAAAAAElFTkSuQmCC\"}]}"]}} \ No newline at end of file diff --git a/2dkg_zad5/readme.txt b/2dkg_zad5/readme.txt new file mode 100644 index 0000000..a875c3b --- /dev/null +++ b/2dkg_zad5/readme.txt @@ -0,0 +1,16 @@ +IDE: Microsoft Visual Studio 2022 +Kompilator: Microsoft Visual C++ +Biblioteki: SDL2, SDL2_ttf, nlohmann-json + +Przetestowane pady: DualSense, DualShock 3 (ze sterownikiem/wrapperem XInputowym ScpToolkit), telefon Android (ze sterownikiem/wrapperem XInputowym HandyGamePad). + +Sterowanie (możliwa zmiana w config.json): +AD/lewa gałka pada - ruch poziomy +Spacja/A na padzie - skok + +F1 - tryb deweloperski +F2 (w trybie deweloperskim) - wł./wył. kolizji +F3/F4 - (w trybie deweloperskim) - zwiększ/zmiejsz maksymalną wysokość skoku. +F5/F6 - (w trybie deweloperskim) - zwiększ/zmiejsz maksymalną odległość poziomą skoku + +Uwaga: Do pobrania bibliotek projekt wykorzystuje vcpkg, menedżer bibliotek C++, w trybie Manifest. Należy go pobrać i skonfigurować zgodnie z instrukcją: https://github.com/microsoft/vcpkg#getting-started