diff --git a/CMakeLists.txt b/CMakeLists.txt index 80a049d..1e43796 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -170,6 +170,8 @@ set(SOURCE_FILES SpaceCadetPinball/imstb_textedit.h SpaceCadetPinball/imstb_rectpack.h SpaceCadetPinball/imstb_truetype.h + SpaceCadetPinball/DebugOverlay.cpp + SpaceCadetPinball/DebugOverlay.h ) # On Windows, include resource file with the icon diff --git a/SpaceCadetPinball/DebugOverlay.cpp b/SpaceCadetPinball/DebugOverlay.cpp new file mode 100644 index 0000000..2c1f226 --- /dev/null +++ b/SpaceCadetPinball/DebugOverlay.cpp @@ -0,0 +1,255 @@ +#include "pch.h" +#include "DebugOverlay.h" + +#include "maths.h" +#include "proj.h" +#include "winmain.h" +#include "TFlipperEdge.h" +#include "TFlipper.h" +#include "pb.h" +#include "TLine.h" +#include "TCircle.h" +#include "TPinballTable.h" +#include "TEdgeBox.h" +#include "TTableLayer.h" +#include "TBall.h" +#include "render.h" +#include "options.h" + + +gdrv_bitmap8* DebugOverlay::dbScreen = nullptr; + +int SDL_RenderDrawCircle(SDL_Renderer* renderer, int x, int y, int radius) +{ + int offsetx, offsety, d; + int status; + + offsetx = 0; + offsety = radius; + d = radius - 1; + status = 0; + + while (offsety >= offsetx) { + status += SDL_RenderDrawPoint(renderer, x + offsetx, y + offsety); + status += SDL_RenderDrawPoint(renderer, x + offsety, y + offsetx); + status += SDL_RenderDrawPoint(renderer, x - offsetx, y + offsety); + status += SDL_RenderDrawPoint(renderer, x - offsety, y + offsetx); + status += SDL_RenderDrawPoint(renderer, x + offsetx, y - offsety); + status += SDL_RenderDrawPoint(renderer, x + offsety, y - offsetx); + status += SDL_RenderDrawPoint(renderer, x - offsetx, y - offsety); + status += SDL_RenderDrawPoint(renderer, x - offsety, y - offsetx); + + if (status < 0) { + status = -1; + break; + } + + if (d >= 2 * offsetx) { + d -= 2 * offsetx + 1; + offsetx += 1; + } + else if (d < 2 * (radius - offsety)) { + d += 2 * offsety - 1; + offsety -= 1; + } + else { + d += 2 * (offsety - offsetx - 1); + offsety -= 1; + offsetx += 1; + } + } + + return status; +} + +void DebugOverlay::UnInit() +{ + delete dbScreen; + dbScreen = nullptr; +} + +void DebugOverlay::DrawOverlay() +{ + if (dbScreen == nullptr) + { + dbScreen = new gdrv_bitmap8(render::vscreen->Width, render::vscreen->Height, false, false); + dbScreen->CreateTexture("nearest", SDL_TEXTUREACCESS_TARGET); + SDL_SetTextureBlendMode(dbScreen->Texture, SDL_BLENDMODE_BLEND); + } + + // Setup overlay rendering + Uint8 initialR, initialG, initialB, initialA; + auto initialRenderTarget = SDL_GetRenderTarget(winmain::Renderer); + SDL_GetRenderDrawColor(winmain::Renderer, &initialR, &initialG, &initialB, &initialA); + SDL_SetRenderTarget(winmain::Renderer, dbScreen->Texture); + SDL_SetRenderDrawColor(winmain::Renderer, 0, 0, 0, 0); + SDL_RenderClear(winmain::Renderer); + + // Draw EdgeManager box grid + if (options::Options.DebugOverlayGrid) + DrawBoxGrid(); + + // Draw all edges registered in TCollisionComponent.EdgeList + flippers + if (options::Options.DebugOverlayAllEdges) + DrawAllEdges(); + + // Draw ball collision + if (options::Options.DebugOverlayBallPosition || options::Options.DebugOverlayBallEdges) + DrawBallInfo(); + + // Restore render target + SDL_SetRenderTarget(winmain::Renderer, initialRenderTarget); + SDL_SetRenderDrawColor(winmain::Renderer, + initialR, initialG, initialB, initialA); + + // Copy overlay with alpha blending + SDL_BlendMode blendMode; + SDL_GetRenderDrawBlendMode(winmain::Renderer, &blendMode); + SDL_SetRenderDrawBlendMode(winmain::Renderer, SDL_BLENDMODE_BLEND); + SDL_RenderCopy(winmain::Renderer, dbScreen->Texture, nullptr, &render::DestinationRect); + SDL_SetRenderDrawBlendMode(winmain::Renderer, blendMode); +} + +void DebugOverlay::DrawBoxGrid() +{ + auto& edgeMan = *TTableLayer::edge_manager; + + SDL_SetRenderDrawColor(winmain::Renderer, 0, 255, 0, 255); + for (int x = 0; x <= edgeMan.MaxBoxX; x++) + { + vector2 boxPt{ x * edgeMan.AdvanceX + edgeMan.X , edgeMan.Y }; + auto pt1 = proj::xform_to_2d(boxPt); + boxPt.Y = edgeMan.MaxBoxY * edgeMan.AdvanceY + edgeMan.Y; + auto pt2 = proj::xform_to_2d(boxPt); + + SDL_RenderDrawLine(winmain::Renderer, pt1.X, pt1.Y, pt2.X, pt2.Y); + } + for (int y = 0; y <= edgeMan.MaxBoxY; y++) + { + vector2 boxPt{ edgeMan.X, y * edgeMan.AdvanceY + edgeMan.Y }; + auto pt1 = proj::xform_to_2d(boxPt); + boxPt.X = edgeMan.MaxBoxX * edgeMan.AdvanceX + edgeMan.X; + auto pt2 = proj::xform_to_2d(boxPt); + + SDL_RenderDrawLine(winmain::Renderer, pt1.X, pt1.Y, pt2.X, pt2.Y); + } +} + +void DebugOverlay::DrawAllEdges() +{ + SDL_SetRenderDrawColor(winmain::Renderer, 0, 200, 200, 255); + for (auto cmp : pb::MainTable->ComponentList) + { + auto collCmp = dynamic_cast(cmp); + if (collCmp) + { + for (auto edge : collCmp->EdgeList) + { + DrawEdge(edge); + } + } + auto flip = dynamic_cast(cmp); + if (flip) + { + DrawEdge(flip->FlipperEdge); + } + } +} + +void DebugOverlay::DrawBallInfo() +{ + auto& edgeMan = *TTableLayer::edge_manager; + for (auto ball : pb::MainTable->BallList) + { + if (ball->ActiveFlag) + { + vector2 ballPosition = { ball->Position.X, ball->Position.Y }; + + if (options::Options.DebugOverlayBallEdges) + { + SDL_SetRenderDrawColor(winmain::Renderer, 255, 0, 0, 255); + auto x = edgeMan.box_x(ballPosition.X), y = edgeMan.box_y(ballPosition.Y); + auto& box = edgeMan.BoxArray[x + y * edgeMan.MaxBoxX]; + for (auto edge : box.EdgeList) + { + DrawEdge(edge); + } + } + + if (options::Options.DebugOverlayBallPosition) + { + SDL_SetRenderDrawColor(winmain::Renderer, 0, 0, 255, 255); + + auto pt1 = proj::xform_to_2d(ballPosition); + SDL_RenderDrawCircle(winmain::Renderer, pt1.X, pt1.Y, 10); + + auto nextPos = ballPosition; + maths::vector_add(nextPos, maths::vector_mul(ball->Acceleration, ball->Speed / 10.0f)); + auto pt2 = proj::xform_to_2d(nextPos); + SDL_RenderDrawLine(winmain::Renderer, pt1.X, pt1.Y, pt2.X, pt2.Y); + } + } + } +} + +void DebugOverlay::DrawCicleType(circle_type& circle) +{ + vector2 linePt{ circle.Center.X + sqrt(circle.RadiusSq), circle.Center.Y }; + auto pt1 = proj::xform_to_2d(circle.Center); + auto pt2 = proj::xform_to_2d(linePt); + auto radius = abs(pt2.X - pt1.X); + + SDL_RenderDrawCircle(winmain::Renderer, pt1.X, pt1.Y, radius); +} + +void DebugOverlay::DrawLineType(line_type& line) +{ + auto pt1 = proj::xform_to_2d(line.Origin); + auto pt2 = proj::xform_to_2d(line.End); + + SDL_RenderDrawLine(winmain::Renderer, pt1.X, pt1.Y, pt2.X, pt2.Y); +} + +void DebugOverlay::DrawEdge(TEdgeSegment* edge) +{ + if (options::Options.DebugOverlayCollisionMask) + { + TBall* refBall = nullptr; + for (auto ball : pb::MainTable->BallList) + { + if (ball->ActiveFlag) + { + refBall = ball; + break; + } + } + if (refBall != nullptr && (refBall->FieldFlag & edge->CollisionGroup) == 0) + return; + } + + auto line = dynamic_cast(edge); + if (line) + { + DrawLineType(line->Line); + return; + } + + auto circle = dynamic_cast(edge); + if (circle) + { + DrawCicleType(circle->Circle); + return; + } + + auto flip = dynamic_cast(edge); + if (flip) + { + flip->set_control_points(pb::time_now); + flip->build_edges_in_motion(); + + DrawLineType(TFlipperEdge::lineA); + DrawLineType(TFlipperEdge::lineB); + DrawCicleType(TFlipperEdge::circlebase); + DrawCicleType(TFlipperEdge::circleT1); + } +} diff --git a/SpaceCadetPinball/DebugOverlay.h b/SpaceCadetPinball/DebugOverlay.h new file mode 100644 index 0000000..136b7ce --- /dev/null +++ b/SpaceCadetPinball/DebugOverlay.h @@ -0,0 +1,22 @@ +#pragma once + +struct gdrv_bitmap8; +struct circle_type; +struct line_type; +class TEdgeSegment; + +class DebugOverlay +{ +public: + static void UnInit(); + static void DrawOverlay(); +private: + static gdrv_bitmap8* dbScreen; + + static void DrawCicleType(circle_type& circle); + static void DrawLineType(line_type& line); + static void DrawEdge(TEdgeSegment* edge); + static void DrawBoxGrid(); + static void DrawAllEdges(); + static void DrawBallInfo(); +}; \ No newline at end of file diff --git a/SpaceCadetPinball/GroupData.cpp b/SpaceCadetPinball/GroupData.cpp index dbffde3..eda4d73 100644 --- a/SpaceCadetPinball/GroupData.cpp +++ b/SpaceCadetPinball/GroupData.cpp @@ -47,7 +47,7 @@ void GroupData::AddEntry(EntryData* entry) if (srcBmp->BitmapType == BitmapTypes::Spliced) { // Get rid of spliced bitmap early on, to simplify render pipeline - auto bmp = new gdrv_bitmap8(srcBmp->Width, srcBmp->Height, srcBmp->Width); + auto bmp = new gdrv_bitmap8(srcBmp->Width, srcBmp->Height, true); auto zMap = new zmap_header_type(srcBmp->Width, srcBmp->Height, srcBmp->Width); SplitSplicedBitmap(*srcBmp, *bmp, *zMap); diff --git a/SpaceCadetPinball/TBall.cpp b/SpaceCadetPinball/TBall.cpp index f19bcad..823df3f 100644 --- a/SpaceCadetPinball/TBall.cpp +++ b/SpaceCadetPinball/TBall.cpp @@ -50,7 +50,7 @@ TBall::TBall(TPinballTable* table) : TPinballComponent(table, -1, false) if (ListBitmap) ListBitmap->push_back(visual.Bitmap); auto visVec = reinterpret_cast(loader::query_float_attribute(groupIndex, index, 501)); - auto zDepth = proj::z_distance(visVec); + auto zDepth = proj::z_distance(*visVec); VisualZArray[index] = zDepth; } RenderSprite = render::create_sprite(VisualTypes::Ball, nullptr, nullptr, 0, 0, nullptr); @@ -60,8 +60,6 @@ TBall::TBall(TPinballTable* table) : TPinballComponent(table, -1, false) void TBall::Repaint() { - int pos2D[2]; - if (CollisionFlag) { Position.Z = @@ -70,8 +68,8 @@ void TBall::Repaint() Offset + CollisionOffset.Z; } - proj::xform_to_2d(&Position, pos2D); - auto zDepth = proj::z_distance(&Position); + auto pos2D = proj::xform_to_2d(Position); + auto zDepth = proj::z_distance(Position); auto zArrPtr = VisualZArray; auto index = 0u; @@ -85,8 +83,8 @@ void TBall::Repaint() RenderSprite, bmp, zDepth, - pos2D[0] - bmp->Width / 2, - pos2D[1] - bmp->Height / 2); + pos2D.X - bmp->Width / 2, + pos2D.Y - bmp->Height / 2); } void TBall::not_again(TEdgeSegment* edge) diff --git a/SpaceCadetPinball/gdrv.cpp b/SpaceCadetPinball/gdrv.cpp index fc46860..048bd98 100644 --- a/SpaceCadetPinball/gdrv.cpp +++ b/SpaceCadetPinball/gdrv.cpp @@ -9,7 +9,15 @@ ColorRgba gdrv::current_palette[256]{}; -gdrv_bitmap8::gdrv_bitmap8(int width, int height, bool indexed) +gdrv_bitmap8::gdrv_bitmap8(int width, int height) : gdrv_bitmap8(width, height, true, true) +{ +} + +gdrv_bitmap8::gdrv_bitmap8(int width, int height, bool indexed) : gdrv_bitmap8(width, height, indexed, true) +{ +} + +gdrv_bitmap8::gdrv_bitmap8(int width, int height, bool indexed, bool bmpBuff) { assertm(width >= 0 && height >= 0, "Negative bitmap8 dimensions"); @@ -20,13 +28,15 @@ gdrv_bitmap8::gdrv_bitmap8(int width, int height, bool indexed) BitmapType = BitmapTypes::DibBitmap; Texture = nullptr; IndexedBmpPtr = nullptr; + BmpBufPtr1 = nullptr; XPosition = 0; YPosition = 0; Resolution = 0; if (indexed) IndexedBmpPtr = new char[Height * IndexedStride]; - BmpBufPtr1 = new ColorRgba[Height * Stride]; + if (bmpBuff) + BmpBufPtr1 = new ColorRgba[Height * Stride]; } gdrv_bitmap8::gdrv_bitmap8(const dat8BitBmpHeader& header) diff --git a/SpaceCadetPinball/gdrv.h b/SpaceCadetPinball/gdrv.h index f30fd96..cc27f15 100644 --- a/SpaceCadetPinball/gdrv.h +++ b/SpaceCadetPinball/gdrv.h @@ -47,7 +47,9 @@ static_assert(sizeof(ColorRgba) == 4, "Wrong size of RGBA color"); struct gdrv_bitmap8 { + gdrv_bitmap8(int width, int height); gdrv_bitmap8(int width, int height, bool indexed); + gdrv_bitmap8(int width, int height, bool indexed, bool bmpBuff); gdrv_bitmap8(const struct dat8BitBmpHeader& header); ~gdrv_bitmap8(); void ScaleIndexed(float scaleX, float scaleY); diff --git a/SpaceCadetPinball/maths.cpp b/SpaceCadetPinball/maths.cpp index 069bec4..0ae2250 100644 --- a/SpaceCadetPinball/maths.cpp +++ b/SpaceCadetPinball/maths.cpp @@ -132,6 +132,7 @@ float maths::normalize_2d(vector2& vec) void maths::line_init(line_type& line, float x0, float y0, float x1, float y1) { line.Origin = { x0, y0 }; + line.End = { x1, y1 }; line.Direction.X = x1 - x0; line.Direction.Y = y1 - y0; normalize_2d(line.Direction); @@ -219,6 +220,11 @@ vector2 maths::vector_sub(const vector2& vec1, const vector2& vec2) return { vec1.X - vec2.X, vec1.Y - vec2.Y }; } +vector2 maths::vector_mul(const vector2& vec1, float val) +{ + return { vec1.X * val, vec1.Y * val }; +} + float maths::basic_collision(TBall* ball, vector2* nextPosition, vector2* direction, float elasticity, float smoothness, float threshold, float boost) { diff --git a/SpaceCadetPinball/maths.h b/SpaceCadetPinball/maths.h index ce7a67a..863eccc 100644 --- a/SpaceCadetPinball/maths.h +++ b/SpaceCadetPinball/maths.h @@ -19,9 +19,18 @@ struct vector2 struct vector3 :vector2 { + vector3() = default; + vector3(float x, float y) : vector3{ x, y, 0 } {} + vector3(float x, float y, float z) : vector2{ x, y }, Z(z) {} float Z; }; +struct vector2i +{ + int X; + int Y; +}; + struct rectangle_type { int XPosition; @@ -52,6 +61,7 @@ struct line_type vector2 PerpendicularC; vector2 Direction; vector2 Origin; + vector2 End; float MinCoord; float MaxCoord; vector2 RayIntersect; @@ -98,6 +108,7 @@ public: static float magnitude(const vector3& vec); static void vector_add(vector2& vec1Dst, const vector2& vec2); static vector2 vector_sub(const vector2& vec1, const vector2& vec2); + static vector2 vector_mul(const vector2& vec1, float val); static float basic_collision(TBall* ball, vector2* nextPosition, vector2* direction, float elasticity, float smoothness, float threshold, float boost); diff --git a/SpaceCadetPinball/options.cpp b/SpaceCadetPinball/options.cpp index d05074e..f0ebb5c 100644 --- a/SpaceCadetPinball/options.cpp +++ b/SpaceCadetPinball/options.cpp @@ -104,6 +104,12 @@ void options::InitPrimary() Options.IntegerScaling = get_int("Integer Scaling", false); Options.SoundVolume = Clamp(get_int("Sound Volume", DefVolume), MinVolume, MaxVolume); Options.MusicVolume = Clamp(get_int("Music Volume", DefVolume), MinVolume, MaxVolume); + Options.DebugOverlay = get_int("Debug Overlay", false); + Options.DebugOverlayGrid = get_int("Debug Overlay Grid", true); + Options.DebugOverlayAllEdges = get_int("Debug Overlay All Edges", true); + Options.DebugOverlayBallPosition = get_int("Debug Overlay Ball Position", true); + Options.DebugOverlayBallEdges = get_int("Debug Overlay Ball Edges", true); + Options.DebugOverlayCollisionMask = get_int("Debug Overlay Collision Mask", true); } void options::InitSecondary() @@ -143,6 +149,12 @@ void options::uninit() set_int("Integer Scaling", Options.IntegerScaling); set_int("Sound Volume", Options.SoundVolume); set_int("Music Volume", Options.MusicVolume); + set_int("Debug Overlay", Options.DebugOverlay); + set_int("Debug Overlay Grid", Options.DebugOverlayGrid); + set_int("Debug Overlay All Edges", Options.DebugOverlayAllEdges); + set_int("Debug Overlay Ball Position", Options.DebugOverlayBallPosition); + set_int("Debug Overlay Ball Edges", Options.DebugOverlayBallEdges); + set_int("Debug Overlay Collision Mask", Options.DebugOverlayCollisionMask); } diff --git a/SpaceCadetPinball/options.h b/SpaceCadetPinball/options.h index 7ba4a20..202871b 100644 --- a/SpaceCadetPinball/options.h +++ b/SpaceCadetPinball/options.h @@ -80,6 +80,12 @@ struct optionsStruct bool IntegerScaling; int SoundVolume; int MusicVolume; + bool DebugOverlay; + bool DebugOverlayGrid; + bool DebugOverlayAllEdges; + bool DebugOverlayBallPosition; + bool DebugOverlayBallEdges; + bool DebugOverlayCollisionMask; }; struct ControlRef diff --git a/SpaceCadetPinball/proj.cpp b/SpaceCadetPinball/proj.cpp index c53ddc7..7491591 100644 --- a/SpaceCadetPinball/proj.cpp +++ b/SpaceCadetPinball/proj.cpp @@ -26,33 +26,42 @@ void proj::init(float* mat4x3, float d, float centerX, float centerY) centery = centerY; } -void proj::matrix_vector_multiply(mat4_row_major* mat, vector3* vec, vector3* dstVec) +vector3 proj::matrix_vector_multiply(const mat4_row_major& mat, const vector3& vec) { - const float x = vec->X, y = vec->Y, z = vec->Z; - dstVec->X = z * mat->Row0.Z + y * mat->Row0.Y + x * mat->Row0.X + mat->Row0.W; - dstVec->Y = z * mat->Row1.Z + y * mat->Row1.Y + x * mat->Row1.X + mat->Row1.W; - dstVec->Z = z * mat->Row2.Z + y * mat->Row2.Y + x * mat->Row2.X + mat->Row2.W; + vector3 dstVec; + const float x = vec.X, y = vec.Y, z = vec.Z; + dstVec.X = z * mat.Row0.Z + y * mat.Row0.Y + x * mat.Row0.X + mat.Row0.W; + dstVec.Y = z * mat.Row1.Z + y * mat.Row1.Y + x * mat.Row1.X + mat.Row1.W; + dstVec.Z = z * mat.Row2.Z + y * mat.Row2.Y + x * mat.Row2.X + mat.Row2.W; + return dstVec; } -float proj::z_distance(vector3* vec) +float proj::z_distance(const vector3& vec) { - vector3 dstVec{}; - matrix_vector_multiply(&matrix, vec, &dstVec); - return maths::magnitude(dstVec); + auto projVec = matrix_vector_multiply(matrix, vec); + return maths::magnitude(projVec); } -void proj::xform_to_2d(vector3* vec, int* dst) +vector2i proj::xform_to_2d(const vector2& vec) +{ + vector3 vec3{ vec.X, vec.Y, 0 }; + return xform_to_2d(vec3); +} + +vector2i proj::xform_to_2d(const vector3& vec) { float projCoef; - vector3 dstVec2{}; - matrix_vector_multiply(&matrix, vec, &dstVec2); - if (dstVec2.Z == 0.0f) + auto projVec = matrix_vector_multiply(matrix, vec); + if (projVec.Z == 0.0f) projCoef = 999999.88f; else - projCoef = d_ / dstVec2.Z; - dst[0] = static_cast(dstVec2.X * projCoef + centerx); - dst[1] = static_cast(dstVec2.Y * projCoef + centery); + projCoef = d_ / projVec.Z; + return + { + static_cast(projVec.X * projCoef + centerx), + static_cast(projVec.Y * projCoef + centery) + }; } void proj::recenter(float centerX, float centerY) diff --git a/SpaceCadetPinball/proj.h b/SpaceCadetPinball/proj.h index e872925..d8cb728 100644 --- a/SpaceCadetPinball/proj.h +++ b/SpaceCadetPinball/proj.h @@ -22,9 +22,10 @@ class proj { public: static void init(float* mat4x3, float d, float centerX, float centerY); - static void matrix_vector_multiply(mat4_row_major* mat, vector3* vec, vector3* dstVec); - static float z_distance(vector3* vec); - static void xform_to_2d(vector3* vec, int* dst); + static vector3 matrix_vector_multiply(const mat4_row_major& mat, const vector3& vec); + static float z_distance(const vector3& vec); + static vector2i xform_to_2d(const vector3& vec); + static vector2i xform_to_2d(const vector2& vec); static void recenter(float centerX, float centerY); private: static mat4_row_major matrix; diff --git a/SpaceCadetPinball/render.cpp b/SpaceCadetPinball/render.cpp index 78c94b5..0a36f96 100644 --- a/SpaceCadetPinball/render.cpp +++ b/SpaceCadetPinball/render.cpp @@ -8,6 +8,7 @@ #include "score.h" #include "TPinballTable.h" #include "winmain.h" +#include "DebugOverlay.h" std::vector render::dirty_list, render::sprite_list, render::ball_list; zmap_header_type* render::background_zmap; @@ -57,6 +58,7 @@ void render::uninit() ball_list.clear(); dirty_list.clear(); sprite_list.clear(); + DebugOverlay::UnInit(); } void render::recreate_screen_texture() @@ -585,4 +587,9 @@ void render::PresentVScreen() SDL_RenderCopy(winmain::Renderer, vscreen->Texture, &srcSidebarRect, &dstSidebarRect); #endif } + + if (options::Options.DebugOverlay) + { + DebugOverlay::DrawOverlay(); + } } diff --git a/SpaceCadetPinball/winmain.cpp b/SpaceCadetPinball/winmain.cpp index 12e5d1a..89e233a 100644 --- a/SpaceCadetPinball/winmain.cpp +++ b/SpaceCadetPinball/winmain.cpp @@ -577,6 +577,24 @@ void winmain::RenderUi() { DispGRhistory ^= true; } + if (ImGui::MenuItem("Debug Overlay", nullptr, Options.DebugOverlay)) + { + Options.DebugOverlay ^= true; + } + if (Options.DebugOverlay && ImGui::BeginMenu("Overlay Options")) + { + if (ImGui::MenuItem("Box Grid", nullptr, Options.DebugOverlayGrid)) + Options.DebugOverlayGrid ^= true; + if (ImGui::MenuItem("All Edges", nullptr, Options.DebugOverlayAllEdges)) + Options.DebugOverlayAllEdges ^= true; + if (ImGui::MenuItem("Ball Position", nullptr, Options.DebugOverlayBallPosition)) + Options.DebugOverlayBallPosition ^= true; + if (ImGui::MenuItem("Ball Box Edges", nullptr, Options.DebugOverlayBallEdges)) + Options.DebugOverlayBallEdges ^= true; + if (ImGui::MenuItem("Apply Collision Mask", nullptr, Options.DebugOverlayCollisionMask)) + Options.DebugOverlayCollisionMask ^= true; + ImGui::EndMenu(); + } if (ImGui::BeginMenu("Cheats")) { if (ImGui::MenuItem("hidden test", nullptr, pb::cheat_mode))