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
+6
-5
@@ -4,6 +4,7 @@
|
||||
|
||||
constexpr float FONT_SIZE = 20;
|
||||
constexpr const char* COMMON_FOLDER = "common/";
|
||||
constexpr const char* ICONS_PATH = "gfx/ui/icons.anm2";
|
||||
constexpr const char* TAG_NAMES[] = {"animation", "big", "color", "delay", "screenshake",
|
||||
"shake", "silent", "small", "sound", "wave"};
|
||||
constexpr const char* USE_NAMES[] = {"", "edible", "tool"};
|
||||
@@ -110,7 +111,7 @@ Anm2* Character::anm2_get(const std::string& path, Renderer& renderer, Audio& au
|
||||
static void interact_parse(object source, InteractArea& area, Character& character, Audio& audio)
|
||||
{
|
||||
json_keys_check(source,
|
||||
{"type", "null", "layer", "glyph", "isHold", "animation", "idleOnEnd", "cursorHover", "cursorActive",
|
||||
{"type", "null", "layer", "icon", "isHold", "animation", "idleOnEnd", "cursorHover", "cursorActive",
|
||||
"dialoguePool", "dialoguePoolFull", "dialogueCooldown", "digestionClick", "digestionHover",
|
||||
"scaleAmplitude", "scaleCycles", "scaleTime", "sounds", "particles"},
|
||||
"an interact area");
|
||||
@@ -119,7 +120,7 @@ static void interact_parse(object source, InteractArea& area, Character& charact
|
||||
area.cursorHover = json_string(source, "cursorHover", area.cursorHover);
|
||||
area.dialoguePool = json_string(source, "dialoguePool", area.dialoguePool);
|
||||
area.dialoguePoolFull = json_string(source, "dialoguePoolFull", area.dialoguePoolFull);
|
||||
area.glyph = json_string(source, "glyph", area.glyph);
|
||||
area.icon = json_string(source, "icon", area.icon);
|
||||
area.idleOnEnd = json_string(source, "idleOnEnd", area.idleOnEnd);
|
||||
area.digestionClick = json_float(source, "digestionClick", area.digestionClick);
|
||||
area.digestionHover = json_float(source, "digestionHover", area.digestionHover * TICK_RATE) / TICK_RATE;
|
||||
@@ -286,15 +287,15 @@ static void character_parse(object root, Character& character, Renderer& rendere
|
||||
character.anm2_get(json_string(cursor, "anm2"), renderer, audio)};
|
||||
|
||||
object menu = json_object(root, "menu");
|
||||
json_keys_check(menu, {"font", "iconsFont", "rounding", "homeGlyph", "sounds"}, "menu");
|
||||
json_keys_check(menu, {"font", "rounding", "sounds"}, "menu");
|
||||
auto font_get = [&](const char* key) {
|
||||
std::string path = json_string(menu, key);
|
||||
return path.empty() ? nullptr
|
||||
: ImGui::GetIO().Fonts->AddFontFromFileTTF(file_find(character.roots, path).c_str(),
|
||||
FONT_SIZE);
|
||||
};
|
||||
character.menu = {json_string(menu, "homeGlyph"), font_get("font"), font_get("iconsFont"),
|
||||
json_float(menu, "rounding")};
|
||||
character.menu = {font_get("font"), json_float(menu, "rounding")};
|
||||
character.icons = character.anm2_get(ICONS_PATH, renderer, audio);
|
||||
|
||||
for (auto [name, value] : json_object(root, "particles"))
|
||||
{
|
||||
|
||||
+2
-3
@@ -81,7 +81,7 @@ struct InteractArea
|
||||
std::string cursorHover;
|
||||
std::string dialoguePool;
|
||||
std::string dialoguePoolFull;
|
||||
std::string glyph;
|
||||
std::string icon;
|
||||
std::string idleOnEnd;
|
||||
std::string name;
|
||||
std::vector<std::string> particles;
|
||||
@@ -297,9 +297,7 @@ struct Cursor
|
||||
|
||||
struct Menu
|
||||
{
|
||||
std::string homeGlyph;
|
||||
ImFont* font = nullptr;
|
||||
ImFont* iconsFont = nullptr;
|
||||
float rounding = 0;
|
||||
};
|
||||
|
||||
@@ -351,6 +349,7 @@ public:
|
||||
Swap talk;
|
||||
Traits traits;
|
||||
Anm2* anm2 = nullptr;
|
||||
Anm2* icons = nullptr;
|
||||
Texture* portrait = nullptr;
|
||||
Texture* render = nullptr;
|
||||
Color color = {0.5f, 0.5f, 0.5f, 1};
|
||||
|
||||
+10
-22
@@ -13,9 +13,7 @@ constexpr const char* KEY_DISCOVERED = "discovered";
|
||||
constexpr const char* KEY_ITEM = "item";
|
||||
constexpr float AUTOSAVE_TIME = 30;
|
||||
constexpr float CHEAT_TIME = 5;
|
||||
constexpr float FOCUS_MENU_WIDTH = 0.3f;
|
||||
constexpr float FOCUS_PADDING = 100;
|
||||
constexpr float FOCUS_TOOLS_WIDTH = 0.1f;
|
||||
constexpr float SHAKE_DECAY = 0.97f;
|
||||
constexpr float SHAKE_EPSILON = 0.1f;
|
||||
constexpr float SHAKE_LERP = 0.72f;
|
||||
@@ -133,16 +131,9 @@ Actor& Play::pointer_actor_get()
|
||||
return autofeed.isActive ? autoCursor : cursor;
|
||||
}
|
||||
|
||||
Focus Play::focus_get()
|
||||
{
|
||||
if (text.isScene)
|
||||
return FOCUS_CENTER;
|
||||
return isMenuOpen && isToolsOpen ? FOCUS_BOTH : isMenuOpen ? FOCUS_MENU : isToolsOpen ? FOCUS_TOOLS : FOCUS_CENTER;
|
||||
}
|
||||
|
||||
void Play::focus(Vec2 screenSize)
|
||||
{
|
||||
camera.focus(body.actor.bounds_get(), screenSize, focus_get());
|
||||
camera.focus(body.actor.bounds_get(), screenSize, text.isScene ? 0 : inset);
|
||||
}
|
||||
|
||||
void Play::particles_spawn(const std::vector<std::string>& names, Vec2 position)
|
||||
@@ -195,7 +186,7 @@ void Play::events_handle(Vec2 screenSize)
|
||||
if (isPostgame)
|
||||
continue;
|
||||
text.set(*this, dialogue.hook_get(HOOK_END), TEXT_UNINTERRUPTIBLE);
|
||||
isMenuOpen = isToolsOpen = false;
|
||||
isTaskbarOpen = false;
|
||||
body.stats.calories = 0;
|
||||
body.stats.digestionProgress = 0;
|
||||
items.drop();
|
||||
@@ -215,7 +206,7 @@ void Play::update(Vec2 screenSize)
|
||||
text.update(*this);
|
||||
if (isPostgamePending && (!text.entry || (text.entry->isTerminal && text.isFinished)) && !text.isScene)
|
||||
{
|
||||
isMenuOpen = isPostgame = isCheats = true;
|
||||
isTaskbarOpen = isPostgame = isCheats = true;
|
||||
isPostgamePending = false;
|
||||
focus(screenSize);
|
||||
}
|
||||
@@ -225,7 +216,7 @@ void Play::update(Vec2 screenSize)
|
||||
Pointer pointer;
|
||||
pointer.position = camera.world_get({io.MousePos.x, io.MousePos.y});
|
||||
pointer.type = cursorType;
|
||||
pointer.isCaptured = io.WantCaptureMouse;
|
||||
pointer.isCaptured = io.WantCaptureMouse || isBlocked;
|
||||
pointer.isClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
||||
pointer.isDown = ImGui::IsMouseDown(ImGuiMouseButton_Left);
|
||||
pointer.isReleased = ImGui::IsMouseReleased(ImGuiMouseButton_Left);
|
||||
@@ -308,20 +299,17 @@ void Camera::shake_add(float magnitude, int time)
|
||||
shake = {random_get(-magnitude, magnitude), random_get(-magnitude, magnitude)};
|
||||
}
|
||||
|
||||
// Zooms and pans so a target fills the view, leaving room for the open panels.
|
||||
void Camera::focus(Rect target, Vec2 screenSize, Focus panels)
|
||||
// Zooms and pans so a target fills the view above the inset at the screen's bottom.
|
||||
void Camera::focus(Rect target, Vec2 screenSize, float inset)
|
||||
{
|
||||
if (!is_rect_valid(target))
|
||||
return;
|
||||
target = rect_center(target, FOCUS_PADDING);
|
||||
float factor = std::min(screenSize.x / target.w, screenSize.y / target.h);
|
||||
Vec2 view = {screenSize.x, std::max(1.0f, screenSize.y - inset)};
|
||||
float factor = std::min(view.x / target.w, view.y / target.h);
|
||||
zoom = ZOOM_LADDER[zoom_index_get(std::clamp(factor * PERCENT, ZOOM_LADDER[0], std::end(ZOOM_LADDER)[-1]))];
|
||||
factor = zoom / PERCENT;
|
||||
pan = rect_middle(target) - screenSize / factor / 2;
|
||||
if (panels == FOCUS_MENU || panels == FOCUS_BOTH)
|
||||
pan.x += screenSize.x * FOCUS_MENU_WIDTH / factor / 2;
|
||||
if (panels == FOCUS_TOOLS || panels == FOCUS_BOTH)
|
||||
pan.x -= screenSize.x * FOCUS_TOOLS_WIDTH / factor / 2;
|
||||
pan = rect_middle(target) - view / factor / 2;
|
||||
}
|
||||
|
||||
// Pans with the middle mouse (or control and the left), zooms through the ladder with the wheel toward the mouse, and
|
||||
@@ -330,7 +318,7 @@ void Camera::update(Play& play, Vec2 screenSize)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
Cursor& cursor = play.character->cursor;
|
||||
if (!ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow) && !ImGui::IsAnyItemActive())
|
||||
if (!play.isBlocked && !ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow) && !ImGui::IsAnyItemActive())
|
||||
{
|
||||
if (ImGui::IsMouseDown(ImGuiMouseButton_Middle) ||
|
||||
(ImGui::IsMouseDown(ImGuiMouseButton_Left) && ImGui::IsKeyDown(ImGuiMod_Ctrl)))
|
||||
|
||||
+5
-14
@@ -22,15 +22,6 @@ enum CursorState
|
||||
CURSOR_ACTION
|
||||
};
|
||||
|
||||
// Which side panels are open, which shifts where the camera centers the character.
|
||||
enum Focus
|
||||
{
|
||||
FOCUS_CENTER,
|
||||
FOCUS_MENU,
|
||||
FOCUS_TOOLS,
|
||||
FOCUS_BOTH
|
||||
};
|
||||
|
||||
enum Hold
|
||||
{
|
||||
HOLD_RELEASE,
|
||||
@@ -191,14 +182,14 @@ public:
|
||||
bool isFocusPending = false;
|
||||
|
||||
void update(Play& play, Vec2 screenSize);
|
||||
void focus(Rect target, Vec2 screenSize, Focus panels);
|
||||
void focus(Rect target, Vec2 screenSize, float inset);
|
||||
void shake_add(float magnitude, int time);
|
||||
Vec2 world_get(Vec2 screen);
|
||||
Affine view_get(Vec2 screenSize);
|
||||
};
|
||||
|
||||
// A character being played, sequencing its parts each tick: the body, items, interactions, autofeed, camera and
|
||||
// dialogue.
|
||||
// dialogue. Inset is how much of the screen's bottom the UI covers, and blocked means a window has the mouse.
|
||||
class Play
|
||||
{
|
||||
public:
|
||||
@@ -218,15 +209,16 @@ public:
|
||||
std::vector<Toast> toasts;
|
||||
Rect bounds;
|
||||
float autosaveTime = 0;
|
||||
float inset = 0;
|
||||
float cheatTime = 0;
|
||||
int cheatIndex = 0;
|
||||
int cursorType = -1;
|
||||
CursorState cursorState = CURSOR_DEFAULT;
|
||||
bool isBlocked = false;
|
||||
bool isCheats = false;
|
||||
bool isMenuOpen = true;
|
||||
bool isPostgame = false;
|
||||
bool isPostgamePending = false;
|
||||
bool isToolsOpen = false;
|
||||
bool isTaskbarOpen = true;
|
||||
|
||||
void set(Character& played, Game game, Renderer& renderer, Audio& sound);
|
||||
void save();
|
||||
@@ -236,7 +228,6 @@ public:
|
||||
void toast_add(const std::string& message);
|
||||
void particles_spawn(const std::vector<std::string>& names, Vec2 position);
|
||||
Actor& pointer_actor_get();
|
||||
Focus focus_get();
|
||||
|
||||
template <class Visitor> void visit(Visitor&& visitor)
|
||||
{
|
||||
|
||||
+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);
|
||||
|
||||
@@ -4,18 +4,32 @@
|
||||
#include "save.h"
|
||||
#include <imgui.h>
|
||||
|
||||
constexpr float FONT_NORMAL = 20;
|
||||
constexpr float FONT_HEADER_3 = 24;
|
||||
constexpr float FONT_HEADER_2 = 30;
|
||||
constexpr float FONT_HEADER_1 = 40;
|
||||
constexpr float FONT_HUGE = 80;
|
||||
constexpr float FONT_NORMAL = 26;
|
||||
constexpr float FONT_HEADER_3 = 30;
|
||||
constexpr float FONT_HEADER_2 = 34;
|
||||
constexpr float FONT_DIALOGUE = 32;
|
||||
|
||||
class Play;
|
||||
|
||||
enum Direction
|
||||
{
|
||||
DIRECTION_LEFT,
|
||||
DIRECTION_RIGHT
|
||||
DIRECTION_UP,
|
||||
DIRECTION_DOWN
|
||||
};
|
||||
|
||||
enum Panel
|
||||
{
|
||||
PANEL_NONE,
|
||||
PANEL_STATS,
|
||||
PANEL_CATALOG,
|
||||
PANEL_SETTINGS
|
||||
};
|
||||
|
||||
enum ButtonState
|
||||
{
|
||||
BUTTON_NORMAL,
|
||||
BUTTON_ACTIVE,
|
||||
BUTTON_DISABLED
|
||||
};
|
||||
|
||||
enum CommandType
|
||||
@@ -39,7 +53,7 @@ using Commands = std::vector<Command>;
|
||||
|
||||
void style_set(Color color, float rounding = 0);
|
||||
|
||||
// The menus, tools, dialogue box, toasts and select screen.
|
||||
// The taskbar, its windows, the dialogue box, toasts and the select screen.
|
||||
class Ui
|
||||
{
|
||||
public:
|
||||
@@ -52,12 +66,13 @@ public:
|
||||
Character* catalogCharacter = nullptr;
|
||||
Character* character = nullptr;
|
||||
Commands* commands = nullptr;
|
||||
Measurement* measurement = nullptr;
|
||||
Renderer* renderer = nullptr;
|
||||
float menuSlide = 1;
|
||||
float toolsSlide = 0;
|
||||
float taskbarSlide = 1;
|
||||
int characterIndex = 0;
|
||||
int selectedIndex = -1;
|
||||
int selectedItem = -1;
|
||||
Panel panel = PANEL_NONE;
|
||||
bool isFullscreen = false;
|
||||
bool isNewGameWarning = false;
|
||||
bool isSelectedSaved = false;
|
||||
@@ -67,14 +82,15 @@ public:
|
||||
|
||||
private:
|
||||
bool is_fx(bool result);
|
||||
bool icon_button(const char* icon, const char* tooltip, ButtonState state);
|
||||
void icon_draw(const char* name, ImVec2 position, float size, ImU32 tint);
|
||||
void command_add(CommandType type, const std::string& key = "");
|
||||
void window_open(Play& play, Panel next);
|
||||
void taskbar_draw(Play& play, ImVec2 screen);
|
||||
void window_draw(Play& play, Settings& settings, ImVec2 screen);
|
||||
void stats_draw(Play& play);
|
||||
void catalog_draw(Play& play);
|
||||
void settings_draw(Settings& settings);
|
||||
void interact_tab_draw(Play& play, Settings& settings);
|
||||
void catalog_tab_draw(Play& play);
|
||||
void cheats_tab_draw(Play& play);
|
||||
void tools_draw(Play& play, ImVec2 position, ImVec2 size);
|
||||
void bar_draw(Play& play, bool& isOpen, float slide, ImVec2 position, ImVec2 size, Direction direction,
|
||||
const char* openTooltip, const char* closeTooltip);
|
||||
void text_draw(Play& play, ImVec2 screen);
|
||||
void toasts_draw(Play& play);
|
||||
};
|
||||
Reference in new issue
Block a user