mirror of
https://github.com/TerryCavanagh/VVVVVV.git
synced 2024-06-24 13:38:29 +02:00
4922fa4599
This fixes a bug where you could still drag an entity around with the debugger inactive if you were holding the entity while disabling the debugger with Y. Furthermore, you couldn't even drop the entity even if you wanted to.
459 lines
15 KiB
C++
459 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));
|
|
}
|
|
}
|
|
|
|
char buffer[SCREEN_WIDTH_CHARS + 1];
|
|
vformat_buf(
|
|
buffer, sizeof(buffer),
|
|
loc::gettext("[Press {button} to toggle gameplay]"),
|
|
"button:str",
|
|
"TAB"
|
|
);
|
|
|
|
font::print(PR_BOR, 5, 14, buffer, 220 - (help.glow), 220 - (help.glow), 255 - (help.glow / 2));
|
|
}
|
|
}
|