forked from mirrors/principia
Split off Imgui dialogs into one source file for each
Also refactor all other dialog backends into their own proper source files
This commit is contained in:
parent
820237874e
commit
91695dafe7
33 changed files with 3871 additions and 3776 deletions
|
|
@ -127,7 +127,8 @@ file(GLOB SRCS
|
|||
|
||||
if(BACKEND_IMGUI)
|
||||
file(GLOB IMGUI_SRCS
|
||||
lib/imgui/*.cpp)
|
||||
lib/imgui/*.cpp
|
||||
src/ui/*.cc)
|
||||
list(APPEND SRCS ${IMGUI_SRCS})
|
||||
endif()
|
||||
|
||||
|
|
|
|||
101
src/ui.cc
101
src/ui.cc
|
|
@ -3,58 +3,9 @@
|
|||
|
||||
#include "main.hh"
|
||||
#include "game.hh"
|
||||
#include "menu_main.hh"
|
||||
#include "menu-play.hh"
|
||||
#include "loading_screen.hh"
|
||||
#include "game-message.hh"
|
||||
#include "beam.hh"
|
||||
#include "wheel.hh"
|
||||
#include "pixel.hh"
|
||||
#include "command.hh"
|
||||
#include "i1o1gate.hh"
|
||||
#include "pkgman.hh"
|
||||
#include "object_factory.hh"
|
||||
#include "box.hh"
|
||||
#include "settings.hh"
|
||||
#include "fxemitter.hh"
|
||||
#include "i0o1gate.hh"
|
||||
#include "i2o0gate.hh"
|
||||
#include "display.hh"
|
||||
#include "prompt.hh"
|
||||
#include "robot_base.hh"
|
||||
#include "adventure.hh"
|
||||
#include "speaker.hh"
|
||||
#include "timer.hh"
|
||||
#include "jumper.hh"
|
||||
#include "item.hh"
|
||||
#include "escript.hh"
|
||||
#include "tpixel.hh"
|
||||
#include "factory.hh"
|
||||
#include "faction.hh"
|
||||
#include "anchor.hh"
|
||||
#include "resource.hh"
|
||||
#include "animal.hh"
|
||||
#include "simplebg.hh"
|
||||
#include "soundman.hh"
|
||||
#include "polygon.hh"
|
||||
#include "treasure_chest.hh"
|
||||
#include "decorations.hh"
|
||||
#include "sequencer.hh"
|
||||
#include "sfxemitter.hh"
|
||||
#include "key_listener.hh"
|
||||
#include "soundmanager.hh"
|
||||
|
||||
#include <tms/core/tms.h>
|
||||
#ifdef BUILD_VALGRIND
|
||||
#include <valgrind/valgrind.h>
|
||||
#endif
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#define SAVE_REGULAR 0
|
||||
#define SAVE_COPY 1
|
||||
|
||||
#define MAX_GRAVITY 75.f
|
||||
|
||||
const char *tips[] = {
|
||||
#ifdef TMS_BACKEND_PC
|
||||
|
|
@ -82,6 +33,8 @@ const int num_tips = sizeof(tips)/sizeof(char*);
|
|||
int ctip = -1;
|
||||
int ui::next_action = ACTION_IGNORE;
|
||||
|
||||
bool prompt_is_open = false;
|
||||
|
||||
void
|
||||
ui::message(const char *msg, bool long_duration)
|
||||
{
|
||||
|
|
@ -111,55 +64,7 @@ ui::messagef(const char *format, ...)
|
|||
#ifndef TMS_BACKEND_ANDROID
|
||||
void ui::open_url(const char *url)
|
||||
{
|
||||
ui:messagef("Opening the page in your web browser...", url);
|
||||
#if SDL_VERSION_ATLEAST(2,0,14)
|
||||
ui::messagef("Opening the page in your web browser...", url);
|
||||
SDL_OpenURL(url);
|
||||
#else
|
||||
#error "SDL2 2.0.14+ is required for this platform"
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(PRINCIPIA_BACKEND_IMGUI)
|
||||
void ui::render(){};
|
||||
#endif
|
||||
|
||||
#if defined(NO_UI)
|
||||
|
||||
bool prompt_is_open = false;
|
||||
void ui::init(){};
|
||||
void ui::open_dialog(int num, void *data/*=0*/){}
|
||||
void ui::open_sandbox_tips(){};
|
||||
void ui::emit_signal(int num, void *data/*=0*/){};
|
||||
void ui::quit(){};
|
||||
void ui::set_next_action(int action_id){};
|
||||
void ui::open_error_dialog(const char *error_msg){};
|
||||
void
|
||||
ui::confirm(const char *text,
|
||||
const char *button1, principia_action action1,
|
||||
const char *button2, principia_action action2,
|
||||
const char *button3/*=0*/, principia_action action3/*=ACTION_IGNORE*/,
|
||||
struct confirm_data _confirm_data/*=none*/
|
||||
)
|
||||
{
|
||||
P.add_action(action1.action_id, 0);
|
||||
}
|
||||
void ui::alert(const char*, uint8_t/*=ALERT_INFORMATION*/) {};
|
||||
|
||||
#elif defined(PRINCIPIA_BACKEND_IMGUI)
|
||||
|
||||
#include "ui_imgui.hh"
|
||||
|
||||
#elif defined(TMS_BACKEND_ANDROID)
|
||||
|
||||
#include "ui_android.hh"
|
||||
|
||||
#elif defined(TMS_BACKEND_PC)
|
||||
|
||||
#include "ui_gtk3.hh"
|
||||
|
||||
#else
|
||||
|
||||
#error "No dialog functions, to compile without please define NO_UI"
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -109,7 +109,6 @@ struct confirm_data
|
|||
{ }
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
class ui
|
||||
{
|
||||
public:
|
||||
|
|
@ -136,11 +135,8 @@ class ui
|
|||
static void alert(const char *text, uint8_t alert_type=ALERT_INFORMATION);
|
||||
static void render();
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(TMS_BACKEND_PC) && !defined(NO_UI)
|
||||
extern bool prompt_is_open;
|
||||
#endif
|
||||
|
||||
extern const char* tips[];
|
||||
extern const int num_tips;
|
||||
|
|
|
|||
31
src/ui/animal.cc
Normal file
31
src/ui/animal.cc
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#include "animal.hh"
|
||||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiAnimal {
|
||||
static bool do_open = false;
|
||||
|
||||
void open() {
|
||||
do_open = true;
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "Animal type");
|
||||
ImGui::SetNextWindowSize(ImVec2(200, .0));
|
||||
if (ImGui::BeginPopupModal("Animal type", REF_TRUE, MODAL_FLAGS)) {
|
||||
for (int i = 0; i < NUM_ANIMAL_TYPES; ++i) {
|
||||
if (ImGui::MenuItem(animal_data[i].name)) {
|
||||
entity* e = G->selection.e;
|
||||
|
||||
if (e && e->g_id == O_ANIMAL) {
|
||||
W->add_action(e->id, ACTION_SET_ANIMAL_TYPE, UINT_TO_VOID((uint32_t)i));
|
||||
|
||||
P.add_action(ACTION_HIGHLIGHT_SELECTED, 0);
|
||||
P.add_action(ACTION_RESELECT, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
81
src/ui/confirm.cc
Normal file
81
src/ui/confirm.cc
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#include "ui.hh"
|
||||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiConfirm {
|
||||
static bool do_open = false;
|
||||
const char *confirm_text;
|
||||
|
||||
const char *confirm_button1;
|
||||
const char *confirm_button2;
|
||||
const char *confirm_button3;
|
||||
|
||||
int confirm_action1;
|
||||
int confirm_action2;
|
||||
int confirm_action3;
|
||||
|
||||
void *confirm_action1_data = 0;
|
||||
void *confirm_action2_data = 0;
|
||||
void *confirm_action3_data = 0;
|
||||
|
||||
struct confirm_data confirm_data(CONFIRM_TYPE_DEFAULT);
|
||||
|
||||
void open(const char *text,
|
||||
const char *button1, principia_action action1,
|
||||
const char *button2, principia_action action2,
|
||||
const char *button3, principia_action action3,
|
||||
struct confirm_data _confirm_data) {
|
||||
|
||||
confirm_text = strdup(text);
|
||||
|
||||
confirm_button1 = strdup(button1);
|
||||
confirm_button2 = strdup(button2);
|
||||
if (button3) {
|
||||
confirm_button3 = strdup(button3);
|
||||
} else {
|
||||
confirm_button3 = 0;
|
||||
}
|
||||
|
||||
confirm_action1 = action1.action_id;
|
||||
confirm_action2 = action2.action_id;
|
||||
confirm_action3 = action3.action_id;
|
||||
|
||||
confirm_action1_data = action1.action_data;
|
||||
confirm_action2_data = action2.action_data;
|
||||
confirm_action3_data = action3.action_data;
|
||||
|
||||
confirm_data = _confirm_data;
|
||||
|
||||
do_open = true;
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "Confirm");
|
||||
ImGui_CenterNextWindow();
|
||||
ImGui::SetNextWindowSize(ImVec2(400, .0));
|
||||
|
||||
if (ImGui::BeginPopupModal("Confirm", NULL, MODAL_FLAGS)) {
|
||||
ImGui::TextWrapped("%s", confirm_text);
|
||||
|
||||
ImGui::Dummy(ImVec2(0.0f, 40.0f));
|
||||
|
||||
if (ImGui::Button(confirm_button1)) {
|
||||
P.add_action(confirm_action1, confirm_action1_data);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(confirm_button2)) {
|
||||
P.add_action(confirm_action2, confirm_action2_data);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (confirm_button3 != 0) {
|
||||
if (ImGui::Button(confirm_button3)) {
|
||||
P.add_action(confirm_action3, confirm_action3_data);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
52
src/ui/decoration.cc
Normal file
52
src/ui/decoration.cc
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#include "decorations.hh"
|
||||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiDecoration {
|
||||
static bool do_open = false;
|
||||
static int selected_index = 0;
|
||||
|
||||
void open() {
|
||||
selected_index = 0;
|
||||
do_open = true;
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "Decoration type");
|
||||
if (ImGui::BeginPopupModal("Decoration type", nullptr, MODAL_FLAGS)) {
|
||||
if (ImGui::BeginCombo(" ", decorations[selected_index].name)) {
|
||||
for (int i = 0; i < NUM_DECORATIONS; ++i) {
|
||||
bool is_selected = (selected_index == i);
|
||||
if (ImGui::Selectable(decorations[i].name, is_selected)) {
|
||||
selected_index = i;
|
||||
}
|
||||
if (is_selected) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::SeparatorText("");
|
||||
if (ImGui::Button("Apply")) {
|
||||
entity* e = G->selection.e;
|
||||
if (e && e->g_id == O_DECORATION) {
|
||||
((decoration*)e)->set_decoration_type((uint32_t)selected_index);
|
||||
((decoration*)e)->do_recreate_shape = true;
|
||||
|
||||
P.add_action(ACTION_HIGHLIGHT_SELECTED, 0);
|
||||
P.add_action(ACTION_RESELECT, 0);
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("Cancel")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
169
src/ui/frequency.cc
Normal file
169
src/ui/frequency.cc
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiFrequency {
|
||||
static uint32_t __always_zero = 0;
|
||||
|
||||
static bool do_open = false;
|
||||
static bool range = false;
|
||||
static entity *entity_ptr;
|
||||
|
||||
static bool this_is_tx = false;
|
||||
static uint32_t* this_freq_range_start;
|
||||
static uint32_t* this_freq_range_size;
|
||||
|
||||
typedef struct {
|
||||
//only used by update_freq_space, now freq_user
|
||||
size_t index;
|
||||
entity *ent;
|
||||
bool is_tx;
|
||||
//this is an inclusive range
|
||||
//a.k.a range_start..=range_end
|
||||
uint32_t range_start; uint32_t range_end;
|
||||
} FreqUsr;
|
||||
|
||||
static uint32_t max_freq = 0;
|
||||
static std::vector<FreqUsr> freq_space;
|
||||
|
||||
//emulating std::optional from c++17 (rust Option) with std::pair<T, bool>
|
||||
static std::pair<FreqUsr, bool> freq_user(entity *e) {
|
||||
FreqUsr usr;
|
||||
usr.ent = e;
|
||||
if ((e->g_id == O_TRANSMITTER) || (e->g_id == O_MINI_TRANSMITTER)) {
|
||||
usr.is_tx = true;
|
||||
usr.range_start = usr.range_end = e->properties[0].v.i;
|
||||
} else if (e->g_id == O_BROADCASTER) {
|
||||
usr.is_tx = true;
|
||||
usr.range_start = e->properties[0].v.i;
|
||||
usr.range_end = e->properties[0].v.i + e->properties[1].v.i;
|
||||
} else if (e->g_id == O_RECEIVER) {
|
||||
usr.is_tx = false;
|
||||
usr.range_start = usr.range_end = e->properties[0].v.i;
|
||||
} else if ((e->g_id == O_PIXEL) && (e->properties[4].v.i8 != 0)) {
|
||||
usr.is_tx = false;
|
||||
usr.range_start = usr.range_end = (uint32_t)(int8_t)e->properties[4].v.i8;
|
||||
} else {
|
||||
//usr is not fully inited~
|
||||
return std::pair<FreqUsr, bool>(usr, false);
|
||||
}
|
||||
return std::pair<FreqUsr, bool>(usr, true);
|
||||
}
|
||||
|
||||
//Update freq_space and max_freq
|
||||
//freq_space does not include the current tx
|
||||
static void update_freq_space() {
|
||||
freq_space.clear();
|
||||
max_freq = 0;
|
||||
uint32_t counter = 0;
|
||||
std::map<uint32_t, entity*> all_entities = W->get_all_entities();
|
||||
for (auto i = all_entities.begin(); i != all_entities.end(); i++) {
|
||||
entity *e = i->second;
|
||||
//Skip currently selected entity
|
||||
if (e == entity_ptr) continue;
|
||||
auto x = freq_user(e);
|
||||
if (x.second) {
|
||||
max_freq = (std::max)(max_freq, x.first.range_start);
|
||||
freq_space.push_back(x.first);
|
||||
}
|
||||
}
|
||||
std::sort(freq_space.begin(), freq_space.end(), [](const FreqUsr& a, const FreqUsr& b) {
|
||||
//return a.ent->id < b.ent->id;
|
||||
//XXX: sort by range_start looks better
|
||||
return a.range_start < b.range_start;
|
||||
});
|
||||
for (size_t i = 0; i < freq_space.size(); i++) freq_space[i].index = i;
|
||||
}
|
||||
|
||||
void open(bool is_range, entity *e) {
|
||||
do_open = true;
|
||||
|
||||
range = is_range;
|
||||
entity_ptr = e;
|
||||
|
||||
this_is_tx =
|
||||
(e->g_id == O_TRANSMITTER) ||
|
||||
(e->g_id == O_MINI_TRANSMITTER) ||
|
||||
(e->g_id == O_BROADCASTER);
|
||||
|
||||
//XXX: pixel->properties[4].v.i (need i8) is incorrect but this menu is currently unused for pixel anyway
|
||||
this_freq_range_start = (e->g_id == O_PIXEL) ? &e->properties[4].v.i : &e->properties[0].v.i;
|
||||
this_freq_range_size = range ? &e->properties[1].v.i : &__always_zero;
|
||||
|
||||
update_freq_space();
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "Frequency");
|
||||
ImGui_CenterNextWindow();
|
||||
if (ImGui::BeginPopupModal("Frequency", REF_TRUE, MODAL_FLAGS)) {
|
||||
//XXX: This assumes int is 4 bytes
|
||||
ImGui::DragInt("Frequency", (int*) this_freq_range_start, .1, 0, 10000);
|
||||
(*this_freq_range_size)++;
|
||||
if (range) ImGui::SliderInt("Range", (int*) this_freq_range_size, 1, 30);
|
||||
(*this_freq_range_size)--;
|
||||
|
||||
ImGui::Text(
|
||||
range ? "%s on frequencies: %d-%d" : "%s on frequency: %d",
|
||||
this_is_tx ? "Transmitting" : "Listening",
|
||||
*this_freq_range_start,
|
||||
*this_freq_range_start + *this_freq_range_size
|
||||
);
|
||||
|
||||
ImVec2 size = ImVec2(0., 133.);
|
||||
if (ImGui::BeginChild(ImGui::GetID("###x-table-frame"), size, false, FRAME_FLAGS | ImGuiWindowFlags_NavFlattened)) {
|
||||
if (ImGui::BeginTable("###x-table", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_NoSavedSettings)) {
|
||||
ImGui::TableSetupColumn("###", ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("Object");
|
||||
ImGui::TableSetupColumn("Frequency");
|
||||
ImGui::TableHeadersRow();
|
||||
for (const FreqUsr& usr: freq_space) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetBgColor(
|
||||
ImGuiTableBgTarget_RowBg0,
|
||||
(
|
||||
(usr.is_tx != this_is_tx) &&
|
||||
(this_is_tx ? (
|
||||
(usr.range_start >= *this_freq_range_start) &&
|
||||
(usr.range_end <= (*this_freq_range_start + *this_freq_range_size))
|
||||
) : (
|
||||
(*this_freq_range_start >= usr.range_start) &&
|
||||
((*this_freq_range_start + *this_freq_range_size) <= usr.range_end)
|
||||
))
|
||||
) ? (usr.is_tx ? ImColor(48, 255, 48, 48) : ImColor(255, 48, 48, 48))
|
||||
: ImColor(0,0,0,0)
|
||||
);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", usr.is_tx ? "^" : "v");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s (id: %d)", usr.ent->get_name(), usr.ent->id);
|
||||
ImGui::SetItemTooltip("Click to set frequency\nShift + click to select object");
|
||||
if (ImGui::IsItemClicked()) {
|
||||
if (ImGui::GetIO().KeyShift) {
|
||||
G->lock();
|
||||
b2Vec2 xy = usr.ent->get_position();
|
||||
float z = G->cam->_position.z;
|
||||
G->cam->set_position(xy.x, xy.y, z);
|
||||
G->selection.reset();
|
||||
G->selection.select(usr.ent);
|
||||
G->unlock();
|
||||
//ImGui::CloseCurrentPopup();
|
||||
} else {
|
||||
*this_freq_range_start = usr.range_start;
|
||||
if (range) *this_freq_range_size = usr.range_end - usr.range_start;
|
||||
}
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (usr.range_end == usr.range_start) {
|
||||
ImGui::Text("%d", usr.range_start);
|
||||
} else {
|
||||
ImGui::Text("%d-%d", usr.range_start, usr.range_end);
|
||||
}
|
||||
}
|
||||
//ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, 0);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
215
src/ui/level_manager.cc
Normal file
215
src/ui/level_manager.cc
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiLevelManager {
|
||||
struct lvlinfo_ext {
|
||||
lvlinfo info;
|
||||
uint32_t id;
|
||||
int type;
|
||||
};
|
||||
|
||||
static bool do_open = false;
|
||||
static std::string search_query{""};
|
||||
|
||||
static lvlfile *level_list = nullptr;
|
||||
static int level_list_type = LEVEL_LOCAL;
|
||||
|
||||
static lvlinfo_ext *level_metadata = nullptr;
|
||||
static tms_texture *level_icon;
|
||||
|
||||
static void upload_level_icon() {
|
||||
tms_texture_load_mem(level_icon, (const char*) &level_metadata->info.icon, 128, 128, 1);
|
||||
tms_texture_upload(level_icon);
|
||||
}
|
||||
|
||||
static int update_level_info(int id_type, uint32_t id) {
|
||||
if (level_metadata) {
|
||||
//Check if data needs to be reloaded
|
||||
if ((level_metadata->id == id) && (level_metadata->type == id_type)) return 0;
|
||||
|
||||
//Dealloc current data
|
||||
level_metadata->info.~lvlinfo();
|
||||
free(level_metadata);
|
||||
}
|
||||
|
||||
level_metadata = new lvlinfo_ext;
|
||||
|
||||
//Update meta
|
||||
level_metadata->id = id;
|
||||
level_metadata->type = id_type;
|
||||
|
||||
//Read level info
|
||||
lvledit lvl;
|
||||
if (lvl.open(id_type, id)) {
|
||||
level_metadata->info = lvl.lvl;
|
||||
if (level_metadata->info.descr_len && level_metadata->info.descr) {
|
||||
level_metadata->info.descr = strdup(level_metadata->info.descr);
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
delete level_metadata;
|
||||
level_metadata = nullptr;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void reload_level_list() {
|
||||
//Recursively deallocate the linked list
|
||||
while (level_list) {
|
||||
lvlfile* next = level_list->next;
|
||||
delete level_list;
|
||||
level_list = next;
|
||||
}
|
||||
//Get a new list of levels
|
||||
level_list = pkgman::get_levels(level_list_type);
|
||||
}
|
||||
|
||||
void init() {
|
||||
level_icon = tms_texture_alloc();
|
||||
}
|
||||
|
||||
void open() {
|
||||
do_open = true;
|
||||
search_query = "";
|
||||
level_list_type = LEVEL_LOCAL;
|
||||
reload_level_list();
|
||||
}
|
||||
|
||||
void layout() {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
handle_do_open(&do_open, "Level Manager");
|
||||
ImGui_CenterNextWindow();
|
||||
ImGui::SetNextWindowSize(ImVec2(800., 0.));
|
||||
if (ImGui::BeginPopupModal("Level Manager", REF_TRUE, MODAL_FLAGS)) {
|
||||
bool any_level_found = false;
|
||||
|
||||
//Top action bar
|
||||
{
|
||||
//Align stuff to the right
|
||||
//lvlname width + padding
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetContentRegionMax().x - 200.);
|
||||
|
||||
//Actual level name field
|
||||
ImGui::PushItemWidth(200.);
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
}
|
||||
ImGui::InputTextWithHint("##LvlmanLevelName", "Search levels", &search_query);
|
||||
ImGui::PopItemWidth();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
//Actual level list
|
||||
ImGui::BeginChild("save_list_child", ImVec2(0., 500.), false, FRAME_FLAGS | ImGuiWindowFlags_NavFlattened);
|
||||
if (ImGui::BeginTable("save_list", 5, ImGuiTableFlags_NoSavedSettings | ImGuiWindowFlags_NavFlattened | ImGuiTableFlags_Borders)) {
|
||||
//Setup table columns
|
||||
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("Last modified", ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("Version", ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
lvlfile *level = level_list;
|
||||
while (level) {
|
||||
//Search (lax_search is used to ignore case)
|
||||
if ((search_query.length() > 0) && !(
|
||||
lax_search(level->name, search_query) ||
|
||||
(std::to_string(level->id).find(search_query) != std::string::npos)
|
||||
)) {
|
||||
//Just skip levels we don't like
|
||||
level = level->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
//This is required to prevent ID conflicts
|
||||
ImGui::PushID(level->id);
|
||||
|
||||
//Start laying out the table row...
|
||||
ImGui::TableNextRow();
|
||||
|
||||
//ID
|
||||
if (ImGui::TableNextColumn()) {
|
||||
ImGui::Text("%d", level->id);
|
||||
}
|
||||
|
||||
//Name
|
||||
if (ImGui::TableNextColumn()) {
|
||||
ImGui::SetNextItemWidth(999.);
|
||||
ImGui::LabelText("##levelname", "%s", level->name);
|
||||
|
||||
//Display description if hovered
|
||||
if (ImGui::BeginItemTooltip()) {
|
||||
update_level_info(level->id_type, level->id);
|
||||
|
||||
if (!level_metadata) {
|
||||
ImGui::TextColored(ImVec4(1.,.3,.3,1.), "Failed to load level metadata");
|
||||
} else if (level_metadata->info.descr_len && level_metadata->info.descr) {
|
||||
ImGui::PushTextWrapPos(400);
|
||||
ImGui::TextWrapped("%s", level_metadata->info.descr);
|
||||
ImGui::PopTextWrapPos();
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(.6,.6,.6,1.), "<no description>");
|
||||
}
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
//Modified date
|
||||
if (ImGui::TableNextColumn()) {
|
||||
ImGui::TextUnformatted(level->modified_date);
|
||||
}
|
||||
|
||||
//Version
|
||||
if (ImGui::TableNextColumn()) {
|
||||
const char* version_str = level_version_string(level->version);
|
||||
ImGui::Text("%s", version_str);
|
||||
}
|
||||
|
||||
//Actions
|
||||
if (ImGui::TableNextColumn()) {
|
||||
// Delete level ---
|
||||
// To prevent accidental level deletion,
|
||||
// Shift must be held while clicking the button
|
||||
bool allow_delete = io.KeyShift;
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, allow_delete ? 1. : .6);
|
||||
if (ImGui::Button("Delete##delete-sandbox-level")) {
|
||||
G->lock();
|
||||
if (allow_delete && G->delete_level(level->id_type, level->id, level->save_id)) {
|
||||
//If deleting current local level, remove it's local_id
|
||||
//This disables the "save" option
|
||||
if ((level->id_type == LEVEL_LOCAL) && (level->id == W->level.local_id)) {
|
||||
W->level.local_id = 0;
|
||||
}
|
||||
//Reload the list of levels
|
||||
reload_level_list();
|
||||
}
|
||||
G->unlock();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
if (!allow_delete) ImGui::SetItemTooltip("Hold Shift to unlock");
|
||||
|
||||
// Open level ---
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Open level")) {
|
||||
P.add_action(ACTION_OPEN, level->id);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
|
||||
level = level->next;
|
||||
any_level_found = true;
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
if (!any_level_found) {
|
||||
ImGui::TextUnformatted("No levels found");
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
450
src/ui/level_properties.cc
Normal file
450
src/ui/level_properties.cc
Normal file
|
|
@ -0,0 +1,450 @@
|
|||
#include "simplebg.hh"
|
||||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiLevelProperties {
|
||||
static bool do_open = false;
|
||||
|
||||
void open() {
|
||||
do_open = true;
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "Level properties");
|
||||
ImGui_CenterNextWindow();
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(450., 550.), ImVec2(FLT_MAX, FLT_MAX));
|
||||
if (ImGui::BeginPopupModal("Level properties", REF_TRUE, MODAL_FLAGS)) {
|
||||
if (ImGui::BeginTabBar("###lvlproptabbar")) {
|
||||
if (ImGui::BeginTabItem("Information")) {
|
||||
ImGui::SeparatorText("Metadata");
|
||||
|
||||
std::string lvl_name(W->level.name, W->level.name_len);
|
||||
bool over_soft_limit = lvl_name.length() >= LEVEL_NAME_LEN_SOFT_LIMIT + 1;
|
||||
ImGui::BeginDisabled(over_soft_limit);
|
||||
ImGui::TextUnformatted("Name");
|
||||
if (ImGui::InputTextWithHint("##LevelName", LEVEL_NAME_PLACEHOLDER, &lvl_name)) {
|
||||
size_t to_copy = (size_t)(std::min)((int) lvl_name.length(), LEVEL_NAME_LEN_HARD_LIMIT);
|
||||
memcpy(&W->level.name, lvl_name.data(), to_copy);
|
||||
W->level.name_len = lvl_name.length();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
std::string lvl_descr(W->level.descr, W->level.descr_len);
|
||||
ImGui::TextUnformatted("Description");
|
||||
if (ImGui::InputTextMultiline("###LevelDescr", &lvl_descr)) {
|
||||
W->level.descr = strdup(lvl_descr.c_str());
|
||||
W->level.descr_len = lvl_descr.length();
|
||||
}
|
||||
|
||||
ImGui::SeparatorText("Type");
|
||||
if (ImGui::RadioButton("Adventure", W->level.type == LCAT_ADVENTURE)) {
|
||||
P.add_action(ACTION_SET_LEVEL_TYPE, (void*)LCAT_ADVENTURE);
|
||||
}
|
||||
if (ImGui::RadioButton("Puzzle", W->level.type == LCAT_PUZZLE)) {
|
||||
P.add_action(ACTION_SET_LEVEL_TYPE, (void*)LCAT_PUZZLE);
|
||||
}
|
||||
if (ImGui::RadioButton("Custom", W->level.type == LCAT_CUSTOM)) {
|
||||
P.add_action(ACTION_SET_LEVEL_TYPE, (void*)LCAT_CUSTOM);
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("World")) {
|
||||
//Background
|
||||
{
|
||||
static int current_item = 0;
|
||||
const char* items[] = { "Item 1", "Item 2", "Item 3", "Item 4" };
|
||||
|
||||
if (ImGui::Combo("Background", ¤t_item, available_bgs, num_bgs)) {
|
||||
W->level.bg = current_item;
|
||||
P.add_action(ACTION_RELOAD_LEVEL, 0);
|
||||
}
|
||||
ImGuiStyle style = ImGui::GetStyle();
|
||||
|
||||
bool current_bg_colored = false;
|
||||
for (const int *ptr = colored_bgs; ; ++ptr) {
|
||||
if ((*ptr == -1) || (*ptr == W->level.bg)) {
|
||||
current_bg_colored = *ptr != -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (current_bg_colored) {
|
||||
float col[4];
|
||||
unpack_rgba(W->level.bg_color, &col[0], &col[1], &col[2], &col[3]);
|
||||
col[3] = 1.;
|
||||
//ImGui::SameLine();
|
||||
if (ImGui::ColorEdit4("###bgc", col, ImGuiColorEditFlags_NoAlpha)) {
|
||||
W->level.bg_color = pack_rgba(col[0], col[1], col[2], 1.);
|
||||
}
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||
P.add_action(ACTION_RELOAD_LEVEL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Gravity
|
||||
{
|
||||
ImGui::SeparatorText("Gravity");
|
||||
ImGui::SliderFloat("X###gravityx", &W->level.gravity_x, -40., 40., "%.01f");
|
||||
ImGui::SliderFloat("Y###gravityy", &W->level.gravity_y, -40., 40., "%.01f");
|
||||
}
|
||||
//ImGui::SameLine();
|
||||
|
||||
//Border size
|
||||
//TODO check for "impossible" border sizes (<2, causes glitchy rendering)
|
||||
{
|
||||
static const ImVec2 bs = ImVec2(128. + 24., 100.);
|
||||
|
||||
float ix = 48.;
|
||||
float iy = ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y * 2.;
|
||||
|
||||
bool do_reload = false;
|
||||
|
||||
auto slider = [ix, &do_reload](const char *id, uint16_t *x, float flip, int flags = 0) {
|
||||
ImGui::PushItemWidth(ix);
|
||||
int xint = *x;
|
||||
if (ImGui::DragInt(id, &xint, flip * 0.1f, 0, 0, "%d", flags)) {
|
||||
*x = (std::max)(0, xint);
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
do_reload |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
};
|
||||
|
||||
ImGui::SeparatorText("Border size");
|
||||
|
||||
ImVec2 c = ImGui::GetCursorPos();
|
||||
ImVec2 p_min = ImGui::GetCursorScreenPos();
|
||||
ImVec2 p_max = ImVec2(p_min.x + bs.x, p_min.y + bs.y);
|
||||
ImGui::Dummy(bs);
|
||||
ImVec2 after_dummy = ImGui::GetCursorPos();
|
||||
|
||||
ImGui::GetWindowDrawList()->AddRect(
|
||||
ImVec2(p_min.x + ix / 2, p_min.y + iy / 2),
|
||||
ImVec2(p_max.x - ix / 2, p_max.y - iy / 2),
|
||||
ImColor(255, 255, 255, 64),
|
||||
5., 0, 2.
|
||||
);
|
||||
|
||||
ImGui::SetCursorPos(ImVec2(c.x, c.y + bs.y / 2 - iy / 2));
|
||||
slider("###x0", &W->level.size_x[0], -1.);
|
||||
|
||||
ImGui::SetCursorPos(ImVec2(c.x + bs.x - ix, c.y + bs.y / 2 - iy / 2));
|
||||
slider("###x1", &W->level.size_x[1], 1.);
|
||||
|
||||
ImGui::SetCursorPos(ImVec2(c.x + bs.x / 2 - ix / 2, c.y + bs.y - iy));
|
||||
slider("###y0", &W->level.size_y[0], -1., ImGuiSliderFlags_Vertical);
|
||||
|
||||
ImGui::SetCursorPos(ImVec2(c.x + bs.x / 2 - ix / 2, c.y));
|
||||
slider("###y1", &W->level.size_y[1], 1., ImGuiSliderFlags_Vertical);
|
||||
|
||||
//Button right in the center
|
||||
// const char *btext = "FIT";
|
||||
// float bix = ImGui::CalcTextSize(btext).y + ImGui::GetStyle().FramePadding.x * 2.;
|
||||
// ImGui::SetCursorPos(ImVec2(c.x + bs.x / 2 - bix / 2, c.y + bs.y / 2 - iy / 2));
|
||||
// ImGui::Button(btext);
|
||||
|
||||
if (do_reload) P.add_action(ACTION_RELOAD_LEVEL, 0);
|
||||
|
||||
ImGui::SetCursorPos(after_dummy);
|
||||
|
||||
if (ImGui::Button("Auto-fit borders", ImVec2(bs.x, 0.))) {
|
||||
P.add_action(ACTION_AUTOFIT_LEVEL_BORDERS, 0);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Physics")) {
|
||||
ImGui::TextUnformatted("These settings can affect simulation performance.\nyada yada yada this is a placeholder text\nfor the physics tab :3");
|
||||
|
||||
auto reload_if_changed = [](){
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||
P.add_action(ACTION_RELOAD_LEVEL, 0);
|
||||
}
|
||||
};
|
||||
auto slider_uint8t = [](uint8_t* x) {
|
||||
int tmp = (int)*x;
|
||||
//HACK: use pointer as unique id
|
||||
ImGui::PushID((size_t)x);
|
||||
if (ImGui::SliderInt("###slider", &tmp, 10, 255)) {
|
||||
*x = tmp & 0xff;
|
||||
}
|
||||
ImGui::PopID();
|
||||
};
|
||||
auto slider_float = [](float* x, float rng) {
|
||||
float tmp = *x;
|
||||
//HACK: use pointer as unique id
|
||||
ImGui::PushID((size_t)x);
|
||||
if (ImGui::SliderFloat("###slider", &tmp, 0.0f, rng)) {
|
||||
*x = tmp;
|
||||
}
|
||||
ImGui::PopID();
|
||||
};
|
||||
|
||||
ImGui::SeparatorText("");
|
||||
ImGui::TextUnformatted("Position Iterations");
|
||||
slider_uint8t(&W->level.position_iterations);
|
||||
reload_if_changed();
|
||||
ImGui::TextUnformatted("Velocity Iterations");
|
||||
slider_uint8t(&W->level.velocity_iterations);
|
||||
reload_if_changed();
|
||||
|
||||
ImGui::SeparatorText("");
|
||||
ImGui::TextUnformatted("Prismatic Tolerance");
|
||||
slider_float(&W->level.prismatic_tolerance, 0.075f);
|
||||
reload_if_changed();
|
||||
ImGui::TextUnformatted("Pivot Tolerance");
|
||||
slider_float(&W->level.pivot_tolerance, 0.075f);
|
||||
reload_if_changed();
|
||||
|
||||
ImGui::SeparatorText("");
|
||||
ImGui::TextUnformatted("Linear Damping");
|
||||
slider_float(&W->level.linear_damping, 10.00f);
|
||||
reload_if_changed();
|
||||
ImGui::TextUnformatted("Angular Damping");
|
||||
slider_float(&W->level.angular_damping, 10.00f);
|
||||
reload_if_changed();
|
||||
|
||||
ImGui::SeparatorText("");
|
||||
ImGui::TextUnformatted("Joint Friction");
|
||||
slider_float(&W->level.joint_friction, 10.00f);
|
||||
reload_if_changed();
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Gameplay")) {
|
||||
auto lvl_flag_toggle = [](uint64_t flag, const char *label, const char *help, bool disabled = false) {
|
||||
bool x = (W->level.flags & flag) != 0;
|
||||
if (disabled) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
if (ImGui::Checkbox(label, &x)) {
|
||||
if (x) {
|
||||
W->level.flags |= flag;
|
||||
} else {
|
||||
W->level.flags &= ~flag;
|
||||
}
|
||||
P.add_action(ACTION_RELOAD_LEVEL, 0);
|
||||
}
|
||||
if (disabled) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_AllowWhenDisabled)) {
|
||||
if (ImGui::BeginTooltip()) {
|
||||
if ((help != 0) && (*help != 0)) {
|
||||
ImGui::TextUnformatted(help);
|
||||
} else {
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::TextUnformatted("<no help available>");
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (ImGui::BeginChild("###gameplay-scroll", ImVec2(0, ImGui::GetContentRegionAvail().y))) {
|
||||
ImGui::SeparatorText("Flags");
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_LAYER_SWITCH,
|
||||
"Disable layer switch",
|
||||
"In adventure mode, disable manual robot layer switching.\nIn puzzle mode, restrict layer switching for objects.",
|
||||
!((W->level.type == LCAT_PUZZLE) || (W->level.type == LCAT_ADVENTURE))
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_INTERACTIVE,
|
||||
"Disable interactive",
|
||||
"Disable the ability to handle interactive objects."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_FALL_DAMAGE,
|
||||
"Disable fall damage",
|
||||
"Disable the damage robots take when they fall."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_CONNECTIONS,
|
||||
"Disable connections",
|
||||
"Disable the ability to create connections\n(Puzzle mode only)",
|
||||
W->level.type != LCAT_PUZZLE
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_STATIC_CONNS,
|
||||
"Disable static connections",
|
||||
"Disable connections to static objects such as platforms\n(Puzzle mode only)",
|
||||
W->level.type != LCAT_PUZZLE
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_JUMP,
|
||||
"Disable jumping",
|
||||
"Disable the robots ability to jump manually\n(Adventure mode only)",
|
||||
W->level.type != LCAT_ADVENTURE
|
||||
);
|
||||
///XXX: this applies to sandbox mode too, right?
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_ROBOT_HIT_SCORE,
|
||||
"Disable robot hit score",
|
||||
"Do not award points for shooting other robots"
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_ZOOM,
|
||||
"Disable zoom",
|
||||
"Disable the player's ability to zoom."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_CAM_MOVEMENT,
|
||||
"Disable cam movement",
|
||||
"Disable the players ability to manually move the camera."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_INITIAL_WAIT,
|
||||
"Disable initial wait",
|
||||
"Disable the waiting state when a level is started."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_UNLIMITED_ENEMY_VISION,
|
||||
"Unlimited enemy vision",
|
||||
"If enabled, enemy robots will be able see their target from any distance and through obstacles, and will always try to find a path to it."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_ENABLE_INTERACTIVE_DESTRUCTION,
|
||||
"Interactive destruction",
|
||||
"If enabled, interactive objects can be destroyed by shooting or blowing them up."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_ABSORB_DEAD_ENEMIES,
|
||||
"Absorb dead enemies",
|
||||
"If enabled, enemy corpses will despawn after a short amount of time."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_SNAP,
|
||||
"Snap by default",
|
||||
"When the player drags or rotates an object it will snap to a grid by default (good for easy beginner levels).\n(Puzzle mode only)",
|
||||
W->level.type != LCAT_PUZZLE
|
||||
);
|
||||
|
||||
lvl_flag_toggle(
|
||||
LVL_NAIL_CONNS,
|
||||
"Hide beam connections",
|
||||
"Use less visible nail-shaped connections for planks and beams.\nExisting connections will not be changed if this flag is modified."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_CONTINUE_BUTTON,
|
||||
"Disable continue button",
|
||||
"If initial wait is disabled, this option disables the Continue button in the lower right corner. Use pkgwarp to go to the next level instead."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_SINGLE_LAYER_EXPLOSIONS,
|
||||
"Single-layer explosions",
|
||||
"Enable this flag to prevent explosions from reaching objects in other layers."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_DAMAGE,
|
||||
"Disable damage",
|
||||
"Disable damage to any robot."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_3RD_LAYER,
|
||||
"Disable third layer",
|
||||
"If enabled, prevents objects from being moved to the third layer."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_PORTRAIT_MODE,
|
||||
"Portrait mode",
|
||||
"If enabled, the view will be set to portrait (vertical) mode during play."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_RC_CAMERA_SNAP,
|
||||
"Disable RC camera snap",
|
||||
"If enabled, the camera won't move to any selected RC."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_PHYSICS,
|
||||
"Disable physics",
|
||||
"If enabled, physics simulation in the level will be disabled."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DO_NOT_REQUIRE_DRAGFIELD,
|
||||
"Do not require dragfield",
|
||||
"If enabled, dragfields will not be required in order to move interactive objects."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_ROBOT_SPECIAL_ACTION,
|
||||
"Disable robot special action",
|
||||
"If enabled, the adventure robot won't be able to perform it's special action."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_ADVENTURE_MAX_ZOOM,
|
||||
"Disable adventure max zoom",
|
||||
"If enabled, the zoom will no longer be limited while following the adventure robot.\n(Adventure mode only)",
|
||||
W->level.type != LCAT_ADVENTURE
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_ROAM_LAYER_SWITCH,
|
||||
"Disable roam layer switch",
|
||||
"Disable the roaming robot's ability to change layers."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_CHUNKED_LEVEL_LOADING,
|
||||
"Chunked level loading",
|
||||
NULL
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_CAVEVIEW,
|
||||
"Disable adventure caveview",
|
||||
"Disable the caveview which appears when the adventure robot is in the second layer, with terrain in front of it in the third layer"
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_ROCKET_TRIGGER_EXPLOSIVES,
|
||||
"Disable rocket triggering explosives",
|
||||
"Prevent rockets from triggering any explosives when in contact"
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_STORE_SCORE_ON_GAME_OVER,
|
||||
"Store high score on game over",
|
||||
NULL
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_ALLOW_HIGH_SCORE_SUBMISSIONS,
|
||||
"Allow high score submissions",
|
||||
"Allow players to submit their high scores to be displayed on your levels community page."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_LOWER_SCORE_IS_BETTER,
|
||||
"Lower score is better",
|
||||
"A lower score is considered better than a higher score."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_AUTOMATICALLY_SUBMIT_SCORE,
|
||||
"Automatically submit score on finish",
|
||||
"Automatically submit score for the user when the level finishes."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DISABLE_ENDSCREENS,
|
||||
"Disable end-screens",
|
||||
"Disable any end-game sound or messages. Works well when \"Pause on win\" is disabled.\nNote that this also disables the score submission button.\nYou can still use the `game:submit_score()` lua function in order to submit highscores."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_ALLOW_QUICKSAVING,
|
||||
"Allow quicksaving",
|
||||
"If enabled, the player can save their progress at any time."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_ALLOW_RESPAWN_WITHOUT_CHECKPOINT,
|
||||
"Allow respawn without checkpoint",
|
||||
"If disabled, robots cannot respawn if they are not associated with any checkpoint."
|
||||
);
|
||||
lvl_flag_toggle(
|
||||
LVL_DEAD_CREATURE_DESTRUCTION,
|
||||
"Allow dead creature destruction",
|
||||
"If enabled, creature corpses can be destroyed by shooting them."
|
||||
);
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
103
src/ui/login.cc
Normal file
103
src/ui/login.cc
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
#include "ui.hh"
|
||||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiLogin {
|
||||
enum class LoginStatus {
|
||||
No,
|
||||
LoggingIn,
|
||||
ResultSuccess,
|
||||
ResultFailure
|
||||
};
|
||||
|
||||
static bool do_open = false;
|
||||
static std::string username{""};
|
||||
static std::string password{""};
|
||||
static LoginStatus login_status = LoginStatus::No;
|
||||
|
||||
void complete_login(int signal) {
|
||||
switch (signal) {
|
||||
case SIGNAL_LOGIN_SUCCESS:
|
||||
login_status = LoginStatus::ResultSuccess;
|
||||
break;
|
||||
case SIGNAL_LOGIN_FAILED:
|
||||
login_status = LoginStatus::ResultFailure;
|
||||
P.user_id = 0;
|
||||
P.username = nullptr;
|
||||
username = "";
|
||||
password = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void open() {
|
||||
do_open = true;
|
||||
username = "";
|
||||
password = "";
|
||||
login_status = LoginStatus::No;
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "Log in");
|
||||
ImGui_CenterNextWindow();
|
||||
//Only allow closing the window if a login attempt is not in progress
|
||||
bool *allow_closing = (login_status != LoginStatus::LoggingIn) ? REF_TRUE : NULL;
|
||||
if (ImGui::BeginPopupModal("Log in", allow_closing, MODAL_FLAGS)) {
|
||||
if (login_status == LoginStatus::ResultSuccess) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
ImGui::EndPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
bool req_username_len = username.length() > 0;
|
||||
bool req_pass_len = password.length() > 0;
|
||||
|
||||
ImGui::BeginDisabled(
|
||||
(login_status == LoginStatus::LoggingIn) ||
|
||||
(login_status == LoginStatus::ResultSuccess)
|
||||
);
|
||||
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
}
|
||||
bool activate = false;
|
||||
activate |= ImGui::InputTextWithHint("###username", "Username", &username, ImGuiInputTextFlags_EnterReturnsTrue);
|
||||
activate |= ImGui::InputTextWithHint("###password", "Password", &password, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_Password);
|
||||
|
||||
ImGui::EndDisabled();
|
||||
|
||||
bool can_submit =
|
||||
(login_status != LoginStatus::LoggingIn) &&
|
||||
(login_status != LoginStatus::ResultSuccess) &&
|
||||
(req_pass_len && req_username_len);
|
||||
ImGui::BeginDisabled(!can_submit);
|
||||
if (ImGui::Button(" Log in ") || (can_submit && activate)) {
|
||||
login_status = LoginStatus::LoggingIn;
|
||||
login_data *data = new login_data;
|
||||
strncpy(data->username, username.c_str(), 256);
|
||||
strncpy(data->password, password.c_str(), 256);
|
||||
P.add_action(ACTION_LOGIN, data);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(" Register ") || (can_submit && activate)) {
|
||||
COMMUNITY_URL("register");
|
||||
ui::open_url(url);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
switch (login_status) {
|
||||
case LoginStatus::LoggingIn:
|
||||
ImGui::TextUnformatted("Logging in...");
|
||||
break;
|
||||
case LoginStatus::ResultFailure:
|
||||
ImGui::TextColored(ImVec4(1., 0., 0., 1.), "Login failed"); // Login attempt failed
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
84
src/ui/lua_editor.cc
Normal file
84
src/ui/lua_editor.cc
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiLuaEditor {
|
||||
static bool do_open = false;
|
||||
static entity *entity_ptr;
|
||||
static bool has_unsaved_changes = false;
|
||||
|
||||
static std::string codeText;
|
||||
|
||||
void init() {}
|
||||
|
||||
static void flash_controller() {
|
||||
tms_infof("Flashing controller");
|
||||
|
||||
//get ptr to len and buf, freeing the old buf if present
|
||||
uint32_t *len = &entity_ptr->properties[0].v.s.len;
|
||||
char **buf = &entity_ptr->properties[0].v.s.buf;
|
||||
if (*buf) free(*buf);
|
||||
|
||||
//get code ptr and len
|
||||
const char *src = codeText.c_str();
|
||||
*len = codeText.size();
|
||||
|
||||
//trim trailing newlines
|
||||
while (*len && (src[*len - 1] == '\n')) --*len;
|
||||
|
||||
//create a new buffer and copy the data
|
||||
//principia lua code is not zero terminated
|
||||
*buf = (char*) malloc(*len);
|
||||
memcpy(*buf, src, *len);
|
||||
|
||||
has_unsaved_changes = false;
|
||||
}
|
||||
|
||||
static void reload_code() {
|
||||
uint32_t len = entity_ptr->properties[0].v.s.len;
|
||||
char *buf = entity_ptr->properties[0].v.s.buf;
|
||||
// char *code = (char*) malloc(len + 1);
|
||||
// memcpy(code, buf, len);
|
||||
// code[len] = '\0';
|
||||
tms_infof("code len %d", len);
|
||||
std::string code = std::string(buf, len);
|
||||
codeText = code;
|
||||
tms_infof("buf load success");
|
||||
has_unsaved_changes = false;
|
||||
}
|
||||
|
||||
void open(entity *entity /*= G->selection.e*/) {
|
||||
do_open = true;
|
||||
entity_ptr = entity;
|
||||
reload_code();
|
||||
}
|
||||
|
||||
void layout() {
|
||||
//TODO better ui design
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
handle_do_open(&do_open, "Code editor");
|
||||
ImGui_CenterNextWindow();
|
||||
ImGui::SetNextWindowSize(ImVec2(800, 0.));
|
||||
if (ImGui::BeginPopupModal("Code editor", REF_TRUE, MODAL_FLAGS | ImGuiWindowFlags_UnsavedDocument)) {
|
||||
ImGui::PushFont(ui_font_mono.font);
|
||||
|
||||
ImGui::InputTextMultiline("##source", &codeText, ImVec2(775., 500.), ImGuiInputTextFlags_AllowTabInput, nullptr);
|
||||
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
if (ImGui::Button("Save (Ctrl+S)") | (io.KeyCtrl && ImGui::IsKeyReleased(ImGuiKey_S))) {
|
||||
flash_controller();
|
||||
reload_code();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Close")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
ImGui::EndPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
43
src/ui/message.cc
Normal file
43
src/ui/message.cc
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiMessage {
|
||||
static bool do_open = false;
|
||||
static std::string message {""};
|
||||
static MessageType msg_type = MessageType::Error;
|
||||
|
||||
void open(const char* msg, MessageType typ /*=MessageType::Message*/) {
|
||||
do_open = true;
|
||||
msg_type = typ;
|
||||
message.assign(msg);
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "###info-popup");
|
||||
ImGui_CenterNextWindow();
|
||||
const char* typ;
|
||||
switch (msg_type) {
|
||||
case MessageType::Message:
|
||||
typ = "Message###info-popup";
|
||||
break;
|
||||
|
||||
case MessageType::Error:
|
||||
typ = "Error###info-popup";
|
||||
break;
|
||||
|
||||
case MessageType::LevelInfo:
|
||||
typ = "Level description###info-popup";
|
||||
break;
|
||||
}
|
||||
ImGui::SetNextWindowSize(ImVec2(400., 0.));
|
||||
if (ImGui::BeginPopupModal(typ, NULL, MODAL_FLAGS)) {
|
||||
ImGui::TextWrapped("%s", message.c_str());
|
||||
|
||||
ImGui::Dummy(ImVec2(0.0f, 40.0f));
|
||||
|
||||
if (ImGui::Button("Close")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
38
src/ui/new_level.cc
Normal file
38
src/ui/new_level.cc
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiNewLevel {
|
||||
static bool do_open = false;
|
||||
|
||||
void open() {
|
||||
do_open = true;
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "###new-level");
|
||||
ImGui_CenterNextWindow();
|
||||
if (ImGui::BeginPopupModal("New level###new-level", REF_TRUE, MODAL_FLAGS)) {
|
||||
|
||||
if (ImGui::Button("Custom")) {
|
||||
P.add_action(ACTION_NEW_LEVEL, LCAT_CUSTOM);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
if (ImGui::Button("Adventure")) {
|
||||
P.add_action(ACTION_NEW_LEVEL, LCAT_ADVENTURE);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
if (ImGui::Button("Procedural Adventure")) {
|
||||
P.add_action(ACTION_NEW_GENERATED_LEVEL, LCAT_ADVENTURE);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
if (ImGui::Button("Puzzle")) {
|
||||
P.add_action(ACTION_NEW_LEVEL, LCAT_PUZZLE);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
63
src/ui/obj_color_picker.cc
Normal file
63
src/ui/obj_color_picker.cc
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiObjColorPicker {
|
||||
static bool do_open = false;
|
||||
static entity *entity_ptr;
|
||||
static float ref_color[4];
|
||||
|
||||
static bool use_alpha = false;
|
||||
std::string wintitle{"Color"};
|
||||
|
||||
void open(bool alpha, entity *entity) {
|
||||
do_open = true;
|
||||
entity_ptr = entity;
|
||||
tvec4 color = entity->get_color();
|
||||
ref_color[0] = color.r;
|
||||
ref_color[1] = color.g;
|
||||
ref_color[2] = color.b;
|
||||
ref_color[3] = use_alpha ? color.a : 1.f;
|
||||
use_alpha = alpha;
|
||||
tms_infof("opening color picker for %s", entity->get_name());
|
||||
wintitle = string_format("%s###beam-color", entity_ptr->get_name());
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "###beam-color");
|
||||
ImGui_CenterNextWindow();
|
||||
|
||||
if (ImGui::BeginPopupModal(wintitle.c_str(), REF_TRUE, MODAL_FLAGS)) {
|
||||
tvec4 color = entity_ptr->get_color();
|
||||
float color_arr[4] = {
|
||||
color.r,
|
||||
color.g,
|
||||
color.b,
|
||||
use_alpha ? color.a : 1.f
|
||||
};
|
||||
ImGuiColorEditFlags flags =
|
||||
(use_alpha ? (
|
||||
ImGuiColorEditFlags_AlphaPreviewHalf |
|
||||
ImGuiColorEditFlags_AlphaBar
|
||||
) : ImGuiColorEditFlags_NoAlpha)
|
||||
| ImGuiColorEditFlags_NoDragDrop
|
||||
| ImGuiColorEditFlags_PickerHueWheel; //TODO: decide! (color wheel/square)
|
||||
if (ImGui::ColorPicker4("Color", (float*) &color_arr, flags, (const float*) &ref_color)) {
|
||||
entity_ptr->set_color4(color_arr[0], color_arr[1], color_arr[2], color_arr[3]);
|
||||
}
|
||||
|
||||
//*SPECIAL CASE*: Pixel frequency
|
||||
//This used to be controlled by the alpha value
|
||||
//but a separate slider is more user-friendly
|
||||
if (entity_ptr->g_id == O_PIXEL) {
|
||||
ImGui::Separator();
|
||||
ImGui::SliderInt(
|
||||
"Frequency",
|
||||
(int*) &entity_ptr->properties[4].v.i8,
|
||||
0, 255,
|
||||
(entity_ptr->properties[4].v.i8 == 0) ? "<none>" : "%d"
|
||||
);
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/ui/play_menu.cc
Normal file
25
src/ui/play_menu.cc
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiPlayMenu {
|
||||
static bool do_open = false;
|
||||
|
||||
void open() {
|
||||
do_open = true;
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "play_menu");
|
||||
if (ImGui::BeginPopup("play_menu", POPUP_FLAGS)) {
|
||||
if (ImGui::MenuItem("Controls")) {
|
||||
G->render_controls = true;
|
||||
}
|
||||
if (ImGui::MenuItem("Restart")) {
|
||||
P.add_action(ACTION_RESTART_LEVEL, 0);
|
||||
}
|
||||
if (ImGui::MenuItem("Back")) {
|
||||
P.add_action(ACTION_BACK, 0);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
52
src/ui/polygon.cc
Normal file
52
src/ui/polygon.cc
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#include "polygon.hh"
|
||||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiPolygon {
|
||||
static bool do_open = false;
|
||||
static int sublayer_depth = 1;
|
||||
static bool front_align = false;
|
||||
|
||||
void open() {
|
||||
do_open = true;
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "Polygon");
|
||||
ImGui::SetNextWindowSize(ImVec2(350, 0));
|
||||
|
||||
if (ImGui::BeginPopupModal("Polygon", nullptr, MODAL_FLAGS)) {
|
||||
entity *e = G->selection.e;
|
||||
|
||||
ImGui::Text("Sublayer Depth");
|
||||
ImGui::SliderInt("##depth", &sublayer_depth, 1, 4);
|
||||
|
||||
ImGui::Checkbox("Front Align", &front_align);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Sublayer depth from front instead of back");
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::SeparatorText("");
|
||||
if (ImGui::Button("Apply")) {
|
||||
if (e && e->g_id == O_PLASTIC_POLYGON) {
|
||||
((polygon*)e)->do_recreate_shape = true;
|
||||
|
||||
e->properties[1].v.i8 = static_cast<uint8_t>(front_align ? 1 : 0);
|
||||
e->properties[0].v.i8 = static_cast<uint8_t>(sublayer_depth - 1);
|
||||
|
||||
P.add_action(ACTION_HIGHLIGHT_SELECTED, 0);
|
||||
P.add_action(ACTION_RESELECT, 0);
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("Cancel")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
163
src/ui/quickadd.cc
Normal file
163
src/ui/quickadd.cc
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiQuickadd {
|
||||
static bool do_open = false;
|
||||
static std::string query{""};
|
||||
|
||||
enum class ItemCategory {
|
||||
MenuObject,
|
||||
//TODO other categories (like items, animals etc) like in gtk3
|
||||
};
|
||||
struct SearchItem {
|
||||
ItemCategory cat;
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
static bool is_haystack_inited = false;
|
||||
static std::vector<SearchItem> haystack;
|
||||
static std::vector<size_t> search_results; //referencing idx in haystack
|
||||
//last known best item, will be used in case there are no search results
|
||||
static size_t last_viable_solution;
|
||||
|
||||
static std::string resolve_item_name(SearchItem item) {
|
||||
std::string name;
|
||||
switch (item.cat) {
|
||||
case ItemCategory::MenuObject: {
|
||||
const struct menu_obj &obj = menu_objects[item.id];
|
||||
name = obj.e->get_name(); // XXX: get_real_name??
|
||||
break;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
//TODO fuzzy search and scoring
|
||||
static std::vector<uint32_t> low_confidence;
|
||||
static void search() {
|
||||
search_results.clear();
|
||||
low_confidence.clear();
|
||||
for (int i = 0; i < haystack.size(); i++) {
|
||||
SearchItem item = haystack[i];
|
||||
std::string name = resolve_item_name(item);
|
||||
if (lax_search(name, query)) {
|
||||
search_results.push_back(i);
|
||||
} else if (lax_search(query, name)) {
|
||||
low_confidence.push_back(i);
|
||||
}
|
||||
}
|
||||
//Low confidence results are pushed after regular ones
|
||||
//Low confidence = no query in name, but name is in query.
|
||||
//So while searching for "Thick plank"...
|
||||
// Thick plank is a regular match
|
||||
// Plank is a low confidence match
|
||||
for (int i = 0; i < low_confidence.size(); i++) {
|
||||
search_results.push_back(low_confidence[i]);
|
||||
}
|
||||
tms_infof(
|
||||
"search \"%s\" %d/%d matched, (%d low confidence)",
|
||||
query.c_str(),
|
||||
(int) search_results.size(),
|
||||
(int) haystack.size(),
|
||||
(int) low_confidence.size()
|
||||
);
|
||||
if (search_results.size() > 0) {
|
||||
last_viable_solution = search_results[0];
|
||||
}
|
||||
}
|
||||
|
||||
// HAYSTACK IS LAZY-INITED!
|
||||
// WE CAN'T CALL THIS RIGHT AWAY
|
||||
// AS MENU OBJECTS ARE INITED *AFTER* UI!!!
|
||||
static void init_haystack() {
|
||||
if (is_haystack_inited) return;
|
||||
is_haystack_inited = true;
|
||||
|
||||
//Setup haystack
|
||||
haystack.clear();
|
||||
haystack.reserve(menu_objects.size());
|
||||
for (int i = 0; i < menu_objects.size(); i++) {
|
||||
SearchItem itm;
|
||||
itm.cat = ItemCategory::MenuObject;
|
||||
itm.id = i;
|
||||
haystack.push_back(itm);
|
||||
}
|
||||
tms_infof("init qs haystack with size %d", (int) haystack.size());
|
||||
tms_debugf("DEBUG: menu obj cnt %d", (int) menu_objects.size());
|
||||
|
||||
//for opt. reasons
|
||||
search_results.reserve(haystack.size());
|
||||
low_confidence.reserve(haystack.size());
|
||||
}
|
||||
|
||||
void open() {
|
||||
do_open = true;
|
||||
query = "";
|
||||
search_results.clear();
|
||||
init_haystack();
|
||||
search();
|
||||
}
|
||||
|
||||
static void activate_item(SearchItem item) {
|
||||
switch (item.cat) {
|
||||
case ItemCategory::MenuObject: {
|
||||
p_gid g_id = menu_objects[item.id].e->g_id;
|
||||
P.add_action(ACTION_CONSTRUCT_ENTITY, g_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "quickadd");
|
||||
if (ImGui::BeginPopup("quickadd", POPUP_FLAGS)) {
|
||||
if (ImGui::IsKeyReleased(ImGuiKey_Escape)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
ImGui::EndPopup();
|
||||
return;
|
||||
}
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
}
|
||||
|
||||
if (ImGui::InputTextWithHint(
|
||||
"###qs-search",
|
||||
"Search for components",
|
||||
&query,
|
||||
ImGuiInputTextFlags_EnterReturnsTrue
|
||||
)) {
|
||||
if (search_results.size() > 0) {
|
||||
activate_item(haystack[search_results[0]]);
|
||||
} else {
|
||||
tms_infof("falling back to last best solution, can't just refuse to do anything!");
|
||||
activate_item(haystack[last_viable_solution]);
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
};
|
||||
if (ImGui::IsItemEdited()) {
|
||||
search();
|
||||
}
|
||||
|
||||
const float area_height = ImGui::GetTextLineHeightWithSpacing() * 7.25f + ImGui::GetStyle().FramePadding.y * 2.0f;
|
||||
if (ImGui::BeginChildFrame(ImGui::GetID("qsbox"), ImVec2(-FLT_MIN, area_height), ImGuiWindowFlags_NavFlattened)) {
|
||||
for (int i = 0; i < search_results.size(); i++) {
|
||||
ImGui::PushID(i);
|
||||
SearchItem item = haystack[search_results[i]];
|
||||
if (ImGui::Selectable(resolve_item_name(item).c_str())) {
|
||||
activate_item(item);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
if (search_results.size() == 0) {
|
||||
SearchItem item = haystack[last_viable_solution];
|
||||
if (ImGui::Selectable(resolve_item_name(item).c_str())) {
|
||||
activate_item(item);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
ImGui::EndChildFrame();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
285
src/ui/robot.cc
Normal file
285
src/ui/robot.cc
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
#include "faction.hh"
|
||||
#include "item.hh"
|
||||
#include "robot_base.hh"
|
||||
#include "ui_imgui.hh"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace UiRobot {
|
||||
static std::vector<uint32_t> equipment;
|
||||
static bool do_open = false;
|
||||
static bool needs_refresh = true;
|
||||
static entity* last_selected = nullptr;
|
||||
|
||||
void open() {
|
||||
entity* e = G->selection.e;
|
||||
|
||||
last_selected = e;
|
||||
needs_refresh = false;
|
||||
|
||||
if (e->properties[ROBOT_PROPERTY_HEAD].v.i8 < 0 || e->properties[ROBOT_PROPERTY_HEAD].v.i8 >= NUM_HEAD_TYPES) {
|
||||
e->properties[ROBOT_PROPERTY_HEAD].v.i8 = 0;
|
||||
}
|
||||
if (e->properties[ROBOT_PROPERTY_HEAD_EQUIPMENT].v.i8 < 0 || e->properties[ROBOT_PROPERTY_HEAD_EQUIPMENT].v.i8 >= NUM_HEAD_EQUIPMENT_TYPES) {
|
||||
e->properties[ROBOT_PROPERTY_HEAD_EQUIPMENT].v.i8 = 0;
|
||||
}
|
||||
if (e->properties[ROBOT_PROPERTY_BACK].v.i8 < 0 || e->properties[ROBOT_PROPERTY_BACK].v.i8 >= NUM_BACK_EQUIPMENT_TYPES) {
|
||||
e->properties[ROBOT_PROPERTY_BACK].v.i8 = 0;
|
||||
}
|
||||
if (e->properties[ROBOT_PROPERTY_FRONT].v.i8 < 0 || e->properties[ROBOT_PROPERTY_FRONT].v.i8 >= NUM_FRONT_EQUIPMENT_TYPES) {
|
||||
e->properties[ROBOT_PROPERTY_FRONT].v.i8 = 0;
|
||||
}
|
||||
if (e->properties[ROBOT_PROPERTY_FEET].v.i8 < 0 || e->properties[ROBOT_PROPERTY_FEET].v.i8 >= NUM_FEET_TYPES) {
|
||||
e->properties[ROBOT_PROPERTY_FEET].v.i8 = 0;
|
||||
}
|
||||
if (e->properties[ROBOT_PROPERTY_BOLT_SET].v.i8 < 0 || e->properties[ROBOT_PROPERTY_BOLT_SET].v.i8 >= NUM_BOLT_SETS) {
|
||||
e->properties[ROBOT_PROPERTY_BOLT_SET].v.i8 = 0;
|
||||
}
|
||||
|
||||
if (e->properties[ROBOT_PROPERTY_STATE].v.i8 < CREATURE_IDLE || e->properties[ROBOT_PROPERTY_STATE].v.i8 > CREATURE_DEAD) {
|
||||
e->properties[ROBOT_PROPERTY_STATE].v.i8 = CREATURE_IDLE;
|
||||
}
|
||||
if (e->properties[ROBOT_PROPERTY_ROAMING].v.i8 != 0 && e->properties[ROBOT_PROPERTY_ROAMING].v.i8 != 1) {
|
||||
e->properties[ROBOT_PROPERTY_ROAMING].v.i8 = 0;
|
||||
}
|
||||
if (e->properties[ROBOT_PROPERTY_DIR].v.i8 < 0 || e->properties[ROBOT_PROPERTY_DIR].v.i8 > 2) {
|
||||
e->properties[ROBOT_PROPERTY_DIR].v.i8 = 1;
|
||||
}
|
||||
if (e->properties[ROBOT_PROPERTY_FACTION].v.i8 < 0 || e->properties[ROBOT_PROPERTY_FACTION].v.i8 >= NUM_FACTIONS) {
|
||||
e->properties[ROBOT_PROPERTY_FACTION].v.i8 = FACTION_ENEMY;
|
||||
}
|
||||
|
||||
equipment.clear();
|
||||
if (e->properties[ROBOT_PROPERTY_EQUIPMENT].v.s.buf) {
|
||||
std::vector<char*> eq_parts = p_split(e->properties[ROBOT_PROPERTY_EQUIPMENT].v.s.buf, e->properties[ROBOT_PROPERTY_EQUIPMENT].v.s.len, ";");
|
||||
for (char* part : eq_parts) {
|
||||
uint32_t item_id = atoi(part);
|
||||
if (item_id < NUM_ITEMS) {
|
||||
equipment.push_back(item_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do_open = true;
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "Robot Settings:");
|
||||
if (ImGui::BeginPopupModal("Robot Settings:", nullptr, MODAL_FLAGS)) {
|
||||
entity* e = G->selection.e;
|
||||
if (e != last_selected) {
|
||||
last_selected = e;
|
||||
needs_refresh = true;
|
||||
}
|
||||
creature* c = static_cast<creature*>(e);
|
||||
// Default State
|
||||
bool isAdventure = (e->id == G->state.adventure_id && W->is_adventure());
|
||||
if (isAdventure) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
|
||||
ImGui::Text("Default State");
|
||||
int state = e->properties[ROBOT_PROPERTY_STATE].v.i8;
|
||||
if (ImGui::RadioButton("Idle", &state, CREATURE_IDLE)) {
|
||||
e->properties[ROBOT_PROPERTY_STATE].v.i8 = static_cast<uint8_t>(state);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Walking", &state, CREATURE_WALK)) {
|
||||
e->properties[ROBOT_PROPERTY_STATE].v.i8 = static_cast<uint8_t>(state);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Dead", &state, CREATURE_DEAD)) {
|
||||
e->properties[ROBOT_PROPERTY_STATE].v.i8 = static_cast<uint8_t>(state);
|
||||
}
|
||||
ImGui::SeparatorText("");
|
||||
|
||||
// Roaming
|
||||
bool roaming = e->properties[ROBOT_PROPERTY_ROAMING].v.i8 != 0;
|
||||
if (ImGui::Checkbox("Roaming", &roaming)) {
|
||||
e->properties[ROBOT_PROPERTY_ROAMING].v.i8 = roaming ? 1 : 0;
|
||||
}
|
||||
|
||||
// Initial Direction
|
||||
ImGui::SeparatorText("Initial Direction");
|
||||
int direction = e->properties[ROBOT_PROPERTY_DIR].v.i8;
|
||||
if (ImGui::RadioButton("Left", &direction, 0)) {
|
||||
e->properties[ROBOT_PROPERTY_DIR].v.i8 = static_cast<uint8_t>(direction);
|
||||
((robot_base*)e)->set_i_dir(DIR_LEFT);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Random", &direction, 1)) {
|
||||
e->properties[ROBOT_PROPERTY_DIR].v.i8 = static_cast<uint8_t>(direction);
|
||||
((robot_base*)e)->set_i_dir(0.f);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Right", &direction, 2)) {
|
||||
e->properties[ROBOT_PROPERTY_DIR].v.i8 = static_cast<uint8_t>(direction);
|
||||
((robot_base*)e)->set_i_dir(DIR_RIGHT);
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (isAdventure) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
// Faction
|
||||
ImGui::SeparatorText("Faction");
|
||||
int faction = e->properties[ROBOT_PROPERTY_FACTION].v.i8;
|
||||
for (int x = 0; x < NUM_FACTIONS; ++x) {
|
||||
if (ImGui::RadioButton(factions[x].name, &faction, x)) {
|
||||
e->properties[ROBOT_PROPERTY_FACTION].v.i8 = static_cast<uint8_t>(faction);
|
||||
((robot_base*)e)->set_faction(faction);
|
||||
}
|
||||
}
|
||||
if (faction >= NUM_FACTIONS) {
|
||||
e->properties[ROBOT_PROPERTY_FACTION].v.i8 = FACTION_ENEMY;
|
||||
((robot_base*)e)->set_faction(FACTION_ENEMY);
|
||||
}
|
||||
|
||||
// Equipment
|
||||
ImGui::SeparatorText("Equipment");
|
||||
auto item_cb_append = [&e](const char* labelPrefix, uint8_t* equipment, int numTypes, const int* itemArray, bool hasFeature = true) {
|
||||
if (!hasFeature) return;
|
||||
int globalItemId = (*equipment < numTypes) ? itemArray[*equipment] : 0;
|
||||
std::string currentLabel = item::get_ui_name(globalItemId);
|
||||
if (currentLabel.empty() || globalItemId == 0) {
|
||||
currentLabel = "None";
|
||||
}
|
||||
if (ImGui::BeginCombo(labelPrefix, currentLabel.c_str())) {
|
||||
if (ImGui::Selectable("None", *equipment == 0)) {
|
||||
*equipment = 0;
|
||||
needs_refresh = true;
|
||||
}
|
||||
for (int i = 0; i < numTypes; ++i) {
|
||||
int itemId = itemArray[i];
|
||||
if (itemId <= 0 || itemId >= NUM_ITEMS) {
|
||||
continue;
|
||||
}
|
||||
const char* label = item::get_ui_name(itemId);
|
||||
if (label == nullptr || strlen(label) == 0) {
|
||||
label = "Unknown Item";
|
||||
}
|
||||
bool selected = (globalItemId == itemId);
|
||||
if (ImGui::Selectable(label, selected)) {
|
||||
*equipment = static_cast<uint8_t>(i);
|
||||
needs_refresh = true;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
};
|
||||
if (c->has_feature(CREATURE_FEATURE_HEAD)) {
|
||||
item_cb_append("Head", &e->properties[ROBOT_PROPERTY_HEAD].v.i8, NUM_HEAD_TYPES, _head_to_item);
|
||||
item_cb_append("Head Equipment", &e->properties[ROBOT_PROPERTY_HEAD_EQUIPMENT].v.i8, NUM_HEAD_EQUIPMENT_TYPES, _head_equipment_to_item);
|
||||
} else {
|
||||
ImGui::BeginDisabled();
|
||||
item_cb_append("Head", &e->properties[ROBOT_PROPERTY_HEAD].v.i8, NUM_HEAD_TYPES, _head_to_item, false);
|
||||
item_cb_append("Head Equipment", &e->properties[ROBOT_PROPERTY_HEAD_EQUIPMENT].v.i8, NUM_HEAD_EQUIPMENT_TYPES, _head_equipment_to_item, false);
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
if (c->has_feature(CREATURE_FEATURE_BACK_EQUIPMENT)) {
|
||||
item_cb_append("Back Equipment", &e->properties[ROBOT_PROPERTY_BACK].v.i8, NUM_BACK_EQUIPMENT_TYPES, _back_to_item);
|
||||
} else {
|
||||
ImGui::BeginDisabled();
|
||||
item_cb_append("Back Equipment", &e->properties[ROBOT_PROPERTY_BACK].v.i8, NUM_BACK_EQUIPMENT_TYPES, _back_to_item, false);
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
if (c->has_feature(CREATURE_FEATURE_FRONT_EQUIPMENT)) {
|
||||
item_cb_append("Front Equipment", &e->properties[ROBOT_PROPERTY_FRONT].v.i8, NUM_FRONT_EQUIPMENT_TYPES, _front_to_item);
|
||||
} else {
|
||||
ImGui::BeginDisabled();
|
||||
item_cb_append("Front Equipment", &e->properties[ROBOT_PROPERTY_FRONT].v.i8, NUM_FRONT_EQUIPMENT_TYPES, _front_to_item, false);
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
item_cb_append("Feet", &e->properties[ROBOT_PROPERTY_FEET].v.i8, NUM_FEET_TYPES, _feet_to_item);
|
||||
item_cb_append("Bolt Set", &e->properties[ROBOT_PROPERTY_BOLT_SET].v.i8, NUM_BOLT_SETS, _bolt_to_item);
|
||||
|
||||
if (needs_refresh) {
|
||||
equipment.clear();
|
||||
if (e->properties[ROBOT_PROPERTY_EQUIPMENT].v.s.buf) {
|
||||
std::vector<char*> eq_parts = p_split(e->properties[ROBOT_PROPERTY_EQUIPMENT].v.s.buf, e->properties[ROBOT_PROPERTY_EQUIPMENT].v.s.len, ";");
|
||||
for (char* part : eq_parts) {
|
||||
uint32_t item_id = atoi(part);
|
||||
if (item_id < NUM_ITEMS && item_id != 0 && item_id != 1) {
|
||||
equipment.push_back(item_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
needs_refresh = false;
|
||||
}
|
||||
|
||||
ImGui::SeparatorText("Equipped items");
|
||||
if (ImGui::BeginListBox("##EquipmentList", ImVec2(0, ImGui::GetTextLineHeight() * 10))) {
|
||||
for (int x = 0; x < NUM_ITEMS; ++x) {
|
||||
struct item_option* io = &item_options[x];
|
||||
if (io->category != ITEM_CATEGORY_WEAPON &&
|
||||
io->category != ITEM_CATEGORY_TOOL &&
|
||||
io->category != ITEM_CATEGORY_CIRCUIT) {
|
||||
continue;
|
||||
}
|
||||
bool equipped = std::find(equipment.begin(), equipment.end(), x) != equipment.end();
|
||||
std::string label = item::get_ui_name(x);
|
||||
if (equipped) {
|
||||
label += " (Equipped)";
|
||||
}
|
||||
if (ImGui::Selectable(label.c_str())) {
|
||||
if (equipped) {
|
||||
equipment.erase(std::remove(equipment.begin(), equipment.end(), x), equipment.end());
|
||||
} else {
|
||||
equipment.push_back(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
|
||||
// Buttons
|
||||
ImGui::SeparatorText("");
|
||||
if (ImGui::Button("Apply")) {
|
||||
std::vector<int> equippedItems;
|
||||
|
||||
auto push_if_valid = [&](uint8_t typeSpecificId, const int* itemArray, int numTypes) {
|
||||
if (typeSpecificId < numTypes && typeSpecificId > 0) {
|
||||
int globalItemId = itemArray[typeSpecificId];
|
||||
if (globalItemId > 0 && globalItemId < NUM_ITEMS) {
|
||||
equippedItems.push_back(globalItemId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
push_if_valid(e->properties[ROBOT_PROPERTY_HEAD].v.i8, _head_to_item, NUM_HEAD_TYPES);
|
||||
push_if_valid(e->properties[ROBOT_PROPERTY_HEAD_EQUIPMENT].v.i8, _head_equipment_to_item, NUM_HEAD_EQUIPMENT_TYPES);
|
||||
push_if_valid(e->properties[ROBOT_PROPERTY_BACK].v.i8, _back_to_item, NUM_BACK_EQUIPMENT_TYPES);
|
||||
push_if_valid(e->properties[ROBOT_PROPERTY_FRONT].v.i8, _front_to_item, NUM_FRONT_EQUIPMENT_TYPES);
|
||||
push_if_valid(e->properties[ROBOT_PROPERTY_FEET].v.i8, _feet_to_item, NUM_FEET_TYPES);
|
||||
push_if_valid(e->properties[ROBOT_PROPERTY_BOLT_SET].v.i8, _bolt_to_item, NUM_BOLT_SETS);
|
||||
for (uint32_t itemId : equipment) {
|
||||
if (itemId < NUM_ITEMS && itemId != 0 && itemId != 1) {
|
||||
equippedItems.push_back(itemId);
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
for (size_t i = 0; i < equippedItems.size(); ++i) {
|
||||
if (i > 0) ss << ";";
|
||||
ss << equippedItems[i];
|
||||
}
|
||||
e->set_property(ROBOT_PROPERTY_EQUIPMENT, ss.str().c_str());
|
||||
ui::message("Robot properties saved!");
|
||||
P.add_action(ACTION_HIGHLIGHT_SELECTED, 0);
|
||||
P.add_action(ACTION_RESELECT, 0);
|
||||
W->add_action(e->id, ACTION_CALL_ON_LOAD);
|
||||
ImGui::CloseCurrentPopup();
|
||||
last_selected = nullptr;
|
||||
needs_refresh = true;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
last_selected = nullptr;
|
||||
needs_refresh = true;
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
45
src/ui/rubber.cc
Normal file
45
src/ui/rubber.cc
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiRubber {
|
||||
static bool do_open = false;
|
||||
static float restitution = 0.5f;
|
||||
static float friction = 1.8f;
|
||||
|
||||
void open() {
|
||||
entity* e = G->selection.e;
|
||||
restitution = e->properties[1].v.f;
|
||||
friction = e->properties[2].v.f;
|
||||
do_open = true;
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "Rubber");
|
||||
if (ImGui::BeginPopupModal("Rubber", REF_TRUE, MODAL_FLAGS)) {
|
||||
|
||||
ImGui::SliderFloat("Restitution", &restitution, 0.0f, 1.0f);
|
||||
ImGui::SliderFloat("Friction", &friction, 1.0f, 10.0f);
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::SeparatorText("");
|
||||
if (ImGui::Button("Apply")) {
|
||||
entity* e = G->selection.e;
|
||||
if (e && (e->g_id == O_WHEEL || e->g_id == O_RUBBER_BEAM)) {
|
||||
e->properties[1].v.f = restitution;
|
||||
e->properties[2].v.f = friction;
|
||||
P.add_action(ACTION_HIGHLIGHT_SELECTED, 0);
|
||||
P.add_action(ACTION_RESELECT, 0);
|
||||
do_open = false;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("Cancel")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
237
src/ui/sandbox_menu.cc
Normal file
237
src/ui/sandbox_menu.cc
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
#include "adventure.hh"
|
||||
#include "faction.hh"
|
||||
#include "robot_base.hh"
|
||||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiSandboxMenu {
|
||||
static bool do_open = false;
|
||||
static b2Vec2 sb_position = b2Vec2_zero;
|
||||
static std::vector<uint32_t> bookmarks = {};
|
||||
|
||||
void open() {
|
||||
do_open = true;
|
||||
sb_position = G->get_last_cursor_pos(0);
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "sandbox_menu");
|
||||
ImGui::SetNextWindowSizeConstraints(
|
||||
ImVec2(225., 0.),
|
||||
ImVec2(FLT_MAX, FLT_MAX)
|
||||
);
|
||||
if (ImGui::BeginPopup("sandbox_menu", POPUP_FLAGS)) {
|
||||
//TODO keyboard shortcuts
|
||||
|
||||
//True if current level can be saved as a copy
|
||||
//Saves can only be created if current level state is sandbox
|
||||
bool is_sandbox = G->state.sandbox;
|
||||
|
||||
//True if already saved and the save can be updated
|
||||
//Saves can only be updated if:
|
||||
// - Current level state is sandbox
|
||||
// - Level is local (and not an auto-save)
|
||||
// - Level is already saved
|
||||
bool can_update_save =
|
||||
G->state.sandbox &&
|
||||
(W->level_id_type == LEVEL_LOCAL) &&
|
||||
(W->level.local_id != 0); //&& W->level.name_len;
|
||||
|
||||
//Info panel
|
||||
|
||||
//Cursor:
|
||||
ImGui::Text("Cursor: (%.2f, %.2f)", sb_position.x, sb_position.y);
|
||||
|
||||
//Go to menu
|
||||
if (ImGui::BeginMenu("Go to...")) {
|
||||
float z = G->cam->_position.z;
|
||||
|
||||
auto goto_entity = [z](entity *ment) {
|
||||
b2Vec2 xy = ment->get_position();
|
||||
G->cam->set_position(xy.x, xy.y, z);
|
||||
sb_position = xy;
|
||||
};
|
||||
auto goto_position = [z](b2Vec2 xy) {
|
||||
G->cam->set_position(xy.x, xy.y, z);
|
||||
sb_position = xy;
|
||||
};
|
||||
|
||||
if (ImGui::MenuItem("0, 0")) {
|
||||
goto_position(b2Vec2_zero);
|
||||
}
|
||||
|
||||
if (W->is_adventure()) {
|
||||
if (ImGui::MenuItem("Player")) {
|
||||
if (adventure::player) {
|
||||
goto_entity(adventure::player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unimplemented: "Last camera position"
|
||||
// (functions as an undo from the last position that was gone to)
|
||||
|
||||
if (ImGui::MenuItem("Last created entity")) {
|
||||
if (!W->all_entities.empty()) {
|
||||
goto_entity(W->all_entities.rbegin()->second);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
if (bookmarks.size() > 0) {
|
||||
for (uint32_t eid : bookmarks) {
|
||||
//TODO: remove bookmark by right clicking
|
||||
//XXX: maybe auto remove if id is no longer valid???
|
||||
ImGui::PushID(eid);
|
||||
entity* ment = W->get_entity_by_id(eid);
|
||||
if (!ment) continue;
|
||||
//ImGui::PushItemFlag(ImGuiItemFlags_AutoClosePopups, false);
|
||||
std::string item_name = string_format("%s (id: %d)", ment->get_name(), eid);
|
||||
bool activated = ImGui::MenuItem(item_name.c_str());
|
||||
ImGui::SetItemTooltip(
|
||||
"Position: (%.02f, %.02f)\n(Layer %d)",
|
||||
// ment->get_name(),
|
||||
// ment->g_id,
|
||||
// eid,
|
||||
ment->get_position().x,
|
||||
ment->get_position().y,
|
||||
ment->get_layer() + 1
|
||||
);
|
||||
if (activated) {
|
||||
goto_entity(ment);
|
||||
}
|
||||
// Context menu for deleting right-clicked bookmark
|
||||
if (ImGui::BeginPopupContextItem("BookmarkContext")) {
|
||||
if (ImGui::MenuItem("Delete Bookmark")) {
|
||||
bookmarks.erase(std::find(bookmarks.begin(), bookmarks.end(), eid));
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
//ImGui::PopItemFlag();
|
||||
ImGui::PopID();
|
||||
}
|
||||
} else {
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::TextUnformatted("<no bookmarks>");
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
//Selected object info:
|
||||
if (G->selection.e) {
|
||||
//If an object is selected, display it's info...
|
||||
//XXX: some of this stuff does the same things as principia ui items...
|
||||
//---- consider removal?
|
||||
entity* sent = G->selection.e;
|
||||
b2Vec2 sent_pos = sent->get_position();
|
||||
|
||||
//ImGui::PushItemFlag(ImGuiItemFlags_AutoClosePopups, false);
|
||||
bool is_bookmarked = std::find(bookmarks.begin(), bookmarks.end(), sent->id) != bookmarks.end();
|
||||
if (ImGui::MenuItem("Bookmark entity", NULL, &is_bookmarked)) {
|
||||
///XXX: this is UB if called multiple times with the same is_bookmarked value (which should never happen)
|
||||
if (is_bookmarked) {
|
||||
bookmarks.push_back(sent->id);
|
||||
} else {
|
||||
auto x = std::remove(bookmarks.begin(), bookmarks.end(), sent->id);
|
||||
bookmarks.erase(x, bookmarks.end());
|
||||
}
|
||||
}
|
||||
//ImGui::PopItemFlag();
|
||||
ImGui::SetItemTooltip("Save the entity to the 'Go to...' menu");
|
||||
|
||||
bool already_at_cursor = sent_pos.x == sb_position.x && sent_pos.y == sb_position.y;
|
||||
ImGui::BeginDisabled(already_at_cursor);
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_AutoClosePopups, false);
|
||||
if (ImGui::MenuItem("Move to cursor" /*, NULL, already_at_cursor*/)) {
|
||||
G->selection.e->set_position(sb_position);
|
||||
};
|
||||
ImGui::PopItemFlag();
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (sent->is_creature() && W->is_adventure()) {
|
||||
int adventure_id = W->level.get_adventure_id();
|
||||
ImGui::BeginDisabled(sent->id == adventure_id);
|
||||
if (ImGui::MenuItem("Set as player", NULL, sent->id == adventure_id)) {
|
||||
creature *player = static_cast<class creature*>(G->selection.e);
|
||||
if (player->is_robot()) {
|
||||
robot_base *r = static_cast<class robot_base*>(player);
|
||||
r->set_faction(FACTION_FRIENDLY);
|
||||
}
|
||||
W->level.set_adventure_id(player->id);
|
||||
G->state.adventure_id = player->id;
|
||||
adventure::player = player;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
//"Level properties"
|
||||
if (ImGui::MenuItem("Level properties")) {
|
||||
UiLevelProperties::open();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("New level")) {
|
||||
UiNewLevel::open();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
//"Save": update current save
|
||||
if (can_update_save && ImGui::MenuItem("Save")) {
|
||||
P.add_action(ACTION_SAVE, 0);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
//"Save as...": create a new save
|
||||
if (is_sandbox && ImGui::MenuItem("Save copy")) {
|
||||
//TODO
|
||||
UiSave::open();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
// Open the Level Manager
|
||||
if (ImGui::MenuItem("Open")) {
|
||||
UiLevelManager::open();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
//"Publish online"
|
||||
if (is_sandbox) {
|
||||
ImGui::BeginDisabled(!P.user_id);
|
||||
ImGui::MenuItem("Publish online");
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
if (P.user_id && P.username) {
|
||||
// blah
|
||||
} else {
|
||||
if (ImGui::MenuItem("Log in")) {
|
||||
UiLogin::open();
|
||||
};
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Settings")) {
|
||||
UiSettings::open();
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Back to menu")) {
|
||||
P.add_action(ACTION_GOTO_MAINMENU, 0);
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Help: Principia Wiki")) {
|
||||
ui::open_url("https://principia-web.se/wiki/");
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Help: Getting Started")) {
|
||||
ui::open_url("https://principia-web.se/wiki/Getting_Started");
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/ui/sandbox_mode.cc
Normal file
31
src/ui/sandbox_mode.cc
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiSandboxMode {
|
||||
static bool do_open = false;
|
||||
|
||||
void open() {
|
||||
do_open = true;
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "sandbox_mode");
|
||||
if (ImGui::BeginPopup("sandbox_mode", POPUP_FLAGS)) {
|
||||
if (ImGui::MenuItem("Multiselect")) {
|
||||
G->lock();
|
||||
G->set_mode(GAME_MODE_MULTISEL);
|
||||
G->unlock();
|
||||
}
|
||||
if (ImGui::MenuItem("Connection edit")) {
|
||||
G->lock();
|
||||
G->set_mode(GAME_MODE_CONN_EDIT);
|
||||
G->unlock();
|
||||
}
|
||||
if (ImGui::MenuItem("Terrain paint")) {
|
||||
G->lock();
|
||||
G->set_mode(GAME_MODE_DRAW);
|
||||
G->unlock();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
62
src/ui/save.cc
Normal file
62
src/ui/save.cc
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiSave {
|
||||
static bool do_open = false;
|
||||
static std::string level_name{""};
|
||||
|
||||
void open() {
|
||||
do_open = true;
|
||||
size_t sz = (std::min)((int) W->level.name_len, LEVEL_NAME_LEN_HARD_LIMIT);
|
||||
level_name = std::string((const char*) &W->level.name, sz);
|
||||
if (level_name == std::string{LEVEL_NAME_PLACEHOLDER}) {
|
||||
level_name = "";
|
||||
}
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "###sas");
|
||||
ImGui_CenterNextWindow();
|
||||
if (ImGui::BeginPopupModal("Save as...###sas", REF_TRUE, MODAL_FLAGS)) {
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
ImGui::TextUnformatted("Level name:");
|
||||
|
||||
//Level name input field
|
||||
if (ImGui::IsWindowAppearing()) ImGui::SetKeyboardFocusHere();
|
||||
bool activate = ImGui::InputTextWithHint(
|
||||
"###levelname",
|
||||
LEVEL_NAME_PLACEHOLDER,
|
||||
&level_name,
|
||||
ImGuiInputTextFlags_EnterReturnsTrue
|
||||
);
|
||||
|
||||
//Validation
|
||||
bool invalid = level_name.length() > LEVEL_NAME_LEN_SOFT_LIMIT;
|
||||
|
||||
//Char counter, X/250
|
||||
float cpy = ImGui::GetCursorPosY();
|
||||
ImGui::SetCursorPosY(cpy + style.FramePadding.y);
|
||||
ImGui::TextColored(
|
||||
invalid ? ImColor(255, 0, 0) : ImColor(1.f, 1.f, 1.f, style.DisabledAlpha),
|
||||
"%zu/%d", level_name.length(), LEVEL_NAME_LEN_SOFT_LIMIT
|
||||
);
|
||||
ImGui::SetCursorPosY(cpy);
|
||||
|
||||
//Save button, right-aligned
|
||||
const char *save_str = "Save";
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetContentRegionMax().x - (ImGui::CalcTextSize(save_str).x + style.FramePadding.x * 2.));
|
||||
ImGui::BeginDisabled(invalid);
|
||||
if (ImGui::Button(save_str) || (activate && !invalid)) {
|
||||
size_t sz = (std::min)((int) level_name.length(), LEVEL_NAME_LEN_SOFT_LIMIT);
|
||||
memcpy((char*) &W->level.name, level_name.c_str(), sz);
|
||||
W->level.name_len = sz;
|
||||
P.add_action(ACTION_SAVE_COPY, 0);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
367
src/ui/settings.cc
Normal file
367
src/ui/settings.cc
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
#include "settings.hh"
|
||||
#include "soundmanager.hh"
|
||||
#include "ui_imgui.hh"
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace UiSettings {
|
||||
static bool do_open = false;
|
||||
|
||||
enum class IfDone {
|
||||
Nothing,
|
||||
Exit,
|
||||
Reload,
|
||||
};
|
||||
|
||||
static IfDone if_done = IfDone::Nothing;
|
||||
static bool is_saving = false;
|
||||
|
||||
static std::unordered_map<const char*, setting*> local_settings;
|
||||
|
||||
static const char* copy_settings[] = {
|
||||
//GRAPHICS
|
||||
"enable_shadows",
|
||||
"shadow_quality",
|
||||
"shadow_map_resx",
|
||||
"shadow_map_resy",
|
||||
"enable_ao",
|
||||
"ao_map_res",
|
||||
"postprocess",
|
||||
"enable_bloom",
|
||||
"vsync",
|
||||
"gamma_correct",
|
||||
//VOLUME
|
||||
"volume",
|
||||
"muted",
|
||||
//CONTROLS
|
||||
"touch_controls",
|
||||
"jail_cursor",
|
||||
"cam_speed_modifier",
|
||||
"smooth_cam",
|
||||
"zoom_speed",
|
||||
"smooth_zoom",
|
||||
//"smooth_menu",
|
||||
//INTERFACE
|
||||
"hide_tips",
|
||||
"display_grapher_value",
|
||||
"display_object_id",
|
||||
"display_fps",
|
||||
"uiscale",
|
||||
"first_adventure", "tutorial",
|
||||
"menu_speed",
|
||||
"smooth_menu",
|
||||
"emulate_touch",
|
||||
"rc_lock_cursor",
|
||||
#ifdef DEBUG
|
||||
"debug",
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
static void on_before_apply() {
|
||||
tms_infof("Preparing to reload stuff later...");
|
||||
}
|
||||
|
||||
static void on_after_apply() {
|
||||
tms_infof("Now, reloading some stuff (as promised!)...");
|
||||
|
||||
//Reload sound manager settings to apply new volume
|
||||
sm::load_settings();
|
||||
}
|
||||
|
||||
static void save_thread() {
|
||||
tms_debugf("inside save_thread()");
|
||||
tms_infof("Waiting for can_set_settings...");
|
||||
while (!P.can_set_settings) {
|
||||
tms_debugf("Waiting for can_set_settings...");
|
||||
SDL_Delay(1);
|
||||
}
|
||||
tms_debugf("Ok, ready, saving...");
|
||||
on_before_apply();
|
||||
for (size_t i = 0; copy_settings[i] != NULL; i++) {
|
||||
tms_infof("writing setting %s", copy_settings[i]);
|
||||
memcpy(settings[copy_settings[i]], local_settings[copy_settings[i]], sizeof(setting));
|
||||
}
|
||||
tms_assertf(settings.save(), "Unable to save settings.");
|
||||
on_after_apply();
|
||||
tms_infof("Successfully saved settings, returning...");
|
||||
P.can_reload_graphics = true;
|
||||
is_saving = false;
|
||||
tms_debugf("save_thread() completed");
|
||||
}
|
||||
|
||||
static void save_settings() {
|
||||
tms_infof("Saving settings...");
|
||||
is_saving = true;
|
||||
P.can_reload_graphics = false;
|
||||
P.can_set_settings = false;
|
||||
P.add_action(ACTION_RELOAD_GRAPHICS, 0);
|
||||
std::thread thread(save_thread);
|
||||
thread.detach();
|
||||
}
|
||||
|
||||
static void read_settings() {
|
||||
tms_infof("Reading settings...");
|
||||
for (auto& it: local_settings) {
|
||||
tms_debugf("free %s", it.first);
|
||||
free((void*) local_settings[it.first]);
|
||||
}
|
||||
local_settings.clear();
|
||||
for (size_t i = 0; copy_settings[i] != NULL; i++) {
|
||||
tms_debugf("reading setting %s", copy_settings[i]);
|
||||
setting *heap_setting = new setting;
|
||||
memcpy(heap_setting, settings[copy_settings[i]], sizeof(setting));
|
||||
local_settings[copy_settings[i]] = heap_setting;
|
||||
}
|
||||
}
|
||||
|
||||
void open() {
|
||||
do_open = true;
|
||||
is_saving = false;
|
||||
if_done = IfDone::Nothing;
|
||||
read_settings();
|
||||
}
|
||||
|
||||
static void im_resolution_picker(
|
||||
std::string friendly_name,
|
||||
const char *setting_x,
|
||||
const char *setting_y,
|
||||
const char* items[],
|
||||
int32_t items_x[],
|
||||
int32_t items_y[]
|
||||
) {
|
||||
int item_count = 0;
|
||||
while (items[item_count] != NULL) { item_count++; }
|
||||
item_count++; //to overwrite the terminator
|
||||
|
||||
std::string cust = string_format("%dx%d", local_settings[setting_x]->v.i, local_settings[setting_y]->v.i);
|
||||
items_x[item_count - 1] = local_settings[setting_x]->v.i;
|
||||
items_y[item_count - 1] = local_settings[setting_y]->v.i;
|
||||
items[item_count - 1] = cust.c_str();
|
||||
|
||||
int item_current = item_count - 1;
|
||||
for (int i = 0; i < item_count; i++) {
|
||||
if (
|
||||
(items_x[i] == local_settings[setting_x]->v.i) &&
|
||||
(items_y[i] == local_settings[setting_y]->v.i)
|
||||
) {
|
||||
item_current = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PushID(friendly_name.c_str());
|
||||
ImGui::TextUnformatted(friendly_name.c_str());
|
||||
ImGui::Combo("###combo", &item_current, items, (std::max)(item_count - 1, item_current + 1));
|
||||
ImGui::PopID();
|
||||
|
||||
local_settings[setting_x]->v.i = items_x[item_current];
|
||||
local_settings[setting_y]->v.i = items_y[item_current];
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "Settings");
|
||||
ImGui_CenterNextWindow();
|
||||
//TODO unsaved changes indicator
|
||||
if (ImGui::BeginPopupModal("Settings", is_saving ? NULL : REF_TRUE, MODAL_FLAGS)) {
|
||||
if ((if_done == IfDone::Exit) && !is_saving) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
ImGui::EndPopup();
|
||||
return;
|
||||
} else if ((if_done == IfDone::Reload) && !is_saving) {
|
||||
if_done = IfDone::Nothing;
|
||||
read_settings();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabBar("###settings-tabbbar")) {
|
||||
bool graphics_tab = ImGui::BeginTabItem("Graphics");
|
||||
ImGui::SetItemTooltip("Configure graphics and display settings");
|
||||
if (graphics_tab) {
|
||||
// ImGui::BeginTable("###graphics-settings", 2);
|
||||
// ImGui::TableNextColumn();
|
||||
|
||||
ImGui::SeparatorText("Shadows");
|
||||
ImGui::Checkbox("Enable shadows", (bool*) &local_settings["enable_shadows"]->v.b);
|
||||
ImGui::BeginDisabled(!local_settings["enable_shadows"]->v.b);
|
||||
ImGui::Checkbox("Smooth shadows", (bool*) &local_settings["shadow_quality"]->v.u8);
|
||||
{
|
||||
const char* resolutions[] = { "4096x4096", "4096x2048", "2048x2048", "2048x1024", "1024x1024", "1024x512", "512x512", "512x256", NULL };
|
||||
int32_t values_x[] = { 4096, 4096, 2048, 2048, 1024, 1024, 512, 512, -1 };
|
||||
int32_t values_y[] = { 4096, 2048, 2048, 1024, 1024, 512, 512, 256, -1 };
|
||||
im_resolution_picker(
|
||||
"Shadow resolution",
|
||||
"shadow_map_resx",
|
||||
"shadow_map_resy",
|
||||
resolutions,
|
||||
values_x,
|
||||
values_y
|
||||
);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SeparatorText("Ambient Occlusion");
|
||||
ImGui::Checkbox("Enable AO", (bool*) &local_settings["enable_ao"]->v.b);
|
||||
ImGui::SetItemTooltip("Adds subtle shading behind objects");
|
||||
ImGui::BeginDisabled(!local_settings["enable_ao"]->v.b);
|
||||
{
|
||||
const char* resolutions[] = { "512x512", "256x256", "128x128", NULL };
|
||||
int32_t values[] = { 512, 256, 128, -1 };
|
||||
im_resolution_picker(
|
||||
"AO resolution",
|
||||
"ao_map_res",
|
||||
"ao_map_res",
|
||||
resolutions,
|
||||
values,
|
||||
values
|
||||
);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SeparatorText("Post-processing");
|
||||
|
||||
// ImGui::Checkbox("Enable post-processing", (bool*) &local_settings["postprocess"]->v.b);
|
||||
// ImGui::BeginDisabled(!local_settings["postprocess"]->v.b);
|
||||
// ImGui::Checkbox("Enable bloom", local_settings["postprocess"]->v.b ? ((bool*) &local_settings["enable_bloom"]->v.b) : REF_FALSE);
|
||||
// ImGui::SetItemTooltip("Adds a subtle glow effect to bright objects");
|
||||
// ImGui::EndDisabled();ImGui::Checkbox("Enable post-processing", (bool*) &local_settings["postprocess"]->v.b);
|
||||
|
||||
//XXX: Post-processing always enables bloom, so these two settings basically do the same thing
|
||||
bool is_bloom_enabled = local_settings["enable_bloom"]->v.b && local_settings["postprocess"]->v.b;
|
||||
if (ImGui::Checkbox("Enable bloom", &is_bloom_enabled)) {
|
||||
local_settings["postprocess"]->v.b = is_bloom_enabled;
|
||||
local_settings["enable_bloom"]->v.b = is_bloom_enabled;
|
||||
}
|
||||
ImGui::SetItemTooltip("Adds a subtle glow effect to bright objects");
|
||||
|
||||
ImGui::Checkbox("Gamma correction", (bool*) &local_settings["gamma_correct"]->v.b);
|
||||
ImGui::SetItemTooltip("Adjusts the brightness and contrast to ensure accurate color representation");
|
||||
|
||||
ImGui::SeparatorText("Display");
|
||||
|
||||
//VSync option has no effect on Android
|
||||
#ifdef TMS_BACKEND_PC
|
||||
ImGui::Checkbox("Enable V-Sync", (bool*) &local_settings["vsync"]->v.b);
|
||||
ImGui::SetItemTooltip("Helps eliminate screen tearing by limiting the refresh rate.\nMay introduce a slight input delay.");
|
||||
#endif
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
bool sound_tab = ImGui::BeginTabItem("Sound");
|
||||
ImGui::SetItemTooltip("Change volume and other sound settings");
|
||||
if (sound_tab) {
|
||||
ImGui::SeparatorText("Volume");
|
||||
|
||||
ImGui::BeginDisabled(local_settings["muted"]->v.b);
|
||||
ImGui::SliderFloat(
|
||||
"###volume-slider",
|
||||
local_settings["muted"]->v.b ? REF_FZERO : ((float*) &local_settings["volume"]->v.f),
|
||||
0.f, 1.f
|
||||
);
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||
float volume = sm::volume;
|
||||
sm::volume = local_settings["volume"]->v.f;
|
||||
sm::play(&sm::click, sm::position.x, sm::position.y, rand(), 1., false, 0, true);
|
||||
sm::volume = volume;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::Checkbox("Mute", (bool*) &local_settings["muted"]->v.b);
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
bool controls_tab = ImGui::BeginTabItem("Controls");
|
||||
ImGui::SetItemTooltip("Mouse, keyboard and touchscreen settings");
|
||||
if (controls_tab) {
|
||||
ImGui::EndTabItem();
|
||||
|
||||
ImGui::SeparatorText("Camera");
|
||||
|
||||
ImGui::TextUnformatted("Camera speed");
|
||||
ImGui::SliderFloat("###Camera-speed", (float*) &local_settings["cam_speed_modifier"]->v.f, 0.1, 15.);
|
||||
|
||||
ImGui::Checkbox("Smooth camera", (bool*) &local_settings["smooth_cam"]->v.b);
|
||||
|
||||
ImGui::TextUnformatted("Zoom speed");
|
||||
ImGui::SliderFloat("###Camera-zoom-speed", (float*) &local_settings["zoom_speed"]->v.f, 0.1, 3.);
|
||||
|
||||
ImGui::Checkbox("Smooth zoom", (bool*) &local_settings["smooth_zoom"]->v.b);
|
||||
|
||||
ImGui::SeparatorText("Menu");
|
||||
|
||||
ImGui::TextUnformatted("Menu scroll speed");
|
||||
ImGui::SliderFloat("###Menu-speed", (float*) &local_settings["menu_speed"]->v.f, 1., 15.);
|
||||
|
||||
ImGui::Checkbox("Smooth menu scrolling", (bool*) &local_settings["smooth_menu"]->v.b);
|
||||
|
||||
ImGui::SeparatorText("Mouse");
|
||||
|
||||
ImGui::Checkbox("Enable cursor jail", (bool*) &local_settings["jail_cursor"]->v.b);
|
||||
ImGui::SetItemTooltip("Lock the cursor inside the the game window while playing a level");
|
||||
|
||||
ImGui::Checkbox("Enable RC cursor lock", (bool*) &local_settings["rc_lock_cursor"]->v.b);
|
||||
ImGui::SetItemTooltip("Lock the cursor while controlling RC widgets");
|
||||
|
||||
ImGui::SeparatorText("Touchscreen");
|
||||
|
||||
ImGui::Checkbox("Enable on-screen controls", (bool*) &local_settings["touch_controls"]->v.b);
|
||||
ImGui::SetItemTooltip("Enable touch-friendly on-screen controls");
|
||||
|
||||
ImGui::Checkbox("Emulate touch", (bool*) &local_settings["emulate_touch"]->v.b);
|
||||
ImGui::SetItemTooltip("Enable this if you use an external device other than a mouse to control Principia, such as a Wacom pad.");
|
||||
}
|
||||
|
||||
bool interface_tab = ImGui::BeginTabItem("Interface");
|
||||
ImGui::SetItemTooltip("Change UI scaling, visibility options and other interface settings");
|
||||
if (interface_tab) {
|
||||
ImGui::SeparatorText("Interface");
|
||||
|
||||
ImGui::TextUnformatted("UI Scale (requires restart)");
|
||||
std::string display_value = string_format("%.01f", local_settings["uiscale"]->v.f);
|
||||
ImGui::SliderFloat("###uiScale", &local_settings["uiscale"]->v.f, 0.2, 2., display_value.c_str());
|
||||
local_settings["uiscale"]->v.f = (int)(local_settings["uiscale"]->v.f * 10) * 0.1f;
|
||||
|
||||
ImGui::SeparatorText("Help & Tips");
|
||||
|
||||
ImGui::Checkbox("Do not show tips", (bool*) &local_settings["hide_tips"]->v.b);
|
||||
|
||||
ImGui::SeparatorText("Advanced");
|
||||
|
||||
ImGui::Checkbox("Display grapher values", (bool*) &local_settings["display_grapher_value"]->v.b);
|
||||
|
||||
ImGui::Checkbox("Display object IDs", (bool*) &local_settings["display_object_id"]->v.b);
|
||||
|
||||
ImGui::TextUnformatted("Display FPS");
|
||||
ImGui::Combo("###displayFPS", (int*) &local_settings["display_fps"]->v.u8, "Off\0On\0Graph\0Graph (Raw)\0", 4);
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
|
||||
//This assumes separator height == 1. which results in actual height of 0
|
||||
float button_area_height =
|
||||
ImGui::GetStyle().ItemSpacing.y + //Separator spacing
|
||||
(ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y * 2.); // Buttons
|
||||
if (ImGui::GetContentRegionAvail().y > button_area_height) {
|
||||
ImGui::SetCursorPosY(ImGui::GetContentRegionMax().y - button_area_height);
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::BeginDisabled(is_saving);
|
||||
bool do_save = false;
|
||||
if (ImGui::Button("Apply")) {
|
||||
if_done = IfDone::Reload;
|
||||
save_settings();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Save")) {
|
||||
if_done = IfDone::Exit;
|
||||
save_settings();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
56
src/ui/sticky.cc
Normal file
56
src/ui/sticky.cc
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiSticky {
|
||||
static bool do_open = false;
|
||||
static bool center_x = true;
|
||||
static bool center_y = true;
|
||||
static int font_size = 2;
|
||||
static std::string text = "Hello!";
|
||||
|
||||
void open() {
|
||||
entity* e = G->selection.e;
|
||||
text = e->properties[0].v.s.buf ? e->properties[0].v.s.buf : "Hello!";
|
||||
center_x = e->properties[1].v.i8 != 0;
|
||||
center_y = e->properties[2].v.i8 != 0;
|
||||
font_size = e->properties[3].v.i8;
|
||||
do_open = true;
|
||||
}
|
||||
|
||||
void layout() {
|
||||
|
||||
|
||||
handle_do_open(&do_open, "Sticky Note");
|
||||
if (ImGui::BeginPopupModal("Sticky Note", nullptr, MODAL_FLAGS)) {
|
||||
ImGui::Checkbox("Center X", ¢er_x);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Center Y", ¢er_y);
|
||||
ImGui::Spacing();
|
||||
ImGui::SliderInt("Font Size", &font_size, 0, 3);
|
||||
ImGui::SeparatorText("Text");
|
||||
ImGui::InputTextMultiline("##text", &text, ImVec2(300, ImGui::GetTextLineHeight() * 10));
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::SeparatorText("");
|
||||
if (ImGui::Button("Apply")) {
|
||||
entity* e = G->selection.e;
|
||||
if (e->properties[0].v.s.buf) {
|
||||
free(e->properties[0].v.s.buf);
|
||||
}
|
||||
e->properties[0].v.s.buf = strdup(text.c_str());
|
||||
e->properties[1].v.i8 = static_cast<uint8_t>(center_x);
|
||||
e->properties[2].v.i8 = static_cast<uint8_t>(center_y);
|
||||
e->properties[3].v.i8 = static_cast<uint8_t>(font_size);
|
||||
|
||||
P.add_action(ACTION_SET_STICKY_TEXT, text.c_str());
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
249
src/ui/synthesizer.cc
Normal file
249
src/ui/synthesizer.cc
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
#include "speaker.hh"
|
||||
#include "ui_imgui.hh"
|
||||
#include <chrono>
|
||||
|
||||
namespace UiSynthesizer {
|
||||
static bool do_open = false;
|
||||
static entity *entity_ptr = nullptr;
|
||||
|
||||
static std::chrono::steady_clock::time_point init_time;
|
||||
|
||||
#define SYNTH_GRAPH_SIZE ImVec2(400., 100.)
|
||||
//Points must be multiple of 400
|
||||
#define SYNTH_GRAPH_POINTS_0 400
|
||||
#define SYNTH_GRAPH_POINTS_1 800
|
||||
#define SYNTH_GRAPH_POINTS_2 1600
|
||||
#define SYNTH_GRAPH_VX 0.01f
|
||||
#define SYNTH_GRAPH_VY 1.f
|
||||
|
||||
// static const char *graph_type_names[] = { "Sine" };
|
||||
//static const graph_type_fns = {};
|
||||
enum {
|
||||
WAVEFORM_SINE,
|
||||
WAVEFORM_SQR,
|
||||
WAVEFORM_PULSE,
|
||||
WAVEFORM_SAWTOOTH,
|
||||
WAVEFORM_TRIANGLE,
|
||||
WAVEFORM_ETC
|
||||
};
|
||||
|
||||
void init() {
|
||||
init_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
void open(entity *entity /*= G->selection.e*/) {
|
||||
do_open = true;
|
||||
entity_ptr = entity;
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "Synthesizer");
|
||||
ImGui_CenterNextWindow();
|
||||
if (ImGui::BeginPopupModal("Synthesizer", REF_TRUE, MODAL_FLAGS)) {
|
||||
ImDrawList *draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
float *low_hz = &entity_ptr->properties[0].v.f;
|
||||
float *high_hz = &entity_ptr->properties[1].v.f;
|
||||
uint32_t *waveform = &entity_ptr->properties[2].v.i;
|
||||
float *bit_crushing = &entity_ptr->properties[3].v.f;
|
||||
float *vol_vibrato = &entity_ptr->properties[4].v.f;
|
||||
float *freq_vibrato = &entity_ptr->properties[5].v.f;
|
||||
float *vol_vibrato_ext = &entity_ptr->properties[6].v.f;
|
||||
float *freq_vibrato_ext = &entity_ptr->properties[7].v.f;
|
||||
float *pulse_width = &entity_ptr->properties[8].v.f;
|
||||
|
||||
if (*waveform < WAVEFORM_ETC) {
|
||||
//Render graph
|
||||
ImVec2 p_min = ImGui::GetCursorScreenPos();
|
||||
ImVec2 p_max = ImVec2(p_min.x + SYNTH_GRAPH_SIZE.x, p_min.y + SYNTH_GRAPH_SIZE.y);
|
||||
ImVec2 size = SYNTH_GRAPH_SIZE;
|
||||
|
||||
ImGui::PushID("synth-graph");
|
||||
ImGui::Dummy(SYNTH_GRAPH_SIZE);
|
||||
ImGui::PopID();
|
||||
|
||||
draw_list->PushClipRect(p_min, p_max);
|
||||
|
||||
//Background
|
||||
draw_list->AddRectFilled(p_min, p_max, ImColor(0, 0, 0));
|
||||
|
||||
//Center h. line
|
||||
draw_list->AddLine(
|
||||
ImVec2(0., p_min.y + (size.y / 2)),
|
||||
ImVec2(p_max.x, p_min.y + (size.y / 2)),
|
||||
ImColor(128, 128, 128),
|
||||
2.
|
||||
);
|
||||
|
||||
//Graph
|
||||
{
|
||||
double time_seconds;
|
||||
{
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
time_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(now - init_time).count();
|
||||
}
|
||||
|
||||
float freq = *low_hz;
|
||||
|
||||
// if ((*freq_vibrato > 0.) && (*freq_vibrato_ext > 0.)) {
|
||||
// freq *= 1. - (sin(2. * M_PI * time_seconds * *freq_vibrato) * SYNTH_GRAPH_VX) * *freq_vibrato_ext;
|
||||
// }
|
||||
|
||||
static const float scr_spd_inv = 10.;
|
||||
float x_offset = (time_seconds * SYNTH_GRAPH_VX) / scr_spd_inv;
|
||||
float x = 0.;
|
||||
|
||||
int points = (freq > 1000.) ? ((freq > 2000.) ? SYNTH_GRAPH_POINTS_2 : SYNTH_GRAPH_POINTS_1) : SYNTH_GRAPH_POINTS_0;
|
||||
|
||||
float bcc = 0;
|
||||
|
||||
float prev_draw_x, prev_draw_y;
|
||||
for (int i = 0; i < points; i++) {
|
||||
float y;
|
||||
float sx = (x + x_offset) * freq;
|
||||
float wave = sinf(sx * 2. * M_PI);
|
||||
switch (*waveform) {
|
||||
case WAVEFORM_SINE:
|
||||
y = wave;
|
||||
break;
|
||||
case WAVEFORM_SQR:
|
||||
//XXX: is this correct?
|
||||
y = ((int)(sx) & 1) ? 1 : -1;
|
||||
break;
|
||||
case WAVEFORM_PULSE:
|
||||
if (*pulse_width >= 1.) {
|
||||
y = 1.;
|
||||
} else if (*pulse_width <= 0.) {
|
||||
y = -1.;
|
||||
} else {
|
||||
y = (((wave + 1.f) / 2.f) >= (1.f - *pulse_width)) ? 1.f : -1.f;
|
||||
}
|
||||
break;
|
||||
case WAVEFORM_SAWTOOTH:
|
||||
y = fmod(sx * 2., 2.) - 1.;
|
||||
break;
|
||||
case WAVEFORM_TRIANGLE:
|
||||
// y = fmod(sx * freq, 1.);
|
||||
// if (y > 0.5) y = .5 - y;
|
||||
// y = (y - .25) * 4.;
|
||||
y = 4.0 * fabs(fmod(sx, 1.0) - 0.5) -1.;
|
||||
break;
|
||||
case WAVEFORM_ETC:
|
||||
break;
|
||||
}
|
||||
if (*vol_vibrato > 0.) {
|
||||
//y *= .5 - .5 * sin(M_PI * 2. * *vol_vibrato * (((float) i / points) + x_offset)) * *vol_vibrato_ext;
|
||||
//Limited to 15 hz to prevent epilepsy and stuff
|
||||
y *= .5 - .5 * sin(M_PI * 2. * (std::min)(*vol_vibrato, 15.f) * (time_seconds / 2.)) * *vol_vibrato_ext;
|
||||
}
|
||||
|
||||
float draw_x = p_min.x + ((x / SYNTH_GRAPH_VX) * size.x);
|
||||
float draw_y = p_min.y + (((y / SYNTH_GRAPH_VY) * -.5) + .5) * size.y;
|
||||
|
||||
if ((int) *bit_crushing > 0) {
|
||||
if (bcc != 0) draw_y = prev_draw_y;
|
||||
bcc += 1600.f / points;
|
||||
if (bcc >= (*bit_crushing + 1)) bcc = 0;
|
||||
}
|
||||
|
||||
if (i != 0) draw_list->AddLine(
|
||||
ImVec2(prev_draw_x, prev_draw_y),
|
||||
ImVec2(draw_x, draw_y),
|
||||
ImColor(255, 0, 0),
|
||||
2.f
|
||||
);
|
||||
|
||||
prev_draw_x = draw_x;
|
||||
prev_draw_y = draw_y;
|
||||
|
||||
x += SYNTH_GRAPH_VX / (float) points;
|
||||
}
|
||||
}
|
||||
|
||||
//Numbers
|
||||
draw_list->AddText(
|
||||
ImVec2(p_min.x, p_max.y - ImGui::GetFontSize()),
|
||||
ImColor(128, 128, 128),
|
||||
"0.0"
|
||||
);
|
||||
static const std::string end = string_format("%.02f", SYNTH_GRAPH_VX);
|
||||
draw_list->AddText(
|
||||
ImVec2(p_max.x - ImGui::CalcTextSize(end.c_str()).x, p_max.y - ImGui::GetFontSize()),
|
||||
ImColor(128, 128, 128),
|
||||
end.c_str()
|
||||
);
|
||||
draw_list->PopClipRect();
|
||||
} else {
|
||||
// ImGui::TextColored(ImColor(255, 0, 0), "Unable to [preview this waveform");
|
||||
// ImGui::SetCursorPosY(ImGui::GetStyle().ItemSpacing.y);
|
||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||
ImGui::PushID("synth-graph-dummy");
|
||||
ImGui::Dummy(SYNTH_GRAPH_SIZE);
|
||||
ImGui::PopID();
|
||||
draw_list->AddText(p, ImColor(255, 0, 0), "Unable to preview this waveform");
|
||||
}
|
||||
|
||||
//Controls
|
||||
|
||||
ImGui::SeparatorText("Waveform");
|
||||
|
||||
//Waveform
|
||||
if (ImGui::BeginCombo("Waveform", (*waveform < NUM_WAVEFORMS) ? speaker_options[*waveform]: "???")) {
|
||||
for (int i = 0; i < NUM_WAVEFORMS; i++) {
|
||||
ImGui::PushID(i);
|
||||
if (ImGui::Selectable(speaker_options[i])) {
|
||||
*waveform = i;
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
//Pulse -> Pulse width
|
||||
if (*waveform == WAVEFORM_PULSE) {
|
||||
ImGui::SliderFloat("Pulse width", pulse_width, 0., 1.);
|
||||
}
|
||||
|
||||
//Frequency
|
||||
ImGui::SeparatorText("Frequency");
|
||||
|
||||
//Base freq
|
||||
int hz_int = (int) roundf(*low_hz);
|
||||
if (ImGui::SliderInt("Base requency", &hz_int, 100, 3520)) {
|
||||
*low_hz = (float) hz_int;
|
||||
*high_hz = (std::max)(*high_hz, *low_hz);
|
||||
}
|
||||
|
||||
//Max freq
|
||||
hz_int = (int) roundf(*high_hz);
|
||||
if (ImGui::SliderInt("Max frequency", &hz_int, 100, 3520)) {
|
||||
*high_hz = (float) hz_int;
|
||||
*low_hz = (std::min)(*high_hz, *low_hz);
|
||||
}
|
||||
|
||||
//Vibrato
|
||||
ImGui::SeparatorText("Volume vibrato");
|
||||
|
||||
ImGui::SliderFloat("Frequency###vol-freq", vol_vibrato, 0., 32.);
|
||||
ImGui::SliderFloat("Extent###vol-ext", vol_vibrato_ext, 0., 1.);
|
||||
|
||||
//Freq vibrato
|
||||
ImGui::SeparatorText("Frequency vibrato");
|
||||
|
||||
ImGui::SliderFloat("Frequency###freq-freq", freq_vibrato, 0., 32.);
|
||||
ImGui::SliderFloat("Extent###freq-ext", freq_vibrato_ext, 0., 1.);
|
||||
|
||||
//Bitcrush
|
||||
ImGui::SeparatorText("Bitcrushing");
|
||||
|
||||
int bc_int = (int) *bit_crushing;
|
||||
if (ImGui::SliderInt("Bitcrushing", &bc_int, 0, 64)) {
|
||||
*bit_crushing = (float) bc_int;
|
||||
}
|
||||
|
||||
ImGui::TextDisabled("Visualization may be inaccurate");
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
54
src/ui/tips.cc
Normal file
54
src/ui/tips.cc
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#include "settings.hh"
|
||||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiTips {
|
||||
static bool do_open = false;
|
||||
|
||||
void open() {
|
||||
ctip = rand() % num_tips;
|
||||
do_open = true;
|
||||
}
|
||||
|
||||
void layout() {
|
||||
//TODO: optimize for uiscale!
|
||||
handle_do_open(&do_open, "Tips and tricks");
|
||||
ImGui_CenterNextWindow();
|
||||
ImGui::SetNextWindowSize(ImVec2(400, 200));
|
||||
if (ImGui::BeginPopupModal("Tips and tricks", REF_TRUE, MODAL_FLAGS)) {
|
||||
ImGuiStyle &style = ImGui::GetStyle();
|
||||
float font_size = ImGui::GetFontSize();
|
||||
ImVec2 frame_padding = style.FramePadding;
|
||||
ImVec2 content_region = ImGui::GetContentRegionMax();
|
||||
|
||||
//TODO remove hardcoded size
|
||||
if (ImGui::BeginChild("###tips-content-ctx", ImVec2(0, 115), false, FRAME_FLAGS)) {
|
||||
ImGui::TextWrapped("%s", tips[ctip]);
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
//Align at the bottom of the window
|
||||
ImGui::SetCursorPosY(content_region.y - (font_size + (2. * frame_padding.y)));
|
||||
if (ImGui::Button("<###tips-prev")) {
|
||||
if (--ctip < 0) ctip = num_tips - 1;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(ImVec4(.8, .8, .8, 1), "Tip %d/%d", ctip + 1, num_tips);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(">###tips-next")) {
|
||||
ctip = (ctip + 1) % num_tips;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Checkbox("Don't show again", (bool*) &settings["hide_tips"]->v.b)) {
|
||||
settings.save();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
const char* close_text = "Close";
|
||||
const ImVec2 close_text_size = ImGui::CalcTextSize(close_text);
|
||||
ImGui::SetCursorPosX(content_region.x - (close_text_size.x + frame_padding.x * 2.));
|
||||
if (ImGui::Button(close_text)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
211
src/ui/treasure_chest.cc
Normal file
211
src/ui/treasure_chest.cc
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
#include "treasure_chest.hh"
|
||||
#include "item.hh"
|
||||
#include "ui_imgui.hh"
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
namespace UiTreasureChest {
|
||||
static bool do_open = false;
|
||||
static int selected_index = -1;
|
||||
static int selected_entity = 0;
|
||||
static int selected_sub_entity = 0;
|
||||
static int selected_count = 1;
|
||||
|
||||
struct ChestItem {
|
||||
int g_id;
|
||||
int sub_id;
|
||||
int count;
|
||||
};
|
||||
|
||||
static std::vector<ChestItem> chest_items = {};
|
||||
|
||||
void open() {
|
||||
do_open = true;
|
||||
entity* e = G->selection.e;
|
||||
chest_items.clear();
|
||||
selected_index = -1;
|
||||
|
||||
if (e && e->g_id == O_TREASURE_CHEST) {
|
||||
char* str = strdup(e->properties[0].v.s.buf);
|
||||
std::vector<treasure_chest_item> parsed_items = treasure_chest::parse_items(str);
|
||||
free(str);
|
||||
for (const auto& item : parsed_items) {
|
||||
chest_items.push_back({ item.g_id, item.sub_id, item.count });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "Treasure chest");
|
||||
if (ImGui::BeginPopupModal("Treasure chest", REF_TRUE, MODAL_FLAGS)) {
|
||||
entity* e = G->selection.e;
|
||||
|
||||
// Entities
|
||||
std::vector<std::string> entity_labels;
|
||||
std::vector<const char*> entity_label_ptrs;
|
||||
for (const auto& obj : menu_objects) {
|
||||
if (obj.e != nullptr) {
|
||||
entity_labels.push_back(obj.e->get_name());
|
||||
}
|
||||
}
|
||||
for (auto& label : entity_labels) {
|
||||
entity_label_ptrs.push_back(label.c_str());
|
||||
}
|
||||
if (!entity_label_ptrs.empty()) {
|
||||
static int prev_selected_entity = -1;
|
||||
if (ImGui::Combo("##Entity", &selected_entity, entity_label_ptrs.data(), (int)entity_label_ptrs.size())) {
|
||||
selected_sub_entity = 0;
|
||||
prev_selected_entity = selected_entity;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ImGui::TextDisabled("No entities");
|
||||
}
|
||||
|
||||
// Sub-Entities
|
||||
std::vector<std::string> sub_entity_labels;
|
||||
std::vector<const char*> sub_entity_ptrs;
|
||||
if (selected_entity >= 0 && selected_entity < menu_objects.size()) {
|
||||
entity* selected_entity_ptr = menu_objects[selected_entity].e;
|
||||
if (selected_entity_ptr) {
|
||||
int g_id = selected_entity_ptr->g_id;
|
||||
|
||||
if (g_id == O_ITEM) {
|
||||
for (int i = 0; i < NUM_ITEMS; ++i) {
|
||||
sub_entity_labels.emplace_back(item_options[i].name);
|
||||
}
|
||||
} else if (g_id == O_RESOURCE) {
|
||||
for (int i = 0; i < NUM_RESOURCES; ++i) {
|
||||
sub_entity_labels.emplace_back(resource_data[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& label : sub_entity_labels) {
|
||||
sub_entity_ptrs.push_back(label.c_str());
|
||||
}
|
||||
|
||||
if (!sub_entity_ptrs.empty()) {
|
||||
ImGui::Combo("##SubEntity", &selected_sub_entity, sub_entity_ptrs.data(), (int)sub_entity_ptrs.size());
|
||||
} else {
|
||||
ImGui::TextDisabled("No sub-entities");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// (consider adding amount limit)
|
||||
ImGui::InputInt("Amount", &selected_count);
|
||||
if (selected_count < 1){
|
||||
selected_count = 1;
|
||||
}
|
||||
|
||||
static std::vector<treasure_chest_item> parsed_items;
|
||||
parsed_items.clear();
|
||||
if (e && e->g_id == O_TREASURE_CHEST) {
|
||||
char* str = strdup(e->properties[0].v.s.buf);
|
||||
parsed_items = treasure_chest::parse_items(str);
|
||||
free(str);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button("Add entity")) {
|
||||
if (selected_entity >= 0 && selected_entity < menu_objects.size()) {
|
||||
entity* selected_entity_ptr = menu_objects[selected_entity].e;
|
||||
if (selected_entity_ptr) {
|
||||
chest_items.push_back({ selected_entity_ptr->g_id, selected_sub_entity, selected_count });
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Remove selected")) {
|
||||
if (selected_index >= 0 && selected_index < chest_items.size()) {
|
||||
chest_items.erase(chest_items.begin() + selected_index);
|
||||
selected_index = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Table
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginTable("ChestItems", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("Entity");
|
||||
ImGui::TableSetupColumn("Sub-Entity");
|
||||
ImGui::TableSetupColumn("Count");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (int i = 0; i < chest_items.size(); ++i) {
|
||||
auto& item = chest_items[i];
|
||||
ImGui::TableNextRow();
|
||||
|
||||
const char* g_name = "Unknown";
|
||||
const char* sub_name = "-";
|
||||
|
||||
switch (item.g_id) {
|
||||
case O_ITEM:
|
||||
g_name = "Item";
|
||||
if (item.sub_id >= 0 && item.sub_id < NUM_ITEMS) {
|
||||
sub_name = item_options[item.sub_id].name;
|
||||
}
|
||||
break;
|
||||
|
||||
case O_RESOURCE:
|
||||
g_name = "Resource";
|
||||
if (item.sub_id >= 0 && item.sub_id < NUM_RESOURCES) {
|
||||
sub_name = resource_data[item.sub_id].name;
|
||||
}
|
||||
break;
|
||||
|
||||
default: {
|
||||
// (no sub-entity)
|
||||
entity* e = of::create(item.g_id);
|
||||
if (e) {
|
||||
g_name = e->get_name();
|
||||
delete e;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::PushID(i);
|
||||
bool is_selected = (i == selected_index);
|
||||
if (ImGui::RadioButton(g_name, is_selected)) {
|
||||
selected_index = i;
|
||||
}
|
||||
ImGui::PopID();
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::TextUnformatted(sub_name);
|
||||
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
ImGui::Text("%d", item.count);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::SeparatorText("");
|
||||
if (ImGui::Button("Apply")) {
|
||||
if (e && e->g_id == O_TREASURE_CHEST) {
|
||||
treasure_chest* tc = static_cast<treasure_chest*>(e);
|
||||
std::stringstream ss;
|
||||
for (size_t i = 0; i < chest_items.size(); ++i) {
|
||||
if (i > 0) ss << ";";
|
||||
ss << chest_items[i].g_id << ":" << chest_items[i].sub_id << ":" << chest_items[i].count;
|
||||
}
|
||||
tc->set_property(0, ss.str().c_str());
|
||||
P.add_action(ACTION_HIGHLIGHT_SELECTED, 0);
|
||||
P.add_action(ACTION_RESELECT, 0);
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("Cancel")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
87
src/ui/variable.cc
Normal file
87
src/ui/variable.cc
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
#include "ui_imgui.hh"
|
||||
|
||||
namespace UiVariable {
|
||||
static bool do_open = false;
|
||||
static char variable_name[128] = "";
|
||||
|
||||
void open() {
|
||||
do_open = true;
|
||||
|
||||
entity *e = G->selection.e;
|
||||
|
||||
if (e && (e->g_id == O_VAR_GETTER || e->g_id == O_VAR_SETTER)) {
|
||||
strcpy(variable_name, e->properties[0].v.s.buf);
|
||||
}
|
||||
}
|
||||
|
||||
void layout() {
|
||||
handle_do_open(&do_open, "Variable chooser");
|
||||
|
||||
ImGui_CenterNextWindow();
|
||||
ImGui::SetNextWindowSize(ImVec2(400., 0.));
|
||||
if (ImGui::BeginPopupModal("Variable chooser", REF_TRUE, MODAL_FLAGS)) {
|
||||
|
||||
ImGui::Text("Variable name:");
|
||||
ImGui::InputText("##VariableName", variable_name, IM_ARRAYSIZE(variable_name));
|
||||
|
||||
ImGui::Dummy(ImVec2(0.0f, 10.0f));
|
||||
|
||||
if (ImGui::Button("Reset Variable")) {
|
||||
std::map<std::string, float>::size_type num_deleted = W->level_variables.erase(variable_name);
|
||||
if (num_deleted != 0) {
|
||||
W->save_cache(W->level_id_type, W->level.local_id);
|
||||
ui::messagef("Successfully deleted data for variable '%s'", variable_name);
|
||||
} else
|
||||
ui::messagef("No data found for variable '%s'", variable_name);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("Reset All Variables")) {
|
||||
W->level_variables.clear();
|
||||
if (W->save_cache(W->level_id_type, W->level.local_id))
|
||||
ui::message("All level-specific variables cleared.");
|
||||
else
|
||||
ui::message("Unable to delete level-specific variables.");
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0.0f, 60.0f));
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::Button("Save")) {
|
||||
entity *e = G->selection.e;
|
||||
|
||||
if (e && (e->g_id == O_VAR_SETTER || e->g_id == O_VAR_GETTER)) {
|
||||
if (strlen(variable_name) && strlen(variable_name) <= 50) {
|
||||
char var_name[51];
|
||||
|
||||
int i = 0;
|
||||
for (int x=0; x<strlen(variable_name); ++x) {
|
||||
if (isalnum(variable_name[x]) || variable_name[x] == '_' || variable_name[x] == '-') {
|
||||
var_name[i++] = variable_name[x];
|
||||
}
|
||||
}
|
||||
var_name[i] = '\0';
|
||||
|
||||
if (strlen(var_name)) {
|
||||
e->set_property(0, var_name);
|
||||
ui::messagef("Variable name '%s' saved.", var_name);
|
||||
P.add_action(ACTION_HIGHLIGHT_SELECTED, 0);
|
||||
P.add_action(ACTION_RESELECT, 0);
|
||||
ImGui::CloseCurrentPopup();
|
||||
} else
|
||||
ui::message("The variable name must contain at least one 'a-z0-9-_'-character.");
|
||||
} else
|
||||
ui::message("Variable name too long.");
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,52 @@
|
|||
#include "ui.hh"
|
||||
#include <SDL.h>
|
||||
|
||||
#include "main.hh"
|
||||
#include "game.hh"
|
||||
#include "menu_main.hh"
|
||||
#include "menu-play.hh"
|
||||
#include "loading_screen.hh"
|
||||
#include "game-message.hh"
|
||||
#include "beam.hh"
|
||||
#include "wheel.hh"
|
||||
#include "pixel.hh"
|
||||
#include "command.hh"
|
||||
#include "i1o1gate.hh"
|
||||
#include "pkgman.hh"
|
||||
#include "object_factory.hh"
|
||||
#include "box.hh"
|
||||
#include "settings.hh"
|
||||
#include "fxemitter.hh"
|
||||
#include "i0o1gate.hh"
|
||||
#include "i2o0gate.hh"
|
||||
#include "display.hh"
|
||||
#include "prompt.hh"
|
||||
#include "robot_base.hh"
|
||||
#include "adventure.hh"
|
||||
#include "speaker.hh"
|
||||
#include "timer.hh"
|
||||
#include "jumper.hh"
|
||||
#include "item.hh"
|
||||
#include "escript.hh"
|
||||
#include "tpixel.hh"
|
||||
#include "factory.hh"
|
||||
#include "faction.hh"
|
||||
#include "anchor.hh"
|
||||
#include "resource.hh"
|
||||
#include "animal.hh"
|
||||
#include "simplebg.hh"
|
||||
#include "soundman.hh"
|
||||
#include "polygon.hh"
|
||||
#include "treasure_chest.hh"
|
||||
#include "decorations.hh"
|
||||
#include "sequencer.hh"
|
||||
#include "sfxemitter.hh"
|
||||
#include "key_listener.hh"
|
||||
#include "soundmanager.hh"
|
||||
|
||||
#include <tms/core/tms.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#if defined(TMS_BACKEND_ANDROID)
|
||||
|
||||
|
|
@ -2239,4 +2288,6 @@ Java_org_libsdl_app_PrincipiaBackend_triggerCreateLevel(
|
|||
P.add_action(ACTION_NEW_LEVEL, level_type);
|
||||
}
|
||||
|
||||
void ui::render() {}
|
||||
|
||||
#endif
|
||||
27
src/ui_dummy.cc
Normal file
27
src/ui_dummy.cc
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#include "ui.hh"
|
||||
|
||||
// Dummy dialog backend for minimal builds (e.g. the screenshotter build)
|
||||
|
||||
#ifdef NO_UI
|
||||
|
||||
void ui::init(){};
|
||||
void ui::open_dialog(int num, void *data/*=0*/){}
|
||||
void ui::open_sandbox_tips(){};
|
||||
void ui::emit_signal(int num, void *data/*=0*/){};
|
||||
void ui::quit(){};
|
||||
void ui::set_next_action(int action_id){};
|
||||
void ui::open_error_dialog(const char *error_msg){};
|
||||
void
|
||||
ui::confirm(const char *text,
|
||||
const char *button1, principia_action action1,
|
||||
const char *button2, principia_action action2,
|
||||
const char *button3/*=0*/, principia_action action3/*=ACTION_IGNORE*/,
|
||||
struct confirm_data _confirm_data/*=none*/
|
||||
)
|
||||
{
|
||||
P.add_action(action1.action_id, 0);
|
||||
}
|
||||
void ui::alert(const char*, uint8_t/*=ALERT_INFORMATION*/) {};
|
||||
void ui::render(){};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,5 +1,58 @@
|
|||
/**
|
||||
* Note: The GTK3 dialog backend is deprecated and will be replaced with the
|
||||
* Imgui backend on desktop platforms when it is finished. Don't spend any
|
||||
* more time than absolutely necessary on this backend.
|
||||
*/
|
||||
|
||||
#ifdef TMS_BACKEND_PC
|
||||
#include "ui.hh"
|
||||
#include "adventure.hh"
|
||||
#include "anchor.hh"
|
||||
#include "animal.hh"
|
||||
#include "beam.hh"
|
||||
#include "command.hh"
|
||||
#include "decorations.hh"
|
||||
#include "display.hh"
|
||||
#include "escript.hh"
|
||||
#include "faction.hh"
|
||||
#include "factory.hh"
|
||||
#include "fxemitter.hh"
|
||||
#include "game.hh"
|
||||
#include "item.hh"
|
||||
#include "jumper.hh"
|
||||
#include "key_listener.hh"
|
||||
#include "main.hh"
|
||||
#include "menu-play.hh"
|
||||
#include "object_factory.hh"
|
||||
#include "pkgman.hh"
|
||||
#include "polygon.hh"
|
||||
#include "prompt.hh"
|
||||
#include "resource.hh"
|
||||
#include "robot_base.hh"
|
||||
#include "sequencer.hh"
|
||||
#include "settings.hh"
|
||||
#include "sfxemitter.hh"
|
||||
#include "simplebg.hh"
|
||||
#include "soundmanager.hh"
|
||||
#include "speaker.hh"
|
||||
#include "timer.hh"
|
||||
#include "treasure_chest.hh"
|
||||
#include "wheel.hh"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <tms/core/tms.h>
|
||||
|
||||
#ifdef BUILD_VALGRIND
|
||||
#include <valgrind/valgrind.h>
|
||||
#endif
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#if defined(TMS_BACKEND_PC) && !defined(PRINCIPIA_BACKEND_IMGUI) && !defined(NO_UI)
|
||||
|
||||
#define SAVE_REGULAR 0
|
||||
#define SAVE_COPY 1
|
||||
|
||||
#define MAX_GRAVITY 75.f
|
||||
|
||||
// fuckgtk3
|
||||
#pragma GCC diagnostic push
|
||||
|
|
@ -81,7 +134,6 @@ typedef struct {
|
|||
long time;
|
||||
} oc_column;
|
||||
|
||||
bool prompt_is_open = false;
|
||||
GtkDialog *cur_prompt = 0;
|
||||
|
||||
enum mark_type {
|
||||
|
|
@ -11633,6 +11685,8 @@ ui::alert(const char *text, uint8_t alert_type/*=ALERT_INFORMATION*/)
|
|||
gdk_display_flush(gdk_display_get_default());
|
||||
}
|
||||
|
||||
void ui::render() {}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#endif
|
||||
432
src/ui_imgui.cc
Normal file
432
src/ui_imgui.cc
Normal file
|
|
@ -0,0 +1,432 @@
|
|||
#ifdef PRINCIPIA_BACKEND_IMGUI
|
||||
|
||||
#include "ui_imgui.hh"
|
||||
|
||||
#include "game.hh"
|
||||
#include "main.hh"
|
||||
#include "misc.hh"
|
||||
#include "settings.hh"
|
||||
#include "ui.hh"
|
||||
|
||||
#include "tms/backend/print.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_opengl.h>
|
||||
#include <SDL_syswm.h>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "imgui_stdlib.h"
|
||||
|
||||
#include "ui_imgui_impl_tms.hh"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
// Misc helper functions
|
||||
|
||||
ImVec4 rgba(uint32_t color) {
|
||||
float components[4]; //ABGR
|
||||
for (int i = 0; i < 4; i++) {
|
||||
components[i] = (float)(color & 0xFF) / 255.;
|
||||
color >>= 8;
|
||||
}
|
||||
return ImVec4(components[3], components[2], components[1], components[0]);
|
||||
}
|
||||
|
||||
bool lax_search(const std::string& where, const std::string& what) {
|
||||
return std::search(
|
||||
where.begin(), where.end(),
|
||||
what.begin(), what.end(),
|
||||
[](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }
|
||||
) != where.end();
|
||||
}
|
||||
|
||||
void ImGui_CenterNextWindow() {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f),
|
||||
ImGuiCond_Always, //ImGuiCond_Appearing,
|
||||
ImVec2(0.5f, 0.5f)
|
||||
);
|
||||
}
|
||||
|
||||
void ImGui_BeginScaleFont(float scale) {
|
||||
ImGui::GetFont()->Scale = scale;
|
||||
ImGui::PushFont(ImGui::GetFont());
|
||||
}
|
||||
|
||||
void ImGui_EndScaleFont() {
|
||||
ImGui::GetFont()->Scale = 1.;
|
||||
ImGui::PopFont();
|
||||
ImGui::GetFont()->Scale = 1.;
|
||||
}
|
||||
|
||||
void handle_do_open(bool *do_open, const char* name) {
|
||||
if (*do_open) {
|
||||
*do_open = false;
|
||||
ImGui::OpenPopup(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// FILE LOADING //
|
||||
std::vector<uint8_t> *load_ass(const char *path) {
|
||||
tms_infof("(imgui-backend) loading asset from %s...", path);
|
||||
|
||||
FILE_IN_ASSET(true);
|
||||
FILE *file = (FILE*) _fopen(path, "rb");
|
||||
tms_assertf(file, "file not found");
|
||||
|
||||
_fseek(file, 0, SEEK_END);
|
||||
size_t size = _ftell(file);
|
||||
tms_debugf("buf size %d", (int) size);
|
||||
void *buffer = malloc(size + 1);
|
||||
|
||||
_fseek(file, 0, SEEK_SET);
|
||||
_fread(buffer, 1, size, file);
|
||||
_fclose(file);
|
||||
|
||||
uint8_t *typed_buffer = (uint8_t*) buffer;
|
||||
std::vector<uint8_t> *vec = new std::vector<uint8_t>(typed_buffer, typed_buffer + size);
|
||||
free(buffer);
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// PFONT ///
|
||||
|
||||
static struct PFont im_load_ttf(const char *path, float size_pixels) {
|
||||
std::vector<uint8_t>* buf = load_ass(path);
|
||||
|
||||
ImFontConfig font_cfg;
|
||||
font_cfg.FontDataOwnedByAtlas = false;
|
||||
if (size_pixels <= 16.) {
|
||||
font_cfg.OversampleH = 3;
|
||||
}
|
||||
|
||||
ImFont *font = ImGui::GetIO().Fonts->AddFontFromMemoryTTF(buf->data(), buf->size(), size_pixels, &font_cfg);
|
||||
|
||||
struct PFont pfont;
|
||||
pfont.fontbuffer = buf;
|
||||
pfont.font = font;
|
||||
|
||||
return pfont;
|
||||
}
|
||||
|
||||
struct PFont ui_font;
|
||||
struct PFont ui_font_mono;
|
||||
|
||||
static void load_fonts() {
|
||||
//TODO free existing fonts
|
||||
|
||||
float size_pixels = 12.f;
|
||||
size_pixels *= settings["uiscale"]->v.f;
|
||||
size_pixels = roundf(size_pixels);
|
||||
|
||||
tms_infof("font size %fpx", size_pixels);
|
||||
|
||||
ui_font = im_load_ttf("data/fonts/Roboto-Bold.ttf", size_pixels);
|
||||
ui_font_mono = im_load_ttf("data/fonts/SourceCodePro-Medium.ttf", size_pixels + 2);
|
||||
|
||||
}
|
||||
|
||||
static void update_imgui_ui_scale() {
|
||||
float scale_factor = settings["uiscale"]->v.f;
|
||||
ImGui::GetStyle().ScaleAllSizes(scale_factor);
|
||||
|
||||
//ImGui::GetIO().FontGlobalScale = roundf(9. * scale_factor) / 9.;
|
||||
}
|
||||
|
||||
static void principia_style() {
|
||||
ImGui::StyleColorsDark();
|
||||
ImGuiStyle *style = &ImGui::GetStyle();
|
||||
ImVec4* colors = style->Colors;
|
||||
|
||||
//Rounding
|
||||
style->FrameRounding = style->GrabRounding = 2.3f;
|
||||
style->WindowRounding = style->PopupRounding = style->ChildRounding = 3.0f;
|
||||
|
||||
//style->FrameBorderSize = .5;
|
||||
|
||||
//TODO style
|
||||
//colors[ImGuiCol_WindowBg] = rgba(0xfdfdfdff);
|
||||
//colors[ImGuiCol_ScrollbarBg] = rgba(0x767676ff);
|
||||
//colors[ImGuiCol_ScrollbarGrab] = rgba(0x767676ff);
|
||||
//colors[ImGuiCol_ScrollbarGrabActive] = rgba(0xb1b1b1);
|
||||
}
|
||||
|
||||
static bool init_ready = false;
|
||||
|
||||
// UI helpers implemented here to avoid multiple definitions when header is included
|
||||
void ui_init() {
|
||||
UiLevelManager::init();
|
||||
UiLuaEditor::init();
|
||||
//UiQuickadd::init();
|
||||
UiSynthesizer::init();
|
||||
}
|
||||
|
||||
//On debug builds, open imgui demo window by pressing Shift+F9
|
||||
#ifdef DEBUG
|
||||
static bool show_demo = false;
|
||||
static void ui_demo_layout() {
|
||||
if (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_F9) && ImGui::GetIO().KeyShift) {
|
||||
show_demo ^= 1;
|
||||
}
|
||||
if (show_demo) {
|
||||
ImGui::ShowDemoWindow(&show_demo);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ui_layout() {
|
||||
#ifdef DEBUG
|
||||
ui_demo_layout();
|
||||
#endif
|
||||
UiSandboxMenu::layout();
|
||||
UiPlayMenu::layout();
|
||||
UiLevelManager::layout();
|
||||
UiVariable::layout();
|
||||
UiLogin::layout();
|
||||
UiMessage::layout();
|
||||
UiSettings::layout();
|
||||
UiLuaEditor::layout();
|
||||
UiTips::layout();
|
||||
UiSandboxMode::layout();
|
||||
UiQuickadd::layout();
|
||||
UiSynthesizer::layout();
|
||||
UiObjColorPicker::layout();
|
||||
UiLevelProperties::layout();
|
||||
UiSave::layout();
|
||||
UiNewLevel::layout();
|
||||
UiFrequency::layout();
|
||||
UiConfirm::layout();
|
||||
UiAnimal::layout();
|
||||
UiRobot::layout();
|
||||
UiSticky::layout();
|
||||
UiTreasureChest::layout();
|
||||
UiPolygon::layout();
|
||||
UiRubber::layout();
|
||||
UiDecoration::layout();
|
||||
}
|
||||
|
||||
// Non-header ui::* definitions
|
||||
void ui::init() {
|
||||
tms_assertf(!init_ready, "ui::init called twice");
|
||||
|
||||
//create context
|
||||
#ifdef DEBUG
|
||||
IMGUI_CHECKVERSION();
|
||||
#endif
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
//set flags
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange | ImGuiConfigFlags_NavEnableKeyboard;
|
||||
//io.ConfigInputTrickleEventQueue = false;
|
||||
io.ConfigWindowsResizeFromEdges = true; //XXX: not active until custom cursors are implemented...
|
||||
io.ConfigDragClickToInputText = true;
|
||||
//Disable saving state/logging
|
||||
io.IniFilename = NULL;
|
||||
io.LogFilename = NULL;
|
||||
|
||||
//style
|
||||
principia_style();
|
||||
|
||||
//update scale
|
||||
update_imgui_ui_scale();
|
||||
|
||||
//load fonts
|
||||
load_fonts();
|
||||
|
||||
//ensure gl ctx exists
|
||||
tms_assertf(_tms._window != NULL, "window does not exist yet");
|
||||
tms_assertf(SDL_GL_GetCurrentContext() != NULL, "no gl ctx");
|
||||
|
||||
//init
|
||||
if (!ImGui_ImplOpenGL3_Init()) {
|
||||
tms_fatalf("(imgui-backend) gl impl init failed");
|
||||
}
|
||||
|
||||
if (ImGui_ImplTMS_Init() != T_OK) {
|
||||
tms_fatalf("(imgui-backend) tms impl init failed");
|
||||
}
|
||||
|
||||
//call ui_init
|
||||
ui_init();
|
||||
|
||||
init_ready = true;
|
||||
}
|
||||
|
||||
void ui::render() {
|
||||
if (settings["render_gui"]->is_false()) return;
|
||||
|
||||
tms_assertf(init_ready, "ui::render called before ui::init");
|
||||
tms_assertf(GImGui != NULL, "gimgui is null. is imgui ready?");
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
//start frame
|
||||
if (ImGui_ImplTms_NewFrame() <= 0) return;
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
ImGui::PushFont(ui_font.font);
|
||||
|
||||
//layout
|
||||
ui_layout();
|
||||
|
||||
ImGui::PopFont();
|
||||
|
||||
//render
|
||||
ImGui::Render();
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
}
|
||||
|
||||
void ui::open_dialog(int num, void *data) {
|
||||
tms_assertf(init_ready, "ui::open_dialog called before ui::init");
|
||||
switch (num) {
|
||||
//XXX: this gets called after opening the sandbox menu, closing it immediately
|
||||
case CLOSE_ABSOLUTELY_ALL_DIALOGS:
|
||||
case CLOSE_ALL_DIALOGS:
|
||||
tms_infof("XXX: CLOSE_ALL_DIALOGS/CLOSE_ABSOLUTELY_ALL_DIALOGS (200/201) are intentionally ignored");
|
||||
break;
|
||||
case DIALOG_SANDBOX_MENU:
|
||||
UiSandboxMenu::open();
|
||||
break;
|
||||
case DIALOG_PLAY_MENU:
|
||||
UiPlayMenu::open();
|
||||
break;
|
||||
case DIALOG_OPEN:
|
||||
UiLevelManager::open();
|
||||
break;
|
||||
case DIALOG_VARIABLE:
|
||||
UiVariable::open();
|
||||
break;
|
||||
case DIALOG_LOGIN:
|
||||
UiLogin::open();
|
||||
break;
|
||||
case DIALOG_SETTINGS:
|
||||
UiSettings::open();
|
||||
break;
|
||||
case DIALOG_ESCRIPT:
|
||||
UiLuaEditor::open();
|
||||
break;
|
||||
case DIALOG_SANDBOX_MODE:
|
||||
UiSandboxMode::open();
|
||||
break;
|
||||
case DIALOG_QUICKADD:
|
||||
UiQuickadd::open();
|
||||
break;
|
||||
case DIALOG_SYNTHESIZER:
|
||||
UiSynthesizer::open();
|
||||
break;
|
||||
case DIALOG_BEAM_COLOR:
|
||||
case DIALOG_POLYGON_COLOR:
|
||||
case DIALOG_PIXEL_COLOR:
|
||||
UiObjColorPicker::open();
|
||||
break;
|
||||
case DIALOG_LEVEL_PROPERTIES:
|
||||
UiLevelProperties::open();
|
||||
break;
|
||||
case DIALOG_SAVE:
|
||||
case DIALOG_SAVE_COPY:
|
||||
UiSave::open();
|
||||
break;
|
||||
case DIALOG_NEW_LEVEL:
|
||||
UiNewLevel::open();
|
||||
break;
|
||||
case DIALOG_SET_FREQUENCY:
|
||||
UiFrequency::open(false);
|
||||
break;
|
||||
case DIALOG_SET_FREQ_RANGE:
|
||||
UiFrequency::open(true);
|
||||
break;
|
||||
case DIALOG_LEVEL_INFO:
|
||||
UiMessage::open((char *)data, MessageType::LevelInfo);
|
||||
break;
|
||||
case DIALOG_ANIMAL:
|
||||
UiAnimal::open();
|
||||
break;
|
||||
case DIALOG_ROBOT:
|
||||
UiRobot::open();
|
||||
break;
|
||||
case DIALOG_STICKY:
|
||||
UiSticky::open();
|
||||
break;
|
||||
case DIALOG_TREASURE_CHEST:
|
||||
UiTreasureChest::open();
|
||||
break;
|
||||
case DIALOG_POLYGON:
|
||||
UiPolygon::open();
|
||||
break;
|
||||
case DIALOG_RUBBER:
|
||||
UiRubber::open();
|
||||
break;
|
||||
case DIALOG_DECORATION:
|
||||
UiDecoration::open();
|
||||
break;
|
||||
default:
|
||||
tms_errorf("dialog %d not implemented yet", num);
|
||||
}
|
||||
}
|
||||
|
||||
void ui::open_sandbox_tips() {
|
||||
UiTips::open();
|
||||
}
|
||||
|
||||
void ui::emit_signal(int num, void *data){
|
||||
switch (num) {
|
||||
case SIGNAL_LOGIN_SUCCESS:
|
||||
UiLogin::complete_login(num);
|
||||
if (ui::next_action != ACTION_IGNORE) {
|
||||
P.add_action(ui::next_action, 0);
|
||||
ui::next_action = ACTION_IGNORE;
|
||||
}
|
||||
break;
|
||||
case SIGNAL_LOGIN_FAILED:
|
||||
ui::next_action = ACTION_IGNORE;
|
||||
UiLogin::complete_login(num);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ui::quit() {
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplTMS_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
|
||||
void ui::set_next_action(int action_id) {
|
||||
tms_infof("set_next_action %d", action_id);
|
||||
ui::next_action = action_id;
|
||||
}
|
||||
|
||||
void ui::open_error_dialog(const char *error_msg) {
|
||||
UiMessage::open(error_msg, MessageType::Error);
|
||||
}
|
||||
|
||||
void ui::confirm(
|
||||
const char *text,
|
||||
const char *button1, principia_action action1,
|
||||
const char *button2, principia_action action2,
|
||||
const char *button3, principia_action action3,
|
||||
struct confirm_data _confirm_data
|
||||
) {
|
||||
UiConfirm::open(text, button1, action1, button2, action2, button3, action3, _confirm_data);
|
||||
}
|
||||
|
||||
void ui::alert(const char* text, uint8_t type) {
|
||||
UiMessage::open(text, MessageType::Message);
|
||||
}
|
||||
|
||||
#endif
|
||||
3722
src/ui_imgui.hh
3722
src/ui_imgui.hh
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue