1
0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-06-02 19:13:31 +02:00
VVVVVV/desktop_version/src/LevelDebugger.cpp
Dav999 60fd0bc0c2 Replace [Press {button} to toggle gameplay] by freeze/unfreeze
This is both easier for translators ("toggle" can be an annoying word)
and is useful in general because you can tell if gameplay is frozen
without having to have anything in the room that should normally be
moving but isn't.

I didn't follow the rule in lang/README-programmers.txt to keep the
original string around as ***OUTDATED*** in this case, since I know
only Arabic has it translated - we can just tell the Arabic translators
on Discord that this string was replaced.
2024-01-19 16:31:39 -08:00

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));
}
}