2023-07-02 04:02:25 +02:00
|
|
|
#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;
|
|
|
|
|
2023-07-02 16:27:20 +02:00
|
|
|
bool right_mouse_held = false;
|
|
|
|
|
2023-07-02 04:02:25 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-07-02 16:27:20 +02:00
|
|
|
if ((game.press_map || key.isDown(27)) && !game.mapheld)
|
|
|
|
{
|
|
|
|
/* If we press enter, allow the rest of the code in Input.cpp to fire
|
|
|
|
* Intended to return to the editor, but it's fine if it activates
|
|
|
|
* something instead */
|
|
|
|
|
|
|
|
active = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-07-02 04:02:25 +02:00
|
|
|
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;
|
|
|
|
}
|
2023-07-02 16:27:20 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2023-07-02 04:02:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-02 16:27:20 +02:00
|
|
|
void render_info(int y, const char* text, std::string value)
|
2023-07-02 04:02:25 +02:00
|
|
|
{
|
|
|
|
char buffer[SCREEN_WIDTH_CHARS + 1];
|
2023-07-02 16:27:20 +02:00
|
|
|
vformat_buf(buffer, sizeof(buffer), text, "value:str", value.c_str());
|
2023-07-02 04:02:25 +02:00
|
|
|
font::print(PR_BOR | PR_FONT_8X8, 5, 32 + (10 * y), buffer, 220 - (help.glow), 220 - (help.glow), 255 - (help.glow / 2));
|
|
|
|
}
|
|
|
|
|
2023-07-02 16:27:20 +02:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2023-07-02 04:02:25 +02:00
|
|
|
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];
|
2023-07-02 16:27:20 +02:00
|
|
|
render_info(line++, "Index: {value}", help.String(hovered));
|
|
|
|
render_info(line++, "X: {value}", help.String(entity->xp));
|
|
|
|
render_info(line++, "Y: {value}", help.String(entity->yp));
|
|
|
|
render_info(line++, "Width: {value}", help.String(entity->w));
|
|
|
|
render_info(line++, "Height: {value}", help.String(entity->h));
|
2023-07-02 04:02:25 +02:00
|
|
|
line++;
|
2023-07-02 16:27:20 +02:00
|
|
|
render_info(line++, "Rule: {value}", help.String(entity->rule));
|
|
|
|
render_info(line++, "Type: {value}", help.String(entity->type));
|
|
|
|
render_info(line++, "Behave: {value}", help.String(entity->behave));
|
|
|
|
render_info(line++, "Para: {value}", help.String(entity->para));
|
|
|
|
render_info(line++, "State: {value}", help.String(entity->state));
|
2023-07-02 04:02:25 +02:00
|
|
|
line++;
|
2023-07-02 16:27:20 +02:00
|
|
|
render_info(line++, "Tile: {value}", help.String(entity->tile));
|
|
|
|
render_info(line++, "Draw Frame: {value}", help.String(entity->drawframe));
|
|
|
|
render_info(line++, "Size: {value}", help.String(entity->size));
|
|
|
|
render_info(line++, "Direction: {value}", help.String(entity->dir));
|
|
|
|
|
|
|
|
line++;
|
|
|
|
|
|
|
|
// Mostly contains duplicates, but for ease of use
|
|
|
|
switch (entity->type)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
// Player
|
|
|
|
render_info(line++, "Gravity: {value}", help.String(game.gravitycontrol));
|
|
|
|
render_info(line++, "Checkpoint: {value}", help.String(game.savepoint));
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
// Moving platforms and enemies
|
|
|
|
render_info(line++, "Speed: {value}", help.String(entity->para));
|
|
|
|
render_info(line++, "Movement type: {value}", help.String(entity->behave));
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
// Trinkets
|
|
|
|
render_info(line++, "ID: {value}", help.String(entity->para));
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
// Checkpoints
|
|
|
|
render_info(line++, "ID: {value}", help.String(entity->para));
|
|
|
|
render_info(line++, "Active: {value}", 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;
|
|
|
|
}
|
|
|
|
|
2023-07-02 04:02:25 +02:00
|
|
|
|
|
|
|
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];
|
2023-07-02 16:27:20 +02:00
|
|
|
render_info(line++, "Index: {value}", help.String(hovered));
|
|
|
|
render_info(line++, "X: {value}", help.String(block->rect.x));
|
|
|
|
render_info(line++, "Y: {value}", help.String(block->rect.y));
|
|
|
|
render_info(line++, "Width: {value}", help.String(block->rect.w));
|
|
|
|
render_info(line++, "Height: {value}", help.String(block->rect.h));
|
2023-07-02 04:02:25 +02:00
|
|
|
|
|
|
|
line++;
|
|
|
|
|
|
|
|
if (block->type == TRIGGER || block->type == ACTIVITY)
|
|
|
|
{
|
2023-07-02 16:27:20 +02:00
|
|
|
render_info(line++, "Script: {value}", block->script);
|
|
|
|
render_info(line++, "State: {value}", help.String(block->trigger));
|
2023-07-02 04:02:25 +02:00
|
|
|
}
|
|
|
|
else if (block->type == DIRECTIONAL)
|
|
|
|
{
|
2023-07-02 16:27:20 +02:00
|
|
|
render_info(line++, "Direction: {value}", help.String(block->trigger));
|
2023-07-02 04:02:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
graphics.draw_rect(hover_box.x, hover_box.y, hover_box.w, hover_box.h, graphics.getRGB(255 - help.glow, 32, 32));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|