Rewrite on simdjson with a flat src and smack-style build
This commit is contained in:
1 parent
2d11cddc5e
commit
1a0db1abbd
178 files changed
+5935
-44757
No files matched your search
+950
@@ -0,0 +1,950 @@
|
||||
#include "ui.h"
|
||||
#include "play.h"
|
||||
#include "renderer.h"
|
||||
#include "save.h"
|
||||
#include <cfloat>
|
||||
#include <cstdio>
|
||||
|
||||
constexpr ImGuiWindowFlags PANEL_FLAGS =
|
||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
|
||||
constexpr ImGuiWindowFlags BAR_FLAGS = PANEL_FLAGS | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
|
||||
constexpr float COLOR_ACCENT = 2;
|
||||
constexpr float COLOR_ACCENT_ACTIVE = 2.25f;
|
||||
constexpr float COLOR_ACTIVE = 1.3f;
|
||||
constexpr float COLOR_BACKGROUND = 0.25f;
|
||||
constexpr float COLOR_BACKGROUND_ALPHA = 0.9f;
|
||||
constexpr float COLOR_HOVERED = 1.6f;
|
||||
constexpr float COLOR_MAX = 0.9f;
|
||||
constexpr float COLOR_MIN = 0.1f;
|
||||
constexpr float FX_FREQUENCY = 10;
|
||||
constexpr float FX_THICKNESS = 2;
|
||||
constexpr float FX_THICKNESS_WAVE = 3;
|
||||
constexpr float ITEM_SIZE = 96;
|
||||
constexpr float KG_TO_G = 1000;
|
||||
constexpr float KG_TO_LB = 2.20462f;
|
||||
constexpr float LB_TO_OZ = 16;
|
||||
constexpr float MENU_WIDTH = 0.3f;
|
||||
constexpr float SCROLLBAR_SIZE = 30;
|
||||
constexpr float SLIDE_TIME = 0.125f;
|
||||
constexpr float SLIDE_EASE = 2;
|
||||
constexpr float TEXT_FIT_MIN = 1;
|
||||
constexpr float TOOLS_WIDTH = 0.05f;
|
||||
constexpr float TOOLTIP_WRAP = 24;
|
||||
constexpr float TRIANGLE_SIZE = 0.22f;
|
||||
constexpr float UNDISCOVERED_ALPHA = 0.5f;
|
||||
constexpr int SECONDS_PER_MINUTE = 60;
|
||||
constexpr int SECONDS_PER_HOUR = 3600;
|
||||
constexpr int TEXT_SIZE = 512;
|
||||
constexpr const char* UNKNOWN = "???";
|
||||
|
||||
static Audio* widgetAudio = nullptr;
|
||||
static const Sounds* widgetHover = nullptr;
|
||||
static const Sounds* widgetSelect = nullptr;
|
||||
|
||||
void widget_sounds_set(Audio* audio, const Sounds* hover, const Sounds* select)
|
||||
{
|
||||
widgetAudio = audio;
|
||||
widgetHover = hover;
|
||||
widgetSelect = select;
|
||||
}
|
||||
|
||||
void widget_fx()
|
||||
{
|
||||
ImGuiStorage* storage = ImGui::GetStateStorage();
|
||||
ImGuiID id = ImGui::GetItemID();
|
||||
if (ImGui::IsItemActivated() && widgetAudio && widgetSelect)
|
||||
widgetAudio->play(*widgetSelect);
|
||||
if (!ImGui::IsItemHovered())
|
||||
{
|
||||
if (id)
|
||||
storage->SetBool(id, false);
|
||||
return;
|
||||
}
|
||||
if (id && !storage->GetBool(id) && widgetAudio && widgetHover)
|
||||
widgetAudio->play(*widgetHover);
|
||||
if (id)
|
||||
storage->SetBool(id, true);
|
||||
float wave = std::sin((float)ImGui::GetTime() * FX_FREQUENCY);
|
||||
ImVec4 border = ImGui::GetStyleColorVec4(ImGuiCol_CheckMark);
|
||||
border.w = 1 + wave;
|
||||
ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), ImGui::GetColorU32(border),
|
||||
ImGui::GetStyle().FrameRounding, 0, FX_THICKNESS + FX_THICKNESS_WAVE * wave);
|
||||
}
|
||||
|
||||
// Colors every widget from one color: darker backgrounds, lighter hovers, brighter accents.
|
||||
void style_set(Color color, float rounding)
|
||||
{
|
||||
auto shade = [&](float amount, float alpha = 1) {
|
||||
return ImVec4(color.r * amount, color.g * amount, color.b * amount, alpha);
|
||||
};
|
||||
auto bright = [&](float amount) {
|
||||
return ImVec4(std::clamp(color.r * amount, COLOR_MIN, COLOR_MAX),
|
||||
std::clamp(color.g * amount, COLOR_MIN, COLOR_MAX),
|
||||
std::clamp(color.b * amount, COLOR_MIN, COLOR_MAX), 1);
|
||||
};
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
style.WindowRounding = style.FrameRounding = style.GrabRounding = rounding;
|
||||
style.ScrollbarSize = SCROLLBAR_SIZE;
|
||||
ImVec4* colors = style.Colors;
|
||||
for (ImGuiCol id : {ImGuiCol_Button, ImGuiCol_FrameBg, ImGuiCol_Header, ImGuiCol_Tab, ImGuiCol_TitleBg,
|
||||
ImGuiCol_TitleBgCollapsed, ImGuiCol_TitleBgActive, ImGuiCol_ScrollbarBg})
|
||||
colors[id] = shade(1);
|
||||
for (ImGuiCol id : {ImGuiCol_FrameBgHovered, ImGuiCol_HeaderHovered, ImGuiCol_ButtonHovered, ImGuiCol_TabHovered,
|
||||
ImGuiCol_ScrollbarGrab, ImGuiCol_ScrollbarGrabHovered})
|
||||
colors[id] = bright(COLOR_HOVERED);
|
||||
for (ImGuiCol id : {ImGuiCol_FrameBgActive, ImGuiCol_HeaderActive, ImGuiCol_ButtonActive, ImGuiCol_TabSelected})
|
||||
colors[id] = bright(COLOR_ACTIVE);
|
||||
for (ImGuiCol id : {ImGuiCol_ScrollbarGrabActive, ImGuiCol_PlotHistogram, ImGuiCol_CheckMark,
|
||||
ImGuiCol_SliderGrab})
|
||||
colors[id] = shade(COLOR_ACCENT);
|
||||
colors[ImGuiCol_SliderGrabActive] = shade(COLOR_ACCENT_ACTIVE);
|
||||
colors[ImGuiCol_WindowBg] = colors[ImGuiCol_PopupBg] = shade(COLOR_BACKGROUND, COLOR_BACKGROUND_ALPHA);
|
||||
colors[ImGuiCol_ChildBg] = shade(COLOR_BACKGROUND, 0);
|
||||
}
|
||||
|
||||
float row_width_get(int count)
|
||||
{
|
||||
return (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x * (count - 1)) / count;
|
||||
}
|
||||
|
||||
void tooltip_set(const char* text, float size)
|
||||
{
|
||||
ImGui::PushFont(nullptr, size);
|
||||
ImGui::SetItemTooltip("%s", text);
|
||||
ImGui::PopFont();
|
||||
}
|
||||
|
||||
void triangle_draw(ImVec2 position, ImVec2 size, Direction direction)
|
||||
{
|
||||
ImVec2 center = {position.x + size.x / 2, position.y + size.y / 2};
|
||||
float half = size.x * TRIANGLE_SIZE;
|
||||
float sign = direction == DIRECTION_LEFT ? -1 : 1;
|
||||
ImGui::GetWindowDrawList()->AddTriangleFilled({center.x + half * sign, center.y},
|
||||
{center.x - half * sign, center.y - half},
|
||||
{center.x - half * sign, center.y + half},
|
||||
ImGui::GetColorU32(ImGuiCol_Text));
|
||||
}
|
||||
|
||||
// Text through a printf format, for formats that come from character strings.
|
||||
template <class... Args> static std::string text_format(const std::string& format, Args... args)
|
||||
{
|
||||
char text[TEXT_SIZE];
|
||||
std::snprintf(text, sizeof(text), format.c_str(), args...);
|
||||
return text;
|
||||
}
|
||||
|
||||
// A weight in the chosen units, as text with thousands separators, dropping to grams or ounces below one.
|
||||
static std::string weight_format(float kilograms, Measurement measurement)
|
||||
{
|
||||
float value = measurement == MEASUREMENT_IMPERIAL ? kilograms * KG_TO_LB : kilograms;
|
||||
const char* unit = measurement == MEASUREMENT_IMPERIAL ? "lbs" : "kg";
|
||||
if (value < 1)
|
||||
{
|
||||
value *= measurement == MEASUREMENT_IMPERIAL ? LB_TO_OZ : KG_TO_G;
|
||||
unit = measurement == MEASUREMENT_IMPERIAL ? "oz" : "g";
|
||||
}
|
||||
std::string digits = text_format("%.2f", value);
|
||||
for (int i = (int)digits.find('.') - 3; i > 0; i -= 3)
|
||||
digits.insert(i, ",");
|
||||
return digits + " " + unit;
|
||||
}
|
||||
|
||||
static std::string weight_number_format(float kilograms, Measurement measurement, const char** unit)
|
||||
{
|
||||
std::string text = weight_format(kilograms, measurement);
|
||||
*unit = text.ends_with("lbs") ? "lbs" : text.ends_with("oz") ? "oz" : text.ends_with(" g") ? "g" : "kg";
|
||||
return text;
|
||||
}
|
||||
|
||||
static std::string playtime_format(float seconds)
|
||||
{
|
||||
int total = std::max(0, (int)seconds);
|
||||
if (total < SECONDS_PER_MINUTE)
|
||||
return text_format("%02d", total);
|
||||
if (total < SECONDS_PER_HOUR)
|
||||
return text_format("%02d:%02d", total / SECONDS_PER_MINUTE, total % SECONDS_PER_MINUTE);
|
||||
return text_format("%02d:%02d:%02d", total / SECONDS_PER_HOUR, total / SECONDS_PER_MINUTE % SECONDS_PER_MINUTE,
|
||||
total % SECONDS_PER_MINUTE);
|
||||
}
|
||||
|
||||
// Draws text into bounds, shrinking its font until it fits.
|
||||
static void text_fit_draw(const std::string& text, ImVec2 bounds, float size)
|
||||
{
|
||||
ImFont* font = ImGui::GetFont();
|
||||
ImVec2 measured = font->CalcTextSizeA(size, FLT_MAX, 0, text.c_str());
|
||||
if (measured.x > 0 && measured.y > 0)
|
||||
size = std::clamp(size * std::min({1.0f, bounds.x / measured.x, bounds.y / measured.y}), TEXT_FIT_MIN, size);
|
||||
measured = font->CalcTextSizeA(size, FLT_MAX, 0, text.c_str());
|
||||
ImVec2 position = ImGui::GetCursorScreenPos();
|
||||
ImGui::Dummy(bounds);
|
||||
ImVec4 clip = {position.x, position.y, position.x + bounds.x, position.y + bounds.y};
|
||||
ImGui::GetWindowDrawList()->AddText(font, size, {position.x, position.y + std::max(0.0f, (bounds.y - measured.y) / 2)},
|
||||
ImGui::GetColorU32(ImGuiCol_Text), text.c_str(), nullptr, 0, &clip);
|
||||
}
|
||||
|
||||
// Renders an actor onto a canvas, scaled to fit within size, and gives the size it takes up.
|
||||
static ImVec2 canvas_draw(Renderer& renderer, Canvas& canvas, Actor& actor, ImVec2 size)
|
||||
{
|
||||
Rect rect = actor.bounds_get();
|
||||
float scale = is_rect_valid(rect) ? std::min(size.x / rect.w, size.y / rect.h) : 0;
|
||||
ImVec2 image = {rect.w * scale, rect.h * scale};
|
||||
if (!(scale > 0) || !std::isfinite(image.x) || !std::isfinite(image.y))
|
||||
return {};
|
||||
renderer.canvas_set(&canvas, std::max(1, (int)image.x), std::max(1, (int)image.y));
|
||||
actor.render(renderer, {scale, 0, 0, scale, -rect.x * scale, -rect.y * scale});
|
||||
renderer.flush();
|
||||
return image;
|
||||
}
|
||||
|
||||
static ImTextureID texture_id_get(const Texture& texture)
|
||||
{
|
||||
return (ImTextureID)(intptr_t)texture.id;
|
||||
}
|
||||
|
||||
static void item_summary_draw(Character& character, const Item& item)
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, imvec4_get(GRAY));
|
||||
std::string rarity = item.rarity >= 0 ? " (" + character.rarities[item.rarity].name + ")" : "";
|
||||
if (!item.category.empty())
|
||||
ImGui::TextWrapped("-- %s%s --", item.category.c_str(), rarity.c_str());
|
||||
if (!item.flavor.empty())
|
||||
ImGui::TextWrapped("%s", text_format(character.strings[STRING_InventoryFlavorFormat], item.flavor.c_str()).c_str());
|
||||
if (item.calories != 0)
|
||||
ImGui::TextWrapped("%s", text_format(character.strings[STRING_InventoryCaloriesFormat], item.calories).c_str());
|
||||
if (item.capacityBonus != 0)
|
||||
ImGui::TextWrapped("%s",
|
||||
text_format(character.strings[STRING_InventoryCapacityBonusFormat], item.capacityBonus).c_str());
|
||||
if (item.digestionBonus != 0)
|
||||
{
|
||||
StringId format = item.digestionBonus > 0 ? STRING_InventoryDigestionRateBonusFormat
|
||||
: STRING_InventoryDigestionRatePenaltyFormat;
|
||||
ImGui::TextWrapped("%s", text_format(character.strings[format], item.digestionBonus * TICK_RATE).c_str());
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
// An item's name, summary and description, or question marks for one not found yet.
|
||||
static void item_details_draw(Character& character, const Item* item)
|
||||
{
|
||||
ImGui::PushTextWrapPos(ImGui::GetFontSize() * TOOLTIP_WRAP);
|
||||
ImGui::PushFont(nullptr, FONT_HEADER_2);
|
||||
ImGui::TextWrapped("%s", item ? item->name.c_str() : UNKNOWN);
|
||||
ImGui::PopFont();
|
||||
ImGui::Separator();
|
||||
if (item)
|
||||
{
|
||||
item_summary_draw(character, *item);
|
||||
ImGui::Separator();
|
||||
}
|
||||
ImGui::TextWrapped("%s", item ? item->description.c_str() : UNKNOWN);
|
||||
ImGui::PopTextWrapPos();
|
||||
}
|
||||
|
||||
// A panel sliding in from a screen edge, with the bar that opens and closes it. Hovering the bar opens it too.
|
||||
static void panel_bar_draw(Play& play, bool& isOpen, float slide, ImVec2 position, ImVec2 size, Direction direction,
|
||||
StringId openTooltip, StringId closeTooltip)
|
||||
{
|
||||
Character& character = *play.character;
|
||||
ImGui::SetNextWindowSize(size);
|
||||
ImGui::SetNextWindowPos(position);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2());
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, size);
|
||||
if (ImGui::Begin(direction == DIRECTION_LEFT ? "##Menu Bar" : "##Tools Bar", nullptr, BAR_FLAGS))
|
||||
{
|
||||
ImVec2 buttonSize = ImGui::GetContentRegionAvail();
|
||||
ImVec2 cursor = ImGui::GetCursorScreenPos();
|
||||
bool isPressed = fx(ImGui::Button("##Toggle", buttonSize));
|
||||
if (slide <= 0 || slide >= 1)
|
||||
{
|
||||
tooltip_set(character.strings[isOpen ? closeTooltip : openTooltip].c_str());
|
||||
bool isHoverOpen = !isOpen && !isPressed && slide <= 0 && ImGui::IsItemHovered();
|
||||
if (isPressed || isHoverOpen)
|
||||
{
|
||||
isOpen = !isOpen;
|
||||
play.audio->play(character.sounds[isOpen ? SOUND_OPEN : SOUND_CLOSE]);
|
||||
}
|
||||
}
|
||||
bool isPointingLeft = (direction == DIRECTION_LEFT) != isOpen;
|
||||
triangle_draw(cursor, buttonSize, isPointingLeft ? DIRECTION_LEFT : DIRECTION_RIGHT);
|
||||
}
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar(4);
|
||||
}
|
||||
|
||||
static void slide_update(float& slide, bool isOpen)
|
||||
{
|
||||
float step = ImGui::GetIO().DeltaTime / SLIDE_TIME;
|
||||
slide = std::clamp(slide + (isOpen ? step : -step), 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
static void interact_tab_draw(Play& play, Settings& settings)
|
||||
{
|
||||
Character& character = *play.character;
|
||||
Progress& progress = play.progress;
|
||||
std::array<std::string, STRING_COUNT>& strings = character.strings;
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
Dialogue& dialogue = character.dialogue;
|
||||
std::string feelingPool = play.stage <= 0 ? character.dialoguePool
|
||||
: character.stages[std::min(play.stage, (int)character.stages.size()) - 1].dialoguePool;
|
||||
bool isRandom = dialogue.hooks.contains("Random");
|
||||
bool isHelp = dialogue.hooks.contains("Help");
|
||||
bool isFeeling = dialogue.pools.contains(feelingPool);
|
||||
|
||||
ImGui::PushFont(nullptr, FONT_HUGE);
|
||||
ImGui::SeparatorText(character.name.c_str());
|
||||
ImGui::PopFont();
|
||||
|
||||
ImVec2 childSize = {(ImGui::GetContentRegionAvail().x - style.ItemSpacing.x) / 2,
|
||||
FONT_HEADER_2 + ImGui::GetFrameHeightWithSpacing()};
|
||||
if (ImGui::BeginChild("##Weight", childSize))
|
||||
{
|
||||
std::string weight = weight_format(progress.weight, settings.measurement);
|
||||
text_fit_draw(weight, {ImGui::GetContentRegionAvail().x, FONT_HEADER_2}, FONT_HEADER_2);
|
||||
ImGui::SetItemTooltip("%s", weight.c_str());
|
||||
bool isMax = play.stage >= (int)character.stages.size();
|
||||
ImGui::ProgressBar(play.stage_progress_get(), {ImGui::GetContentRegionAvail().x, 0},
|
||||
strings[isMax ? STRING_InfoProgressMax : STRING_InfoProgressToNextStage].c_str());
|
||||
if (ImGui::BeginItemTooltip())
|
||||
{
|
||||
ImGui::Text(strings[STRING_InfoStageProgressFormat].c_str(), play.stage + 1,
|
||||
(int)character.stages.size() + 1, play.stage_progress_get() * PERCENT);
|
||||
ImGui::Separator();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, imvec4_get(GRAY));
|
||||
if (isMax)
|
||||
ImGui::TextUnformatted(strings[STRING_InfoMaxedOut].c_str());
|
||||
else
|
||||
{
|
||||
const char* unit = "";
|
||||
StringId formats[] = {STRING_InfoStageStartFormat, STRING_InfoStageCurrentFormat,
|
||||
STRING_InfoStageNextFormat};
|
||||
float weights[] = {play.stage_threshold_get(play.stage), progress.weight,
|
||||
play.stage_threshold_get(play.stage + 1)};
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
std::string number = weight_number_format(weights[i], settings.measurement, &unit);
|
||||
ImGui::Text(strings[formats[i]].c_str(), (float)SDL_atof(number.c_str()), unit);
|
||||
}
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::SameLine();
|
||||
if (ImGui::BeginChild("##Calories", childSize))
|
||||
{
|
||||
float capacityMax = play.capacity_max_get();
|
||||
float overstuffed = std::max(0.0f, (progress.calories - progress.capacity) / (capacityMax - progress.capacity));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1, 1 - overstuffed, 1 - overstuffed, 1));
|
||||
text_fit_draw(text_format("%.0f / %.0f kcal", progress.calories, progress.capacity),
|
||||
{ImGui::GetContentRegionAvail().x, FONT_HEADER_2}, FONT_HEADER_2);
|
||||
ImGui::PopStyleColor();
|
||||
if (ImGui::BeginItemTooltip())
|
||||
{
|
||||
ImGui::Text("%.0f / %.0f ", progress.calories, progress.capacity);
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::TextColored(imvec4_get(GRAY), "(%.0f)", capacityMax);
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::TextUnformatted(" kcal");
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
float digestion = progress.isDigesting ? (float)progress.digestionTimer / std::max(1, character.digestionTime)
|
||||
: progress.digestionProgress / DIGESTION_MAX;
|
||||
ImGui::ProgressBar(digestion, {ImGui::GetContentRegionAvail().x, 0},
|
||||
strings[progress.isDigesting ? STRING_InfoDigesting : STRING_InfoDigestion].c_str());
|
||||
if (ImGui::BeginItemTooltip())
|
||||
{
|
||||
if (progress.isDigesting)
|
||||
ImGui::TextUnformatted(strings[STRING_InfoDigestionInProgress].c_str());
|
||||
else if (digestion <= 0)
|
||||
ImGui::TextUnformatted(strings[STRING_InfoGiveFoodToStartDigesting].c_str());
|
||||
else
|
||||
ImGui::Text("%0.2f%%", digestion * PERCENT);
|
||||
ImGui::Separator();
|
||||
ImGui::TextColored(imvec4_get(GRAY), strings[STRING_InfoDigestionRateFormat].c_str(),
|
||||
progress.digestionRate * TICK_RATE);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::PushFont(nullptr, FONT_HEADER_2);
|
||||
ImGui::SeparatorText(strings[STRING_InteractStats].c_str());
|
||||
ImGui::PopFont();
|
||||
ImGui::Text(strings[STRING_InteractTotalCaloriesFormat].c_str(), progress.totalCalories);
|
||||
ImGui::Text(strings[STRING_InteractTotalItemsSpawnedFormat].c_str(), progress.totalSpawned);
|
||||
ImGui::Text(strings[STRING_InteractTotalItemsConsumedFormat].c_str(), progress.totalConsumed);
|
||||
ImGui::Text(strings[STRING_InteractTotalPlaytimeFormat].c_str(), playtime_format(progress.playtime).c_str());
|
||||
|
||||
auto frame_height_get = [](float size) {
|
||||
ImGui::PushFont(nullptr, size);
|
||||
float height = ImGui::GetFrameHeightWithSpacing();
|
||||
ImGui::PopFont();
|
||||
return height;
|
||||
};
|
||||
float buttonsHeight = frame_height_get(FONT_HUGE) + frame_height_get(FONT_HEADER_2);
|
||||
buttonsHeight += isRandom || isHelp || isFeeling ? style.ItemSpacing.y + 1 : 0;
|
||||
buttonsHeight += isRandom ? frame_height_get(FONT_HEADER_1) : 0;
|
||||
buttonsHeight += (isHelp + isFeeling) * frame_height_get(FONT_HEADER_2);
|
||||
if (float filler = ImGui::GetContentRegionAvail().y - buttonsHeight; filler > 0)
|
||||
ImGui::Dummy({ImGui::GetContentRegionAvail().x, filler});
|
||||
|
||||
float width = ImGui::GetContentRegionAvail().x;
|
||||
ImGui::PushFont(nullptr, FONT_HUGE);
|
||||
if (play.spawnDelay <= 0)
|
||||
{
|
||||
ImGui::BeginDisabled(!play.is_spawn_possible());
|
||||
if (fx(ImGui::Button(strings[STRING_InteractSpawnItemButton].c_str(), {width, 0})))
|
||||
play.spawn_queue_add();
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::ProgressBar(1 - std::clamp((float)play.spawnDelay / play.spawn_delay_get(), 0.0f, 1.0f),
|
||||
{width, ImGui::GetFrameHeight()}, strings[STRING_InteractSpawnItemButton].c_str());
|
||||
ImGui::EndDisabled();
|
||||
tooltip_set(text_format(strings[STRING_InteractSpawnItemCooldownFormat], play.spawnDelay / TICK_RATE).c_str());
|
||||
}
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::PushFont(nullptr, FONT_HEADER_2);
|
||||
bool isFood = progress.calories > 0 || play.is_spawn_possible();
|
||||
for (ItemEntity& entity : play.items)
|
||||
isFood |= character.items[entity.id].use == USE_EDIBLE;
|
||||
ImGui::BeginDisabled(!isFood);
|
||||
StringId autofeedLabel = play.isAutofeed ? STRING_InventoryDisableAutofeedButton : STRING_InventoryEnableAutofeedButton;
|
||||
if (fx(ImGui::Button(strings[autofeedLabel].c_str(), {width, 0})))
|
||||
play.isAutofeed = !play.isAutofeed;
|
||||
ImGui::EndDisabled();
|
||||
tooltip_set(strings[play.isAutofeed ? STRING_InventoryDisableAutofeedTooltip : STRING_InventoryEnableAutofeedTooltip]
|
||||
.c_str());
|
||||
ImGui::PopFont();
|
||||
|
||||
if (isRandom || isHelp || isFeeling)
|
||||
ImGui::Separator();
|
||||
auto dialogue_button = [&](bool isShown, float size, StringId label, Entry* (Dialogue::*get)(const std::string&),
|
||||
const std::string& name) {
|
||||
if (!isShown)
|
||||
return;
|
||||
ImGui::PushFont(nullptr, size);
|
||||
if (fx(ImGui::Button(strings[label].c_str(), {width, 0})))
|
||||
play.text.set(play, (dialogue.*get)(name));
|
||||
ImGui::PopFont();
|
||||
};
|
||||
dialogue_button(isRandom, FONT_HEADER_1, STRING_InteractChatButton, &Dialogue::hook_get, "Random");
|
||||
dialogue_button(isHelp, FONT_HEADER_2, STRING_InteractHelpButton, &Dialogue::hook_get, "Help");
|
||||
dialogue_button(isFeeling, FONT_HEADER_2, STRING_InteractFeelingButton, &Dialogue::pool_get, feelingPool);
|
||||
}
|
||||
|
||||
static void catalog_tab_draw(Play& play, Ui& ui, Renderer& renderer, std::vector<Canvas>& canvases,
|
||||
std::vector<Actor>& actors)
|
||||
{
|
||||
Character& character = *play.character;
|
||||
std::vector<Item>& items = character.items;
|
||||
Color characterColor = character.color;
|
||||
if (actors.size() != items.size())
|
||||
{
|
||||
actors.clear();
|
||||
for (int i = 0; i < (int)items.size(); i++)
|
||||
actors.push_back(character.item_actor_get(i));
|
||||
canvases.resize(items.size());
|
||||
}
|
||||
bool isSelected = ui.selectedItem >= 0 && ui.selectedItem < (int)items.size();
|
||||
bool isDiscovered = isSelected && play.discovered[ui.selectedItem];
|
||||
ImVec2 available = ImGui::GetContentRegionAvail();
|
||||
float infoHeight = isSelected || items.empty() ? available.y / 2 : 0;
|
||||
|
||||
if (ImGui::BeginChild("##Items", {available.x, available.y - infoHeight - ImGui::GetStyle().ItemSpacing.y}))
|
||||
{
|
||||
bool isAnyHovered = false;
|
||||
float right = ImGui::GetCursorScreenPos().x + ImGui::GetContentRegionAvail().x;
|
||||
for (int i = 0; i < (int)items.size(); i++)
|
||||
{
|
||||
ImGui::PushID(i);
|
||||
bool isFound = play.discovered[i];
|
||||
if (isFound && items[i].color.a > 0)
|
||||
style_set(items[i].color, character.rounding);
|
||||
bool isSelected = i == ui.selectedItem;
|
||||
if (isSelected)
|
||||
for (ImGuiCol id : {ImGuiCol_Button, ImGuiCol_ButtonHovered, ImGuiCol_ButtonActive})
|
||||
ImGui::PushStyleColor(id, ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered));
|
||||
ImVec2 image = canvas_draw(renderer, canvases[i], actors[i], {ITEM_SIZE, ITEM_SIZE});
|
||||
ImVec4 tint = isFound ? ImVec4(1, 1, 1, 1) : ImVec4(0, 0, 0, UNDISCOVERED_ALPHA);
|
||||
ImVec2 padding = {(ITEM_SIZE - image.x) / 2, (ITEM_SIZE - image.y) / 2};
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,
|
||||
{ImGui::GetStyle().FramePadding.x + padding.x, ImGui::GetStyle().FramePadding.y + padding.y});
|
||||
if (fx(ImGui::ImageButton("##Item", texture_id_get(canvases[i].texture), image, {0, 0}, {1, 1}, {}, tint)))
|
||||
ui.selectedItem = i;
|
||||
ImGui::PopStyleVar();
|
||||
if (isSelected)
|
||||
ImGui::PopStyleColor(3);
|
||||
isAnyHovered |= ImGui::IsItemHovered();
|
||||
if (!isSelected && ImGui::BeginItemTooltip())
|
||||
{
|
||||
item_details_draw(character, isFound ? &items[i] : nullptr);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
if (isFound && items[i].color.a > 0)
|
||||
style_set(characterColor, character.rounding);
|
||||
float next = ImGui::GetItemRectMax().x + ImGui::GetStyle().ItemSpacing.x + ITEM_SIZE;
|
||||
if (i + 1 < (int)items.size() && next < right)
|
||||
ImGui::SameLine();
|
||||
ImGui::PopID();
|
||||
}
|
||||
if (ImGui::IsWindowHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left) && !isAnyHovered)
|
||||
ui.selectedItem = -1;
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
if (infoHeight <= 0 || !ImGui::BeginChild("##Info", {available.x, infoHeight}))
|
||||
return;
|
||||
ImGui::Separator();
|
||||
if (!isSelected)
|
||||
{
|
||||
ImGui::PushFont(nullptr, FONT_HEADER_2);
|
||||
ImGui::TextWrapped("%s", character.strings[STRING_InventoryEmptyHint].c_str());
|
||||
ImGui::PopFont();
|
||||
}
|
||||
else
|
||||
{
|
||||
Item& item = items[ui.selectedItem];
|
||||
if (isDiscovered && item.color.a > 0)
|
||||
style_set(item.color, character.rounding);
|
||||
ImGui::PushFont(nullptr, FONT_HEADER_2);
|
||||
ImGui::TextWrapped("%s", isDiscovered ? item.name.c_str() : UNKNOWN);
|
||||
ImGui::PopFont();
|
||||
ImGui::Separator();
|
||||
if (isDiscovered)
|
||||
{
|
||||
float half = (ImGui::GetContentRegionAvail().y - ImGui::GetStyle().ItemSpacing.y) / 2;
|
||||
if (ImGui::BeginChild("##Stats", {0, half}))
|
||||
item_summary_draw(character, item);
|
||||
ImGui::EndChild();
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginChild("##Description", {0, half}))
|
||||
ImGui::TextWrapped("%s", item.description.c_str());
|
||||
ImGui::EndChild();
|
||||
}
|
||||
else
|
||||
ImGui::TextWrapped("%s", UNKNOWN);
|
||||
if (isDiscovered && item.color.a > 0)
|
||||
style_set(characterColor, character.rounding);
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
static void cheats_tab_draw(Play& play)
|
||||
{
|
||||
Character& character = *play.character;
|
||||
Progress& progress = play.progress;
|
||||
std::array<std::string, STRING_COUNT>& strings = character.strings;
|
||||
if (!ImGui::BeginChild("##Cheats"))
|
||||
return ImGui::EndChild();
|
||||
fx(ImGui::SliderFloat(strings[STRING_CheatsCalories].c_str(), &progress.calories, 0, play.capacity_max_get(),
|
||||
"%0.0f kcal"));
|
||||
fx(ImGui::SliderFloat(strings[STRING_CheatsCapacity].c_str(), &progress.capacity, character.capacityMin,
|
||||
character.capacityMax, "%0.0f kcal"));
|
||||
if (fx(ImGui::SliderFloat(strings[STRING_CheatsWeight].c_str(), &progress.weight, character.weight,
|
||||
character.weightMax, strings[STRING_CheatsWeightFormat].c_str())))
|
||||
play.idle_play();
|
||||
int stage = play.stage + 1;
|
||||
if (fx(ImGui::SliderInt(strings[STRING_CheatsStage].c_str(), &stage, 1, (int)character.stages.size() + 1)))
|
||||
{
|
||||
play.stage = stage - 1;
|
||||
progress.weight = play.stage_threshold_get(play.stage);
|
||||
play.idle_play();
|
||||
}
|
||||
float rate = progress.digestionRate * TICK_RATE;
|
||||
if (fx(ImGui::SliderFloat(strings[STRING_CheatsDigestionRate].c_str(), &rate, character.digestionRateMin * TICK_RATE,
|
||||
character.digestionRateMax * TICK_RATE, strings[STRING_CheatsDigestionRateFormat].c_str())))
|
||||
progress.digestionRate = rate / TICK_RATE;
|
||||
if (fx(ImGui::Button(strings[STRING_CheatsDigestButton].c_str())))
|
||||
progress.digestionProgress = DIGESTION_MAX;
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
static void tools_draw(Play& play, ImVec2 position, ImVec2 size)
|
||||
{
|
||||
Character& character = *play.character;
|
||||
ImGui::SetNextWindowSize(size);
|
||||
ImGui::SetNextWindowPos(position);
|
||||
if (!ImGui::Begin("##Tools", nullptr, PANEL_FLAGS))
|
||||
return ImGui::End();
|
||||
float side = ImGui::GetContentRegionAvail().x;
|
||||
auto tool_button = [&](const char* id, const std::string& label, const std::string& glyph,
|
||||
const std::string& tooltip, bool isActive, bool isDisabled) {
|
||||
bool isIcon = character.iconsFont && !glyph.empty();
|
||||
ImGui::PushID(id);
|
||||
if (isActive)
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered));
|
||||
ImGui::BeginDisabled(isDisabled);
|
||||
bool isPressed = fx(ImGui::Button(isIcon ? "##Icon" : label.c_str(), {side, side}));
|
||||
if (isIcon)
|
||||
{
|
||||
ImVec2 min = ImGui::GetItemRectMin();
|
||||
ImVec2 itemSize = ImGui::GetItemRectSize();
|
||||
ImVec2 textSize = character.iconsFont->CalcTextSizeA(itemSize.y, FLT_MAX, 0, glyph.c_str());
|
||||
ImGui::GetWindowDrawList()->AddText(character.iconsFont, itemSize.y,
|
||||
{min.x + (itemSize.x - textSize.x) / 2, min.y + (itemSize.y - textSize.y) / 2},
|
||||
ImGui::GetColorU32(ImGuiCol_Text), glyph.c_str());
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
if (isActive)
|
||||
ImGui::PopStyleColor();
|
||||
if (!tooltip.empty())
|
||||
ImGui::SetItemTooltip("%s", tooltip.c_str());
|
||||
ImGui::PopID();
|
||||
return isPressed;
|
||||
};
|
||||
if (tool_button("Home", character.strings[STRING_ToolsHomeButton], character.homeGlyph,
|
||||
character.strings[STRING_ToolsHomeTooltip], false, false))
|
||||
play.focus({ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y});
|
||||
for (int i = 0; i < (int)character.interactTypes.size(); i++)
|
||||
{
|
||||
InteractArea& type = character.interactTypes[i];
|
||||
std::string tooltip = play.isAutofeed ? character.strings[STRING_ToolsAutofeedDisabledTooltip]
|
||||
: character.iconsFont && !type.glyph.empty() ? type.name
|
||||
: "";
|
||||
if (tool_button(type.name.c_str(), type.name, type.glyph, tooltip, play.cursorType == i, play.isAutofeed))
|
||||
play.cursorType = i;
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
static void toasts_draw(Play& play)
|
||||
{
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImDrawList* drawList = ImGui::GetForegroundDrawList();
|
||||
float height = ImGui::GetTextLineHeightWithSpacing() + style.WindowPadding.y * 2;
|
||||
for (int i = 0; i < (int)play.toasts.size(); i++)
|
||||
{
|
||||
Toast& toast = play.toasts[i];
|
||||
float alpha = (float)toast.life / TOAST_LIFE;
|
||||
ImVec2 position = {viewport->WorkPos.x + style.WindowPadding.x,
|
||||
viewport->WorkPos.y + viewport->WorkSize.y - style.WindowPadding.y -
|
||||
height * (play.toasts.size() - i)};
|
||||
ImVec2 end = {position.x + ImGui::CalcTextSize(toast.message.c_str()).x + style.WindowPadding.x * 2,
|
||||
position.y + height};
|
||||
auto color_get = [&](ImGuiCol id) {
|
||||
ImVec4 color = ImGui::GetStyleColorVec4(id);
|
||||
color.w = alpha;
|
||||
return ImGui::GetColorU32(color);
|
||||
};
|
||||
drawList->AddRectFilled(position, end, color_get(ImGuiCol_WindowBg), style.WindowRounding);
|
||||
drawList->AddRect(position, end, color_get(ImGuiCol_Border), style.WindowRounding);
|
||||
drawList->AddText({position.x + style.WindowPadding.x, position.y + style.WindowPadding.y},
|
||||
color_get(ImGuiCol_Text), toast.message.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Ui::settings_update(Settings& settings, Audio& audio, const Character* character)
|
||||
{
|
||||
auto string_get = [&](StringId id) {
|
||||
return character ? character->strings[id].c_str() : STRING_DEFAULTS[id];
|
||||
};
|
||||
ImGui::SeparatorText(string_get(STRING_SettingsMeasurementSystem));
|
||||
fx(ImGui::RadioButton(string_get(STRING_SettingsMetric), (int*)&settings.measurement, MEASUREMENT_METRIC));
|
||||
ImGui::SetItemTooltip("%s", string_get(STRING_SettingsMetricTooltip));
|
||||
ImGui::SameLine();
|
||||
fx(ImGui::RadioButton(string_get(STRING_SettingsImperial), (int*)&settings.measurement, MEASUREMENT_IMPERIAL));
|
||||
ImGui::SetItemTooltip("%s", string_get(STRING_SettingsImperialTooltip));
|
||||
|
||||
ImGui::SeparatorText(string_get(STRING_SettingsWindow));
|
||||
const char* modes[] = {string_get(STRING_SettingsWindowed), string_get(STRING_SettingsFullscreen)};
|
||||
int mode = isFullscreen;
|
||||
if (fx(ImGui::Combo(string_get(STRING_SettingsWindowMode), &mode, modes, 2)))
|
||||
isFullscreenToggled = true;
|
||||
ImGui::SetItemTooltip("%s", string_get(STRING_SettingsWindowModeTooltip));
|
||||
|
||||
ImGui::SeparatorText(string_get(STRING_SettingsSound));
|
||||
ImGui::BeginDisabled(settings.isMuted);
|
||||
if (fx(ImGui::SliderInt(string_get(STRING_SettingsVolume), &settings.volume, 0, (int)PERCENT, "%d%%")))
|
||||
audio.gain_set(settings.volume / PERCENT);
|
||||
ImGui::SetItemTooltip("%s", string_get(STRING_SettingsVolumeTooltip));
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
if (fx(ImGui::Checkbox(string_get(STRING_SettingsMute), &settings.isMuted)))
|
||||
audio.gain_set(settings.isMuted ? 0 : settings.volume / PERCENT);
|
||||
ImGui::SetItemTooltip("%s", string_get(STRING_SettingsMuteTooltip));
|
||||
|
||||
ImGui::SeparatorText(string_get(STRING_SettingsAppearance));
|
||||
bool isColorChanged = fx(ImGui::Checkbox(string_get(STRING_SettingsUseCharacterColor), &settings.isCharacterColor));
|
||||
ImGui::SetItemTooltip("%s", string_get(STRING_SettingsUseCharacterColorTooltip));
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginDisabled(settings.isCharacterColor);
|
||||
isColorChanged |= fx(ImGui::ColorEdit3(string_get(STRING_SettingsColor), &settings.color.r,
|
||||
ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoTooltip));
|
||||
ImGui::SetItemTooltip("%s", string_get(STRING_SettingsColorTooltip));
|
||||
ImGui::EndDisabled();
|
||||
ImGui::Separator();
|
||||
if (fx(ImGui::Button(string_get(STRING_SettingsResetButton), {-FLT_MIN, 0})))
|
||||
{
|
||||
settings.reset();
|
||||
audio.gain_set(settings.volume / PERCENT);
|
||||
isColorChanged = true;
|
||||
}
|
||||
if (isColorChanged)
|
||||
style_set(character && settings.isCharacterColor ? character->color : settings.color,
|
||||
character ? character->rounding : 0);
|
||||
if (!character)
|
||||
return;
|
||||
ImGui::Separator();
|
||||
if (fx(ImGui::Button(string_get(STRING_SettingsSaveButton), {-FLT_MIN, 0})))
|
||||
isSave = true;
|
||||
ImGui::SetItemTooltip("%s", string_get(STRING_SettingsSaveTooltip));
|
||||
if (fx(ImGui::Button(string_get(STRING_SettingsReturnToCharactersButton), {-FLT_MIN, 0})))
|
||||
isReturn = true;
|
||||
ImGui::SetItemTooltip("%s", string_get(STRING_SettingsReturnToCharactersTooltip));
|
||||
}
|
||||
|
||||
void Ui::play_update(Play& play, Settings& settings, Renderer& renderer, Audio& audio)
|
||||
{
|
||||
Character& character = *play.character;
|
||||
std::array<std::string, STRING_COUNT>& strings = character.strings;
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
ImVec2 screen = ImGui::GetIO().DisplaySize;
|
||||
float barWidth = std::max(ImGui::GetTextLineHeightWithSpacing(), style.WindowMinSize.x);
|
||||
float height = screen.y - style.WindowPadding.y * 2;
|
||||
|
||||
if (play.tooltipItem >= 0 && ImGui::BeginTooltip())
|
||||
{
|
||||
item_details_draw(character, &character.items[play.tooltipItem]);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
toasts_draw(play);
|
||||
if (play.text.isScene)
|
||||
return;
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
|
||||
slide_update(toolsSlide, play.isToolsOpen);
|
||||
float toolsWidth = screen.x * TOOLS_WIDTH;
|
||||
float toolsX = -toolsWidth + toolsWidth * std::pow(toolsSlide, SLIDE_EASE);
|
||||
if (toolsSlide > 0)
|
||||
tools_draw(play, {toolsX, style.WindowPadding.y}, {toolsWidth, height});
|
||||
panel_bar_draw(play, play.isToolsOpen, toolsSlide, {toolsX + toolsWidth, style.WindowPadding.y}, {barWidth, height},
|
||||
DIRECTION_RIGHT, STRING_ToolsOpenTooltip, STRING_ToolsCloseTooltip);
|
||||
|
||||
slide_update(menuSlide, play.isMenuOpen);
|
||||
float menuWidth = screen.x * MENU_WIDTH;
|
||||
float menuX = screen.x - menuWidth * std::pow(menuSlide, SLIDE_EASE);
|
||||
if (menuSlide > 0)
|
||||
{
|
||||
ImGui::SetNextWindowSize({menuWidth, height});
|
||||
ImGui::SetNextWindowPos({menuX, style.WindowPadding.y});
|
||||
if (ImGui::Begin("##Menu", nullptr, PANEL_FLAGS) && ImGui::BeginTabBar("##Tabs"))
|
||||
{
|
||||
if (fx(ImGui::BeginTabItem(strings[STRING_MenuTabInteract].c_str())))
|
||||
{
|
||||
interact_tab_draw(play, settings);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (fx(ImGui::BeginTabItem(strings[STRING_MenuTabInventory].c_str())))
|
||||
{
|
||||
catalog_tab_draw(play, *this, renderer, itemCanvases, itemActors);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (fx(ImGui::BeginTabItem(strings[STRING_MenuTabSettings].c_str())))
|
||||
{
|
||||
settings_update(settings, audio, &character);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (play.isCheats && fx(ImGui::BeginTabItem(strings[STRING_MenuTabCheats].c_str())))
|
||||
{
|
||||
cheats_tab_draw(play);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
panel_bar_draw(play, play.isMenuOpen, menuSlide, {menuX - barWidth, style.WindowPadding.y}, {barWidth, height},
|
||||
DIRECTION_LEFT, STRING_MenuOpenTooltip, STRING_MenuCloseTooltip);
|
||||
ImGui::PopStyleVar(2);
|
||||
}
|
||||
|
||||
bool Ui::select_update(std::vector<Character>& characters, Settings& settings, Renderer& renderer, Audio& audio)
|
||||
{
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
ImVec2 screen = ImGui::GetIO().DisplaySize;
|
||||
ImVec2 half = {screen.x / 2 - style.WindowPadding.x * 2, screen.y / 2 - style.WindowPadding.y * 2};
|
||||
bool isRefresh = false;
|
||||
characterIndex = characters.empty() ? -1 : std::clamp(characterIndex, 0, (int)characters.size() - 1);
|
||||
Character* selected = characterIndex >= 0 ? &characters[characterIndex] : nullptr;
|
||||
if (selected && progressIndex != characterIndex)
|
||||
{
|
||||
selectedProgress.load(selected->save_path_get());
|
||||
progressIndex = characterIndex;
|
||||
}
|
||||
|
||||
if (selected)
|
||||
{
|
||||
style_set(selected->color);
|
||||
ImGui::SetNextWindowSize(half);
|
||||
ImGui::SetNextWindowPos({style.WindowPadding.x, style.WindowPadding.y});
|
||||
if (ImGui::Begin("##Preview", nullptr, PANEL_FLAGS) && ImGui::BeginTabBar("##Preview Tabs"))
|
||||
{
|
||||
ImVec2 available = ImGui::GetContentRegionAvail();
|
||||
if (fx(ImGui::BeginTabItem("Render")))
|
||||
{
|
||||
if (Texture* render = selected->render)
|
||||
{
|
||||
float scale = std::min(available.x / render->width, available.y / render->height);
|
||||
ImVec2 size = {render->width * scale, render->height * scale};
|
||||
ImGui::SetCursorPos({ImGui::GetCursorPosX() + (available.x - size.x) / 2,
|
||||
ImGui::GetCursorPosY() + (available.y - size.y) / 2});
|
||||
ImGui::Image(texture_id_get(*render), size);
|
||||
}
|
||||
else
|
||||
ImGui::TextWrapped("%s\n%s", selected->name.c_str(), selected->description.c_str());
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
isPreview = fx(ImGui::BeginTabItem("In Game"));
|
||||
if (isPreview)
|
||||
{
|
||||
Anm2* anm2 = selected->anm2_get(selected->anm2Path, renderer, audio);
|
||||
if (previewActor.anm2 != anm2 || !anm2)
|
||||
previewActor = Actor(anm2);
|
||||
previewActor.update(audio);
|
||||
ImVec2 size = canvas_draw(renderer, previewCanvas, previewActor, available);
|
||||
ImGui::SetCursorPos({ImGui::GetCursorPosX() + (available.x - size.x) / 2,
|
||||
ImGui::GetCursorPosY() + (available.y - size.y) / 2});
|
||||
ImGui::Image(texture_id_get(previewCanvas.texture), size);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
ImGui::SetNextWindowSize(half);
|
||||
ImGui::SetNextWindowPos({style.WindowPadding.x, screen.y / 2 + style.WindowPadding.y});
|
||||
if (ImGui::Begin("##Info", nullptr, PANEL_FLAGS))
|
||||
{
|
||||
float footer = ImGui::GetTextLineHeightWithSpacing() + style.WindowPadding.y + style.ItemSpacing.y;
|
||||
ImGui::PushFont(nullptr, FONT_HEADER_3);
|
||||
if (ImGui::BeginChild("##Info Child", {0, ImGui::GetContentRegionAvail().y - footer}))
|
||||
{
|
||||
ImGui::TextUnformatted(selected->name.c_str());
|
||||
ImGui::PushFont(nullptr, FONT_NORMAL);
|
||||
if (ImGui::BeginTabBar("##Info Tabs"))
|
||||
{
|
||||
ImGui::PushFont(nullptr, FONT_HEADER_2);
|
||||
if (ImGui::BeginTabItem("Overview"))
|
||||
{
|
||||
ImGui::Separator();
|
||||
if (!selected->description.empty())
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, imvec4_get(GRAY));
|
||||
ImGui::TextWrapped("%s", selected->description.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Separator();
|
||||
}
|
||||
float weight = selectedProgress.isValid ? selectedProgress.weight : selected->weight;
|
||||
ImGui::Text("Weight: %s", weight_format(weight, settings.measurement).c_str());
|
||||
ImGui::Text("Stages: %i", (int)selected->stages.size() + 1);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Credits"))
|
||||
{
|
||||
ImGui::Separator();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, imvec4_get(GRAY));
|
||||
ImGui::TextWrapped("%s", selected->credits.empty() ? "No credits listed." : selected->credits.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::PopFont();
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
ImGui::PopFont();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImVec2 buttonSize = {row_width_get(selectedProgress.isValid ? 2 : 1), 0};
|
||||
if (selectedProgress.isValid)
|
||||
{
|
||||
isContinue = fx(ImGui::Button("Continue", buttonSize));
|
||||
tooltip_set("Continue from a saved game.");
|
||||
ImGui::SameLine();
|
||||
}
|
||||
if (fx(ImGui::Button("New Game", buttonSize)))
|
||||
{
|
||||
isNewGame = !selectedProgress.isValid;
|
||||
isNewGameWarning = selectedProgress.isValid;
|
||||
if (isNewGameWarning)
|
||||
ImGui::OpenPopup("New Game Warning");
|
||||
}
|
||||
tooltip_set("Start a new game.\nThis will delete progress!");
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::SetNextWindowSize({screen.x / 2, 0});
|
||||
ImGui::SetNextWindowPos({screen.x / 2, screen.y / 2}, ImGuiCond_Always, {0.5f, 0.5f});
|
||||
if (ImGui::BeginPopupModal("New Game Warning", &isNewGameWarning,
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize))
|
||||
{
|
||||
ImGui::TextWrapped("This will delete saved progress! Are you sure?");
|
||||
ImVec2 popupSize = {row_width_get(2), 0};
|
||||
isNewGame = fx(ImGui::Button("Yes", popupSize));
|
||||
ImGui::SameLine();
|
||||
if (fx(ImGui::Button("No", popupSize)))
|
||||
isNewGameWarning = false;
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
style_set(settings.color);
|
||||
ImGui::SetNextWindowSize({screen.x / 2 - style.WindowPadding.x, screen.y - style.WindowPadding.y * 2});
|
||||
ImGui::SetNextWindowPos({screen.x / 2, style.WindowPadding.y});
|
||||
if (ImGui::Begin("##Main Menu", nullptr, PANEL_FLAGS) && ImGui::BeginTabBar("##Main Tabs"))
|
||||
{
|
||||
if (fx(ImGui::BeginTabItem("Characters")))
|
||||
{
|
||||
if (ImGui::BeginPopupContextWindow("##Characters Context"))
|
||||
{
|
||||
isRefresh = ImGui::MenuItem("Refresh");
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
float side = ImGui::GetContentRegionAvail().x / 4;
|
||||
float right = ImGui::GetCursorScreenPos().x + ImGui::GetContentRegionAvail().x;
|
||||
for (int i = 0; i < (int)characters.size(); i++)
|
||||
{
|
||||
Character& character = characters[i];
|
||||
ImGui::PushID(i);
|
||||
style_set(character.color);
|
||||
bool isSelected = i == characterIndex;
|
||||
if (isSelected)
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBgHovered));
|
||||
bool isPressed = character.portrait
|
||||
? ImGui::ImageButton(character.name.c_str(), texture_id_get(*character.portrait),
|
||||
{side, side})
|
||||
: ImGui::Button(character.name.c_str(), {side, side});
|
||||
if (fx(isPressed))
|
||||
characterIndex = i;
|
||||
if (isSelected)
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SetItemTooltip("%s", character.name.c_str());
|
||||
float next = ImGui::GetItemRectMax().x + style.ItemSpacing.x + side;
|
||||
if (i + 1 < (int)characters.size() && next < right)
|
||||
ImGui::SameLine();
|
||||
style_set(settings.color);
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (fx(ImGui::BeginTabItem("Settings")))
|
||||
{
|
||||
settings_update(settings, audio, nullptr);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
ImGui::End();
|
||||
if (isRefresh)
|
||||
progressIndex = -1;
|
||||
return isRefresh;
|
||||
}
|
||||
Reference in new issue
Block a user