VVVVVV/desktop_version/src/LevelDebugger.cpp

468 lines
15 KiB
C++

#include "LevelDebugger.h"
#include "Constants.h"
#include "CustomLevels.h"
#include "Entity.h"
#include "Font.h"
#include "Graphics.h"
#include "KeyPoll.h"
#include "Localization.h"
#include "Map.h"
#include "Script.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;
bool forced = 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 right_mouse_held = false;
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)
{
SDL_Point mouse = { key.mousex, key.mousey };
return SDL_PointInRect(&mouse, rect);
}
void set_forced(void)
{
forced = true;
}
void input(void)
{
if (!forced && (!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 < (int) obj.entities.size(); i++)
{
SDL_Rect bounding_box = {
obj.entities[i].xp + obj.entities[i].cx,
obj.entities[i].yp + obj.entities[i].cy - map.ypos,
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.mousex - obj.entities[i].xp;
grabber_offset_y = key.mousey - obj.entities[i].yp;
if (!key.keymap[SDLK_LSHIFT] && !key.keymap[SDLK_RSHIFT])
{
for (int j = 0; j < (int) 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 (key.rightbutton)
{
if (mouse_within(&bounding_box))
{
if (!right_mouse_held)
{
right_mouse_held = true;
obj.entities[i].state = obj.entities[i].onentity;
obj.entities[i].statedelay = -1;
}
break;
}
}
else
{
right_mouse_held = false;
}
}
if (held_entity == -1)
{
for (int i = 0; i < (int) obj.blocks.size(); i++)
{
SDL_Rect bounding_box = {
obj.blocks[i].rect.x,
obj.blocks[i].rect.y - map.ypos,
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.mousex - obj.blocks[i].rect.x;
grabber_offset_y = key.mousey - obj.blocks[i].rect.y;
}
break;
}
}
else
{
held_entity = -1;
mouse_held = false;
held_block = -1;
}
}
}
}
void logic(void)
{
if (!active)
{
return;
}
if (INBOUNDS_VEC(held_entity, obj.entities))
{
int new_xp = key.mousex - grabber_offset_x;
int new_yp = key.mousey - grabber_offset_y;
if (key.isDown(SDLK_LSHIFT) || key.isDown(SDLK_RSHIFT))
{
new_xp -= new_xp % 8;
new_yp -= new_yp % 8;
}
obj.entities[held_entity].xp = new_xp;
obj.entities[held_entity].yp = new_yp;
obj.entities[held_entity].lerpoldxp = new_xp;
obj.entities[held_entity].lerpoldyp = new_yp;
obj.entities[held_entity].oldxp = new_xp;
obj.entities[held_entity].oldyp = new_yp;
}
if (INBOUNDS_VEC(held_block, obj.blocks))
{
int new_xp = key.mousex - grabber_offset_x;
int new_yp = key.mousey - 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)
{
font::print(PR_BOR | PR_FONT_8X8, 5, 32 + (10 * y), text, 220 - (help.glow), 220 - (help.glow), 255 - (help.glow / 2));
}
void render_coords(int y, const char* text, int first, int second)
{
char buffer[SCREEN_WIDTH_CHARS + 1];
vformat_buf(buffer, sizeof(buffer), "{text}: ({first},{second})", "text:str, first:int, second:int", text, first, second);
render_info(y, buffer);
}
void render_info(int y, const char* text, std::string value)
{
char buffer[SCREEN_WIDTH_CHARS + 1];
vformat_buf(buffer, sizeof(buffer), "{text}: {value}", "text:str, value:str", text, value.c_str());
render_info(y, buffer);
}
void render(void)
{
if (!active)
{
return;
}
int hovered = -1;
bool hovered_entity = true;
SDL_Rect hover_box;
for (int i = 0; i < (int) obj.entities.size(); i++)
{
SDL_Rect bounding_box = {
obj.entities[i].xp + obj.entities[i].cx,
obj.entities[i].yp + obj.entities[i].cy - map.ypos,
obj.entities[i].w,
obj.entities[i].h
};
if (hovered == -1 && mouse_within(&bounding_box))
{
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 gravity lines, show the true hitbox.
if (obj.entities[i].type == 9)
{
graphics.draw_rect(bounding_box.x - 1, bounding_box.y + 1, bounding_box.w + 2, bounding_box.h, graphics.getRGB(90, 90, 15));
}
else if (obj.entities[i].type == 10)
{
graphics.fill_rect(bounding_box.x - 2, bounding_box.y - 1, bounding_box.w + 1, bounding_box.h + 2, graphics.getRGB(90, 90, 15));
}
}
for (int i = 0; i < (int) obj.blocks.size(); i++)
{
SDL_Rect bounding_box = {
obj.blocks[i].rect.x,
obj.blocks[i].rect.y - map.ypos,
obj.blocks[i].rect.w,
obj.blocks[i].rect.h
};
if (hovered == -1 && mouse_within(&bounding_box))
{
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));
}
int line = 0;
if (key.isDown(SDLK_u))
{
SDL_Color on = graphics.getRGB(220 - (help.glow), 220 - (help.glow), 255 - (help.glow / 2));
SDL_Color off = graphics.getRGB(220 / 1.5 - (help.glow), 220 / 1.5 - (help.glow), 255 / 1.5 - (help.glow / 2));
graphics.set_blendmode(SDL_BLENDMODE_BLEND);
graphics.fill_rect(NULL, 0, 0, 0, 127);
graphics.set_blendmode(SDL_BLENDMODE_NONE);
int x = 0;
int y = 0;
for (int i = 0; i < (int) SDL_arraysize(obj.flags); i++)
{
SDL_Color color = obj.flags[i] ? on : off;
font::print(PR_BOR | PR_FONT_8X8, 48 + x * 24, 48 + y * 16, help.String(i), color.r, color.g, color.b);
x++;
if (x >= 10)
{
x = 0;
y++;
}
}
}
else if (hovered == -1)
{
render_coords(line++, "Room", game.roomx % 100, game.roomy % 100);
render_coords(line++, "Cursor", key.mousex, key.mousey);
render_info(line++, "Entities", help.String(obj.entities.size()));
line++;
render_info(line++, "State", help.String(game.state));
render_info(line++, "State Delay", help.String(game.statedelay));
line++;
render_info(line++, "AEM Target", help.String(script.i));
render_info(line++, "Warp Background", help.String(cl.getroomprop(game.roomx % 100, game.roomy % 100)->warpdir));
line++;
if (script.running)
{
render_info(line++, "Script", script.scriptname);
render_info(line++, "Delay", help.String(script.scriptdelay));
render_info(line++, "Position", help.String(script.position));
line++;
render_info(line++, "Loop Line", help.String(script.looppoint));
render_info(line++, "Loop Count", help.String(script.loopcount));
}
}
else
{
if (hovered_entity)
{
entclass* entity = &obj.entities[hovered];
render_info(line++, "Index", help.String(hovered));
render_coords(line++, "Position", entity->xp, entity->yp);
render_coords(line++, "Size", entity->w, entity->h);
line++;
render_info(line++, "Rule", help.String(entity->rule));
render_info(line++, "Type", help.String(entity->type));
render_info(line++, "Behave", help.String(entity->behave));
render_info(line++, "Para", help.String(entity->para));
render_info(line++, "State", help.String(entity->state));
line++;
render_info(line++, "Tile", help.String(entity->tile));
render_info(line++, "Draw Frame", help.String(entity->drawframe));
render_info(line++, "Size", help.String(entity->size));
render_info(line++, "Direction", help.String(entity->dir));
line++;
// Mostly contains duplicates, but for ease of use
switch (entity->type)
{
case 0:
// Player
render_info(line++, "Gravity", help.String(game.gravitycontrol));
render_info(line++, "Checkpoint", help.String(game.savepoint));
break;
case 1:
// Moving platforms and enemies
render_info(line++, "Speed", help.String(entity->para));
render_info(line++, "Movement type", help.String(entity->behave));
break;
case 7:
// Trinkets
render_info(line++, "ID", help.String(entity->para));
break;
case 8:
// Checkpoints
render_info(line++, "ID", help.String(entity->para));
render_info(line++, "Active", game.savepoint == entity->para ? "True" : "False");
break;
case 9:
// Horizontal gravity lines
render_info(line++, "Horizontal");
break;
case 10:
// Vertical gravity lines
render_info(line++, "Vertical");
break;
}
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", help.String(hovered));
render_coords(line++, "Position", block->rect.x, block->rect.y);
render_coords(line++, "Size", block->rect.w, block->rect.h);
line++;
if (block->type == TRIGGER || block->type == ACTIVITY)
{
render_info(line++, "Script", block->script);
render_info(line++, "State", help.String(block->trigger));
}
else if (block->type == DIRECTIONAL)
{
render_info(line++, "Direction", help.String(block->trigger));
}
graphics.draw_rect(hover_box.x, hover_box.y, hover_box.w, hover_box.h, graphics.getRGB(255 - help.glow, 32, 32));
}
}
const char* text;
if (should_pause)
{
text = loc::gettext("[Press {button} to unfreeze gameplay]");
}
else
{
text = loc::gettext("[Press {button} to freeze gameplay]");
}
char buffer[SCREEN_WIDTH_CHARS + 1];
vformat_buf(
buffer, sizeof(buffer),
text,
"button:str",
"TAB"
);
font::print(PR_BOR, 5, 14, buffer, 220 - (help.glow), 220 - (help.glow), 255 - (help.glow / 2));
}
}