// DR. ROBOTNIK'S RING RACERS //----------------------------------------------------------------------------- // Copyright (C) 2024 by James Robert Roman // Copyright (C) 2024 by Kart Krew // // This program is free software distributed under the // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- #ifndef __V_DRAW_HPP__ #define __V_DRAW_HPP__ #include #include #include #include #include #include "doomdef.h" // skincolornum_t #include "doomtype.h" #include "screen.h" // BASEVIDWIDTH #include "typedef.h" #include "v_video.h" namespace srb2 { class Draw { public: enum class Font { kThin, kGamemode, kConsole, kFreeplay, kZVote, kPing, kTimer, kThinTimer, kMenu, kMedium, kRollingNum, kRollingNum4P, }; enum class Align { kLeft, kCenter, kRight, }; enum class Stretch { kNone, kWidth, kHeight, kBoth, }; enum class Button { a, b, c, x, y, z, start, l, r, up, down, right, left, }; class TextElement { public: explicit TextElement() {} explicit TextElement(std::string string) : string_(string) {} template explicit TextElement(fmt::format_string format, Args&&... args) : TextElement(fmt::format(format, args...)) { } const std::string& string() const { return string_; } std::optional font() const { return font_; } std::optional flags() const { return flags_; } int width() const; TextElement& string(std::string string) { string_ = string; return *this; } TextElement& parse(std::string_view string); template TextElement& parse(fmt::format_string format, Args&&... args) { return parse(fmt::format(format, args...)); } TextElement& font(Font font) { font_ = font; return *this; } TextElement& flags(INT32 flags) { flags_ = flags; return *this; } private: std::string string_; std::optional font_; std::optional flags_; }; class Chain { public: float x() const { return x_; } float y() const { return y_; } INT32 flags() const { return flags_; } // Methods add relative to the current state Chain& x(float x); Chain& y(float y); Chain& xy(float x, float y); Chain& flags(INT32 flags); // Methods overwrite the current state Chain& width(float width); Chain& height(float height); Chain& size(float width, float height); Chain& scale(float scale); Chain& font(Font font); Chain& align(Align align); Chain& stretch(Stretch stretch); // Absolute screen coordinates Chain& clipx(float left, float right); // 0 to BASEVIDWIDTH Chain& clipy(float top, float bottom); // 0 to BASEVIDHEIGHT Chain& clipx() { return clipx(x_, x_ + width_); } Chain& clipy() { return clipy(y_, y_ + height_); } // True to use internal clipping state // False to use global state (default) // Changing the clipping dimensions implicitly sets // this to true Chain& clip(bool yes); Chain& colormap(const UINT8* colormap); Chain& colormap(UINT16 color); Chain& colormap(INT32 skin, UINT16 color); Chain& colorize(UINT16 color); void text(const char* str) const { string(str, flags_, font_); } void text(const std::string& str) const { text(str.c_str()); } void text(const TextElement& elm) const { string(elm.string().c_str(), elm.flags().value_or(flags_), elm.font().value_or(font_)); } template void text(fmt::format_string format, Args&&... args) const { text(fmt::format(format, args...)); } TextElement text() const { return TextElement().font(font_).flags(flags_); } void patch(patch_t* patch) const; void patch(const char* name) const { patch(Draw::cache_patch(name)); } void patch(const std::string& name) const { patch(name.c_str()); } void thumbnail(UINT16 mapnum) const; void fill(UINT8 color) const; void button(Button type, std::optional press = {}) const { button_(type, 0, press); } void small_button(Button type, std::optional press = {}) const { button_(type, 1, press); } void sticker(patch_t* end_graphic, UINT8 color) const; void sticker() const { sticker(Draw::cache_patch("K_STIKEN"), 24); } void small_sticker() const { sticker(Draw::cache_patch("K_STIKE2"), 24); } private: constexpr Chain() {} explicit Chain(float x, float y) : x_(x), y_(y) {} Chain(const Chain&) = default; Chain& operator=(const Chain&) = default; struct Clipper { explicit Clipper(const Chain& chain); ~Clipper(); private: cliprect_t save_; }; float x_ = 0.f; float y_ = 0.f; float width_ = 0.f; float height_ = 0.f; float scale_ = 1.f; float clipx1_ = 0.f; float clipx2_ = BASEVIDWIDTH; float clipy1_ = 0.f; float clipy2_ = BASEVIDHEIGHT; bool clip_ = false; INT32 flags_ = 0; Font font_ = Font::kThin; Align align_ = Align::kLeft; Stretch stretch_ = Stretch::kNone; const UINT8* colormap_ = nullptr; void string(const char* str, INT32 flags, Font font) const; void button_(Button type, int ver, std::optional press = {}) const; friend Draw; }; static patch_t* cache_patch(const char* name); constexpr Draw() {} explicit Draw(float x, float y) : chain_(x, y) {} Draw(const Chain& chain) : chain_(chain) {} // See class Chain for documentation float x() const { return chain_.x(); } float y() const { return chain_.y(); } INT32 flags() const { return chain_.flags(); } #define METHOD(Name) \ template \ Chain Name (Args&&... args) const { return Chain(chain_).Name(std::forward(args)...); } METHOD(x); METHOD(y); METHOD(xy); METHOD(flags); METHOD(width); METHOD(height); METHOD(size); METHOD(scale); METHOD(font); METHOD(align); METHOD(stretch); METHOD(clipx); METHOD(clipy); METHOD(clip); METHOD(colormap); METHOD(colorize); #undef METHOD #define VOID_METHOD(Name) \ template \ auto Name (Args&&... args) const { return chain_.Name(std::forward(args)...); } VOID_METHOD(text); VOID_METHOD(patch); VOID_METHOD(thumbnail); VOID_METHOD(fill); VOID_METHOD(button); VOID_METHOD(small_button); VOID_METHOD(sticker); VOID_METHOD(small_sticker); #undef VOID_METHOD private: Chain chain_; static int font_to_fontno(Font font); static INT32 default_font_flags(Font font); static fixed_t font_width(Font font, INT32 flags, const char* string); }; }; // namespace srb2 #include "v_draw_setter.hpp" #endif // __V_DRAW_HPP__