diff --git a/soh/soh/Enhancements/randomizer/logic_expression.cpp b/soh/soh/Enhancements/randomizer/logic_expression.cpp index a0f859c66..54807f946 100644 --- a/soh/soh/Enhancements/randomizer/logic_expression.cpp +++ b/soh/soh/Enhancements/randomizer/logic_expression.cpp @@ -168,7 +168,7 @@ class Parser { if (Match("!")) { expr = std::make_shared(); - expr->type = LogicExpression::Impl::Type::Not; + expr->type = LogicExpression::Type::Not; expr->children.emplace_back(ParsePrimary()); expr->children.back()->parent = expr.get(); } else if (Peek().Type == LETokenType::ParenOpen) { @@ -188,7 +188,7 @@ class Parser { --pos; std::string id = Next().Text; Next(); // consume '(' - expr->type = LogicExpression::Impl::Type::FunctionCall; + expr->type = LogicExpression::Type::FunctionCall; expr->functionName = id; while (Peek().Type != LETokenType::ParenClose) { if (!expr->children.empty()) { @@ -203,7 +203,7 @@ class Parser { } Next(); // consume ')' } else { - expr->type = LogicExpression::Impl::Type::Value; + expr->type = LogicExpression::Type::Value; expr->value = token.Text; if (token.Type == LETokenType::Boolean) expr->valueType = LogicExpression::Impl::ValueType::Boolean; @@ -229,7 +229,7 @@ class Parser { template std::shared_ptr ParseBinaryOp(size_t& pos, const std::vector& tokens, LowerFunc lowerFunc, - const std::vector>& operators) { + const std::vector>& operators) { size_t initial_pos = pos; auto left = (this->*lowerFunc)(); @@ -240,9 +240,7 @@ class Parser { auto right = ParseBinaryOp(pos, tokens, lowerFunc, operators); auto expr = std::make_shared(); expr->type = exprType; - if (exprType == LogicExpression::Impl::Type::Comparison) { - expr->operation = op; - } + expr->operation = op; expr->children.emplace_back(left); expr->children.back()->parent = expr.get(); expr->children.emplace_back(right); @@ -259,33 +257,33 @@ class Parser { std::shared_ptr ParseMulDiv() { return ParseBinaryOp( pos, tokens, &Parser::ParsePrimary, - { { "*", LogicExpression::Impl::Type::Multiply }, { "/", LogicExpression::Impl::Type::Divide } }); + { { "*", LogicExpression::Type::Multiply }, { "/", LogicExpression::Type::Divide } }); } std::shared_ptr ParseAddSub() { return ParseBinaryOp( pos, tokens, &Parser::ParseMulDiv, - { { "+", LogicExpression::Impl::Type::Add }, { "-", LogicExpression::Impl::Type::Subtract } }); + { { "+", LogicExpression::Type::Add }, { "-", LogicExpression::Type::Subtract } }); } std::shared_ptr ParseComparison() { return ParseBinaryOp(pos, tokens, &Parser::ParseAddSub, - { { "==", LogicExpression::Impl::Type::Comparison }, - { "!=", LogicExpression::Impl::Type::Comparison }, - { ">=", LogicExpression::Impl::Type::Comparison }, - { "<=", LogicExpression::Impl::Type::Comparison }, - { ">", LogicExpression::Impl::Type::Comparison }, - { "<", LogicExpression::Impl::Type::Comparison } }); + { { "==", LogicExpression::Type::Comparison }, + { "!=", LogicExpression::Type::Comparison }, + { ">=", LogicExpression::Type::Comparison }, + { "<=", LogicExpression::Type::Comparison }, + { ">", LogicExpression::Type::Comparison }, + { "<", LogicExpression::Type::Comparison } }); } std::shared_ptr ParseAnd() { return ParseBinaryOp(pos, tokens, &Parser::ParseComparison, - { { "&&", LogicExpression::Impl::Type::And } }); + { { "&&", LogicExpression::Type::And } }); } std::shared_ptr ParseOr() { return ParseBinaryOp(pos, tokens, &Parser::ParseAnd, - { { "||", LogicExpression::Impl::Type::Or } }); + { { "||", LogicExpression::Type::Or } }); } std::shared_ptr ParseTernary() { @@ -305,7 +303,7 @@ class Parser { auto falseExpr = ParseTernary(); auto expr = std::make_shared(); - expr->type = LogicExpression::Impl::Type::Ternary; + expr->type = LogicExpression::Type::Ternary; expr->children.emplace_back(cond); expr->children.back()->parent = expr.get(); @@ -368,6 +366,18 @@ const std::vector>& LogicExpression::GetChildre return children; } +LogicExpression::Type LogicExpression::GetType() const { + return impl->type; +} + +std::string LogicExpression::GetOperation() const { + return impl->operation; +} + +std::string LogicExpression::GetFunctionName() const { + return impl->functionName; +} + std::string LogicExpression::ToString() const { if (impl->expressionString != nullptr) { return *impl->expressionString; diff --git a/soh/soh/Enhancements/randomizer/logic_expression.h b/soh/soh/Enhancements/randomizer/logic_expression.h index 35eb40faf..f71d25ed1 100644 --- a/soh/soh/Enhancements/randomizer/logic_expression.h +++ b/soh/soh/Enhancements/randomizer/logic_expression.h @@ -14,11 +14,17 @@ class LogicExpression { public: using ValueVariant = std::variant; // Define callback type for evaluation tracing - using EvaluationCallback = std::function&, const std::string&, int, const std::string&, const ValueVariant&)>; + using EvaluationCallback = std::function&, const std::string&, int, + const std::string&, const ValueVariant&)>; + + enum class Type { And, Or, Not, Comparison, FunctionCall, Value, Add, Subtract, Multiply, Divide, Ternary }; static std::shared_ptr Parse(const std::string& exprStr); std::string ToString() const; const std::vector>& GetChildren() const; + Type GetType() const; + std::string GetOperation() const; + std::string GetFunctionName() const; // Add optional callback parameter to Evaluate template T Evaluate(const EvaluationCallback& callback = nullptr) const { @@ -54,7 +60,6 @@ class LogicExpression { private: struct Impl { - enum class Type { And, Or, Not, Comparison, FunctionCall, Value, Add, Subtract, Multiply, Divide, Ternary }; enum class ValueType { Identifier, Boolean, Number, Enum }; diff --git a/soh/soh/Enhancements/randomizer/randomizer_logic_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_logic_tracker.cpp index 3ef254f45..5dcc22237 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_logic_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_logic_tracker.cpp @@ -175,6 +175,88 @@ static std::string ToString(const std::optional& return ToString(value.value()); } +static void DrawColoredWrappedText(const std::vector>& segments) { + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + ImVec2 pos = ImGui::GetCursorScreenPos(); + float wrapWidth = ImGui::GetContentRegionAvail().x; + float x = pos.x; + float y = pos.y; + float startX = x; + ImFont* font = ImGui::GetFont(); + float fontSize = ImGui::GetFontSize(); + + for (const auto& [color, text] : segments) { + std::istringstream iss(text + " "); + std::string word; + bool firstWordInSegment = true; + while (std::getline(iss, word, ' ')) { + std::string toDraw = word; + if (!firstWordInSegment) { + toDraw = " " + word; + } + ImVec2 textSize = ImGui::CalcTextSize(toDraw.c_str()); + if (x + textSize.x > startX + wrapWidth && x > startX) { + x = startX; + y += fontSize; + toDraw = word; + textSize = ImGui::CalcTextSize(toDraw.c_str()); + } + draw_list->AddText(font, fontSize, ImVec2(x, y), ImGui::ColorConvertFloat4ToU32(color), toDraw.c_str()); + x += textSize.x; + firstWordInSegment = false; + } + } + ImGui::SetCursorScreenPos(ImVec2(x, y + fontSize)); +} + +static void DrawCondition(const LogicExpression& expression) { + static ImVec4 fontColors[] = { + ImVec4(0.5f, 0.5f, 1.0f, 1.0f), // Blue + ImVec4(0.5f, 1.0f, 0.5f, 1.0f), // Green + ImVec4(1.0f, 0.5f, 0.5f, 1.0f), // Red + ImVec4(1.0f, 1.0f, 0.5f, 1.0f), // Yellow + ImVec4(1.0f, 0.5f, 1.0f, 1.0f), // Magenta + ImVec4(0.5f, 1.0f, 1.0f, 1.0f), // Cyan + }; + static size_t fontColorsLength = std::size(fontColors); + static ImVec4 defaultColor = ImGui::GetStyleColorVec4(ImGuiCol_Text); + + LogicExpression::Type type = expression.GetType(); + if (type == LogicExpression::Type::And || type == LogicExpression::Type::Or || + type == LogicExpression::Type::Comparison || type == LogicExpression::Type::Add || + type == LogicExpression::Type::Subtract || type == LogicExpression::Type::Multiply || + type == LogicExpression::Type::Divide) { + DrawColoredWrappedText({ { fontColors[0], expression.GetChildren()[0]->ToString() }, + { defaultColor, " " + expression.GetOperation() + " " }, + { fontColors[1], expression.GetChildren()[1]->ToString() } }); + } else if (type == LogicExpression::Type::Not) { + DrawColoredWrappedText({ { defaultColor, "!" }, + { fontColors[0], expression.GetChildren()[0]->ToString() } }); + } else if (type == LogicExpression::Type::FunctionCall) { + std::vector> segments; + segments.emplace_back(fontColors[0], expression.GetFunctionName()); + segments.emplace_back(defaultColor, "("); + for (size_t i = 0; i < expression.GetChildren().size(); ++i) { + segments.emplace_back(fontColors[i + 1 % fontColorsLength], expression.GetChildren()[i]->ToString()); + if (i < expression.GetChildren().size() - 1) { + segments.emplace_back(defaultColor, ", "); + } + } + segments.emplace_back(defaultColor, ")"); + DrawColoredWrappedText(segments); + } else if (type == LogicExpression::Type::Ternary) { + DrawColoredWrappedText({ { fontColors[0], expression.GetChildren()[0]->ToString() }, + { defaultColor, " ? " }, + { fontColors[1], expression.GetChildren()[1]->ToString() }, + { defaultColor, " : " }, + { fontColors[2], expression.GetChildren()[2]->ToString() } }); + } else if (type == LogicExpression::Type::Value) { + DrawColoredWrappedText({ { fontColors[0], expression.ToString() } }); + } else { + DrawColoredWrappedText({ { defaultColor, expression.ToString() } }); + } +} + static void DrawExpressionRow(const LogicTrackerCheck::Region& region, LogicTrackerCheck::Region::ExpressionRow& row, int level) { ImGui::TableNextRow(); @@ -196,7 +278,7 @@ static void DrawExpressionRow(const LogicTrackerCheck::Region& region, LogicTrac } ImGui::PopID(); ImGui::TableNextColumn(); - ImGui::TextWrapped("%s", row.Expression->ToString().c_str()); + DrawCondition(*row.Expression); ImGui::TableNextColumn(); ImGui::TextUnformatted(ToString(row.ChildDay).c_str());