Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 4 additions & 24 deletions rctctl/src/commands/research_marketing_commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,30 +21,10 @@ void AppendResearchMarketingCommands(std::vector<CommandSpec>& specs)
"research",
{ "status" },
"Show research status.",
"Displays funding level, progress, upcoming discoveries, and category priorities.",
{ CommandArgSpec{ "queue-limit", "Limit items shown from the queue.", false, "INT" },
CommandArgSpec{ "queue-category", "Comma list of queue categories to include.", false, "LIST" },
CommandArgSpec{ "queue-order", "Order queue by scenario, name, or category.", false, "FIELD" },
CommandArgSpec{ "queue-direction", "Sort order asc/desc (ignored for scenario order).", false, "DIR" } },
[](const ParsedArgs& args) {
json params = json::object();
if (auto limit = cli::GetIntOption(args, { "queue-limit", "queueLimit" }))
{
params["queueLimit"] = *limit;
}
if (auto categories = cli::GetStringOption(args, { "queue-category", "queueCategories" }))
{
params["queueCategories"] = cli::SplitCommaSeparated(*categories);
}
if (auto order = cli::GetStringOption(args, { "queue-order", "queueOrder" }))
{
params["queueOrder"] = *order;
}
if (auto direction = cli::GetStringOption(args, { "queue-direction", "queueDirection" }))
{
params["queueDirection"] = *direction;
}
return CommandPlan{ "research.status", params };
"Displays funding level, current research progress, and category priorities. Shows what a player would see in the research window - current item visibility depends on progress stage.",
{},
[](const ParsedArgs&) {
return CommandPlan{ "research.status", json::object() };
},
renderers::RenderResearchStatus });

Expand Down
14 changes: 10 additions & 4 deletions rctctl/src/commands/shop_commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,16 @@ void AppendShopCommands(std::vector<CommandSpec>& specs)
"shops",
{ "catalog" },
"List available shop/stall blueprints.",
"Shows every loaded shop/stall object identifier, ride type, stocked items, and build cost. Once placed, use 'rides get' to inspect shop status and pricing.",
{},
[](const ParsedArgs&) {
return CommandPlan{ "shops.catalog", json::object() };
"Enumerates invented shop/stall objects (or every loaded entry with --all) with ride type, stocked items, and build cost. Once placed, use 'rides get' to inspect shop status and pricing.",
{ CommandArgSpec{ "all", "Include locked/uninvented entries as well.", false, "BOOL" },
CommandArgSpec{ "include-locked", "Alias for --all.", false, "BOOL" } },
[](const ParsedArgs& args) {
json params = json::object();
if (auto includeLocked = cli::GetBoolOption(args, { "all", "include-locked" }))
{
params["includeLocked"] = *includeLocked;
}
return CommandPlan{ "shops.catalog", params };
},
renderers::RenderShopCatalog });

Expand Down
135 changes: 70 additions & 65 deletions rctctl/src/renderers/research_marketing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,59 +10,35 @@ namespace rctctl::renderers {
namespace {
using json = nlohmann::json;

std::string FormatPercent(double value)
std::string DescribeStage(const json& stageValue)
{
std::ostringstream oss;
oss << std::fixed << std::setprecision(1) << value << "%";
return oss.str();
}

double ExtractProgressPercent(const json& result)
{
if (auto it = result.find("progressPercent"); it != result.end() && it->is_number())
{
return it->get<double>();
}
double raw = result.value("progress", 0.0);
int stage = result.value("progressStage", 0);
if (stage == 4)
{
return 100.0;
}
if (stage == 0)
{
return 0.0;
}
constexpr double kProgressUnits = 65535.0;
double percent = (raw / kProgressUnits) * 100.0;
if (percent < 0.0)
{
percent = 0.0;
}
if (percent > 100.0)
// Handle both string format (new) and numeric format (old/compatibility)
if (stageValue.is_string())
{
percent = 100.0;
}
return percent;
}

const char* DescribeStage(int stage)
{
switch (stage)
{
case 0:
std::string stage = stageValue.get<std::string>();
if (stage == "initialResearch")
return "Initial research";
case 1:
if (stage == "designing")
return "Designing";
case 2:
if (stage == "completingDesign")
return "Completing design";
case 3:
return "Unknown";
case 4:
return "Finished";
default:
return "Unknown";
if (stage == "allComplete")
return "All complete";
return "Unknown";
}
if (stageValue.is_number())
{
int stage = stageValue.get<int>();
switch (stage)
{
case 0: return "Initial research";
case 1: return "Designing";
case 2: return "Completing design";
case 255: return "All complete";
default: return "Unknown";
}
}
return "Unknown";
}
}

Expand All @@ -71,8 +47,12 @@ void RenderResearchStatus(const json& result)
TextCanvas canvas(std::cout);
canvas.Section("Research");
canvas.KeyValue("Funding", result.value("fundingLevel", std::string("")));
canvas.KeyValue("Stage", DescribeStage(result.value("progressStage", 0)));
canvas.KeyValue("Progress", FormatPercent(ExtractProgressPercent(result)));

auto stageIt = result.find("progressStage");
if (stageIt != result.end())
{
canvas.KeyValue("Stage", DescribeStage(*stageIt));
}

const auto& priorities = result.value("priorities", json::object());
if (!priorities.empty())
Expand All @@ -89,10 +69,46 @@ void RenderResearchStatus(const json& result)
}
}

// Current research - what we show depends on visibility stage
if (auto nextIt = result.find("next"); nextIt != result.end())
{
canvas.KeyValue("Next discovery", nextIt->value("name", std::string("")));
const auto& next = *nextIt;
std::string status = next.value("status", std::string(""));

if (status == "unknown")
{
canvas.KeyValue("Current research", "Unknown");
}
else if (status == "designing")
{
// Only category is visible
canvas.KeyValue("Current research", next.value("category", std::string("Unknown category")));
}
else if (status == "completingDesign")
{
// Full name is visible
std::string name = next.value("name", std::string(""));
std::string category = next.value("category", std::string(""));
if (!name.empty())
{
canvas.KeyValue("Current research", name + " (" + category + ")");
}
else
{
canvas.KeyValue("Current research", category);
}
}
}

// Expected completion
if (result.contains("expectedMonth") && result.contains("expectedDay"))
{
std::ostringstream expected;
expected << "Day " << result.value("expectedDay", 0) << ", Month " << (result.value("expectedMonth", 0) + 1);
canvas.KeyValue("Expected", expected.str());
}

// Last completed research
if (auto lastIt = result.find("last"); lastIt != result.end())
{
canvas.KeyValue("Last discovery", lastIt->value("name", std::string("")));
Expand All @@ -102,23 +118,12 @@ void RenderResearchStatus(const json& result)
{
canvas.Paragraph("All research complete.");
}

if (auto queueIt = result.find("queue"); queueIt != result.end() && queueIt->is_array())
else
{
const auto& queue = *queueIt;
if (!queue.empty())
int remaining = result.value("uninventedCount", 0);
if (remaining > 0)
{
canvas.Section("Queue");
canvas.KeyValue("Entries", static_cast<int>(queue.size()));
TableView table;
table.headers = { "Name", "Category", "Type" };
for (const auto& entry : queue)
{
table.rows.push_back({ entry.value("name", std::string("")),
entry.value("category", entry.value("categoryKey", std::string(""))),
entry.value("type", std::string("")) });
}
canvas.Table(table);
canvas.KeyValue("Items remaining", remaining);
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions rctctl/src/renderers/shops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,15 @@ void RenderShopCatalog(const json& result)
canvas.KeyValue("Entries", static_cast<int>(entries.size()));

TableView table;
table.headers = { "Name", "Type", "Items", "Build", "Use with" };
table.headers = { "Name", "Type", "Status", "Items", "Build", "Use with" };
for (const auto& entry : entries)
{
auto name = entry.value("name", entry.value("identifier", std::string()));
auto rideType = entry.value("rideType", std::string());
auto classification = entry.value("classification", std::string());
auto buildCost = entry.value("buildCost", 0.0);
auto labels = ExtractItemLabels(entry.value("items", json::array()));
std::string status = entry.value("invented", false) ? "Available" : "Locked";

std::string typeLabel = rideType;
if (!classification.empty())
Expand All @@ -118,7 +119,7 @@ void RenderShopCatalog(const json& result)
typeLabel += classification;
}

table.rows.push_back({ name, typeLabel, JoinItemList(labels), util::FormatCurrency(buildCost),
table.rows.push_back({ name, typeLabel, status, JoinItemList(labels), util::FormatCurrency(buildCost),
BuildSelectorLabel(entry) });
}
canvas.Table(table);
Expand Down
Loading