Taskbar with icon buttons, stat bars and modal stats, catalog and settings windows
This commit is contained in:
1 parent
f900a0d01f
commit
37e87f168a
15 files changed
+500
-445
No files matched your search
+379
-361
@@ -24,10 +24,27 @@ constexpr float INDICATOR_BOB = 0.12f;
|
||||
constexpr float INDICATOR_SIZE = 0.35f;
|
||||
constexpr float INDICATOR_SPEED = 2;
|
||||
constexpr float ITEM_SIZE = 96;
|
||||
constexpr float BAR_WIDTH_MIN = 120;
|
||||
constexpr float BUTTON_SIZE = 64;
|
||||
constexpr float CATALOG_DIVIDER = 2;
|
||||
constexpr float CATALOG_GRID = 0.55f;
|
||||
constexpr float COOLDOWN_ALPHA = 0.6f;
|
||||
constexpr float COOLDOWN_RADIUS = 0.42f;
|
||||
constexpr float ICON_SIZE = 48;
|
||||
constexpr float TOGGLE_FADE_FAR = 220;
|
||||
constexpr float TOGGLE_FADE_NEAR = 60;
|
||||
constexpr float TOGGLE_HEIGHT = 28;
|
||||
constexpr float WINDOW_FIT = 0.9f;
|
||||
constexpr float WINDOW_HEIGHT = 680;
|
||||
constexpr float WINDOW_WIDTH = 1000;
|
||||
constexpr int BUTTONS_FIXED = 9;
|
||||
constexpr float INFO_GAP = 4;
|
||||
constexpr int COOLDOWN_SEGMENTS = 48;
|
||||
constexpr const char* PANEL_ICONS[] = {"", "stats", "catalog", "settings"};
|
||||
constexpr const char* PANEL_NAMES[] = {"", "Stats", "Catalog", "Settings"};
|
||||
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 OSCILLATION_AMPLITUDE = 2;
|
||||
constexpr float OSCILLATION_FREQUENCY = 1;
|
||||
constexpr float OSCILLATION_SPREAD = 0.16f;
|
||||
@@ -37,11 +54,9 @@ constexpr float SHAKE_FREQUENCY = 30;
|
||||
constexpr float SLIDE_EASE = 2;
|
||||
constexpr float SLIDE_TIME = 0.125f;
|
||||
constexpr float TEXT_BIG = 1.25f;
|
||||
constexpr float TEXT_FIT_MIN = 1;
|
||||
constexpr float TEXT_HEIGHT_LINES = 6;
|
||||
constexpr float TEXT_HEIGHT_LINES = 5;
|
||||
constexpr float TEXT_SMALL = 0.75f;
|
||||
constexpr float TEXT_WIDTH = 0.3f;
|
||||
constexpr float TOOLS_WIDTH = 0.05f;
|
||||
constexpr float TEXT_WIDTH = 0.4f;
|
||||
constexpr float TOOLTIP_WRAP = 24;
|
||||
constexpr float TRIANGLE_SIZE = 0.22f;
|
||||
constexpr float UNDISCOVERED_ALPHA = 0.5f;
|
||||
@@ -94,8 +109,9 @@ static Palette palette_get(Color color)
|
||||
void style_set(Color color, float rounding)
|
||||
{
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
style.WindowRounding = style.FrameRounding = style.GrabRounding = rounding;
|
||||
style.WindowRounding = style.FrameRounding = style.GrabRounding = style.PopupRounding = rounding;
|
||||
style.ScrollbarSize = SCROLLBAR_SIZE;
|
||||
style.FontSizeBase = FONT_NORMAL;
|
||||
for (auto [id, value] : palette_get(color))
|
||||
style.Colors[id] = value;
|
||||
style.Colors[ImGuiCol_PopupBg] = style.Colors[ImGuiCol_WindowBg];
|
||||
@@ -138,11 +154,11 @@ static void tooltip_set(const char* text)
|
||||
static 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},
|
||||
float half = size.y * TRIANGLE_SIZE;
|
||||
float sign = direction == DIRECTION_UP ? -1 : 1;
|
||||
ImGui::GetWindowDrawList()->AddTriangleFilled({center.x, center.y + half * sign},
|
||||
{center.x - half * 2, center.y - half * sign},
|
||||
{center.x + half * 2, center.y - half * sign},
|
||||
ImGui::GetColorU32(ImGuiCol_Text));
|
||||
}
|
||||
|
||||
@@ -181,21 +197,6 @@ static std::string playtime_format(float seconds)
|
||||
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)
|
||||
{
|
||||
@@ -387,34 +388,236 @@ void Ui::command_add(CommandType type, const std::string& key)
|
||||
commands->push_back({key, type});
|
||||
}
|
||||
|
||||
// A side panel's bar, which opens and closes it. Hovering the bar opens it too.
|
||||
void Ui::bar_draw(Play& play, bool& isOpen, float slide, ImVec2 position, ImVec2 size, Direction direction,
|
||||
const char* openTooltip, const char* closeTooltip)
|
||||
// An icon from the character's icon sheet (see Character::icons), fit into a square and tinted.
|
||||
void Ui::icon_draw(const char* name, ImVec2 position, float size, ImU32 tint)
|
||||
{
|
||||
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))
|
||||
Anm2* icons = character ? character->icons : nullptr;
|
||||
const Anm2Region* region = icons ? icons->region_get(0, name) : nullptr;
|
||||
Texture* sheet = region && !icons->sheets.empty() ? icons->sheets[0] : nullptr;
|
||||
if (!sheet)
|
||||
return;
|
||||
float scale = size / std::max(region->size.x, region->size.y);
|
||||
ImVec2 extent = {region->size.x * scale, region->size.y * scale};
|
||||
ImVec2 min = {position.x + (size - extent.x) / 2, position.y + (size - extent.y) / 2};
|
||||
ImGui::GetWindowDrawList()->AddImage(
|
||||
texture_id_get(*sheet), min, {min.x + extent.x, min.y + extent.y},
|
||||
{region->crop.x / sheet->width, region->crop.y / sheet->height},
|
||||
{(region->crop.x + region->size.x) / sheet->width, (region->crop.y + region->size.y) / sheet->height}, tint);
|
||||
}
|
||||
|
||||
// A square button showing an icon. Active ones stay lit; disabled ones are dimmed and ignore clicks.
|
||||
bool Ui::icon_button(const char* icon, const char* tooltip, ButtonState state)
|
||||
{
|
||||
ImGui::PushID(icon);
|
||||
if (state == BUTTON_ACTIVE)
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered));
|
||||
ImGui::BeginDisabled(state == BUTTON_DISABLED);
|
||||
bool isPressed = is_fx(ImGui::Button("##Icon", {BUTTON_SIZE, BUTTON_SIZE}));
|
||||
ImVec2 min = ImGui::GetItemRectMin();
|
||||
float inset = (BUTTON_SIZE - ICON_SIZE) / 2;
|
||||
icon_draw(icon, {min.x + inset, min.y + inset}, ICON_SIZE, ImGui::GetColorU32(ImGuiCol_Text));
|
||||
ImGui::EndDisabled();
|
||||
if (state == BUTTON_ACTIVE)
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SetItemTooltip("%s", tooltip);
|
||||
ImGui::PopID();
|
||||
return isPressed;
|
||||
}
|
||||
|
||||
// A clockwise wedge over the last item, filled as far as progress (0 to 1) has come.
|
||||
static void cooldown_draw(float progress)
|
||||
{
|
||||
ImVec2 min = ImGui::GetItemRectMin();
|
||||
ImVec2 max = ImGui::GetItemRectMax();
|
||||
ImVec2 center = {(min.x + max.x) / 2, (min.y + max.y) / 2};
|
||||
float radius = (max.x - min.x) * COOLDOWN_RADIUS;
|
||||
ImU32 color = ImGui::GetColorU32(ImGuiCol_PlotHistogram, COOLDOWN_ALPHA);
|
||||
int segments = (int)std::ceil(COOLDOWN_SEGMENTS * progress);
|
||||
for (int i = 0; i < segments; i++)
|
||||
{
|
||||
ImVec2 buttonSize = ImGui::GetContentRegionAvail();
|
||||
ImVec2 cursor = ImGui::GetCursorScreenPos();
|
||||
bool isPressed = is_fx(ImGui::Button("##Toggle", buttonSize));
|
||||
if (slide <= 0 || slide >= 1)
|
||||
{
|
||||
tooltip_set(isOpen ? closeTooltip : openTooltip);
|
||||
if (isPressed || (!isOpen && slide <= 0 && ImGui::IsItemHovered()))
|
||||
{
|
||||
isOpen = !isOpen;
|
||||
play.audio->play(play.character->sounds[isOpen ? SOUND_OPEN : SOUND_CLOSE]);
|
||||
}
|
||||
}
|
||||
triangle_draw(cursor, buttonSize, (direction == DIRECTION_LEFT) != isOpen ? DIRECTION_LEFT : DIRECTION_RIGHT);
|
||||
float from = -PI / 2 + 2 * PI * progress * i / segments;
|
||||
float to = -PI / 2 + 2 * PI * progress * (i + 1) / segments;
|
||||
ImGui::GetWindowDrawList()->AddTriangleFilled(
|
||||
center, {center.x + std::cos(from) * radius, center.y + std::sin(from) * radius},
|
||||
{center.x + std::cos(to) * radius, center.y + std::sin(to) * radius}, color);
|
||||
}
|
||||
}
|
||||
|
||||
void Ui::window_open(Play& play, Panel next)
|
||||
{
|
||||
panel = next;
|
||||
play.audio->play(play.character->sounds[SOUND_OPEN]);
|
||||
}
|
||||
|
||||
// The bar along the bottom: the character's name and body at a glance, then a button for everything. A thin bar above
|
||||
// it, showing as the mouse nears, slides it away and back.
|
||||
void Ui::taskbar_draw(Play& play, ImVec2 screen)
|
||||
{
|
||||
Character& data = *play.character;
|
||||
Body& body = play.body;
|
||||
Stats& stats = body.stats;
|
||||
Items& items = play.items;
|
||||
Dialogue& dialogue = data.dialogue;
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
std::string moodPool = body.stage <= 0
|
||||
? data.dialoguePool
|
||||
: data.stages[std::min(body.stage, (int)data.stages.size()) - 1].dialoguePool;
|
||||
|
||||
const char* labels[] = {"Weight", "Capacity", "Digestion"};
|
||||
bool isMax = body.stage >= (int)data.stages.size();
|
||||
float digestion = stats.isDigesting ? (float)stats.digestionTimer / std::max(1, data.traits.digestionTime)
|
||||
: stats.digestionProgress / DIGESTION_MAX;
|
||||
std::string weight = weight_format(stats.weight, *measurement);
|
||||
std::string next = weight_format(body.stage_threshold_get(body.stage + 1), *measurement);
|
||||
std::string unit = next.substr(next.find(' '));
|
||||
if (weight.ends_with(unit))
|
||||
weight.resize(weight.size() - unit.size());
|
||||
std::string bars[] = {isMax ? std::string("MAX") : weight + " / " + next,
|
||||
text_format("%.0f / %.0f kcal", stats.calories, stats.capacity),
|
||||
stats.isDigesting ? std::string("Digesting...") : text_format("%.0f%%", digestion * PERCENT)};
|
||||
float fills[] = {body.stage_progress_get(), stats.calories / std::max(1.0f, body.capacity_max_get()), digestion};
|
||||
float overstuffed = std::clamp((stats.calories - stats.capacity) / (body.capacity_max_get() - stats.capacity), 0.0f, 1.0f);
|
||||
|
||||
float widths[std::size(labels)];
|
||||
float used = 0;
|
||||
for (int i = 0; i < (int)std::size(labels); i++)
|
||||
{
|
||||
widths[i] = std::max(BAR_WIDTH_MIN, ImGui::CalcTextSize(bars[i].c_str()).x + style.FramePadding.x * 4);
|
||||
used += ImGui::CalcTextSize(labels[i]).x + style.ItemSpacing.x + widths[i];
|
||||
}
|
||||
int count = BUTTONS_FIXED + (int)data.interactTypes.size();
|
||||
float nameWidth = ImGui::CalcTextSize(data.name.c_str()).x;
|
||||
float rowWidth = count * BUTTON_SIZE + (count - 1) * style.ItemSpacing.x;
|
||||
float infoWidth = nameWidth + used + style.ItemSpacing.x * INFO_GAP * std::size(labels);
|
||||
float width = std::max(rowWidth, infoWidth) + style.WindowPadding.x * 2;
|
||||
float height = style.WindowPadding.y * 2 + ImGui::GetFrameHeight() + style.ItemSpacing.y * 2 + BUTTON_SIZE;
|
||||
slide_update(taskbarSlide, play.isTaskbarOpen && !play.text.isScene);
|
||||
float shown = std::pow(taskbarSlide, SLIDE_EASE) * height;
|
||||
float x = (screen.x - width) / 2;
|
||||
float y = screen.y - shown;
|
||||
play.inset = shown;
|
||||
|
||||
float mouseY = ImGui::GetIO().MousePos.y;
|
||||
float distance = std::abs(mouseY - (y - TOGGLE_HEIGHT / 2));
|
||||
float alpha = std::clamp((TOGGLE_FADE_FAR - distance) / (TOGGLE_FADE_FAR - TOGGLE_FADE_NEAR), 0.0f, 1.0f);
|
||||
if (!play.text.isScene && alpha > 0)
|
||||
{
|
||||
ImGui::SetNextWindowPos({x, y - TOGGLE_HEIGHT});
|
||||
ImGui::SetNextWindowSize({width, TOGGLE_HEIGHT});
|
||||
ImGui::SetNextWindowBgAlpha(0);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, alpha);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2());
|
||||
if (ImGui::Begin("##Taskbar Toggle", nullptr, BAR_FLAGS))
|
||||
{
|
||||
ImVec2 position = ImGui::GetCursorScreenPos();
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg));
|
||||
bool isPressed = is_fx(ImGui::Button("##Toggle", {width, TOGGLE_HEIGHT}));
|
||||
ImGui::PopStyleColor();
|
||||
if (isPressed)
|
||||
{
|
||||
play.isTaskbarOpen = !play.isTaskbarOpen;
|
||||
play.audio->play(data.sounds[play.isTaskbarOpen ? SOUND_OPEN : SOUND_CLOSE]);
|
||||
}
|
||||
triangle_draw({position.x + width / 2 - TOGGLE_HEIGHT / 2, position.y}, {TOGGLE_HEIGHT, TOGGLE_HEIGHT},
|
||||
play.isTaskbarOpen ? DIRECTION_DOWN : DIRECTION_UP);
|
||||
}
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar(3);
|
||||
}
|
||||
if (taskbarSlide <= 0)
|
||||
return;
|
||||
|
||||
ImGui::SetNextWindowPos({x, y});
|
||||
ImGui::SetNextWindowSize({width, height});
|
||||
if (!ImGui::Begin("##Taskbar", nullptr, BAR_FLAGS))
|
||||
return ImGui::End();
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted(data.name.c_str());
|
||||
float gap = std::max(style.ItemSpacing.x * INFO_GAP,
|
||||
(ImGui::GetContentRegionAvail().x - nameWidth - used) / std::size(labels));
|
||||
for (int i = 0; i < (int)std::size(labels); i++)
|
||||
{
|
||||
ImGui::SameLine(0, gap / 2);
|
||||
if (i > 0)
|
||||
{
|
||||
ImVec2 cursor = ImGui::GetCursorScreenPos();
|
||||
ImGui::GetWindowDrawList()->AddLine(cursor, {cursor.x, cursor.y + ImGui::GetFrameHeight()},
|
||||
ImGui::GetColorU32(ImGuiCol_Separator));
|
||||
}
|
||||
ImGui::SameLine(0, gap / 2);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted(labels[i]);
|
||||
ImGui::SameLine();
|
||||
if (i == 1 && overstuffed > 0)
|
||||
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(1, 1 - overstuffed, 1 - overstuffed, 1));
|
||||
ImGui::ProgressBar(fills[i], {widths[i], 0}, bars[i].c_str());
|
||||
if (i == 1 && overstuffed > 0)
|
||||
ImGui::PopStyleColor();
|
||||
if (i == 0 && ImGui::BeginItemTooltip())
|
||||
{
|
||||
ImGui::Text("Stage: %i/%i (%0.1f%%)", body.stage + 1, (int)data.stages.size() + 1, fills[0] * PERCENT);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
if (i == 2 && ImGui::BeginItemTooltip())
|
||||
{
|
||||
ImGui::TextUnformatted(stats.isDigesting ? "Digestion in progress..."
|
||||
: digestion <= 0 ? "Give food to start digesting!"
|
||||
: "Digestion");
|
||||
ImGui::TextColored(imvec4_get(GRAY), "Rate: %0.2f%% / sec", stats.digestionRate * TICK_RATE);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (width - style.WindowPadding.x * 2 - rowWidth) / 2);
|
||||
|
||||
auto state_get = [](bool isEnabled, bool isActive = false) {
|
||||
return !isEnabled ? BUTTON_DISABLED : isActive ? BUTTON_ACTIVE : BUTTON_NORMAL;
|
||||
};
|
||||
if (icon_button("talk", "Talk", state_get(dialogue.is_hook(HOOK_RANDOM))))
|
||||
play.text.set(play, dialogue.hook_get(HOOK_RANDOM));
|
||||
ImGui::SameLine();
|
||||
if (icon_button("mood", "Mood", state_get(dialogue.pools.contains(moodPool))))
|
||||
play.text.set(play, dialogue.pool_get(moodPool));
|
||||
ImGui::SameLine();
|
||||
std::string spawnTooltip =
|
||||
items.spawnDelay > 0 ? text_format("Next item in %0.2fs", items.spawnDelay * TICK) : std::string("Spawn Item");
|
||||
if (icon_button("spawn", spawnTooltip.c_str(), state_get(items.spawnDelay <= 0 && items.is_spawn_possible(play))))
|
||||
items.spawn_queue_add(play);
|
||||
if (items.spawnDelay > 0)
|
||||
cooldown_draw(1 - std::clamp((float)items.spawnDelay / items.spawn_delay_get(play), 0.0f, 1.0f));
|
||||
ImGui::SameLine();
|
||||
Autofeed& autofeed = play.autofeed;
|
||||
if (icon_button("autofeed",
|
||||
autofeed.isEnabled ? "Return to manual interaction."
|
||||
: "Serve the character food and assist in digestion automatically.",
|
||||
state_get(autofeed.is_available(play), autofeed.isEnabled)))
|
||||
autofeed.isEnabled = !autofeed.isEnabled;
|
||||
ImGui::SameLine();
|
||||
if (icon_button("home", "Reset camera view.\n(Shortcut: Home)", BUTTON_NORMAL))
|
||||
play.focus({screen.x, screen.y});
|
||||
for (int i = 0; i < (int)data.interactTypes.size(); i++)
|
||||
{
|
||||
InteractArea& type = data.interactTypes[i];
|
||||
ImGui::SameLine();
|
||||
ImGui::PushID(i);
|
||||
const char* tooltip = autofeed.isEnabled ? "Disabled while Autofeed is active!" : type.name.c_str();
|
||||
if (icon_button(type.icon.c_str(), tooltip, state_get(!autofeed.isEnabled, play.cursorType == i)))
|
||||
play.cursorType = i;
|
||||
ImGui::PopID();
|
||||
}
|
||||
Panel panels[] = {PANEL_STATS, PANEL_CATALOG, PANEL_SETTINGS};
|
||||
for (Panel each : panels)
|
||||
{
|
||||
ImGui::SameLine();
|
||||
if (icon_button(PANEL_ICONS[each], PANEL_NAMES[each], BUTTON_NORMAL))
|
||||
window_open(play, each);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (icon_button("help", "Help", state_get(dialogue.is_hook(HOOK_HELP))))
|
||||
play.text.set(play, dialogue.hook_get(HOOK_HELP));
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar(4);
|
||||
}
|
||||
|
||||
void Ui::text_draw(Play& play, ImVec2 screen)
|
||||
@@ -426,14 +629,15 @@ void Ui::text_draw(Play& play, ImVec2 screen)
|
||||
if (!entry || (line->text.empty() && !isChoosing))
|
||||
return;
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
ImVec2 size = {screen.x * TEXT_WIDTH - style.WindowPadding.x * 2,
|
||||
ImGui::GetTextLineHeightWithSpacing() * TEXT_HEIGHT_LINES};
|
||||
ImGui::PushFont(nullptr, FONT_DIALOGUE);
|
||||
ImVec2 size = {screen.x * TEXT_WIDTH, ImGui::GetTextLineHeightWithSpacing() * TEXT_HEIGHT_LINES};
|
||||
ImGui::SetNextWindowSize(size);
|
||||
ImGui::SetNextWindowPos({screen.x / 2 - size.x / 2, screen.y - size.y - style.WindowPadding.y});
|
||||
ImGui::SetNextWindowPos(
|
||||
{screen.x / 2 - size.x / 2, screen.y - play.inset - TOGGLE_HEIGHT - size.y - style.WindowPadding.y});
|
||||
if (ImGui::Begin("##Text", nullptr, PANEL_FLAGS))
|
||||
{
|
||||
bool isHovered = ImGui::IsWindowHovered();
|
||||
bool isSpace = ImGui::IsKeyReleased(ImGuiKey_Space);
|
||||
bool isSpace = !play.isBlocked && ImGui::IsKeyReleased(ImGuiKey_Space);
|
||||
bool isAdvance = (isHovered && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) || isSpace;
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, {style.ItemSpacing.x, 0});
|
||||
if (ImGui::BeginTabBar("##Name"))
|
||||
@@ -442,7 +646,6 @@ void Ui::text_draw(Play& play, ImVec2 screen)
|
||||
ImGui::EndTabItem();
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
ImGui::PushFont(nullptr, FONT_NORMAL);
|
||||
letters_draw(*line, text.index, (float)ImGui::GetTime());
|
||||
|
||||
ImVec2 position = ImGui::GetWindowPos();
|
||||
@@ -490,10 +693,10 @@ void Ui::text_draw(Play& play, ImVec2 screen)
|
||||
if (isAdvance)
|
||||
text.advance(play);
|
||||
}
|
||||
ImGui::PopFont();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
ImGui::End();
|
||||
ImGui::PopFont();
|
||||
}
|
||||
|
||||
void Ui::toasts_draw(Play& play)
|
||||
@@ -502,6 +705,7 @@ void Ui::toasts_draw(Play& play)
|
||||
ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImDrawList* drawList = ImGui::GetForegroundDrawList();
|
||||
float height = ImGui::GetTextLineHeightWithSpacing() + style.WindowPadding.y * 2;
|
||||
float bottom = viewport->WorkPos.y + viewport->WorkSize.y - play.inset - TOGGLE_HEIGHT - style.WindowPadding.y;
|
||||
for (int i = 0; i < (int)play.toasts.size(); i++)
|
||||
{
|
||||
Toast& toast = play.toasts[i];
|
||||
@@ -510,9 +714,7 @@ void Ui::toasts_draw(Play& play)
|
||||
color.w = (float)toast.life / TOAST_LIFE;
|
||||
return ImGui::GetColorU32(color);
|
||||
};
|
||||
ImVec2 position = {viewport->WorkPos.x + style.WindowPadding.x,
|
||||
viewport->WorkPos.y + viewport->WorkSize.y - style.WindowPadding.y -
|
||||
height * (play.toasts.size() - i)};
|
||||
ImVec2 position = {viewport->WorkPos.x + style.WindowPadding.x, bottom - height * (play.toasts.size() - i)};
|
||||
ImVec2 end = {position.x + ImGui::CalcTextSize(toast.message.c_str()).x + style.WindowPadding.x * 2,
|
||||
position.y + height};
|
||||
drawList->AddRectFilled(position, end, color_get(ImGuiCol_WindowBg), style.WindowRounding);
|
||||
@@ -579,90 +781,36 @@ void Ui::settings_draw(Settings& settings)
|
||||
ImGui::SetItemTooltip("Go back to the character selection screen.\nProgress will be saved.");
|
||||
}
|
||||
|
||||
void Ui::interact_tab_draw(Play& play, Settings& settings)
|
||||
// The body in full (weight and stage, calories and digestion, totals), and the cheats when unlocked.
|
||||
void Ui::stats_draw(Play& play)
|
||||
{
|
||||
Character& data = *play.character;
|
||||
Body& body = play.body;
|
||||
Stats& stats = body.stats;
|
||||
Dialogue& dialogue = data.dialogue;
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
std::string feelingPool = body.stage <= 0
|
||||
? data.dialoguePool
|
||||
: data.stages[std::min(body.stage, (int)data.stages.size()) - 1].dialoguePool;
|
||||
bool isRandom = dialogue.is_hook(HOOK_RANDOM);
|
||||
bool isHelp = dialogue.is_hook(HOOK_HELP);
|
||||
bool isFeeling = dialogue.pools.contains(feelingPool);
|
||||
Traits& traits = data.traits;
|
||||
bool isMax = body.stage >= (int)data.stages.size();
|
||||
|
||||
ImGui::PushFont(nullptr, FONT_HUGE);
|
||||
ImGui::SeparatorText(data.name.c_str());
|
||||
ImGui::PushFont(nullptr, FONT_HEADER_2);
|
||||
ImGui::SeparatorText("Weight");
|
||||
ImGui::PopFont();
|
||||
ImGui::Text("%s (Stage %i/%i)", weight_format(stats.weight, *measurement).c_str(), body.stage + 1,
|
||||
(int)data.stages.size() + 1);
|
||||
ImGui::ProgressBar(body.stage_progress_get(), {-FLT_MIN, 0}, isMax ? "MAX" : "To Next Stage");
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, imvec4_get(GRAY));
|
||||
if (isMax)
|
||||
ImGui::TextUnformatted("Maxed out!");
|
||||
else
|
||||
ImGui::Text("Start: %s Next: %s", weight_format(body.stage_threshold_get(body.stage), *measurement).c_str(),
|
||||
weight_format(body.stage_threshold_get(body.stage + 1), *measurement).c_str());
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImVec2 childSize = {(ImGui::GetContentRegionAvail().x - style.ItemSpacing.x) / 2,
|
||||
FONT_HEADER_2 + ImGui::GetFrameHeightWithSpacing()};
|
||||
if (ImGui::BeginChild("##Weight", childSize))
|
||||
{
|
||||
std::string weight = weight_format(stats.weight, settings.measurement);
|
||||
text_fit_draw(weight, {ImGui::GetContentRegionAvail().x, FONT_HEADER_2}, FONT_HEADER_2);
|
||||
ImGui::SetItemTooltip("%s", weight.c_str());
|
||||
bool isMax = body.stage >= (int)data.stages.size();
|
||||
ImGui::ProgressBar(body.stage_progress_get(), {ImGui::GetContentRegionAvail().x, 0},
|
||||
isMax ? "MAX" : "To Next Stage");
|
||||
if (ImGui::BeginItemTooltip())
|
||||
{
|
||||
ImGui::Text("Stage: %i/%i (%0.1f%%)", body.stage + 1, (int)data.stages.size() + 1,
|
||||
body.stage_progress_get() * PERCENT);
|
||||
ImGui::Separator();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, imvec4_get(GRAY));
|
||||
if (isMax)
|
||||
ImGui::TextUnformatted("Maxed out!");
|
||||
else
|
||||
{
|
||||
ImGui::Text("Start: %s", weight_format(body.stage_threshold_get(body.stage), settings.measurement).c_str());
|
||||
ImGui::Text("Current: %s", weight.c_str());
|
||||
ImGui::Text("Next: %s",
|
||||
weight_format(body.stage_threshold_get(body.stage + 1), settings.measurement).c_str());
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::SameLine();
|
||||
if (ImGui::BeginChild("##Calories", childSize))
|
||||
{
|
||||
float capacityMax = body.capacity_max_get();
|
||||
float overstuffed = std::max(0.0f, (stats.calories - stats.capacity) / (capacityMax - stats.capacity));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1, 1 - overstuffed, 1 - overstuffed, 1));
|
||||
text_fit_draw(text_format("%.0f / %.0f kcal", stats.calories, stats.capacity),
|
||||
{ImGui::GetContentRegionAvail().x, FONT_HEADER_2}, FONT_HEADER_2);
|
||||
ImGui::PopStyleColor();
|
||||
if (ImGui::BeginItemTooltip())
|
||||
{
|
||||
ImGui::Text("%.0f / %.0f ", stats.calories, stats.capacity);
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::TextColored(imvec4_get(GRAY), "(%.0f)", capacityMax);
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::TextUnformatted(" kcal");
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
float digestion = stats.isDigesting ? (float)stats.digestionTimer / std::max(1, data.traits.digestionTime)
|
||||
: stats.digestionProgress / DIGESTION_MAX;
|
||||
ImGui::ProgressBar(digestion, {ImGui::GetContentRegionAvail().x, 0},
|
||||
stats.isDigesting ? "Digesting..." : "Digestion");
|
||||
if (ImGui::BeginItemTooltip())
|
||||
{
|
||||
if (stats.isDigesting)
|
||||
ImGui::TextUnformatted("Digestion in progress...");
|
||||
else if (digestion <= 0)
|
||||
ImGui::TextUnformatted("Give food to start digesting!");
|
||||
else
|
||||
ImGui::Text("%0.2f%%", digestion * PERCENT);
|
||||
ImGui::Separator();
|
||||
ImGui::TextColored(imvec4_get(GRAY), "Rate: %0.2f%% / sec", stats.digestionRate * TICK_RATE);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::PushFont(nullptr, FONT_HEADER_2);
|
||||
ImGui::SeparatorText("Digestion");
|
||||
ImGui::PopFont();
|
||||
ImGui::Text("%.0f / %.0f kcal", stats.calories, stats.capacity);
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::TextColored(imvec4_get(GRAY), " (up to %.0f)", body.capacity_max_get());
|
||||
ImGui::Text("Rate: %0.2f%% / sec", stats.digestionRate * TICK_RATE);
|
||||
|
||||
ImGui::PushFont(nullptr, FONT_HEADER_2);
|
||||
ImGui::SeparatorText("Stats");
|
||||
@@ -671,67 +819,36 @@ void Ui::interact_tab_draw(Play& play, Settings& settings)
|
||||
ImGui::Text("Total Items Spawned: %i", stats.totalSpawned);
|
||||
ImGui::Text("Total Items Consumed: %i", stats.totalConsumed);
|
||||
ImGui::Text("Total Playtime: %s", playtime_format(stats.playtime).c_str());
|
||||
if (!play.isCheats)
|
||||
return;
|
||||
|
||||
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;
|
||||
Items& items = play.items;
|
||||
ImGui::PushFont(nullptr, FONT_HUGE);
|
||||
if (items.spawnDelay <= 0)
|
||||
{
|
||||
ImGui::BeginDisabled(!items.is_spawn_possible(play));
|
||||
if (is_fx(ImGui::Button("Spawn Item", {width, 0})))
|
||||
items.spawn_queue_add(play);
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::ProgressBar(1 - std::clamp((float)items.spawnDelay / items.spawn_delay_get(play), 0.0f, 1.0f),
|
||||
{width, ImGui::GetFrameHeight()}, "Spawn Item");
|
||||
ImGui::EndDisabled();
|
||||
tooltip_set(text_format("Next item in %0.2fs", items.spawnDelay * TICK).c_str());
|
||||
}
|
||||
ImGui::PopFont();
|
||||
|
||||
Autofeed& autofeed = play.autofeed;
|
||||
ImGui::PushFont(nullptr, FONT_HEADER_2);
|
||||
ImGui::BeginDisabled(!autofeed.is_available(play));
|
||||
if (is_fx(ImGui::Button(autofeed.isEnabled ? "Disable Autofeed" : "Enable Autofeed", {width, 0})))
|
||||
autofeed.isEnabled = !autofeed.isEnabled;
|
||||
ImGui::EndDisabled();
|
||||
tooltip_set(autofeed.isEnabled ? "Return to manual interaction."
|
||||
: "Serve the character food and assist in digestion automatically.");
|
||||
ImGui::SeparatorText("Cheats");
|
||||
ImGui::PopFont();
|
||||
|
||||
if (isRandom || isHelp || isFeeling)
|
||||
ImGui::Separator();
|
||||
auto dialogue_button = [&](bool isShown, float size, const char* label, Entry* entry) {
|
||||
if (!isShown)
|
||||
return;
|
||||
ImGui::PushFont(nullptr, size);
|
||||
if (is_fx(ImGui::Button(label, {width, 0})))
|
||||
play.text.set(play, entry);
|
||||
ImGui::PopFont();
|
||||
};
|
||||
dialogue_button(isRandom, FONT_HEADER_1, "Let's chat!", dialogue.hook_get(HOOK_RANDOM));
|
||||
dialogue_button(isHelp, FONT_HEADER_2, "Help", dialogue.hook_get(HOOK_HELP));
|
||||
dialogue_button(isFeeling, FONT_HEADER_2, "How are you feeling?", dialogue.pool_get(feelingPool));
|
||||
is_fx(ImGui::SliderFloat("Calories", &stats.calories, 0, body.capacity_max_get(), "%0.0f kcal"));
|
||||
is_fx(ImGui::SliderFloat("Capacity", &stats.capacity, traits.capacityMin, traits.capacityMax, "%0.0f kcal"));
|
||||
if (is_fx(ImGui::SliderFloat("Weight", &stats.weight, traits.weight, traits.weightMax, "%0.2f kg")))
|
||||
{
|
||||
body.stage_set(body.stage_get(stats.weight));
|
||||
body.idle_play();
|
||||
}
|
||||
int stage = body.stage + 1;
|
||||
if (is_fx(ImGui::SliderInt("Stage", &stage, 1, (int)data.stages.size() + 1)))
|
||||
{
|
||||
body.stage_set(stage - 1);
|
||||
stats.weight = body.stage_threshold_get(body.stage);
|
||||
body.idle_play();
|
||||
}
|
||||
float rate = stats.digestionRate * TICK_RATE;
|
||||
if (is_fx(ImGui::SliderFloat("Digestion Rate", &rate, traits.digestionRateMin * TICK_RATE,
|
||||
traits.digestionRateMax * TICK_RATE, "%0.2f%% / sec")))
|
||||
stats.digestionRate = rate / TICK_RATE;
|
||||
if (is_fx(ImGui::Button("Digest")))
|
||||
stats.digestionProgress = DIGESTION_MAX;
|
||||
}
|
||||
|
||||
// The items as a grid of buttons, with the selected one's details below. Icons are drawn once.
|
||||
void Ui::catalog_tab_draw(Play& play)
|
||||
// The items as a grid of buttons, beside the selected one's details. Icons are drawn once per character.
|
||||
void Ui::catalog_draw(Play& play)
|
||||
{
|
||||
Character& data = *play.character;
|
||||
std::vector<Item>& items = data.catalog.items;
|
||||
@@ -747,19 +864,19 @@ void Ui::catalog_tab_draw(Play& play)
|
||||
itemSizes[i] = canvas_draw(*renderer, itemCanvases[i], actor, {ITEM_SIZE, ITEM_SIZE});
|
||||
}
|
||||
}
|
||||
bool isSelected = selectedItem >= 0 && selectedItem < (int)items.size();
|
||||
bool isDiscovered = isSelected && play.items.discovered[selectedItem];
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
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}))
|
||||
float cell = ITEM_SIZE + style.FramePadding.x * 2;
|
||||
int columns = std::max(1, (int)((available.x * CATALOG_GRID + style.ItemSpacing.x) / (cell + style.ItemSpacing.x)));
|
||||
float gridWidth = columns * (cell + style.ItemSpacing.x) - style.ItemSpacing.x + style.ScrollbarSize +
|
||||
style.WindowPadding.x;
|
||||
if (ImGui::BeginChild("##Items", {gridWidth, available.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 isColored = play.items.discovered[i] && items[i].color;
|
||||
bool isFound = play.items.discovered[i];
|
||||
bool isColored = isFound && items[i].color;
|
||||
if (isColored)
|
||||
style_push(*items[i].color);
|
||||
bool isCurrent = i == selectedItem;
|
||||
@@ -767,210 +884,111 @@ void Ui::catalog_tab_draw(Play& play)
|
||||
for (ImGuiCol id : {ImGuiCol_Button, ImGuiCol_ButtonHovered, ImGuiCol_ButtonActive})
|
||||
ImGui::PushStyleColor(id, ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered));
|
||||
ImVec2 image = itemSizes[i];
|
||||
ImVec4 tint = play.items.discovered[i] ? ImVec4(1, 1, 1, 1) : ImVec4(0, 0, 0, UNDISCOVERED_ALPHA);
|
||||
ImVec2 padding = ImGui::GetStyle().FramePadding;
|
||||
ImVec2 padding = style.FramePadding;
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,
|
||||
{padding.x + (ITEM_SIZE - image.x) / 2, padding.y + (ITEM_SIZE - image.y) / 2});
|
||||
ImVec4 tint = isFound ? ImVec4(1, 1, 1, 1) : ImVec4(0, 0, 0, UNDISCOVERED_ALPHA);
|
||||
if (is_fx(ImGui::ImageButton("##Item", texture_id_get(itemCanvases[i].texture), image, {0, 0}, {1, 1}, {},
|
||||
tint)))
|
||||
selectedItem = i;
|
||||
ImGui::PopStyleVar();
|
||||
if (isCurrent)
|
||||
ImGui::PopStyleColor(3);
|
||||
isAnyHovered |= ImGui::IsItemHovered();
|
||||
if (!isCurrent && ImGui::BeginItemTooltip())
|
||||
{
|
||||
item_details_draw(data, play.items.discovered[i] ? &items[i] : nullptr);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
if (isColored)
|
||||
style_pop();
|
||||
float next = ImGui::GetItemRectMax().x + ImGui::GetStyle().ItemSpacing.x + ITEM_SIZE;
|
||||
if (i + 1 < (int)items.size() && next < right)
|
||||
if ((i + 1) % columns != 0 && i + 1 < (int)items.size())
|
||||
ImGui::SameLine();
|
||||
ImGui::PopID();
|
||||
}
|
||||
if (ImGui::IsWindowHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left) && !isAnyHovered)
|
||||
selectedItem = -1;
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::SameLine();
|
||||
ImVec2 cursor = ImGui::GetCursorScreenPos();
|
||||
ImGui::GetWindowDrawList()->AddLine(cursor, {cursor.x, cursor.y + available.y},
|
||||
ImGui::GetColorU32(ImGuiCol_Separator), CATALOG_DIVIDER);
|
||||
ImGui::Dummy({CATALOG_DIVIDER, available.y});
|
||||
ImGui::SameLine();
|
||||
|
||||
if (infoHeight <= 0)
|
||||
return;
|
||||
bool isColored = isDiscovered && items[selectedItem].color;
|
||||
bool isSelected = selectedItem >= 0 && selectedItem < (int)items.size();
|
||||
bool isFound = isSelected && play.items.discovered[selectedItem];
|
||||
bool isColored = isFound && items[selectedItem].color;
|
||||
if (isColored)
|
||||
style_push(*items[selectedItem].color);
|
||||
if (ImGui::BeginChild("##Info", {available.x, infoHeight}))
|
||||
if (ImGui::BeginChild("##Details", {0, available.y}))
|
||||
{
|
||||
ImGui::Separator();
|
||||
ImGui::PushFont(nullptr, FONT_HEADER_2);
|
||||
ImGui::TextWrapped("%s", !isSelected ? "No items." : isDiscovered ? items[selectedItem].name.c_str() : UNKNOWN);
|
||||
ImGui::PopFont();
|
||||
if (isSelected)
|
||||
ImGui::Separator();
|
||||
if (isDiscovered)
|
||||
{
|
||||
float half = (ImGui::GetContentRegionAvail().y - ImGui::GetStyle().ItemSpacing.y) / 2;
|
||||
if (ImGui::BeginChild("##Stats", {0, half}))
|
||||
item_summary_draw(data, items[selectedItem]);
|
||||
ImGui::EndChild();
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginChild("##Description", {0, half}))
|
||||
ImGui::TextWrapped("%s", items[selectedItem].description.c_str());
|
||||
ImGui::EndChild();
|
||||
}
|
||||
if (items.empty())
|
||||
ImGui::TextWrapped("No items.");
|
||||
else if (isSelected)
|
||||
ImGui::TextWrapped("%s", UNKNOWN);
|
||||
{
|
||||
ImGui::PushFont(nullptr, FONT_HEADER_2);
|
||||
ImGui::TextWrapped("%s", isFound ? items[selectedItem].name.c_str() : UNKNOWN);
|
||||
ImGui::PopFont();
|
||||
ImGui::Separator();
|
||||
if (isFound)
|
||||
{
|
||||
item_summary_draw(data, items[selectedItem]);
|
||||
ImGui::Separator();
|
||||
}
|
||||
ImGui::TextWrapped("%s", isFound ? items[selectedItem].description.c_str() : UNKNOWN);
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
if (isColored)
|
||||
style_pop();
|
||||
}
|
||||
|
||||
void Ui::cheats_tab_draw(Play& play)
|
||||
// The open window (stats, catalog or settings): one at a time, fixed in size above the taskbar, closed by its X or
|
||||
// escape, and holding the mouse while open.
|
||||
void Ui::window_draw(Play& play, Settings& settings, ImVec2 screen)
|
||||
{
|
||||
Traits& traits = play.character->traits;
|
||||
Body& body = play.body;
|
||||
Stats& stats = body.stats;
|
||||
if (ImGui::BeginChild("##Cheats"))
|
||||
{
|
||||
is_fx(ImGui::SliderFloat("Calories", &stats.calories, 0, body.capacity_max_get(), "%0.0f kcal"));
|
||||
is_fx(ImGui::SliderFloat("Capacity", &stats.capacity, traits.capacityMin, traits.capacityMax, "%0.0f kcal"));
|
||||
if (is_fx(ImGui::SliderFloat("Weight", &stats.weight, traits.weight, traits.weightMax, "%0.2f kg")))
|
||||
{
|
||||
body.stage_set(body.stage_get(stats.weight));
|
||||
body.idle_play();
|
||||
}
|
||||
int stage = body.stage + 1;
|
||||
if (is_fx(ImGui::SliderInt("Stage", &stage, 1, (int)play.character->stages.size() + 1)))
|
||||
{
|
||||
body.stage_set(stage - 1);
|
||||
stats.weight = body.stage_threshold_get(body.stage);
|
||||
body.idle_play();
|
||||
}
|
||||
float rate = stats.digestionRate * TICK_RATE;
|
||||
if (is_fx(ImGui::SliderFloat("Digestion Rate", &rate, traits.digestionRateMin * TICK_RATE,
|
||||
traits.digestionRateMax * TICK_RATE, "%0.2f%% / sec")))
|
||||
stats.digestionRate = rate / TICK_RATE;
|
||||
if (is_fx(ImGui::Button("Digest")))
|
||||
stats.digestionProgress = DIGESTION_MAX;
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void Ui::tools_draw(Play& play, ImVec2 position, ImVec2 size)
|
||||
{
|
||||
Character& data = *play.character;
|
||||
if (play.text.isScene)
|
||||
panel = PANEL_NONE;
|
||||
play.isBlocked = panel != PANEL_NONE;
|
||||
if (panel == PANEL_NONE)
|
||||
return;
|
||||
const char* title = PANEL_NAMES[panel];
|
||||
float above = screen.y - play.inset - TOGGLE_HEIGHT;
|
||||
ImVec2 size = {std::min(WINDOW_WIDTH, screen.x * WINDOW_FIT), std::min(WINDOW_HEIGHT, above * WINDOW_FIT)};
|
||||
ImGui::SetNextWindowSize(size);
|
||||
ImGui::SetNextWindowPos(position);
|
||||
if (ImGui::Begin("##Tools", nullptr, PANEL_FLAGS))
|
||||
ImGui::SetNextWindowPos({screen.x / 2, above / 2}, ImGuiCond_Always, {0.5f, 0.5f});
|
||||
if (!ImGui::IsPopupOpen(title))
|
||||
ImGui::OpenPopup(title);
|
||||
bool isOpen = true;
|
||||
if (ImGui::BeginPopupModal(title, &isOpen, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove))
|
||||
{
|
||||
float side = ImGui::GetContentRegionAvail().x;
|
||||
ImFont* icons = data.menu.iconsFont;
|
||||
auto tool_button = [&](const char* id, const std::string& glyph, const char* tooltip, bool isActive,
|
||||
bool isDisabled) {
|
||||
bool isIcon = icons && !glyph.empty();
|
||||
ImGui::PushID(id);
|
||||
if (isActive)
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered));
|
||||
ImGui::BeginDisabled(isDisabled);
|
||||
bool isPressed = is_fx(ImGui::Button(isIcon ? "##Icon" : id, {side, side}));
|
||||
if (isIcon)
|
||||
{
|
||||
ImVec2 min = ImGui::GetItemRectMin();
|
||||
ImVec2 itemSize = ImGui::GetItemRectSize();
|
||||
ImVec2 textSize = icons->CalcTextSizeA(itemSize.y, FLT_MAX, 0, glyph.c_str());
|
||||
ImGui::GetWindowDrawList()->AddText(
|
||||
icons, 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)
|
||||
ImGui::SetItemTooltip("%s", tooltip);
|
||||
ImGui::PopID();
|
||||
return isPressed;
|
||||
};
|
||||
if (tool_button("Home", data.menu.homeGlyph, "Reset camera view.\n(Shortcut: Home)", false, false))
|
||||
play.focus({ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y});
|
||||
for (int i = 0; i < (int)data.interactTypes.size(); i++)
|
||||
if (panel == PANEL_STATS)
|
||||
stats_draw(play);
|
||||
else if (panel == PANEL_CATALOG)
|
||||
catalog_draw(play);
|
||||
else
|
||||
settings_draw(settings);
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Escape))
|
||||
{
|
||||
InteractArea& type = data.interactTypes[i];
|
||||
const char* tooltip = play.autofeed.isEnabled ? "Disabled while Autofeed is active!"
|
||||
: icons && !type.glyph.empty() ? type.name.c_str()
|
||||
: "";
|
||||
if (tool_button(type.name.c_str(), type.glyph, tooltip, play.cursorType == i, play.autofeed.isEnabled))
|
||||
play.cursorType = i;
|
||||
isOpen = false;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::End();
|
||||
if (isOpen)
|
||||
return;
|
||||
panel = PANEL_NONE;
|
||||
play.audio->play(play.character->sounds[SOUND_CLOSE]);
|
||||
}
|
||||
|
||||
void Ui::play_update(Play& play, Settings& settings)
|
||||
{
|
||||
character = play.character;
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
measurement = &settings.measurement;
|
||||
ImVec2 screen = ImGui::GetIO().DisplaySize;
|
||||
float barWidth = std::max(ImGui::GetTextLineHeightWithSpacing(), style.WindowMinSize.x);
|
||||
float height = screen.y - style.WindowPadding.y * 2;
|
||||
|
||||
if (play.items.tooltip >= 0 && ImGui::BeginTooltip())
|
||||
if (play.items.tooltip >= 0 && !play.isBlocked && ImGui::BeginTooltip())
|
||||
{
|
||||
item_details_draw(*character, &character->catalog.items[play.items.tooltip]);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
taskbar_draw(play, screen);
|
||||
toasts_draw(play);
|
||||
text_draw(play, screen);
|
||||
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});
|
||||
bar_draw(play, play.isToolsOpen, toolsSlide, {toolsX + toolsWidth, style.WindowPadding.y}, {barWidth, height},
|
||||
DIRECTION_RIGHT, "Open Tools", "Close Tools");
|
||||
|
||||
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 (is_fx(ImGui::BeginTabItem("Interact")))
|
||||
{
|
||||
interact_tab_draw(play, settings);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (is_fx(ImGui::BeginTabItem("Catalog")))
|
||||
{
|
||||
catalog_tab_draw(play);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (is_fx(ImGui::BeginTabItem("Settings")))
|
||||
{
|
||||
settings_draw(settings);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (play.isCheats && is_fx(ImGui::BeginTabItem("Cheats")))
|
||||
{
|
||||
cheats_tab_draw(play);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
bar_draw(play, play.isMenuOpen, menuSlide, {menuX - barWidth, style.WindowPadding.y}, {barWidth, height},
|
||||
DIRECTION_LEFT, "Open Main Menu", "Close Main Menu");
|
||||
ImGui::PopStyleVar(2);
|
||||
window_draw(play, settings, screen);
|
||||
}
|
||||
|
||||
void Ui::select_update(std::vector<Character>& characters, Settings& settings)
|
||||
@@ -1125,7 +1143,7 @@ void Ui::select_update(std::vector<Character>& characters, Settings& settings)
|
||||
}
|
||||
float side = ImGui::GetContentRegionAvail().x / 4;
|
||||
float right = ImGui::GetCursorScreenPos().x + ImGui::GetContentRegionAvail().x;
|
||||
for (int i = 0; i < (int)characters.size(); i++)
|
||||
for (int i = 0; i < (int)characters.size(); i++)
|
||||
{
|
||||
Character& entry = characters[i];
|
||||
ImGui::PushID(i);
|
||||
|
||||
Reference in new issue
Block a user