mirror of
https://github.com/zebrajr/SamRewritten.git
synced 2025-12-06 12:19:51 +01:00
Add support for adding stat modifications
Support is added for both GUI and CLI Remove unused original_value in StatValue_t - changes are encoded in StatChange_t. Make pending modification more intuitive to work with by using Change_t struct types. Actually committing stats changes will be done in next commit.
This commit is contained in:
parent
f891e25535
commit
f153bdbe2c
|
|
@ -49,6 +49,8 @@ bool go_cli_mode(int argc, char* argv[]) {
|
|||
("sort", "Sort option for --ls. You can leave empty or set to 'unlock_rate'", cxxopts::value<std::string>())
|
||||
("unlock", "Unlock achievements for an AppId. Separate achievement names by a comma.", cxxopts::value<std::vector<std::string>>())
|
||||
("lock", "Lock achievements for an AppId. Separate achievement names by a comma.", cxxopts::value<std::vector<std::string>>())
|
||||
("statnames", "Change stats for an AppId. Separate stat names by a comma. Use with statvalues to name the values in order", cxxopts::value<std::vector<std::string>>())
|
||||
("statvalues", "Change stats for an AppId. Separate stat values by a comma. Use with statnames to name the values in order", cxxopts::value<std::vector<std::string>>())
|
||||
("launch", "Actually just launch the app.");
|
||||
|
||||
|
||||
|
|
@ -58,7 +60,7 @@ bool go_cli_mode(int argc, char* argv[]) {
|
|||
bool cli = false;
|
||||
AppId_t app = 0;
|
||||
|
||||
if (result.count("help"))
|
||||
if (result.count("help") > 0)
|
||||
{
|
||||
std::cout << options.help() << std::endl;
|
||||
return true;
|
||||
|
|
@ -206,6 +208,57 @@ bool go_cli_mode(int argc, char* argv[]) {
|
|||
g_steam->quit_game();
|
||||
}
|
||||
|
||||
if (result.count("statnames") > 0 || result.count("statvalues") > 0)
|
||||
{
|
||||
if (app == 0)
|
||||
{
|
||||
std::cout << "Please provide an AppId argument before changing stats." << std::endl;
|
||||
return true;
|
||||
}
|
||||
if (result.count("statnames") != result.count("statvalues"))
|
||||
{
|
||||
std::cout << "Number of statnames does not equal number of statvalues." << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
cli = true;
|
||||
size_t num_stats = result.count("statnames");
|
||||
|
||||
const std::vector<std::string> stat_names = result["statnames"].as<std::vector<std::string>>();
|
||||
const std::vector<std::string> stat_values = result["statvalues"].as<std::vector<std::string>>();
|
||||
|
||||
for (size_t i = 0; i < num_stats; i++) {
|
||||
// We don't know the type at this point, so try both of them
|
||||
StatValue_t stat;
|
||||
bool valid_conversion;
|
||||
std::any new_value;
|
||||
|
||||
valid_conversion = convert_user_stat_value(UserStatType::Integer, stat_values[i], &new_value);
|
||||
|
||||
if (valid_conversion) {
|
||||
stat.type = UserStatType::Integer;
|
||||
} else {
|
||||
valid_conversion = convert_user_stat_value(UserStatType::Float, stat_values[i], &new_value);
|
||||
if (valid_conversion) {
|
||||
stat.type = UserStatType::Float;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid_conversion) {
|
||||
g_steam->clear_changes();
|
||||
return true;
|
||||
}
|
||||
|
||||
// No validation done for the name
|
||||
stat.id = stat_names[i];
|
||||
g_steam->add_modification_stat(stat, new_value);
|
||||
}
|
||||
|
||||
g_steam->launch_app(app);
|
||||
g_steam->commit_changes();
|
||||
g_steam->quit_game();
|
||||
}
|
||||
|
||||
if (result.count("launch") > 0)
|
||||
{
|
||||
if (app == 0)
|
||||
|
|
|
|||
|
|
@ -113,4 +113,34 @@ void escape_html(std::string& data) {
|
|||
|
||||
int zenity(const std::string text, const std::string type) {
|
||||
return system( std::string("zenity " + type + " --text=\"" + text + "\" 2> /dev/null").c_str() );
|
||||
}
|
||||
|
||||
bool convert_user_stat_value(UserStatType type, std::string buf, std::any* new_value) {
|
||||
bool valid_conversion;
|
||||
size_t idx = 0;
|
||||
|
||||
try {
|
||||
// Cast these in their native steam implementation so
|
||||
// we don't truncate any user input precision
|
||||
//
|
||||
// We could also plumb in input validation for the
|
||||
// min/max/incremental values here
|
||||
if (type == UserStatType::Integer) {
|
||||
*new_value = std::stoi(buf, &idx);
|
||||
} else if (type == UserStatType::Float) {
|
||||
*new_value = std::stof(buf, &idx);
|
||||
} else {
|
||||
valid_conversion = false;
|
||||
}
|
||||
} catch(std::exception& e) {
|
||||
// Invalid user input, but this is not fatal to the program
|
||||
valid_conversion = false;
|
||||
}
|
||||
|
||||
// If the whole string hasn't been consumed, treat it as invalid
|
||||
if (buf[idx] != '\0') {
|
||||
valid_conversion = false;
|
||||
}
|
||||
|
||||
return valid_conversion;
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <any>
|
||||
#include "../globals.h"
|
||||
#include "../../steam/steam_api.h"
|
||||
#include "../types/UserStatType.h"
|
||||
|
||||
/**
|
||||
* Stats hold a value of type any, but can be either int or float.
|
||||
|
|
@ -69,6 +71,13 @@ void escape_html(std::string& data);
|
|||
|
||||
/**
|
||||
* Show a regular dialog box. Return value is ignored for now,
|
||||
* but feel free to add functionnlitie to this
|
||||
* but feel free to add functionnlity to this
|
||||
*/
|
||||
int zenity(const std::string text = "An internal error occurred, please open a Github issue with the console output to get it fixed!", const std::string type = "--error --no-wrap");
|
||||
int zenity(const std::string text = "An internal error occurred, please open a Github issue with the console output to get it fixed!", const std::string type = "--error --no-wrap");
|
||||
|
||||
/**
|
||||
* Convert a user stat value string buffer to the specified stat type
|
||||
* Directly modifies new_value
|
||||
* Returns whether the conversion was successful
|
||||
*/
|
||||
bool convert_user_stat_value(UserStatType type, std::string buf, std::any* new_value);
|
||||
|
|
@ -197,10 +197,10 @@ MySteam::refresh_achievements_and_stats() {
|
|||
* Adds an achievement to the list of achievements to unlock/lock
|
||||
*/
|
||||
void
|
||||
MySteam::add_modification_ach(const std::string& ach_id, const bool& new_value) {
|
||||
std::cout << "Adding modification: " << ach_id << ", " << (new_value ? "to unlock" : "to relock") << std::endl;
|
||||
MySteam::add_modification_ach(const std::string& ach_id, bool new_value) {
|
||||
std::cout << "Adding achievement modification: " << ach_id << ", " << (new_value ? "to unlock" : "to relock") << std::endl;
|
||||
if ( m_pending_ach_modifications.find(ach_id) == m_pending_ach_modifications.end() ) {
|
||||
m_pending_ach_modifications.insert( std::pair<std::string, bool>(ach_id, new_value) );
|
||||
m_pending_ach_modifications.insert( std::pair<std::string, AchievementChange_t>(ach_id, (AchievementChange_t){ach_id, new_value} ) );
|
||||
} else {
|
||||
std::cerr << "Warning: Cannot append " << ach_id << ", value already exists." << std::endl;
|
||||
}
|
||||
|
|
@ -212,7 +212,7 @@ MySteam::add_modification_ach(const std::string& ach_id, const bool& new_value)
|
|||
*/
|
||||
void
|
||||
MySteam::remove_modification_ach(const std::string& ach_id) {
|
||||
std::cout << "Removing modification: " << ach_id << std::endl;
|
||||
std::cout << "Removing achievement modification: " << ach_id << std::endl;
|
||||
if ( m_pending_ach_modifications.find(ach_id) == m_pending_ach_modifications.end() ) {
|
||||
std::cerr << "WARNING: Could not cancel: modification was not pending: " << ach_id << std::endl;
|
||||
} else {
|
||||
|
|
@ -221,6 +221,45 @@ MySteam::remove_modification_ach(const std::string& ach_id) {
|
|||
}
|
||||
// => remove_modification_ach
|
||||
|
||||
/**
|
||||
* Adds a stat modification to be done on the launched app.
|
||||
* Commit the change with commit_changes
|
||||
*/
|
||||
void
|
||||
MySteam::add_modification_stat(const StatValue_t& stat, std::any new_value) {
|
||||
// The value must already be the proper type for it to be added to the list
|
||||
std::cout << "Adding stat modification: " << stat.id << ", ";
|
||||
if (stat.type == UserStatType::Integer) {
|
||||
std::cout << "Integer " << std::to_string(std::any_cast<long long>(new_value));
|
||||
} else if (stat.type == UserStatType::Float) {
|
||||
std::cout << "Float " << std::to_string(std::any_cast<double>(new_value));
|
||||
} else {
|
||||
// Input has already been checked in StatBoxRow
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
if ( m_pending_stat_modifications.find(stat.id) == m_pending_stat_modifications.end() ) {
|
||||
m_pending_stat_modifications.insert( std::pair<std::string, StatChange_t>(stat.id, (StatChange_t){stat.type, stat.id, new_value} ) );
|
||||
} else {
|
||||
std::cerr << "Warning: Cannot append " << stat.id << ", value already exists." << std::endl;
|
||||
}
|
||||
}
|
||||
// => add_modification_stat
|
||||
|
||||
/**
|
||||
* Removes a stat modification that would have been done on the launched app.
|
||||
*/
|
||||
void
|
||||
MySteam::remove_modification_stat(const StatValue_t& stat) {
|
||||
std::cout << "Removing stat modification: " << stat.id << std::endl;
|
||||
// If there's not one pending, don't treat it as a warning currently
|
||||
// because we don't really care to differentiate the
|
||||
// 0->1 and 2->1 character length transitions over in StatBoxRow
|
||||
m_pending_stat_modifications.erase(stat.id);
|
||||
}
|
||||
// => remove_modification_stat
|
||||
|
||||
/**
|
||||
* Commit pending achievement changes
|
||||
*/
|
||||
|
|
@ -229,9 +268,10 @@ MySteam::commit_changes() {
|
|||
std::vector<AchievementChange_t> changes;
|
||||
|
||||
for ( const auto& [key, val] : m_pending_ach_modifications) {
|
||||
std::cerr << "key " << key << "val " << val << std::endl;
|
||||
changes.push_back( (AchievementChange_t){ key, val } );
|
||||
changes.push_back(val);
|
||||
}
|
||||
|
||||
//TODO: pending_stat_modifications
|
||||
|
||||
std::string response = m_ipc_socket->request_response(make_store_achivements_request_string(changes));
|
||||
|
||||
|
|
@ -241,6 +281,7 @@ MySteam::commit_changes() {
|
|||
|
||||
// Clear all pending changes
|
||||
m_pending_ach_modifications.clear();
|
||||
m_pending_stat_modifications.clear();
|
||||
}
|
||||
// => commit_changes
|
||||
|
||||
|
|
@ -248,6 +289,7 @@ void
|
|||
MySteam::clear_changes() {
|
||||
// Clear all pending changes
|
||||
m_pending_ach_modifications.clear();
|
||||
m_pending_stat_modifications.clear();
|
||||
}
|
||||
// => clear_changes
|
||||
|
||||
|
|
|
|||
|
|
@ -116,20 +116,26 @@ public:
|
|||
AppId_t get_current_appid() const { return m_app_id; };
|
||||
|
||||
/**
|
||||
* Adds a modification to be done on the launched app.
|
||||
* Adds an achievement modification to be done on the launched app.
|
||||
* Commit the change with commit_changes
|
||||
*/
|
||||
void add_modification_ach(const std::string& ach_id, const bool& new_value);
|
||||
void add_modification_ach(const std::string& ach_id, bool new_value);
|
||||
|
||||
/**
|
||||
* Adds a modification to be done on the launched app.
|
||||
* Removes an achievement modificatiothat would have been done on the launched app.
|
||||
*/
|
||||
void remove_modification_ach(const std::string& ach_id);
|
||||
|
||||
/**
|
||||
* Adds a modification to be done on the launched app.
|
||||
* Commit the change with commit_modifications.
|
||||
* Adds a stat modification to be done on the launched app.
|
||||
* Commit the change with commit_changes
|
||||
*/
|
||||
//void add_modification_stat(const std::string& stat_id, const double& new_value); // TODO: IMPLEMENT
|
||||
void add_modification_stat(const StatValue_t& stat, std::any new_value);
|
||||
|
||||
/**
|
||||
* Removes a stat modification that would have been done on the launched app.
|
||||
*/
|
||||
void remove_modification_stat(const StatValue_t& stat);
|
||||
|
||||
/**
|
||||
* Commit pending changes
|
||||
|
|
@ -171,6 +177,6 @@ private:
|
|||
std::vector<Achievement_t> m_achievements;
|
||||
std::vector<StatValue_t> m_stats;
|
||||
|
||||
std::map<std::string, bool> m_pending_ach_modifications;
|
||||
std::map<std::string, double> m_pending_stat_modifications;
|
||||
std::map<std::string, AchievementChange_t> m_pending_ach_modifications;
|
||||
std::map<std::string, StatChange_t> m_pending_stat_modifications;
|
||||
};
|
||||
|
|
@ -1,32 +1,35 @@
|
|||
#include "StatBoxRow.h"
|
||||
#include "../controller/MySteam.h"
|
||||
#include "../globals.h"
|
||||
#include "../common/functions.h"
|
||||
|
||||
#include <string>
|
||||
#include <gtkmm-3.0/gtkmm/box.h>
|
||||
#include <gtkmm-3.0/gtkmm/label.h>
|
||||
#include <gtkmm-3.0/gtkmm/entry.h>
|
||||
|
||||
StatBoxRow::StatBoxRow(const StatValue_t& data)
|
||||
: m_data(data)
|
||||
{
|
||||
std::string stat_title_text, escaped_name;
|
||||
|
||||
escaped_name = data.display_name;
|
||||
escape_html(escaped_name);
|
||||
stat_title_text = "<b>" + escaped_name + "</b>";
|
||||
|
||||
Gtk::Box* layout = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::ORIENTATION_HORIZONTAL, 0);
|
||||
Gtk::Box* title_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::ORIENTATION_VERTICAL, 0);
|
||||
Gtk::Box* type_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::ORIENTATION_VERTICAL, 0);
|
||||
Gtk::Box* values_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::ORIENTATION_VERTICAL, 0);
|
||||
Gtk::Box* new_values_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::ORIENTATION_HORIZONTAL, 0);
|
||||
Gtk::Label* title_label = Gtk::make_managed<Gtk::Label>("");
|
||||
Gtk::Label* type_label = Gtk::make_managed<Gtk::Label>("");
|
||||
Gtk::Label* cur_value_label = Gtk::make_managed<Gtk::Label>("");
|
||||
// TODO: This will be an input box
|
||||
Gtk::Label* new_value_label = Gtk::make_managed<Gtk::Label>("");
|
||||
|
||||
set_size_request(-1, 40);
|
||||
type_box->set_size_request(150, -1);
|
||||
values_box->set_size_request(150, -1);
|
||||
|
||||
m_new_value_entry.set_width_chars(10);
|
||||
m_new_value_entry.set_max_width_chars(10);
|
||||
// Bound this to some reasonable value
|
||||
m_new_value_entry.set_max_length(100);
|
||||
|
||||
if (data.type == UserStatType::Integer) {
|
||||
type_label->set_label("Type: Integer");
|
||||
// TODO: may want to bound the size of this
|
||||
|
|
@ -40,18 +43,46 @@ StatBoxRow::StatBoxRow(const StatValue_t& data)
|
|||
cur_value_label->set_label("Current value: Unknown");
|
||||
}
|
||||
|
||||
new_value_label->set_label("Placeholder");
|
||||
new_value_label->set_label("New value: ");
|
||||
|
||||
title_label->set_markup(stat_title_text);
|
||||
title_box->pack_start(*title_label, true, true, 0);
|
||||
// Left align labels
|
||||
cur_value_label->set_xalign(0);
|
||||
new_value_label->set_xalign(0);
|
||||
|
||||
title_label->set_label(data.display_name);
|
||||
title_box->pack_start(*title_label, false, true, 0);
|
||||
type_box->pack_start(*type_label, false, true, 0);
|
||||
new_values_box->pack_start(*new_value_label, true, true, 0);
|
||||
new_values_box->pack_start(m_new_value_entry, true, true, 0);
|
||||
values_box->pack_start(*cur_value_label, false, true, 0);
|
||||
values_box->pack_start(*new_value_label, false, true, 0);
|
||||
values_box->pack_start(*new_values_box, false, true, 0);
|
||||
layout->pack_start(*title_box, true, true, 0);
|
||||
layout->pack_start(*type_box, false, true, 0);
|
||||
layout->pack_start(*values_box, false, true, 0);
|
||||
|
||||
add(*layout);
|
||||
|
||||
m_new_value_entry.signal_changed().connect(sigc::mem_fun(this, &StatBoxRow::on_new_value_changed));
|
||||
}
|
||||
|
||||
StatBoxRow::~StatBoxRow() {}
|
||||
StatBoxRow::~StatBoxRow() {}
|
||||
|
||||
void
|
||||
StatBoxRow::on_new_value_changed(void) {
|
||||
// Note this is not run after a short delay of the text not being changed
|
||||
// so maybe we don't want to edit the list every single time a button is pressed.
|
||||
// The alternative is to read through all stat boxes when commit changes is
|
||||
// pressed.
|
||||
bool valid_conversion;
|
||||
std::any new_value;
|
||||
const std::string& buf = m_new_value_entry.get_text();
|
||||
|
||||
valid_conversion = convert_user_stat_value(m_data.type, buf, &new_value);
|
||||
|
||||
// Remove pending modifications with different values, if any
|
||||
g_steam->remove_modification_stat(m_data);
|
||||
|
||||
if (valid_conversion) {
|
||||
g_steam->add_modification_stat(m_data, new_value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <string>
|
||||
#include <gtkmm-3.0/gtkmm/listboxrow.h>
|
||||
#include <gtkmm-3.0/gtkmm/entry.h>
|
||||
|
||||
/**
|
||||
* This class represents a stat entry on the stats view
|
||||
|
|
@ -14,7 +15,12 @@ public:
|
|||
StatBoxRow(const StatValue_t& stat);
|
||||
virtual ~StatBoxRow();
|
||||
|
||||
/**
|
||||
* Interpret a change in the text field
|
||||
*/
|
||||
void on_new_value_changed(void);
|
||||
|
||||
private:
|
||||
StatValue_t m_data;
|
||||
// more stuff here
|
||||
Gtk::Entry m_new_value_entry;
|
||||
};
|
||||
|
|
@ -203,7 +203,6 @@ MyGameSocket::OnUserStatsReceived(UserStatsReceived_t *callback) {
|
|||
sv.display_name = cast->DisplayName;
|
||||
sv.id = cast->Id;
|
||||
sv.incrementonly = cast->IncrementOnly;
|
||||
sv.original_value = value;
|
||||
sv.value = value;
|
||||
sv.permission = cast->Permission;
|
||||
|
||||
|
|
@ -230,7 +229,6 @@ MyGameSocket::OnUserStatsReceived(UserStatsReceived_t *callback) {
|
|||
sv.display_name = cast->DisplayName;
|
||||
sv.id = cast->Id;
|
||||
sv.incrementonly = cast->IncrementOnly;
|
||||
sv.original_value = value;
|
||||
sv.value = value;
|
||||
sv.permission = cast->Permission;
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ typedef struct Achievement_t Achievement_t;
|
|||
* AchievementChange structure
|
||||
* Minimum information needed to change an
|
||||
* achievement
|
||||
* TODO add stats
|
||||
*/
|
||||
struct AchievementChange_t {
|
||||
std::string id;
|
||||
|
|
|
|||
|
|
@ -10,9 +10,20 @@ struct StatValue_t {
|
|||
std::string id;
|
||||
std::string display_name;
|
||||
std::any value;
|
||||
std::any original_value;
|
||||
bool incrementonly;
|
||||
int permission;
|
||||
};
|
||||
|
||||
typedef struct StatValue_t StatValue_t;
|
||||
|
||||
/**
|
||||
* StatChange structure
|
||||
* Minimum information needed to change a stat
|
||||
*/
|
||||
struct StatChange_t {
|
||||
UserStatType type;
|
||||
std::string id;
|
||||
std::any new_value;
|
||||
};
|
||||
|
||||
typedef struct StatChange_t StatChange_t;
|
||||
Loading…
Reference in New Issue
Block a user