Add level debugger screen

The level debugger is toggleable in playtesting mode by pressing Y.
You can toggle whether or not the game is paused inside of the debugger
by pressing TAB. The debugger screen allows you to see entity and block
properties, and allows you to move them around.
This commit is contained in:
AllyTally 2023-07-01 23:02:25 -03:00 committed by Misa Elizabeth Kai
parent 3c4ed36418
commit 91f87fa126
6 changed files with 332 additions and 1 deletions

View File

@ -84,6 +84,7 @@ set(VVV_SRC
src/Input.cpp
src/KeyPoll.cpp
src/Labclass.cpp
src/LevelDebugger.cpp
src/Localization.cpp
src/LocalizationMaint.cpp
src/LocalizationStorage.cpp

View File

@ -17,6 +17,7 @@
#include "Font.h"
#include "GlitchrunnerMode.h"
#include "Graphics.h"
#include "LevelDebugger.h"
#include "Localization.h"
#include "LocalizationStorage.h"
#include "KeyPoll.h"
@ -7491,7 +7492,7 @@ int Game::get_timestep(void)
bool Game::physics_frozen(void)
{
return roomname_translator::is_pausing();
return roomname_translator::is_pausing() || level_debugger::is_pausing();
}
bool Game::incompetitive(void)

View File

@ -13,6 +13,7 @@
#include "Graphics.h"
#include "GraphicsUtil.h"
#include "KeyPoll.h"
#include "LevelDebugger.h"
#include "Localization.h"
#include "LocalizationMaint.h"
#include "LocalizationStorage.h"
@ -2436,6 +2437,12 @@ void gameinput(void)
}
}
level_debugger::input();
if (level_debugger::is_pausing())
{
return;
}
game.press_map = false;
if (key.isDown(KEYBOARD_ENTER) || key.isDown(SDLK_KP_ENTER) || key.isDown(game.controllerButton_map) )
{

View File

@ -0,0 +1,306 @@
#include "LevelDebugger.h"
#include "Constants.h"
#include "Entity.h"
#include "Font.h"
#include "Graphics.h"
#include "KeyPoll.h"
#include "Localization.h"
#include "Map.h"
#include "UtilityClass.h"
#include "VFormat.h"
namespace level_debugger
{
bool active = false;
bool should_pause = true;
bool tab_held = false;
bool debug_held = false;
// Moving entities/blocks
bool mouse_held = false;
int held_entity = -1;
int held_block = -1;
int grabber_offset_x = 0;
int grabber_offset_y = 0;
bool is_pausing(void)
{
return active && should_pause;
}
bool is_active(void)
{
return active;
}
void toggle_active(void)
{
active = !active;
}
bool mouse_within(SDL_Rect* rect)
{
return key.mx >= rect->x && key.mx < rect->x + rect->w &&
key.my >= rect->y && key.my < rect->y + rect->h;
}
void input(void)
{
if (!map.custommode || map.custommodeforreal)
{
active = false;
return;
}
if (key.isDown(SDLK_y))
{
if (!debug_held)
{
debug_held = true;
active = !active;
}
}
else
{
debug_held = false;
}
if (!active)
{
return;
}
if (key.isDown(SDLK_TAB))
{
if (!tab_held)
{
tab_held = true;
should_pause = !should_pause;
}
}
else
{
tab_held = false;
}
for (int i = 0; i < obj.entities.size(); i++)
{
SDL_Rect bounding_box = {
obj.entities[i].xp + obj.entities[i].cx,
obj.entities[i].yp + obj.entities[i].cy,
obj.entities[i].w,
obj.entities[i].h
};
if (key.leftbutton)
{
if (mouse_within(&bounding_box))
{
if (!mouse_held)
{
mouse_held = true;
held_entity = i;
grabber_offset_x = key.mx - obj.entities[i].xp;
grabber_offset_y = key.my - obj.entities[i].yp;
if (!key.keymap[SDLK_LSHIFT] && !key.keymap[SDLK_RSHIFT])
{
for (int j = 0; j < obj.blocks.size(); j++)
{
if (obj.entities[i].xp == obj.blocks[j].rect.x && obj.entities[i].yp == obj.blocks[j].rect.y)
{
held_block = j;
}
}
}
}
break;
}
}
else
{
mouse_held = false;
held_entity = -1;
held_block = -1;
}
}
if (held_entity == -1)
{
for (int i = 0; i < obj.blocks.size(); i++)
{
SDL_Rect bounding_box = {
obj.blocks[i].rect.x,
obj.blocks[i].rect.y,
obj.blocks[i].rect.w,
obj.blocks[i].rect.h
};
if (key.leftbutton)
{
if (mouse_within(&bounding_box))
{
if (!mouse_held)
{
mouse_held = true;
held_block = i;
grabber_offset_x = key.mx - obj.blocks[i].rect.x;
grabber_offset_y = key.my - obj.blocks[i].rect.y;
}
break;
}
}
else
{
held_entity = -1;
mouse_held = false;
held_block = -1;
}
}
}
if (INBOUNDS_VEC(held_entity, obj.entities))
{
obj.entities[held_entity].xp = key.mx - grabber_offset_x;
obj.entities[held_entity].yp = key.my - grabber_offset_y;
if (key.isDown(SDLK_LSHIFT) || key.isDown(SDLK_RSHIFT))
{
obj.entities[held_entity].xp -= obj.entities[held_entity].xp % 8;
obj.entities[held_entity].yp -= obj.entities[held_entity].yp % 8;
}
}
if (INBOUNDS_VEC(held_block, obj.blocks))
{
int new_xp = key.mx - grabber_offset_x;
int new_yp = key.my - grabber_offset_y;
if (key.isDown(SDLK_LSHIFT) || key.isDown(SDLK_RSHIFT))
{
new_xp -= new_xp % 8;
new_yp -= new_yp % 8;
}
obj.blocks[held_block].xp = new_xp;
obj.blocks[held_block].yp = new_yp;
obj.blocks[held_block].rect.x = new_xp;
obj.blocks[held_block].rect.y = new_yp;
}
}
void render_info(int y, const char* text, const char* value)
{
char buffer[SCREEN_WIDTH_CHARS + 1];
vformat_buf(buffer, sizeof(buffer), text, "value:str", value);
font::print(PR_BOR | PR_FONT_8X8, 5, 32 + (10 * y), buffer, 220 - (help.glow), 220 - (help.glow), 255 - (help.glow / 2));
}
void render(void)
{
if (!active)
{
return;
}
int hovered = -1;
bool hovered_entity = true;
SDL_Rect hover_box;
for (int i = 0; i < obj.entities.size(); i++)
{
SDL_Rect bounding_box = {
obj.entities[i].xp + obj.entities[i].cx,
obj.entities[i].yp + obj.entities[i].cy,
obj.entities[i].w,
obj.entities[i].h
};
bool hovering = false;
if (hovered == -1 && mouse_within(&bounding_box))
{
hovering = true;
hovered = i;
hovered_entity = true;
hover_box = bounding_box;
}
graphics.draw_rect(bounding_box.x, bounding_box.y, bounding_box.w, bounding_box.h, graphics.getRGB(15, 90, 90));
}
for (int i = 0; i < obj.blocks.size(); i++)
{
SDL_Rect bounding_box = {
obj.blocks[i].rect.x,
obj.blocks[i].rect.y,
obj.blocks[i].rect.w,
obj.blocks[i].rect.h
};
bool hovering = false;
if (hovered == -1 && mouse_within(&bounding_box))
{
hovering = true;
hovered = i;
hovered_entity = false;
hover_box = bounding_box;
}
graphics.draw_rect(bounding_box.x, bounding_box.y, bounding_box.w, bounding_box.h, graphics.getRGB(90, 15, 15));
}
font::print(PR_BOR | PR_FONT_8X8, 5, 14, loc::gettext("[Press TAB to toggle movement]"), 220 - (help.glow), 220 - (help.glow), 255 - (help.glow / 2));
if (hovered != -1)
{
int line = 0;
if (hovered_entity)
{
entclass* entity = &obj.entities[hovered];
render_info(line++, "Index: {value}", help.String(hovered).c_str());
render_info(line++, "X: {value}", help.String(entity->xp).c_str());
render_info(line++, "Y: {value}", help.String(entity->yp).c_str());
render_info(line++, "Width: {value}", help.String(entity->w).c_str());
render_info(line++, "Height: {value}", help.String(entity->h).c_str());
line++;
render_info(line++, "Rule: {value}", help.String(entity->rule).c_str());
render_info(line++, "Type: {value}", help.String(entity->type).c_str());
render_info(line++, "Behave: {value}", help.String(entity->behave).c_str());
render_info(line++, "Para: {value}", help.String(entity->para).c_str());
line++;
render_info(line++, "Tile: {value}", help.String(entity->tile).c_str());
render_info(line++, "Draw Frame: {value}", help.String(entity->drawframe).c_str());
render_info(line++, "Size: {value}", help.String(entity->size).c_str());
render_info(line++, "Direction: {value}", help.String(entity->dir).c_str());
graphics.draw_rect(hover_box.x, hover_box.y, hover_box.w, hover_box.h, graphics.getRGB(32, 255 - help.glow, 255 - help.glow));
}
else
{
blockclass* block = &obj.blocks[hovered];
render_info(line++, "Index: {value}", help.String(hovered).c_str());
render_info(line++, "X: {value}", help.String(block->rect.x).c_str());
render_info(line++, "Y: {value}", help.String(block->rect.y).c_str());
render_info(line++, "Width: {value}", help.String(block->rect.w).c_str());
render_info(line++, "Height: {value}", help.String(block->rect.h).c_str());
line++;
if (block->type == TRIGGER || block->type == ACTIVITY)
{
render_info(line++, "Script: {value}", block->script.c_str());
render_info(line++, "State: {value}", help.String(block->trigger).c_str());
}
else if (block->type == DIRECTIONAL)
{
render_info(line++, "Direction: {value}", help.String(block->trigger).c_str());
}
graphics.draw_rect(hover_box.x, hover_box.y, hover_box.w, hover_box.h, graphics.getRGB(255 - help.glow, 32, 32));
}
}
}
};

View File

@ -0,0 +1,14 @@
#ifndef LEVELDEBUGGER_H
#define LEVELDEBUGGER_H
#include <SDL.h>
namespace level_debugger
{
bool is_pausing(void);
bool is_active(void);
void input(void);
void render(void);
}
#endif /* LEVELDEBUGGER_H */

View File

@ -14,6 +14,7 @@
#include "GraphicsUtil.h"
#include "InterimVersion.h"
#include "KeyPoll.h"
#include "LevelDebugger.h"
#include "Localization.h"
#include "LocalizationStorage.h"
#include "MakeAndPlay.h"
@ -2472,6 +2473,7 @@ void gamerender(void)
graphics.drawtrophytext();
}
level_debugger::render();
graphics.renderwithscreeneffects();
}