diff --git a/.clang-format b/.clang-format index de881c4..d703bfe 100644 --- a/.clang-format +++ b/.clang-format @@ -89,7 +89,7 @@ DisableFormat: false EmptyLineAfterAccessModifier: Never EmptyLineBeforeAccessModifier: LogicalBlock ExperimentalAutoDetectBinPacking: false -FixNamespaceComments: true +FixNamespaceComments: false ForEachMacros: - foreach - Q_FOREACH @@ -158,7 +158,7 @@ PenaltyBreakString: 1000 PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyIndentedWhitespace: 0 -PenaltyReturnTypeOnItsOwnLine: 60 +PenaltyReturnTypeOnItsOwnLine: 1000000 PointerAlignment: Left PPIndentWidth: -1 QualifierAlignment: Leave @@ -180,8 +180,8 @@ SpaceAroundPointerQualifiers: Default SpaceBeforeAssignmentOperators: true SpaceBeforeCaseColon: false SpaceBeforeCpp11BracedList: false -SpaceBeforeCtorInitializerColon: true -SpaceBeforeInheritanceColon: true +SpaceBeforeCtorInitializerColon: false +SpaceBeforeInheritanceColon: false SpaceBeforeParens: ControlStatements SpaceBeforeParensOptions: AfterControlStatements: true diff --git a/.clangd b/.clangd index c30d2f8..0f5733a 100644 --- a/.clangd +++ b/.clangd @@ -5,3 +5,4 @@ CompileFlags: -I/opt/devkitpro/devkitARM/arm-none-eabi/include, -I/opt/devkitpro/devkitARM/arm-none-eabi/include/c++/13.2.0/arm-none-eabi/, ] + Remove: [-mthumb-interwork] diff --git a/libgba-cpp/arch/cpu/interrupts.cpp b/libgba-cpp/arch/cpu/interrupts.cpp index 3a72d4e..784887e 100644 --- a/libgba-cpp/arch/cpu/interrupts.cpp +++ b/libgba-cpp/arch/cpu/interrupts.cpp @@ -53,7 +53,7 @@ auto& register_at(uint16_t address) { } void set_interrupt_bit(Handler handler, bool enabled) { - const auto& sender = senders[utils::value_of(handler.type)]; + const auto& sender = senders[gba::utils::value_of(handler.type)]; register_at(sender.offset)[sender.bit] = enabled; } diff --git a/libgba-cpp/arch/display/layers.cpp b/libgba-cpp/arch/display/layers.cpp index 585b702..6ece70e 100644 --- a/libgba-cpp/arch/display/layers.cpp +++ b/libgba-cpp/arch/display/layers.cpp @@ -1,5 +1,4 @@ #include - #include #include @@ -8,15 +7,16 @@ namespace { using gba::display::BackgroundControl; using gba::display::RawPalette; -using gba::arch::registers::display::bg_controls; - static auto const bg_address = reinterpret_cast*>(0x0500'0000); static auto& bg_palette = *new (bg_address) RawPalette<256>{}; -} +} // namespace BackgroundControl& gba::display::bg_control(gba::display::Layer layer) { - return *(reinterpret_cast(0x0400'0008) + utils::value_of(layer)); + return *( + reinterpret_cast(0x0400'0008) + + utils::value_of(layer) + ); } RawPalette<256>& gba::display::bg_palette() { diff --git a/libgba-cpp/arch/display/layers.h b/libgba-cpp/arch/display/layers.h index 74e22b9..02a4ffe 100644 --- a/libgba-cpp/arch/display/layers.h +++ b/libgba-cpp/arch/display/layers.h @@ -110,7 +110,7 @@ enum class MapSize { }; -constexpr geometry::Size extract_size(MapSize size) { +constexpr auto get_size_values(MapSize size) -> geometry::Size { switch (size) { case MapSize::TEXT_256X256: return {256, 256}; diff --git a/libgba-cpp/arch/display/objects.cpp b/libgba-cpp/arch/display/objects.cpp index 47f67e6..130ab12 100644 --- a/libgba-cpp/arch/display/objects.cpp +++ b/libgba-cpp/arch/display/objects.cpp @@ -1,20 +1,29 @@ #include +#include -using gba::display::Color; using gba::display::RawPalette; namespace { -static auto const obj_palette_address = reinterpret_cast*>(0x0500'0200); -static auto& obj_palette = - *new (obj_palette_address) RawPalette<256>{}; +static auto const obj_palette_address = + reinterpret_cast*>(0x0500'0200); +static auto& obj_palette = *new (obj_palette_address) RawPalette<256>{}; -} +static auto& oam = *new (reinterpret_cast(0x0700'0000)) + std::array{}; +static auto& sprite_tiles = *new (reinterpret_cast(0x0601'0000)) std::array{}; +// auto oam_map = array, int>, 128>{}; -namespace gba { +} // namespace -RawPalette<256>& display::obj_palette() { +auto gba::display::obj_palette() -> RawPalette<256>& { return ::obj_palette; } +auto gba::display::oam_entry(int index) -> OAMEntry& { + return ::oam[index]; } + +auto gba::display::sprite_tile(int index) -> gba::display::map::Tile& { + return sprite_tiles[index]; +}; diff --git a/libgba-cpp/arch/display/objects.h b/libgba-cpp/arch/display/objects.h index ec6c9e1..9fa89c8 100644 --- a/libgba-cpp/arch/display/objects.h +++ b/libgba-cpp/arch/display/objects.h @@ -1,7 +1,9 @@ #ifndef GBA_DRIVERS_DISPLAY_OBJ_H #define GBA_DRIVERS_DISPLAY_OBJ_H +#include #include +#include namespace gba::display { @@ -10,22 +12,20 @@ namespace gba::display { */ enum class ObjectMapping { /** - * Object data as unidimensional array. + * Object data as tile matrix. */ - MAP_ARRAY, + MAP_TILE_MATRIX, /** - * Object data as tile matrix. + * Object data as unidimensional array. */ - MAP_TILE_MATRIX, + MAP_ARRAY, }; - /** * Object Color Palette array. */ -RawPalette<256>& obj_palette(); - +auto obj_palette() -> RawPalette<256>&; /** * Changes object mapping mode. @@ -34,6 +34,108 @@ inline void object_mapping(ObjectMapping map) { gba::arch::registers::display::lcd_control[6] = utils::value_of(map); } +enum class ObjectMode { + NORMAL, + SEMI_TRANSPARENT, + OBJECT_WINDOW, +}; + +enum class ObjectColorMode { + COLORS_16, + COLORS_256, +}; + +enum class ObjectShape { + SQUARE, + HORIZONTAL, + VERTICAL, +}; + +enum class ObjectSize { + TINY, + SMALL, + MEDIUM, + BIG, +}; + +enum class ObjectPriority { + HIGHEST, + HIGH, + LOW, + LOWEST, +}; + +struct OAMEntry { + gba::utils::bitset attr0; + gba::utils::bitset attr1; + gba::utils::bitset attr2; + uint16_t _unused; + + auto set_x(int y) -> void { + attr1 = (attr1.to_ulong() & ~0b11111111) | (y & 0b11111111); + } + + auto set_y(int y) -> void { + attr0 = (attr0.to_ulong() & ~0b11111111) | (y & 0b11111111); + } + + auto rotation_scaling(bool enabled) -> void { + attr0[8] = enabled; + } + + auto visible(bool visible) -> void { + attr0[9] = not visible; + } + + auto mode(ObjectMode mode) -> void { + attr0 = (attr0.to_ulong() & ~0b110000000000) | + (utils::value_of(mode) << 10); + } + + auto mosaic(bool enabled) -> void { + attr0[12] = enabled; + } + + auto color_mode(ObjectColorMode mode) -> void { + attr0[13] = utils::value_of(mode); + } + + auto shape(ObjectShape shape) -> void { + attr0 = (attr0.to_ulong() & 0b0011111111111111) | + (utils::value_of(shape) << 14); + } + + auto size(ObjectSize size) -> void { + attr1 = (attr1.to_ulong() & 0b0011111111111111) | + (utils::value_of(size) << 14); + } + + auto flip_horizontally(bool flip) -> void { + attr1[12] = flip; + } + + auto flip_vertically(bool flip) -> void { + attr1[13] = flip; + } + + auto base_tile(int index) -> void { + attr2 = (attr2.to_ulong() & ~0b111111111) | (index & 0b111111111); + } + + auto priority(ObjectPriority priority) -> void { + attr2 = (attr2.to_ulong() & ~0b11000000000) | + (utils::value_of(priority) << 10); + } + + auto palette(int index) -> void { + attr2 = (attr2.to_ulong() & ~(0xff << 12)) | ((index & 0xff) << 12); + } +}; + +auto oam_entry(int index) -> OAMEntry&; + +auto sprite_tile(int index) -> gba::display::map::Tile&; + } #endif diff --git a/libgba-cpp/arch/display/tilemap.h b/libgba-cpp/arch/display/tilemap.h index ed76522..e9841d0 100644 --- a/libgba-cpp/arch/display/tilemap.h +++ b/libgba-cpp/arch/display/tilemap.h @@ -40,7 +40,7 @@ class Tile { Tile() = default; Tile(std::array rows): - rows_{move(rows)} + rows_{std::move(rows)} {} /** diff --git a/libgba-cpp/arch/display/video.h b/libgba-cpp/arch/display/video.h index 0f75ba5..dded336 100644 --- a/libgba-cpp/arch/display/video.h +++ b/libgba-cpp/arch/display/video.h @@ -95,14 +95,14 @@ class Color { /** * Constructs black color. */ - Color() = default; + constexpr Color() = default; /** * Constructs color from specified value. * * @param value Value (in X1B5G5R5 format). */ - Color(unsigned value): + constexpr Color(unsigned value): value_{static_cast(value)} {} @@ -113,7 +113,7 @@ class Color { * @param g Green channel intensity. * @param b Blue channel intensity. */ - Color(int r, int g, int b): + constexpr Color(int r, int g, int b): value_{static_cast( ((r & 0x1f) << 0) | ((g & 0x1f) << 5) | diff --git a/libgba-cpp/arch/registers.h b/libgba-cpp/arch/registers.h index e962a97..ab8de54 100644 --- a/libgba-cpp/arch/registers.h +++ b/libgba-cpp/arch/registers.h @@ -6,75 +6,7 @@ #include #include - -template -class bitset_bit { -public: - bitset_bit(T& value, std::size_t index) - : index_(index) - , value_(value) - {} - - bitset_bit& operator=(bool bit) { - if (bit) { - value_ |= (1 << index_); - } else { - value_ &= ~(1 << index_); - } - return *this; - }; - - operator bool() const { - return value_ & (1 << index_); - } - - operator bool() { - return value_ & (1 << index_); - } - -private: - std::size_t index_; - T& value_; -}; - -template -class bitset { -public: - bitset() = default; - - bitset(T value) - : value_(value) - {} - - auto operator[](std::size_t index) -> bitset_bit { - return bitset_bit(value_, index); - } - - auto operator[](std::size_t index) const -> const bitset_bit { - return bitset_bit(value_, index); - } - - auto operator[](std::size_t index) volatile const -> const bitset_bit { - return bitset_bit(value_, index); - } - - auto operator=(const T& other) -> uint32_t { - value_ = other; - return value_; - } - - auto operator|=(const T& other) -> T& { - value_ |= other; - return value_; - } - - auto to_ulong() const -> unsigned long int { - return value_; - } - -private: - T value_; -}; +#include /** * Elements related to GBA's physical architecture. @@ -118,18 +50,18 @@ namespace display { /** * Display Control Register. */ -static auto& lcd_control = at>(0x0400'0000); +static auto& lcd_control = at>(0x0400'0000); static_assert(sizeof(lcd_control) == 2, "LCD Control must exactly have 2 bytes."); /** * Green Swap Register (no official documentation). */ -static auto& green_swap = raw_at>(0x0400'0002); +static auto& green_swap = raw_at>(0x0400'0002); /** * Display status register. */ -static auto& lcd_status = raw_at>(0x0400'0004); +static auto& lcd_status = raw_at>(0x0400'0004); /** * Number of scanline currently being processed by PPU. @@ -218,7 +150,7 @@ static auto& window_inside = raw_at(0x0400'0048); static auto& window_outside = raw_at(0x0400'004a); static auto& mosaic_size = raw_at(0x0400'004c); -static auto& effect_control = raw_at>(0x0400'0050); +static auto& effect_control = raw_at>(0x0400'0050); static auto& blend_a = raw_at(0x0400'0052); static auto& blend_b = raw_at(0x0400'0054); @@ -230,38 +162,38 @@ static auto& bg_palette = raw_at>(0x05 */ namespace sound { -static auto& channel1_sweep = raw_at>(0x0400'0060); -static auto& channel1_envelope = raw_at>(0x0400'0062); -static auto& channel1_control = raw_at>(0x0400'0064); +static auto& channel1_sweep = raw_at>(0x0400'0060); +static auto& channel1_envelope = raw_at>(0x0400'0062); +static auto& channel1_control = raw_at>(0x0400'0064); -static auto& channel2_envelope = raw_at>(0x0400'0068); -static auto& channel2_control = raw_at>(0x0400'006c); +static auto& channel2_envelope = raw_at>(0x0400'0068); +static auto& channel2_control = raw_at>(0x0400'006c); -static auto& channel3_wave_bank = raw_at>(0x0400'0070); -static auto& channel3_length = raw_at>(0x0400'0072); -static auto& channel3_control = raw_at>(0x0400'0074); +static auto& channel3_wave_bank = raw_at>(0x0400'0070); +static auto& channel3_length = raw_at>(0x0400'0072); +static auto& channel3_control = raw_at>(0x0400'0074); -static auto& channel4_envelope = raw_at>(0x0400'0078); -static auto& channel4_noise = raw_at>(0x0400'007c); +static auto& channel4_envelope = raw_at>(0x0400'0078); +static auto& channel4_noise = raw_at>(0x0400'007c); -static auto& dmg_control = raw_at>(0x0400'0080); -static auto& directsound_control = raw_at>(0x0400'0082); -static auto& status = raw_at>(0x0400'0084); +static auto& dmg_control = raw_at>(0x0400'0080); +static auto& directsound_control = raw_at>(0x0400'0082); +static auto& status = raw_at>(0x0400'0084); -static auto& bias = raw_at>(0x0400'0088); +static auto& bias = raw_at>(0x0400'0088); -static auto& wave_ram0_l = raw_at>(0x0400'0090); -static auto& wave_ram0_h = raw_at>(0x0400'0092); +static auto& wave_ram0_l = raw_at>(0x0400'0090); +static auto& wave_ram0_h = raw_at>(0x0400'0092); -static auto& wave_ram1_l = raw_at>(0x0400'0094); -static auto& wave_ram1_h = raw_at>(0x0400'0096); +static auto& wave_ram1_l = raw_at>(0x0400'0094); +static auto& wave_ram1_h = raw_at>(0x0400'0096); -static auto& wave_ram2_l = raw_at>(0x0400'0098); -static auto& wave_ram2_h = raw_at>(0x0400'009a); +static auto& wave_ram2_l = raw_at>(0x0400'0098); +static auto& wave_ram2_h = raw_at>(0x0400'009a); // TODO: Fix data types -static auto& wave_ram3_l = raw_at>(0x0400'009c); -static auto& wave_ram3_h = raw_at>(0x0400'009e); +static auto& wave_ram3_l = raw_at>(0x0400'009c); +static auto& wave_ram3_h = raw_at>(0x0400'009e); static auto& dsa_fifo_0 = raw_at(0x0400'00a0); static auto& dsa_fifo_1 = raw_at(0x0400'00a1); @@ -317,14 +249,14 @@ static auto& serial_third = raw_at(0x0400'0126); static auto& serial_control = raw_at(0x0400'0128); static auto& serial_send_data = raw_at(0x0400'012a); -static auto& keypad_status = raw_at>(0x0400'0130); -static auto& keypad_interrupt = raw_at>(0x0400'0132); +static auto& keypad_status = raw_at>(0x0400'0130); +static auto& keypad_interrupt = raw_at>(0x0400'0132); -static auto& serial_mode = raw_at>(0x0400'0134); -static auto& serial_joy_control = raw_at>(0x0400'0140); +static auto& serial_mode = raw_at>(0x0400'0134); +static auto& serial_joy_control = raw_at>(0x0400'0140); static auto& serial_joy_receive_data = raw_at(0x0400'0150); static auto& serial_joy_transmit_data = raw_at(0x0400'0154); -static auto& serial_joy_receive_status = raw_at>(0x0400'0158); +static auto& serial_joy_receive_status = raw_at>(0x0400'0158); } @@ -333,9 +265,9 @@ static auto& serial_joy_receive_status = raw_at>(0x0400'0158); //---------------------------------------------------------------------------- namespace cpu { -static auto& interrupt_enable = raw_at>(0x0400'0200); +static auto& interrupt_enable = raw_at>(0x0400'0200); static volatile auto& interrupt_request = raw_at(0x0400'0202); -static auto& gamepak_wait_control = raw_at>(0x0400'0204); +static auto& gamepak_wait_control = raw_at>(0x0400'0204); static auto& master_enable = raw_at(0x0400'0208); } @@ -345,7 +277,7 @@ static auto& master_enable = raw_at(0x0400'0208); //---------------------------------------------------------------------------- namespace bios { -static volatile auto& interrupt_check = raw_at>(0x0300'7ff8); +static volatile auto& interrupt_check = raw_at>(0x0300'7ff8); } diff --git a/libgba-cpp/arch/sound/directsound.cpp b/libgba-cpp/arch/sound/directsound.cpp index e9b0ad1..861d21e 100644 --- a/libgba-cpp/arch/sound/directsound.cpp +++ b/libgba-cpp/arch/sound/directsound.cpp @@ -4,7 +4,7 @@ #include "registers.h" -using utils::value_of; +using gba::utils::value_of; using namespace gba::arch::registers::sound; namespace gba { diff --git a/libgba-cpp/arch/sound/sound.cpp b/libgba-cpp/arch/sound/sound.cpp index 710328d..752cdb6 100644 --- a/libgba-cpp/arch/sound/sound.cpp +++ b/libgba-cpp/arch/sound/sound.cpp @@ -3,7 +3,7 @@ #include #include -using utils::value_of; +using gba::utils::value_of; using namespace gba::arch::registers::sound; namespace gba { diff --git a/libgba-cpp/engine/graphics/graphics.h b/libgba-cpp/engine/graphics.h similarity index 51% rename from libgba-cpp/engine/graphics/graphics.h rename to libgba-cpp/engine/graphics.h index 83d0d2d..b7b47e9 100644 --- a/libgba-cpp/engine/graphics/graphics.h +++ b/libgba-cpp/engine/graphics.h @@ -2,6 +2,10 @@ #define LIBGBA_ENGINE_GRAPHICS_H #include +#include +#include +#include +#include /** * Graphics API diff --git a/libgba-cpp/engine/graphics/bitmap.cpp b/libgba-cpp/engine/graphics/bitmap.cpp index 582ed48..abe15df 100644 --- a/libgba-cpp/engine/graphics/bitmap.cpp +++ b/libgba-cpp/engine/graphics/bitmap.cpp @@ -1,4 +1,4 @@ -#include +#include namespace { diff --git a/libgba-cpp/engine/graphics/bitmap.h b/libgba-cpp/engine/graphics/bitmap.h index 8c639df..681be74 100644 --- a/libgba-cpp/engine/graphics/bitmap.h +++ b/libgba-cpp/engine/graphics/bitmap.h @@ -5,28 +5,23 @@ #include - namespace gba::graphics { - class PalettedBitmap { public: - PalettedBitmap( - int width, - int height, - const Palette& palette); + PalettedBitmap(int width, int height, const Palette& palette); PalettedBitmap( - int width, - int height, - const Palette& palette, - uint8_t data[] + int width, + int height, + const Palette& palette, + uint8_t data[] ): width_{width}, height_{height}, palette_{palette}, - data_{data} - {} + data_{data} { + } auto width() const { return width_; @@ -51,19 +46,19 @@ class PalettedBitmap { const uint8_t* data_; }; - class TiledBitmap { public: TiledBitmap( - int width, - int height, - const Tileset& tileset, - const Tilemap& tilemap): + int width, + int height, + const Tileset& tileset, + const Tilemap& tilemap + ): width_{width}, height_{height}, tileset_{tileset}, - tilemap_{tilemap} - {} + tilemap_{tilemap} { + } auto tileset() const { return tileset_; @@ -88,7 +83,6 @@ class TiledBitmap { const Tilemap& tilemap_; }; - -} +} // namespace gba::graphics #endif diff --git a/libgba-cpp/engine/graphics/map.h b/libgba-cpp/engine/graphics/map.h deleted file mode 100644 index 6d84a90..0000000 --- a/libgba-cpp/engine/graphics/map.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef LIBGBA_ENGINE_MAP_H -#define LIBGBA_ENGINE_MAP_H - -#include -#include "tilemap.h" - -namespace gba::graphics { - -using gba::display::MapSize; - -class Map { -public: - Map(MapSize size, - const Tileset& tileset, - const Tilemap& tilemap): - size_{size}, - tileset_{tileset}, - tilemap_{tilemap} - {} - - const auto tileset() const { - return tileset_; - } - - const auto tilemap() const { - return tilemap_; - } - - const auto size() const { - return size_; - } - - const auto width() const { - return gba::display::extract_size(size_).width; - } - - const auto height() const { - return gba::display::extract_size(size_).height; - } - -private: - const MapSize size_; - const Tileset& tileset_; - const Tilemap& tilemap_; -}; - -} - -#endif diff --git a/libgba-cpp/engine/graphics/maps.h b/libgba-cpp/engine/graphics/maps.h deleted file mode 100644 index 00e48cc..0000000 --- a/libgba-cpp/engine/graphics/maps.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef GBA_ENGINE_GRAPHICS_MAPS_H -#define GBA_ENGINE_GRAPHICS_MAPS_H - -#include - -#include - -#include "geometry.h" - -namespace gba::engine::graphics { - -template -using TileSet = std::array; - - -template -class TileMap { - using contents_t = std::array; -public: - TileMap(contents_t& contents): - contents{contents} - {} - - auto& length() const { - return MapLength; - } - - const auto& operator[](int i) const { - return contents[i]; - } - -private: - const contents_t& contents; -}; - - -class Map { -public: - virtual auto length() const -> std::size_t = 0; - virtual auto operator()(int x, int y) -> int& = 0; - virtual auto operator()(int x, int y) const -> const int& = 0; -}; - - -template -class BaseMap: Map { - using tileset_t = TileSet; - using tilemap_t = TileMap; -public: - BaseMap(tileset_t& tileset, tilemap_t& tilemap, geometry::Size size = {0, 0}): - _tileset{tileset}, - _tilemap{tilemap}, - size{size} - {} - - auto length() const override { - return _tilemap.length(); - } - - auto& tileset() { - return _tileset; - } - - const auto& tileset() const { - return _tileset; - } - - auto& tilemap() { - return _tilemap; - } - - const auto& tilemap() const { - return _tilemap; - } - - auto& operator()(int x, int y) override { - return _tilemap[x + size.width * y]; - } - - const auto& operator()(int x, int y) const override { - return _tilemap[x + size.width * y]; - } - -private: - tileset_t& _tileset; - tilemap_t& _tilemap; - geometry::Size size; -}; - -} - -#endif diff --git a/libgba-cpp/engine/graphics/palette.h b/libgba-cpp/engine/graphics/palette.h index 1ee9e97..8718ad0 100644 --- a/libgba-cpp/engine/graphics/palette.h +++ b/libgba-cpp/engine/graphics/palette.h @@ -4,7 +4,7 @@ #include #include -#include "graphics.h" +#include namespace gba::graphics { @@ -14,13 +14,13 @@ namespace gba::graphics { class Palette { public: template - Palette(const std::array& palette): + constexpr Palette(const std::array& palette): size_{N}, palette_{palette.data()} {} template - Palette(const std::array& palette): + constexpr Palette(const std::array& palette): size_{N}, palette_{palette.data()} {} @@ -33,7 +33,7 @@ class Palette { std::size_t size ): size_{size}, - palette_{reinterpret_cast(palette)} + palette_{reinterpret_cast(palette)} {} /** @@ -67,7 +67,7 @@ class Palette { static const Palette EMPTY; private: std::size_t size_; - const Color* palette_; + const gba::display::Color* palette_; }; /** diff --git a/libgba-cpp/engine/graphics/sprite.h b/libgba-cpp/engine/graphics/sprite.h new file mode 100644 index 0000000..51510c4 --- /dev/null +++ b/libgba-cpp/engine/graphics/sprite.h @@ -0,0 +1,122 @@ +#ifndef LIBGBA_CPP_ENGINE_GRAPHICS_SPRITE_H +#define LIBGBA_CPP_ENGINE_GRAPHICS_SPRITE_H + +#include + +#include +#include + +namespace gba::graphics { + enum class BitmapShape { + SQUARE_8x8, + SQUARE_16x16, + SQUARE_32x32, + SQUARE_64x64, + + VERTICAL_8x16, + VERTICAL_8x32, + VERTICAL_16x32, + VERTICAL_32x64, + + HORIZONTAL_16x8, + HORIZONTAL_32x8, + HORIZONTAL_32x16, + HORIZONTAL_64x32, + }; + + inline auto oam_size(BitmapShape const& shape) -> gba::display::ObjectSize { + switch (shape) { + default: + case BitmapShape::SQUARE_8x8: + case BitmapShape::VERTICAL_8x16: + case BitmapShape::HORIZONTAL_16x8: + return gba::display::ObjectSize::TINY; + case BitmapShape::SQUARE_16x16: + case BitmapShape::VERTICAL_8x32: + case BitmapShape::HORIZONTAL_32x8: + return gba::display::ObjectSize::SMALL; + case BitmapShape::SQUARE_32x32: + case BitmapShape::VERTICAL_16x32: + case BitmapShape::HORIZONTAL_32x16: + return gba::display::ObjectSize::MEDIUM; + case BitmapShape::SQUARE_64x64: + case BitmapShape::VERTICAL_32x64: + case BitmapShape::HORIZONTAL_64x32: + return gba::display::ObjectSize::BIG; + } + } + + inline auto oam_shape(BitmapShape const& shape) -> gba::display::ObjectShape { + switch (shape) { + default: + case BitmapShape::SQUARE_8x8: + case BitmapShape::SQUARE_16x16: + case BitmapShape::SQUARE_32x32: + case BitmapShape::SQUARE_64x64: + return gba::display::ObjectShape::SQUARE; + case BitmapShape::VERTICAL_8x16: + case BitmapShape::VERTICAL_8x32: + case BitmapShape::VERTICAL_16x32: + case BitmapShape::VERTICAL_32x64: + return gba::display::ObjectShape::VERTICAL; + case BitmapShape::HORIZONTAL_16x8: + case BitmapShape::HORIZONTAL_32x8: + case BitmapShape::HORIZONTAL_32x16: + case BitmapShape::HORIZONTAL_64x32: + return gba::display::ObjectShape::HORIZONTAL; + } + } + + struct Bitmap { + Tileset tiles; + BitmapShape shape; + std::optional base_tile_index = {}; + + auto add_to_screen(int base_tile_index) -> void { + if (this->base_tile_index) { + return; + } + + this->base_tile_index = base_tile_index; + + for (auto i = 0u; i < tiles.length(); ++i) { + gba::display::sprite_tile(i + base_tile_index) = tiles[i]; + } + } + }; + + struct Sprite { + Bitmap bitmap; + gba::geometry::Point pos; + gba::geometry::Point offset = {0, 0}; + bool visible = true; + std::optional> oam_entry = {}; + + auto add_to_screen(int oam_index, int base_tile_index) -> void { + if (oam_entry) { + return; + } + + this->oam_entry = gba::display::oam_entry(oam_index); + auto& oam_entry = this->oam_entry.value().get(); + oam_entry.base_tile(base_tile_index); + oam_entry.size(oam_size(bitmap.shape)); + oam_entry.shape(oam_shape(bitmap.shape)); + + bitmap.add_to_screen(base_tile_index); + } + + auto update() -> void { + if (not oam_entry) { + return; + } + + auto& entry = oam_entry.value().get(); + entry.set_x(pos.x + offset.x); + entry.set_y(pos.y + offset.y); + entry.visible(visible); + } + }; +} + +#endif diff --git a/libgba-cpp/engine/graphics/tilemap.h b/libgba-cpp/engine/graphics/tilemap.h index f5ad479..2c753c8 100644 --- a/libgba-cpp/engine/graphics/tilemap.h +++ b/libgba-cpp/engine/graphics/tilemap.h @@ -6,8 +6,8 @@ #include #include -#include "palette.h" +#include "palette.h" namespace gba::graphics { @@ -20,20 +20,17 @@ using Tile = gba::display::map::Tile; class Tileset { public: template - Tileset(const Palette& palette, - const std::array& tiles): + constexpr Tileset(const Palette& palette, const std::array& tiles): palette_{palette}, count_{N}, - tiles_{reinterpret_cast(tiles.data())} - {} + tiles_{reinterpret_cast(tiles.data())} { + } - Tileset(const Palette& palette, - const Tile tiles[], - unsigned count): + constexpr Tileset(const Palette& palette, const Tile tiles[], unsigned count): palette_{palette}, count_{count}, - tiles_{tiles} - {} + tiles_{tiles} { + } auto length() const { return count_; @@ -65,13 +62,13 @@ class Tilemap { template Tilemap(const std::array& tiles): count_{N}, - tiles_{reinterpret_cast(tiles.data())} - {} + tiles_{reinterpret_cast(tiles.data())} { + } Tilemap(const uint16_t tiles[], unsigned count): count_{count}, - tiles_{tiles} - {} + tiles_{tiles} { + } auto length() const { return count_; @@ -90,7 +87,6 @@ class Tilemap { const uint16_t* tiles_; }; - class Map { public: Map(const Tileset& tileset, @@ -104,8 +100,8 @@ class Map { layer1_{layer1}, layer2_{layer2}, layer3_{layer3}, - size_{size} - {} + size_{size} { + } const auto& tileset() const { return tileset_; @@ -113,11 +109,16 @@ class Map { const auto& layer(const gba::display::Layer layer) const { switch (layer) { - case display::Layer::BG0: return layer0_; - case display::Layer::BG1: return layer1_; - case display::Layer::BG2: return layer2_; - case display::Layer::BG3: return layer3_; - default: return layer0_; + case display::Layer::BG0: + return layer0_; + case display::Layer::BG1: + return layer1_; + case display::Layer::BG2: + return layer2_; + case display::Layer::BG3: + return layer3_; + default: + return layer0_; } } @@ -126,11 +127,11 @@ class Map { } auto width() const { - return extract_size(size_).width; + return get_size_values(size_).width; } auto height() const { - return extract_size(size_).height; + return get_size_values(size_).height; } const auto& layer0() const { @@ -149,7 +150,11 @@ class Map { /** * Loads map into map memory. */ -inline void load_tilemap(const Tilemap& tilemap, int screenblock, int charblock) { +inline void load_tilemap( + const Tilemap& tilemap, + int screenblock, + int charblock +) { const auto base = 0x400 * screenblock + 0x2000 * charblock; for (auto i = 0u; i < tilemap.length(); ++i) { display::map::tilemap()[i + base] = tilemap[i]; @@ -169,8 +174,8 @@ inline void load_tileset(const Tileset& tileset) { * Setup and load map into memory. */ inline void load_map(const Map& map) { - using gba::display::Layer; using gba::display::BGPriority; + using gba::display::Layer; using gba::display::PaletteMode; /* load the palette from the image into palette memory*/ @@ -180,7 +185,8 @@ inline void load_map(const Map& map) { load_tileset(map.tileset()); /* set all control the bits in this register */ - constexpr auto layers = std::array{Layer::BG0, Layer::BG1, Layer::BG2, Layer::BG3}; + constexpr auto layers = + std::array{Layer::BG0, Layer::BG1, Layer::BG2, Layer::BG3}; constexpr auto priorities = std::array{ BGPriority::LOWEST, BGPriority::LOW, @@ -205,6 +211,41 @@ inline void load_map(const Map& map) { } } +constexpr auto null_tilemap = std::array{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + } #endif diff --git a/libgba-cpp/engine/map/objects.h b/libgba-cpp/engine/map/objects.h new file mode 100644 index 0000000..7389daf --- /dev/null +++ b/libgba-cpp/engine/map/objects.h @@ -0,0 +1,47 @@ +#ifndef LIBGBA_ENGINE_MAPOBJECTS_H +#define LIBGBA_ENGINE_MAPOBJECTS_H + +#include "libgba-cpp/utils/geometry.h" +namespace gba::map { + +/** + * For bitmaps with a custom drawing routine. + */ +class BitmapModeObject { + using DrawFunction = + void (*)(BitmapModeObject const&); + +public: + BitmapModeObject(gba::geometry::Point const& position, gba::geometry::Size size, DrawFunction draw_function): + _position{position}, + _size{size}, + draw_function{draw_function} { + } + + auto draw() const -> void { + draw_function(*this); + } + + auto position() -> gba::geometry::Point& { + return _position; + } + + auto position() const -> gba::geometry::Point const& { + return _position; + } + + auto size() const -> gba::geometry::Size const& { + return _size; + } + +private: + gba::geometry::Point _position; + gba::geometry::Size _size; + + DrawFunction draw_function; +}; + + +} + +#endif diff --git a/libgba-cpp/utils/bitset.h b/libgba-cpp/utils/bitset.h new file mode 100644 index 0000000..f966e92 --- /dev/null +++ b/libgba-cpp/utils/bitset.h @@ -0,0 +1,79 @@ +#ifndef LIBGBACPP_UTILS_BITSET_H +#define LIBGBACPP_UTILS_BITSET_H + +#include + +namespace gba::utils { + +template +class bitset_bit { +public: + bitset_bit(T& value, std::size_t index) + : index_(index) + , value_(value) + {} + + bitset_bit& operator=(bool bit) { + if (bit) { + value_ |= (1 << index_); + } else { + value_ &= ~(1 << index_); + } + return *this; + }; + + operator bool() const { + return value_ & (1 << index_); + } + + operator bool() { + return value_ & (1 << index_); + } + +private: + std::size_t index_; + T& value_; +}; + +template +class bitset { +public: + bitset() = default; + + bitset(T value) + : value_(value) + {} + + auto operator[](std::size_t index) -> bitset_bit { + return bitset_bit(value_, index); + } + + auto operator[](std::size_t index) const -> const bitset_bit { + return bitset_bit(value_, index); + } + + auto operator[](std::size_t index) volatile const -> const bitset_bit { + return bitset_bit(value_, index); + } + + auto operator=(const T& other) -> uint32_t { + value_ = other; + return value_; + } + + auto operator|=(const T& other) -> T& { + value_ |= other; + return value_; + } + + auto to_ulong() const -> unsigned long int { + return value_; + } + +private: + T value_; +}; + +} + +#endif diff --git a/libgba-cpp/utils/general.h b/libgba-cpp/utils/general.h index 261e4cf..4fe80cb 100644 --- a/libgba-cpp/utils/general.h +++ b/libgba-cpp/utils/general.h @@ -2,11 +2,12 @@ #define GBA_UTILS_GENERAL_H #include +#include /** * General C++ Utilities. */ -namespace utils { +namespace gba::utils { /** * Extracts value from enum. @@ -17,6 +18,14 @@ constexpr auto value_of(Enum e) return static_cast>(e); } +inline auto random() -> int { + static auto gen = std::mt19937{std::random_device{}()}; + static auto distrib = std::uniform_int_distribution<>{1, 1500}; + + return distrib(gen); +} + + } #endif diff --git a/tests/gen_tilemap/forest_tileset.cpp b/tests/gen_tilemap/forest_tileset.cpp index d264fe5..2b807d2 100644 --- a/tests/gen_tilemap/forest_tileset.cpp +++ b/tests/gen_tilemap/forest_tileset.cpp @@ -1,5 +1,7 @@ #include "forest_tileset.h" +#include + namespace { using gba::graphics::Color; using gba::graphics::Palette; diff --git a/tests/gen_tilemap/forest_tileset.h b/tests/gen_tilemap/forest_tileset.h index be9051f..99f2ff9 100644 --- a/tests/gen_tilemap/forest_tileset.h +++ b/tests/gen_tilemap/forest_tileset.h @@ -1,10 +1,8 @@ #ifndef TEST_MAPS_FOREST_H #define TEST_MAPS_FOREST_H -#include - -#include #include +#include namespace test::maps { @@ -13,4 +11,4 @@ extern const gba::graphics::Tileset forest_tileset; } -#endif \ No newline at end of file +#endif diff --git a/tests/meson.build b/tests/meson.build index 9a3ac62..a9d8cd1 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,11 +1,12 @@ generic_test_dirs = [ - 'alphablend', - 'interrupts', - 'pong', - 'mode5', - 'mosaic', - 'sound', - 'windowing', + # 'alphablend', + # 'interrupts', + # 'pong', + 'pong-mode0', + # 'mode5', + # 'mosaic', + # 'sound', + # 'windowing', ] foreach test_dir : generic_test_dirs diff --git a/tests/pong-mode0/.gitignore b/tests/pong-mode0/.gitignore new file mode 100644 index 0000000..510ee96 --- /dev/null +++ b/tests/pong-mode0/.gitignore @@ -0,0 +1,2 @@ +build +subprojects/libgba diff --git a/tests/pong-mode0/cross_file.ini b/tests/pong-mode0/cross_file.ini new file mode 100644 index 0000000..1b3defc --- /dev/null +++ b/tests/pong-mode0/cross_file.ini @@ -0,0 +1,20 @@ +[binaries] +c = '/opt/devkitpro/devkitARM/bin/arm-none-eabi-gcc' +cpp = '/opt/devkitpro/devkitARM/bin/arm-none-eabi-g++' +ar = '/opt/devkitpro/devkitARM/bin/arm-none-eabi-ar' +strip = '/opt/devkitpro/devkitARM/bin/arm-none-eabi-strip' + +[build-in options] +cpp_eh = 'none' +cpp_rtti = false +cpp_args = [ + '-ffast-math', + '-fomit-frame-pointer', + '-mthumb-interwork', ] +default_library = 'static' + +[host_machine] +system = 'none' +cpu_family = 'arm' +cpu = 'arm7tdmi' +endian = 'little' diff --git a/tests/pong-mode0/main.cpp b/tests/pong-mode0/main.cpp new file mode 100644 index 0000000..833fb7c --- /dev/null +++ b/tests/pong-mode0/main.cpp @@ -0,0 +1,479 @@ +#include +#include +#include +#include +#include +#include + +#include "libgba-cpp/arch/display/objects.h" + +namespace test { + +constexpr auto WHITE = gba::display::Color{31, 31, 31}; +constexpr auto RED = gba::display::Color{31, 0, 0}; +constexpr auto GREEN = gba::display::Color{0, 15, 0}; + +const auto screen_obj_palette = gba::graphics::Palette{ + std::array{gba::display::Color{}, WHITE, RED, GREEN} +}; + +// clang-format off + +const std::array raw_sample_palette = { + gba::display::Color + { 0, 0, 0}, {31, 31, 31}, +}; + +const std::array raw_tileset = { + /* + 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16, 17,18,19,20,21,22,23,24, 25,26,27,28,29,30,31,32, + */ + /* 1 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 2 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 3 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 4 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 5 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 6 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 7 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 8 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 9 */ /* -------------------------------------------------------------------------------------------------------- */ + /* 10 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 11 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 12 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 13 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 14 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 15 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 16 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 17 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 18 */ /* -------------------------------------------------------------------------------------------------------- */ + /* 19 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 20 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 21 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 22 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 1, 2, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 23 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 24 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 25 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 26 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 27 */ /* -------------------------------------------------------------------------------------------------------- */ + /* 28 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 2, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 29 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 2, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 20 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 2, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 31 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 2, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 32 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 2, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 33 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 2, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 34 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 2, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, + /* 35 */ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, /**/ 2, 0, 0, 0, 0, 0, 0, 0, /**/ 0, 0, 0, 0, 0, 0, 0, 0, +}; + +const std::array sample_tiles = { + gba::display::map::Tile{{ + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + }}, + gba::display::map::Tile{{ + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + }}, + gba::display::map::Tile{{ + 0x00000000, 0x01000000, + 0x00000000, 0x01000000, + 0x00000000, 0x01000000, + 0x00000000, 0x01000000, + }}, + gba::display::map::Tile{{ + 0x00000000, 0x01000000, + 0x00000000, 0x01000000, + 0x00000000, 0x01000000, + 0x00000000, 0x01000000, + }}, + gba::display::map::Tile{{ + 0x00000001, 0x00000000, + 0x00000001, 0x00000000, + 0x00000001, 0x00000000, + 0x00000001, 0x00000000, + }}, + gba::display::map::Tile{{ + 0x00000001, 0x00000000, + 0x00000001, 0x00000000, + 0x00000001, 0x00000000, + 0x00000001, 0x00000000, + }}, +}; + +// clang-format on + +constexpr auto sample_palette = gba::graphics::Palette{raw_sample_palette}; +const auto sample_tileset = + gba::graphics::Tileset{sample_palette, sample_tiles}; +const auto sample_tilemap = gba::graphics::Tilemap{raw_tileset}; +const auto null_tilemap = gba::graphics::Tilemap{gba::graphics::null_tilemap}; + +} + +constexpr auto PLAYER_WIDTH = 4; +constexpr auto PLAYER_HEIGHT = 16; +constexpr auto LIFE_SIZE = 8; +constexpr auto MAX_LIVES = 3; + +constexpr auto SCREEN_SIZE = gba::geometry::Size{ + gba::display::mode3::screen_width, + gba::display::mode3::screen_height, +}; + +struct Ball { + gba::graphics::Sprite sprite; + gba::geometry::Point speed; +}; + +struct Player { + gba::graphics::Sprite sprite; + gba::geometry::Point speed; + int lives; + + auto move() -> void { + sprite.pos = { + .x = this->sprite.pos.x + speed.x, + .y = this->sprite.pos.y + speed.y, + }; + } +}; + +auto create_lives_sprites(gba::geometry::Point const& origin, int oam_start) + -> void { + auto sprite_pos = origin; + for (auto i = 0; i < MAX_LIVES; ++i) { + auto& oam_entry = gba::display::oam_entry(oam_start + i); + oam_entry.set_x(sprite_pos.x); + oam_entry.set_y(sprite_pos.y); + oam_entry.size(gba::display::ObjectSize::TINY); + oam_entry.base_tile(4); + sprite_pos.x += LIFE_SIZE; + } +} + +auto update_player_lives(int lives, int oam_start) -> void { + for (auto i = 0; i < lives; ++i) { + gba::display::oam_entry(oam_start + i).base_tile(4); + } + for (auto i = lives; i < MAX_LIVES; ++i) { + gba::display::oam_entry(oam_start + i).base_tile(5); + } +} + +auto collides(Ball const& ball, gba::geometry::Rect const& hitbox) { + auto [ball_x, ball_y] = ball.sprite.pos; + return ( + ball_x >= hitbox.x and ball_y >= hitbox.y and + ball_x <= hitbox.x + hitbox.width and ball_y <= hitbox.y + hitbox.height + ); +} + +struct MatchScreen { + + const gba::graphics::Bitmap ball_bitmap = gba::graphics::Bitmap{ + .tiles = + gba::graphics::Tileset{ + test::screen_obj_palette, + std::array{ + gba::display::map::Tile{{ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00011000, + 0x00011000, + 0x00000000, + 0x00000000, + 0x00000000, + }}, + }, + }, + .shape = gba::graphics::BitmapShape::SQUARE_8x8, + }; + + const gba::graphics::Bitmap player_bitmap = gba::graphics::Bitmap{ + .tiles = + gba::graphics::Tileset{ + test::screen_obj_palette, + std::array{ + gba::display::map::Tile{{ + 0x00001111, + 0x00001111, + 0x00001111, + 0x00001111, + 0x00001111, + 0x00001111, + 0x00001111, + 0x00001111, + }}, + gba::display::map::Tile{{ + 0x00001111, + 0x00001111, + 0x00001111, + 0x00001111, + 0x00001111, + 0x00001111, + 0x00001111, + 0x00001111, + }}, + }, + }, + .shape = gba::graphics::BitmapShape::VERTICAL_8x16, + }; + bool game_running = false; + + Ball ball = { + .sprite = gba::graphics::Sprite{ + .bitmap = ball_bitmap, + .pos = + { + .x = SCREEN_SIZE.width / 2 - 1, + .y = (SCREEN_SIZE.height / 2) % SCREEN_SIZE.height, + }, + .offset = {-2, -2}, + }, + .speed = {-2, -2}, + }; + + std::array players = { + Player{ + .sprite = + gba::graphics::Sprite{ + .bitmap = player_bitmap, + .pos = {0, 60}, + }, + .speed = {0, 0}, + .lives = 3, + }, + Player{ + .sprite = + gba::graphics::Sprite{ + .bitmap = player_bitmap, + .pos = {SCREEN_SIZE.width - PLAYER_WIDTH - 1, 60}, + }, + .speed = {0, -2}, + .lives = 3 + }, + }; + + auto start() -> void { + using gba::geometry::Point; + using namespace gba::display; + + const auto map = gba::graphics::Map{ + test::sample_tileset, + test::sample_tilemap, + gba::graphics::null_tilemap, + gba::graphics::null_tilemap, + gba::graphics::null_tilemap, + gba::graphics::MapSize::TEXT_256X256 + }; + + gba::display::force_blank(true); + + change_mode(Mode::MODE0); + layer_visible(Layer::BG1); + layer_visible(Layer::OBJ); + + auto& ball_oam_entry = gba::display::oam_entry(2); + ball_oam_entry.base_tile(1); + + players[0].sprite.add_to_screen(0, 2); + players[1].sprite.add_to_screen(1, 2); + ball.sprite.add_to_screen(2, 1); + + gba::display::obj_palette()[1] = test::WHITE; + gba::display::obj_palette()[2] = test::RED; + gba::display::obj_palette()[3] = test::GREEN; + + gba::graphics::load_map(map); + + gba::display::sprite_tile(4) = gba::display::map::Tile{{ + 0x00000000, + 0x00000000, + 0x00000000, + 0x33333333, + 0x33333333, + 0x00000000, + 0x00000000, + 0x00000000, + }}; + + gba::display::sprite_tile(5) = gba::display::map::Tile{{ + 0x20000002, + 0x02000020, + 0x00200200, + 0x00022000, + 0x00022000, + 0x00200200, + 0x02000020, + 0x20000002, + }}; + + create_lives_sprites( + gba::geometry::Point{ + SCREEN_SIZE.width / 2 - LIFE_SIZE * (MAX_LIVES + 1), + 5 + }, + 3 + ); + create_lives_sprites( + gba::geometry::Point{SCREEN_SIZE.width / 2 + LIFE_SIZE, 5}, + 6 + ); + + gba::display::force_blank(false); + } + + auto new_match(int seed) -> void { + auto ball_hor_dir = seed % 2 == 0 ? -1 : 1; + auto ball_ver_dir = (seed / 3) % 2 == 0 ? -1 : 1; + auto ball_h_speed = (seed / 17) % 2 + 1; + auto ball_v_speed = (seed / 7) % 3 + 1; + + auto ball_dir = gba::geometry::Point{ + ball_hor_dir * ball_h_speed, + ball_ver_dir * ball_v_speed + }; + + ball.sprite.pos = + {SCREEN_SIZE.width / 2 - 1, + (SCREEN_SIZE.height / 2 + seed) % SCREEN_SIZE.height}; + ball.speed = ball_dir; + + update_player_lives(players[0].lives, 3); + update_player_lives(players[1].lives, 6); + } + + auto update() -> void { + if (game_running) { + game_loop(); + } else if (gba::input::pressing(gba::input::Key::START)) { + game_running = true; + players[0].lives = MAX_LIVES; + players[1].lives = MAX_LIVES; + new_match(gba::utils::random()); + } + } + + auto game_loop() -> void { + using gba::geometry::Rect; + + auto seed = gba::utils::random(); + // Screen size is 240x160, so value ranges [0..239, 0..159]. + // For the upper index we must consider that the ball starts -2 pixels + // from it, because its reference is top-left and the ball has a size + // of 2x2. + if (ball.sprite.pos.x <= 0 or ball.sprite.pos.x >= SCREEN_SIZE.width - 3) { + ball.speed.x *= -1; + } + + if (ball.sprite.pos.y <= 0 or ball.sprite.pos.y >= SCREEN_SIZE.height - 3) { + ball.speed.y *= -1; + } + ball.sprite.pos.x += ball.speed.x; + ball.sprite.pos.y += ball.speed.y; + + if (ball.sprite.pos.x >= SCREEN_SIZE.width - 3) { + players[1].lives -= 1; + update_player_lives(players[1].lives, 6); + new_match(seed); + } else if (ball.sprite.pos.x == 0) { + players[0].lives -= 1; + update_player_lives(players[0].lives, 3); + new_match(seed); + } + + if (players[0].lives == 0 or players[1].lives == 0) { + game_running = false; + } + + // Update opponent position + if (players[1].sprite.pos.y == 0 or + players[1].sprite.pos.y >= 159 - PLAYER_HEIGHT) { + players[1].speed.y *= -1; + } + + using gba::input::Key; + + if (gba::input::pressing(Key::UP)) { + players[0].speed.y = -2; + } else if (gba::input::pressing(Key::DOWN)) { + players[0].speed.y = 2; + } else { + players[0].speed.y = 0; + } + + if (gba::input::pressing(Key::A)) { + ball.sprite.pos.x = 15; + ball.sprite.pos.y = 15; + } + + for (auto&& player : players) { + player.move(); + if (player.sprite.pos.y + PLAYER_HEIGHT > SCREEN_SIZE.height) { + player.sprite.pos.y = SCREEN_SIZE.height - PLAYER_HEIGHT; + } + if (player.sprite.pos.y < 0) { + player.sprite.pos.y = 0; + } + } + + auto [p0, p1] = std::array{ + players[0].sprite.pos, + players[1].sprite.pos, + }; + + auto players_hitbox = std::array{ + Rect{-10, p0.y, 10 + PLAYER_WIDTH, PLAYER_HEIGHT}, + Rect{ + SCREEN_SIZE.width - PLAYER_WIDTH, + p1.y, + 10 + PLAYER_WIDTH, + PLAYER_HEIGHT + }, + }; + + auto collides_with_p0 = collides(ball, players_hitbox[0]); + auto collides_with_p1 = collides(ball, players_hitbox[1]); + + if (collides_with_p0) { + ball.speed.x *= -1; + ball.speed.y += players[0].speed.y; + } + + if (collides_with_p1) { + ball.speed.x *= -1; + ball.speed.y += players[1].speed.y; + } + } + + auto draw() -> void { + // No additional steps are needed since all graphics updated in this + // screen are sprites handled through hardware. + gba::display::vsync(); + + ball.sprite.update(); + for (auto&& player : players) { + player.sprite.update(); + } + } + + auto post_update() -> void { + } +}; + +int main() { + gba::display::object_mapping(gba::display::ObjectMapping::MAP_ARRAY); + + auto screen = MatchScreen{}; + + screen.start(); + while (true) { + screen.update(); + screen.draw(); + } +} diff --git a/tests/pong-mode0/meson.build b/tests/pong-mode0/meson.build new file mode 100644 index 0000000..7f5e43d --- /dev/null +++ b/tests/pong-mode0/meson.build @@ -0,0 +1,11 @@ +project('gba-pong', + 'cpp', + default_options: [ + 'cpp_std=c++20', + ]) + +libgba = dependency('libgba', fallback : ['libgba', 'libgba_dep']) + +pong = executable('pong.gba', + 'main.cpp', + dependencies : libgba) diff --git a/tests/pong-mode0/readme.md b/tests/pong-mode0/readme.md new file mode 100644 index 0000000..b95022a --- /dev/null +++ b/tests/pong-mode0/readme.md @@ -0,0 +1,21 @@ +Pong Game (mode 3) +================== + +Tests if a pong game example made for mode 3 works as expected. + +| Function | Status | +|----------------|---------| +| Draw objects | Working | +| Basic input | Working | + +Explanation +----------- + +_TODO (waiting for engine code implementation)._ + +Screenshots +----------- + +In-game: + +![Pong Game Example](screenshots/pong-0.png) diff --git a/tests/pong-mode0/screenshots/pong-0.png b/tests/pong-mode0/screenshots/pong-0.png new file mode 100644 index 0000000..63bbf21 Binary files /dev/null and b/tests/pong-mode0/screenshots/pong-0.png differ diff --git a/tests/pong/main.cpp b/tests/pong/main.cpp index 23b3dbe..c9f56af 100644 --- a/tests/pong/main.cpp +++ b/tests/pong/main.cpp @@ -1,11 +1,8 @@ -#include - #include #include #include #include - -using gba::geometry::Rect; +#include "libgba-cpp/engine/map/objects.h" const static auto WHITE = gba::display::Color{31, 31, 31}; const static auto BLACK = gba::display::Color{0, 0, 0}; @@ -22,21 +19,26 @@ constexpr auto SCREEN_SIZE = gba::geometry::Size{ gba::display::mode3::screen_height, }; -struct MovingObject { +struct Ball { gba::geometry::Point position; gba::geometry::Point speed; - - auto move() -> void { - this->position = { - .x = this->position.x + speed.x, - .y = this->position.y + speed.y, - }; - } }; struct Player { - MovingObject object; + gba::map::BitmapModeObject object; + gba::geometry::Point speed; int lives; + + auto draw() const -> void { + object.draw(); + } + + auto move() -> void { + object.position() = { + .x = this->object.position().x + speed.x, + .y = this->object.position().y + speed.y, + }; + } }; auto set_pixel(int x, int y, gba::display::Color const& color) -> void { @@ -74,14 +76,14 @@ auto draw_ball(int x, int y, gba::display::Color const& color) -> void { } auto draw_player( - gba::geometry::Point const& position, - gba::display::Color const& color + gba::map::BitmapModeObject const& bitmap ) -> void { - auto [x, y] = position; + auto [x, y] = bitmap.position(); + auto [width, height] = bitmap.size(); - for (auto l = y; l < y + PLAYER_HEIGHT; ++l) { - for (auto c = x; c < x + PLAYER_WIDTH; ++c) { - set_pixel(c, l, color); + for (auto l = y; l < y + height; ++l) { + for (auto c = x; c < x + width; ++c) { + set_pixel(c, l, WHITE); } } } @@ -97,7 +99,7 @@ auto draw_lives(Player const& player, gba::geometry::Point point) -> void { } } -auto collides(MovingObject const& ball, gba::geometry::Rect const& hitbox) { +auto collides(Ball const& ball, gba::geometry::Rect const& hitbox) { auto [ball_x, ball_y] = ball.position; return ( ball_x >= hitbox.x and ball_y >= hitbox.y and @@ -106,34 +108,40 @@ auto collides(MovingObject const& ball, gba::geometry::Rect const& hitbox) { } struct MainScreen { - using Point = gba::geometry::Point; - bool game_running = false; - MovingObject ball = { + Ball ball = { .position = {SCREEN_SIZE.width / 2 - 1, (SCREEN_SIZE.height / 2) % SCREEN_SIZE.height}, - .speed = Point{-2, -2}, + .speed = {-2, -2}, }; std::array players = { Player{ .object = { - .position = Point{0, 60}, - .speed = {0, 0}, + {0, 60}, + {PLAYER_WIDTH, PLAYER_HEIGHT}, + draw_player, }, + .speed = {0, 0}, .lives = 3 }, Player{ .object = { - .position = Point{SCREEN_SIZE.width - PLAYER_WIDTH - 1, 60}, - .speed = {0, -2}, + {SCREEN_SIZE.width - PLAYER_WIDTH - 1, 60}, + {PLAYER_WIDTH, PLAYER_HEIGHT}, + draw_player, }, + .speed = {0, -2}, .lives = 3 }, }; + gba::geometry::Point old_ball_position; + gba::geometry::Point old_player_position; + gba::geometry::Point old_player2_position; + auto new_match(int seed) -> void { auto ball_hor_dir = seed % 2 == 0 ? -1 : 1; auto ball_ver_dir = (seed / 3) % 2 == 0 ? -1 : 1; @@ -141,109 +149,155 @@ struct MainScreen { auto ball_v_speed = (seed / 7) % 3 + 1; auto ball_dir = - Point{ball_hor_dir * ball_h_speed, ball_ver_dir * ball_v_speed}; + gba::geometry::Point{ball_hor_dir * ball_h_speed, ball_ver_dir * ball_v_speed}; - this->ball = { + ball = { .position = {SCREEN_SIZE.width / 2 - 1, (SCREEN_SIZE.height / 2 + seed) % SCREEN_SIZE.height}, .speed = ball_dir, }; } -}; - -auto game_loop(MainScreen& screen, int seed) -> void { - auto& [game_running, ball, players] = screen; - // Screen size is 240x160, so value ranges [0..239, 0..159]. - // For the upper index we must consider that the ball starts -2 pixels - // from it, because its reference is top-left and the ball has a size - // of 2x2. - if (ball.position.x <= 0 or ball.position.x >= SCREEN_SIZE.width - 3) { - ball.speed.x *= -1; + auto update() -> void { + if (game_running) { + game_loop(); + } else { + if (gba::input::pressing(gba::input::Key::START)) { + game_running = true; + players[0].lives = MAX_LIVES; + players[1].lives = MAX_LIVES; + new_match(utils::random()); + } + } } - if (ball.position.y <= 0 or ball.position.y >= SCREEN_SIZE.height - 3) { - ball.speed.y *= -1; - } - ball.position.x += ball.speed.x; - ball.position.y += ball.speed.y; - - if (ball.position.x >= SCREEN_SIZE.width - 3) { - players[1].lives -= 1; - screen.new_match(seed); - } else if (ball.position.x == 0) { - players[0].lives -= 1; - screen.new_match(seed); - } + auto game_loop() -> void { + using gba::geometry::Rect; - if (players[0].lives == 0 or players[1].lives == 0) { - game_running = false; - } + auto seed = utils::random(); + // Screen size is 240x160, so value ranges [0..239, 0..159]. + // For the upper index we must consider that the ball starts -2 pixels + // from it, because its reference is top-left and the ball has a size + // of 2x2. + if (ball.position.x <= 0 or ball.position.x >= SCREEN_SIZE.width - 3) { + ball.speed.x *= -1; + } - // Update opponent position - if (players[1].object.position.y == 0 or - players[1].object.position.y >= 159 - PLAYER_HEIGHT) { - players[1].object.speed.y *= -1; - } + if (ball.position.y <= 0 or ball.position.y >= SCREEN_SIZE.height - 3) { + ball.speed.y *= -1; + } + ball.position.x += ball.speed.x; + ball.position.y += ball.speed.y; + + if (ball.position.x >= SCREEN_SIZE.width - 3) { + players[1].lives -= 1; + new_match(seed); + } else if (ball.position.x == 0) { + players[0].lives -= 1; + new_match(seed); + } - using gba::input::Key; + if (players[0].lives == 0 or players[1].lives == 0) { + game_running = false; + } - if (gba::input::pressing(Key::UP)) { - players[0].object.speed.y = -2; - } else if (gba::input::pressing(Key::DOWN)) { - players[0].object.speed.y = 2; - } else { - players[0].object.speed.y = 0; - } + // Update opponent position + if (players[1].object.position().y == 0 or + players[1].object.position().y >= 159 - PLAYER_HEIGHT) { + players[1].speed.y *= -1; + } - if (gba::input::pressing(Key::A)) { - ball.position.x = 15; - ball.position.y = 15; - } + using gba::input::Key; + + if (gba::input::pressing(Key::UP)) { + players[0].speed.y = -2; + } else if (gba::input::pressing(Key::DOWN)) { + players[0].speed.y = 2; + } else { + players[0].speed.y = 0; + } + + if (gba::input::pressing(Key::A)) { + ball.position.x = 15; + ball.position.y = 15; + } + + for (auto&& player: players) { + player.move(); + if (player.object.position().y + PLAYER_HEIGHT > SCREEN_SIZE.height) { + player.object.position().y = SCREEN_SIZE.height - PLAYER_HEIGHT; + } + if (player.object.position().y < 0) { + player.object.position().y = 0; + } + } - for (auto&& player: players) { - player.object.move(); - if (player.object.position.y + PLAYER_HEIGHT > SCREEN_SIZE.height) { - player.object.position.y = SCREEN_SIZE.height - PLAYER_HEIGHT; + auto [p0, p1] = std::array{players[0].object.position(), players[1].object.position()}; + + auto players_hitbox = std::array{ + Rect{-10, p0.y, 10 + PLAYER_WIDTH, PLAYER_HEIGHT}, + Rect{ + SCREEN_SIZE.width - PLAYER_WIDTH, + p1.y, + 10 + PLAYER_WIDTH, + PLAYER_HEIGHT + }, + }; + + auto collides_with_p0 = collides(ball, players_hitbox[0]); + auto collides_with_p1 = collides(ball, players_hitbox[1]); + + if (collides_with_p0) { + ball.speed.x *= -1; + ball.speed.y += players[0].speed.y; } - if (player.object.position.y < 0) { - player.object.position.y = 0; + + if (collides_with_p1) { + ball.speed.x *= -1; + ball.speed.y += players[1].speed.y; } } - auto [p0, p1] = std::array{players[0].object.position, players[1].object.position}; + auto draw() const -> void { + gba::display::vsync(); - auto players_hitbox = std::array{ - Rect{-10, p0.y, 10 + PLAYER_WIDTH, PLAYER_HEIGHT}, - Rect{ - SCREEN_SIZE.width - PLAYER_WIDTH, - p1.y, - 10 + PLAYER_WIDTH, - PLAYER_HEIGHT - }, - }; + // Clear objects + draw_ball(old_ball_position.x, old_ball_position.y, BLACK); + fill_rect(gba::geometry::Rect{old_player_position.x, old_player_position.y, PLAYER_WIDTH, PLAYER_HEIGHT}, BLACK); + fill_rect(gba::geometry::Rect{old_player2_position.x, old_player2_position.y, PLAYER_WIDTH, PLAYER_HEIGHT}, BLACK); - auto collides_with_p0 = collides(ball, players_hitbox[0]); - auto collides_with_p1 = collides(ball, players_hitbox[1]); + // Draw background + draw_line_mid(); + + // Draw objects + draw_ball(ball.position.x, ball.position.y, WHITE); + players[0].draw(); + players[1].draw(); - if (collides_with_p0) { - ball.speed.x *= -1; - ball.speed.y += players[0].object.speed.y; + draw_lives( + players[0], + gba::geometry::Point{SCREEN_SIZE.width / 2 - LIFE_SIZE * (MAX_LIVES + 1), 5} + ); + draw_lives( + players[1], + gba::geometry::Point{SCREEN_SIZE.width / 2 + LIFE_SIZE, 5} + ); } - if (collides_with_p1) { - ball.speed.x *= -1; - ball.speed.y += players[1].object.speed.y; + auto post_update() -> void { + old_ball_position = ball.position; + old_player_position = players[0].object.position(); + old_player2_position = players[1].object.position(); } -} +}; -int main() { - auto gen = std::mt19937{std::random_device{}()}; - auto distrib = std::uniform_int_distribution<>{1, 1500}; +int main() { namespace display = gba::display; using gba::geometry::Point; + + display::vsync(); display::force_blank(true); display::change_mode(display::Mode::MODE3); @@ -255,45 +309,8 @@ int main() { auto screen = MainScreen{}; while (true) { - // Game logic - auto old_ball_position = screen.ball.position; - auto old_player_position = screen.players[0].object.position; - auto old_player2_position = screen.players[1].object.position; - - if (screen.game_running) { - game_loop(screen, distrib(gen)); - } else { - if (gba::input::pressing(gba::input::Key::START)) { - screen.game_running = true; - screen.players[0].lives = MAX_LIVES; - screen.players[1].lives = MAX_LIVES; - screen.new_match(distrib(gen)); - } - } - - // Draw - display::vsync(); - - // Clear objects - draw_ball(old_ball_position.x, old_ball_position.y, BLACK); - draw_player(old_player_position, BLACK); - draw_player(old_player2_position, BLACK); - - // Draw background - draw_line_mid(); - - // Draw objects - draw_ball(screen.ball.position.x, screen.ball.position.y, WHITE); - draw_player(screen.players[0].object.position, WHITE); - draw_player(screen.players[1].object.position, WHITE); - - draw_lives( - screen.players[0], - Point{SCREEN_SIZE.width / 2 - LIFE_SIZE * (MAX_LIVES + 1), 5} - ); - draw_lives( - screen.players[1], - Point{SCREEN_SIZE.width / 2 + LIFE_SIZE, 5} - ); + screen.update(); + screen.draw(); + screen.post_update(); } } diff --git a/tests/pong/readme.md b/tests/pong/readme.md index 095eefa..b95022a 100644 --- a/tests/pong/readme.md +++ b/tests/pong/readme.md @@ -11,7 +11,7 @@ Tests if a pong game example made for mode 3 works as expected. Explanation ----------- -_TODO._ +_TODO (waiting for engine code implementation)._ Screenshots ----------- diff --git a/tests/tilemap/sample_map.h b/tests/tilemap/sample_map.h index 6dd537c..39bfc56 100644 --- a/tests/tilemap/sample_map.h +++ b/tests/tilemap/sample_map.h @@ -76,41 +76,6 @@ const auto layer0_map = std::array{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; -const auto null_map = std::array{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - const auto sample_tilemap = gba::graphics::Tilemap{raw_tilemap}; const auto null_tilemap = gba::graphics::Tilemap{null_map}; const auto layer0_tilemap = gba::graphics::Tilemap{layer0_map};