diff --git a/resources/graphics/player.png b/resources/graphics/player.png new file mode 100644 index 0000000..ab3d2b2 Binary files /dev/null and b/resources/graphics/player.png differ diff --git a/src/character.cpp b/src/character.cpp index 632d234..b074b02 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -35,4 +35,11 @@ namespace cloonel { void Character::Destroy() noexcept { m_texture->Destroy(); } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + void Character::Draw() const { + const int2 pos(m_pos + 0.5f); + m_texture->Render(pos); + } } //namespace cloonel diff --git a/src/character.hpp b/src/character.hpp index 6fd2a8a..995abad 100644 --- a/src/character.hpp +++ b/src/character.hpp @@ -2,6 +2,7 @@ #define id0CEACFB045ED4C9F8688265AA41E30B0 #include "placeable.hpp" +#include "drawable.hpp" #include "vector.hpp" #include #include @@ -10,14 +11,15 @@ namespace cloonel { class SDLMain; class Texture; - class Character : public Placeable { + class Character : public Placeable, public Drawable { public: Character ( const std::string& parPath, SDLMain* parMain ); Character ( const std::string&& parPath, SDLMain* parMain ); - ~Character ( void ) noexcept; + virtual ~Character ( void ) noexcept; void Prepare ( void ); void Destroy ( void ) noexcept; + virtual void Draw ( void ) const; private: const std::unique_ptr m_texture; diff --git a/src/drawable.hpp b/src/drawable.hpp new file mode 100644 index 0000000..80a6cde --- /dev/null +++ b/src/drawable.hpp @@ -0,0 +1,14 @@ +#ifndef idC5A880D06A03407DB4E9FC21593A47FB +#define idC5A880D06A03407DB4E9FC21593A47FB + +namespace cloonel { + class Drawable { + public: + Drawable ( void ) = default; + virtual ~Drawable ( void ) noexcept = default; + + virtual void Draw ( void ) const = 0; + }; +} //namespace cloonel + +#endif diff --git a/src/gamebase.hpp b/src/gamebase.hpp index 68cea7d..08c9193 100644 --- a/src/gamebase.hpp +++ b/src/gamebase.hpp @@ -19,6 +19,8 @@ namespace cloonel { virtual void Prepare ( void ) = 0; virtual void Destroy ( void ) noexcept = 0; + SDLMain* SDLObject ( void ) { return m_sdlmain; } + private: virtual void OnRender ( void ) = 0; virtual void OnUpdate ( float parDelta ) = 0; diff --git a/src/gameplayscene.cpp b/src/gameplayscene.cpp index 96d5c0b..9e5e442 100644 --- a/src/gameplayscene.cpp +++ b/src/gameplayscene.cpp @@ -1,5 +1,6 @@ #include "gameplayscene.hpp" #include "mover.hpp" +#include "drawable.hpp" namespace cloonel { ///-------------------------------------------------------------------------- @@ -20,5 +21,8 @@ namespace cloonel { ///-------------------------------------------------------------------------- ///-------------------------------------------------------------------------- void GameplayScene::OnRender() { + for (auto itDrawable : m_drawables) { + itDrawable->Draw(); + } } } //namespace cloonel diff --git a/src/gameplayscene.hpp b/src/gameplayscene.hpp index 9ab03f4..887b5af 100644 --- a/src/gameplayscene.hpp +++ b/src/gameplayscene.hpp @@ -2,23 +2,27 @@ #define id1DF84BC48C0547D69F79499E3A25BFC5 #include +#include #include "gamebase.hpp" namespace cloonel { class Mover; + class Drawable; class GameplayScene : public GameBase { public: explicit GameplayScene ( SDLMain* parSdlMain ); virtual ~GameplayScene ( void ) noexcept = default; - void AddMover ( Mover* parMover ) { m_movers.push_back(parMover); } + void AddMover ( Mover* parMover ) { assert(parMover); m_movers.push_back(parMover); } + void AddDrawable ( const Drawable* parDrawable ) { assert(parDrawable); m_drawables.push_back(parDrawable); } private: virtual void OnRender ( void ); virtual void OnUpdate ( float parDelta ); std::vector m_movers; + std::vector m_drawables; }; } //namespace cloonel diff --git a/src/gameplaysceneclassic.cpp b/src/gameplaysceneclassic.cpp index 3b8e587..6bb9140 100644 --- a/src/gameplaysceneclassic.cpp +++ b/src/gameplaysceneclassic.cpp @@ -1,10 +1,17 @@ #include "gameplaysceneclassic.hpp" +#include "character.hpp" +#include "moversine.hpp" +#include namespace cloonel { + struct GameplaySceneClassic::LocalData { + }; + ///-------------------------------------------------------------------------- ///-------------------------------------------------------------------------- GameplaySceneClassic::GameplaySceneClassic (SDLMain* parSdlMain) : - GameplayScene(parSdlMain) + GameplayScene(parSdlMain), + m_local(new LocalData) { } @@ -17,10 +24,23 @@ namespace cloonel { ///-------------------------------------------------------------------------- ///-------------------------------------------------------------------------- void GameplaySceneClassic::Prepare() { + std::unique_ptr moverSine(new MoverSine()); + std::unique_ptr player(new Character("resources/graphics/player.png", SDLObject())); + + player->Prepare(); + player->SwapMover(moverSine.get()); + + std::swap(moverSine, m_moverSine); + std::swap(player, m_player); + + AddMover(m_moverSine.get()); + AddDrawable(m_player.get()); } ///-------------------------------------------------------------------------- ///-------------------------------------------------------------------------- void GameplaySceneClassic::Destroy() noexcept { + m_moverSine = std::move(std::unique_ptr(nullptr)); + m_player = std::move(std::unique_ptr(nullptr)); } } //namespace cloonel diff --git a/src/gameplaysceneclassic.hpp b/src/gameplaysceneclassic.hpp index ec073bd..bd52e74 100644 --- a/src/gameplaysceneclassic.hpp +++ b/src/gameplaysceneclassic.hpp @@ -2,9 +2,12 @@ #define idF6FF1F57C36842DC9B20E2F55C507C2E #include "gameplayscene.hpp" +#include namespace cloonel { class SDLMain; + class Character; + class MoverSine; class GameplaySceneClassic : public GameplayScene { public: @@ -15,6 +18,11 @@ namespace cloonel { virtual void Destroy ( void ) noexcept; private: + struct LocalData; + + const std::unique_ptr m_local; + std::unique_ptr m_player; + std::unique_ptr m_moverSine; }; } //namespace cloonel diff --git a/src/main.cpp b/src/main.cpp index 2b90e47..f2d218b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,27 +10,25 @@ namespace { ///------------------------------------------------------------------------- ///------------------------------------------------------------------------- void RunMainLoop (cloonel::GameplaySceneClassic& parGame) { + parGame.Prepare(); do { parGame.Exec(); } while (not parGame.WantsToQuit()); + parGame.Destroy(); } } //unnamed namespace ///---------------------------------------------------------------------------- ///following http://twinklebeardev.blogspot.co.uk/2012/07/lesson-1-hello-world.html ///---------------------------------------------------------------------------- -int main (int, char* []) { +int main (int, char* parArgv[]) { std::cout << GameName << " v" << GameVersionMajor << "." << GameVersionMinor << std::endl; int retVal = 0; cloonel::SDLMain sdlmain(GameName, DEF_WIN_WIDTH, DEF_WIN_HEIGHT); try { -#if defined(NDEBUG) - cloonel::PhysicsFSWrapper physfs(parArgc[0]); -#else - cloonel::PhysicsFSWrapper physfs(GAME_BASE_PATH); -#endif - physfs.Append("resources", "resources"); + cloonel::PhysicsFSWrapper physfs(parArgv[0]); + physfs.Append(GAME_BASE_PATH "/resources/", "resources"); sdlmain.Init(); diff --git a/src/mover.cpp b/src/mover.cpp index 4dca03f..11b985e 100644 --- a/src/mover.cpp +++ b/src/mover.cpp @@ -1,12 +1,38 @@ #include "mover.hpp" #include "placeable.hpp" #include +#include namespace cloonel { ///-------------------------------------------------------------------------- ///-------------------------------------------------------------------------- - void Mover::ApplyOffsetToPlaceable (std::size_t parIndex, const float2& parOffset) { - assert(m_placeables[parIndex]); - m_placeables[parIndex]->AddOffset(parOffset); + void Mover::ApplyOffsetToPlaceables (const float2& parOffset) { + for (Placeable* currPlaceable : m_placeables) { + if (currPlaceable) { + currPlaceable->AddOffset(parOffset); + } + } + } + + ///-------------------------------------------------------------------------- + ///-------------------------------------------------------------------------- + int Mover::RegisterPlaceable (Placeable* parPlaceable) { + assert(parPlaceable); + m_placeables.push_back(parPlaceable); + const std::size_t retVal = m_placeables.size(); + return static_cast(retVal); + } + + ///-------------------------------------------------------------------------- + ///-------------------------------------------------------------------------- + void Mover::UnregisterPlaceable (int parID) { + assert(static_cast(parID) <= m_placeables.size()); + assert(m_placeables[parID - 1]); + + m_placeables[parID - 1] = nullptr; + auto lastNull = std::find_if(m_placeables.rbegin(), m_placeables.rend(), [](const Placeable* parPlaceable) { return nullptr != parPlaceable; }); + if (m_placeables.rend() != lastNull) { + m_placeables.resize(m_placeables.size() - (lastNull - m_placeables.rbegin()) + 1); + } } } //namespace cloonel diff --git a/src/mover.hpp b/src/mover.hpp index d36d7ae..c082928 100644 --- a/src/mover.hpp +++ b/src/mover.hpp @@ -13,12 +13,13 @@ namespace cloonel { virtual ~Mover ( void ) noexcept = default; virtual void ApplyMotion ( float parDelta ) = 0; - void AddPlaceable ( Placeable* parPlaceable ) { m_placeables.push_back(parPlaceable); } + int RegisterPlaceable ( Placeable* parPlaceable ); + void UnregisterPlaceable ( int parID ); protected: virtual void Update ( float parDelta ) = 0; std::size_t PlaceableCount ( void ) const { return m_placeables.size(); } - void ApplyOffsetToPlaceable ( std::size_t parIndex, const float2& parOffset ); + void ApplyOffsetToPlaceables ( const float2& parOffset ); private: std::vector m_placeables; diff --git a/src/moveroneshot.cpp b/src/moveroneshot.cpp index 5124f64..0db1d5e 100644 --- a/src/moveroneshot.cpp +++ b/src/moveroneshot.cpp @@ -6,9 +6,6 @@ namespace cloonel { void MoverOneShot::ApplyMotion (float parDelta) { Update(parDelta); const float2 offs(GetOffset()); - const std::size_t placeableCount = PlaceableCount(); - for (std::size_t z = 0; z < placeableCount; ++z) { - ApplyOffsetToPlaceable(z, offs); - } + ApplyOffsetToPlaceables(offs); } } //namespace cloonel diff --git a/src/placeable.cpp b/src/placeable.cpp index c016222..b6cb316 100644 --- a/src/placeable.cpp +++ b/src/placeable.cpp @@ -1,10 +1,29 @@ #include "placeable.hpp" +#include "mover.hpp" +#include namespace cloonel { ///------------------------------------------------------------------------- ///------------------------------------------------------------------------- Placeable::Placeable (float parX, float parY) : - m_pos(parX, parY) + m_pos(parX, parY), + m_mover(nullptr), + m_idForMover(0) { } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + void Placeable::SwapMover (Mover* parMover) { + if (m_mover) { + assert(0 != m_idForMover); + m_mover->UnregisterPlaceable(m_idForMover); + m_idForMover = 0; + m_mover = nullptr; + } + if (parMover) { + m_idForMover = parMover->RegisterPlaceable(this); + m_mover = parMover; + } + } } //namespace cloonel diff --git a/src/placeable.hpp b/src/placeable.hpp index 381d52d..c4251ed 100644 --- a/src/placeable.hpp +++ b/src/placeable.hpp @@ -4,16 +4,24 @@ #include "vector.hpp" namespace cloonel { + class Mover; + class Placeable { public: const float2& GetPos ( void ) const noexcept { return m_pos; } void AddOffset ( const float2& parOffset ) noexcept { m_pos += parOffset; } + void SwapMover ( Mover* parMover ); + protected: Placeable ( float parX, float parY ); ~Placeable ( void ) noexcept = default; float2 m_pos; + + private: + Mover* m_mover; + int m_idForMover; }; } //namespace cloonel diff --git a/src/texture.cpp b/src/texture.cpp index 8fc1a1b..16e1e7a 100644 --- a/src/texture.cpp +++ b/src/texture.cpp @@ -8,12 +8,31 @@ #include #include +#define lengthof(a) (static_cast(sizeof(a)) / static_cast(sizeof(a[0]))) + namespace cloonel { namespace { enum GraphicFormat { GraphicFormat_Unknown, GraphicFormat_Png }; + + enum ColorChannelMask { +#if SDL_BYTE_ORDER == SDL_BIGENDIAN + ColorChannelMask_Red = 0xff000000, + ColorChannelMask_Green = 0xff0000, + ColorChannelMask_Blue = 0xff00, + ColorChannelMask_Alpha = 0xff +#elif SDL_BYTE_ORDER == SDL_LITTLEENDIAN + ColorChannelMask_Red = 0xff, + ColorChannelMask_Green = 0xff00, + ColorChannelMask_Blue = 0xff0000, + ColorChannelMask_Alpha = 0xff000000 +#else +# error "Unknonwn endianness" +#endif + }; + struct GraphicFormatItem { const char* extension; size_t length; @@ -26,8 +45,34 @@ namespace cloonel { GraphicFormat GuessGraphicFormatFromName (const std::string& parPath) __attribute__((pure)); - ///-------------------------------------------------------------------- - ///-------------------------------------------------------------------- + ///---------------------------------------------------------------------- + ///---------------------------------------------------------------------- + SDL_Surface* SurfaceFromPngRGBA (ushort2 parSize, int parBpp, const png_structp& parPngPtr, const png_infop& parInfoPtr) { + const png_size_t stride = png_get_rowbytes(parPngPtr, parInfoPtr); + assert(stride > 0); + + std::unique_ptr image(new uint8_t[stride * parSize.y()]); + uint8_t* const imagePtr = image.get(); + for (uint16_t y = 0; y < parSize.y(); ++y) { + png_read_row(parPngPtr, imagePtr + stride * y, nullptr); + } + + SDL_Surface* const retSurf = SDL_CreateRGBSurfaceFrom( + imagePtr, + parSize.x(), + parSize.y(), + parBpp, + static_cast(stride), + ColorChannelMask_Red, + ColorChannelMask_Green, + ColorChannelMask_Blue, + ColorChannelMask_Alpha + ); + return retSurf; + } + + ///---------------------------------------------------------------------- + ///---------------------------------------------------------------------- GraphicFormat GuessGraphicFormatFromName (const std::string& parPath) { const size_t dotPos = parPath.find_last_of('.'); if (parPath.npos == dotPos) { @@ -44,27 +89,82 @@ namespace cloonel { return GraphicFormat_Unknown; } - ///-------------------------------------------------------------------- - ///-------------------------------------------------------------------- + ///---------------------------------------------------------------------- + ///---------------------------------------------------------------------- + void ReadDataFromInputStream (png_structp parPngPtr, png_bytep parOutBytes, png_size_t parByteCountToRead) { + if (not png_get_io_ptr(parPngPtr)) + return; + PhysicsFSFile& rawfile = *static_cast(png_get_io_ptr(parPngPtr)); + + const int64_t read = rawfile.Read(static_cast(parOutBytes), static_cast(parByteCountToRead), 1); + if (read != static_cast(parByteCountToRead)) + return; + + return; + } + + ///---------------------------------------------------------------------- + ///http://blog.hammerian.net/2009/reading-png-images-from-memory/ + ///---------------------------------------------------------------------- SDL_Surface* LoadNewPngSurface (const std::string& parPath) { PhysicsFSFile rawfile(parPath.c_str(), PhysicsFSFile::OpenMode_Read, "graphics"); unsigned char header[8]; assert(rawfile.IsOpen()); - rawfile.Read(header, 8, 1); - if (png_sig_cmp(header, 0, 8)) + //Read the signature from the input stream + rawfile.Read(header, lengthof(header), 1); + if (png_sig_cmp(header, 0, lengthof(header))) return nullptr; - png_structp pngptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); - if (not pngptr) - return nullptr; + //Get the file info struct + png_structp pngptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (not pngptr) + return nullptr; - return nullptr; + //Get the data info struct + png_infop infoptr = png_create_info_struct(pngptr); + if (not infoptr) { + png_destroy_read_struct(&pngptr, nullptr, nullptr); + return nullptr; + } + + //Set the function that will be called to read data from the stream + png_set_read_fn(pngptr, &rawfile, ReadDataFromInputStream); + //Tell the lib we already verified the signature + png_set_sig_bytes(pngptr, lengthof(header)); + + //Read the header + png_uint_32 width, height; + int bitDepth, colorType; + png_read_info(pngptr, infoptr); + const png_uint_32 headerGetInfoRetVal = png_get_IHDR(pngptr, infoptr, &width, &height, &bitDepth, &colorType, nullptr, nullptr, nullptr); + if (static_cast(-1) == headerGetInfoRetVal) { + png_destroy_read_struct(&pngptr, &infoptr, nullptr); + return nullptr; + } + + SDL_Surface* retSurf; + switch (colorType) { + case PNG_COLOR_TYPE_RGB: + retSurf = nullptr; //SurfaceFromPngRGB(); + assert(false); //not implemented + break; + + case PNG_COLOR_TYPE_RGB_ALPHA: + retSurf = SurfaceFromPngRGBA(ushort2(static_cast(width), static_cast(height)), bitDepth * 4, pngptr, infoptr); + break; + + default: + png_destroy_read_struct(&pngptr, &infoptr, nullptr); + retSurf = nullptr; + } + + return retSurf; } } //unnamed namespace - ///------------------------------------------------------------------------ - ///------------------------------------------------------------------------ + ///-------------------------------------------------------------------------- + ///-------------------------------------------------------------------------- Texture::Texture (const std::string& parPath, SDLMain* parMain, bool parLoadNow) : m_path(parPath), m_texture(nullptr), @@ -74,14 +174,14 @@ namespace cloonel { Reload(); } - ///------------------------------------------------------------------------ - ///------------------------------------------------------------------------ + ///-------------------------------------------------------------------------- + ///-------------------------------------------------------------------------- Texture::~Texture() noexcept { Destroy(); } - ///------------------------------------------------------------------------ - ///------------------------------------------------------------------------ + ///-------------------------------------------------------------------------- + ///-------------------------------------------------------------------------- void Texture::Destroy() noexcept { if (m_texture) { SDL_DestroyTexture(m_texture); @@ -89,8 +189,8 @@ namespace cloonel { } } - ///------------------------------------------------------------------------ - ///------------------------------------------------------------------------ + ///-------------------------------------------------------------------------- + ///-------------------------------------------------------------------------- void Texture::Reload() { const GraphicFormat fmt = GuessGraphicFormatFromName(m_path); Destroy(); @@ -99,6 +199,7 @@ namespace cloonel { switch (fmt) { case GraphicFormat_Png: surf = LoadNewPngSurface(m_path.c_str()); + break; default: throw std::runtime_error(std::string("Unsupported file format for \"") + m_path + "\""); @@ -116,9 +217,9 @@ namespace cloonel { } } - ///------------------------------------------------------------------------ - ///------------------------------------------------------------------------ - void Texture::Render (const int2& parPos) { + ///-------------------------------------------------------------------------- + ///-------------------------------------------------------------------------- + void Texture::Render (int2 parPos) const { assert(IsLoaded()); const SDL_Rect dest = { parPos.x(), parPos.y(), m_size.x(), m_size.y() }; SDL_RenderCopy(m_sdlmain->GetRenderer(), m_texture, nullptr, &dest); diff --git a/src/texture.hpp b/src/texture.hpp index 598be44..e3a5fe7 100644 --- a/src/texture.hpp +++ b/src/texture.hpp @@ -17,7 +17,7 @@ namespace cloonel { void Reload ( void ); void Destroy ( void ) noexcept; bool IsLoaded ( void ) const { return nullptr != m_texture; } - void Render ( const int2& parPos ); + void Render ( int2 parPos ) const; private: const std::string m_path;