diff --git a/PTA/PTA.vcxproj b/PTA/PTA.vcxproj
index 6c4afc8..c73773b 100644
--- a/PTA/PTA.vcxproj
+++ b/PTA/PTA.vcxproj
@@ -106,6 +106,7 @@
+
@@ -137,6 +138,7 @@
+
diff --git a/PTA/PTA.vcxproj.filters b/PTA/PTA.vcxproj.filters
index ab67f02..2d0a573 100644
--- a/PTA/PTA.vcxproj.filters
+++ b/PTA/PTA.vcxproj.filters
@@ -61,6 +61,9 @@
Source Files
+
+ Source Files
+
@@ -87,6 +90,9 @@
Header Files
+
+ Header Files
+
diff --git a/PTA/Resources/client.png b/PTA/Resources/client.png
new file mode 100644
index 0000000..678046c
Binary files /dev/null and b/PTA/Resources/client.png differ
diff --git a/PTA/clientmonitor.cpp b/PTA/clientmonitor.cpp
new file mode 100644
index 0000000..1e015af
--- /dev/null
+++ b/PTA/clientmonitor.cpp
@@ -0,0 +1,179 @@
+#include "clientmonitor.h"
+
+#include "pta_types.h"
+
+#include
+#include
+#include
+#include
+#include
+
+using namespace std::chrono_literals;
+
+const std::chrono::milliseconds ClientMonitor::polling_rate = 350ms;
+
+ClientMonitor::ClientMonitor(QObject* parent) : QObject(parent), m_enabled(false), m_last_whisper(QString())
+{
+ QSettings settings;
+
+ QString logpath = settings.value(PTA_CONFIG_CLIENTLOG_PATH, QString()).toString();
+
+ if (logpath.isEmpty())
+ {
+ qInfo() << "Client.txt path not set. Client features disabled.";
+ }
+ else
+ {
+ setPath(logpath);
+ }
+}
+
+ClientMonitor::~ClientMonitor()
+{
+ m_watcher.reset();
+}
+
+void ClientMonitor::setPath(QString logpath)
+{
+ if (logpath.isEmpty())
+ {
+ // If empty path, disable monitor
+ m_enabled = false;
+
+ // Clear file watcher
+ if (m_watcher)
+ {
+ m_watcher.reset();
+ }
+
+ m_logpath.clear();
+
+ return;
+ }
+
+ if (!QFileInfo::exists(logpath))
+ {
+ qWarning() << "Client log file not found at" << logpath;
+ return;
+ }
+
+ if (m_logpath == logpath)
+ {
+ // Same file
+ return;
+ }
+
+ m_logpath = logpath;
+
+ // Pre-read size
+ QFile log(m_logpath);
+
+ if (!log.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
+ qWarning() << "Cannot process client log file found at" << m_logpath;
+ return;
+ }
+
+ m_lastpos = log.size();
+
+ log.close();
+
+ m_watcher.reset(new QTimer(this));
+
+ connect(m_watcher.get(), &QTimer::timeout, this, &ClientMonitor::processLogChange);
+ m_watcher->start(polling_rate);
+
+ m_enabled = true;
+
+ qInfo() << "Client.txt set to" << m_logpath;
+}
+
+bool ClientMonitor::enabled()
+{
+ return m_enabled;
+}
+
+QString ClientMonitor::getLastWhisperer()
+{
+ return m_last_whisper;
+}
+
+void ClientMonitor::processLogLine(QString line)
+{
+ auto parts = line.splitRef("] ", QString::SkipEmptyParts);
+
+ if (parts.size() < 2)
+ {
+ // If not a game info line, skip
+ return;
+ }
+
+ // Get last part
+ auto ltxt = parts[parts.size() - 1].trimmed().toString();
+
+ if (ltxt.startsWith('@'))
+ {
+ // Whisper
+
+ // Remove whisper tags
+ ltxt.remove(QRegularExpression("^@(From|To) (<\\S+> )?"));
+
+ auto msgparts = ltxt.splitRef(": ", QString::SkipEmptyParts);
+
+ if (msgparts.size() < 1)
+ {
+ qWarning() << "Error parsing whisper text:" << ltxt;
+ return;
+ }
+
+ auto pname = msgparts[0].toString();
+
+ m_last_whisper = pname;
+ }
+}
+
+void ClientMonitor::processLogChange()
+{
+ if (!enabled())
+ {
+ // Shouldn't ever get here
+ return;
+ }
+
+ QFile log(m_logpath);
+
+ if (!log.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
+ qWarning() << "Cannot process client log file found at" << m_logpath;
+ return;
+ }
+
+ qint64 lastpos = log.size();
+
+ if (lastpos < m_lastpos)
+ {
+ // File got reset, so reset our position too
+ m_lastpos = lastpos;
+ return;
+ }
+
+ if (lastpos == m_lastpos)
+ {
+ // No change
+ return;
+ }
+
+ // Start reading from
+ log.seek(m_lastpos);
+
+ QTextStream in(&log);
+ while (!in.atEnd())
+ {
+ QString line = in.readLine();
+ processLogLine(line);
+ }
+
+ m_lastpos = lastpos;
+
+ log.close();
+}
diff --git a/PTA/clientmonitor.h b/PTA/clientmonitor.h
new file mode 100644
index 0000000..16808b0
--- /dev/null
+++ b/PTA/clientmonitor.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include
+
+#include
+
+class QTimer;
+
+class ClientMonitor : public QObject
+{
+ Q_OBJECT
+
+public:
+ ClientMonitor(QObject* parent = nullptr);
+ ~ClientMonitor();
+
+ void setPath(QString logpath);
+
+ bool enabled();
+
+ QString getLastWhisperer();
+
+private slots:
+ void processLogChange();
+ void processLogLine(QString line);
+
+private:
+ static const std::chrono::milliseconds polling_rate;
+
+ // We are FORCED to use a polling technique for Client.txt because it doesn't flush AT ALL
+ // unless the file is accessed externally. This renders functions like QFileSystemWatcher and
+ // even the WinAPI ReadDirectoryChangesW UNUSABLE for our purposes :(.
+ std::unique_ptr m_watcher;
+
+ QString m_logpath;
+
+ bool m_enabled;
+ qint64 m_lastpos = -1;
+
+ QString m_last_whisper;
+};
\ No newline at end of file
diff --git a/PTA/configdialog.cpp b/PTA/configdialog.cpp
index e28b191..c0db4d9 100644
--- a/PTA/configdialog.cpp
+++ b/PTA/configdialog.cpp
@@ -25,6 +25,7 @@ ConfigDialog::ConfigDialog(ItemAPI* api)
pagesWidget->addWidget(new HotkeyPage(results));
pagesWidget->addWidget(new PriceCheckPage(results, api));
pagesWidget->addWidget(new MacrosPage(results));
+ pagesWidget->addWidget(new ClientPage(results));
QPushButton* saveButton = new QPushButton(tr("Save"));
QPushButton* closeButton = new QPushButton(tr("Close"));
@@ -86,6 +87,12 @@ void ConfigDialog::createIcons()
macroButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
macroButton->setIcon(QIcon(":/Resources/macros.png"));
+ QListWidgetItem* clientButton = new QListWidgetItem(contentsWidget);
+ clientButton->setText(tr("Client"));
+ clientButton->setTextAlignment(Qt::AlignHCenter);
+ clientButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
+ clientButton->setIcon(QIcon(":/Resources/client.png"));
+
connect(contentsWidget, &QListWidget::currentItemChanged, this, &ConfigDialog::changePage);
}
diff --git a/PTA/configpages.cpp b/PTA/configpages.cpp
index 2b9450a..afafe31 100644
--- a/PTA/configpages.cpp
+++ b/PTA/configpages.cpp
@@ -213,7 +213,7 @@ PriceCheckPage::PriceCheckPage(json& set, ItemAPI* api, QWidget* parent) : QWidg
QStringList leagues;
- for (auto& league : api->getLeagues())
+ for (const auto& league : api->getLeagues())
{
leagues << QString::fromStdString(league.get());
}
@@ -611,7 +611,7 @@ MacrosPage::MacrosPage(json& set, QWidget* parent) : QWidget(parent)
set[PTA_CONFIG_CUSTOM_MACROS] = macrolist;
- for (auto& [k, v] : macrolist.items())
+ for (const auto& [k, v] : macrolist.items())
{
auto key = QString::fromStdString(k);
auto seq = QString::fromStdString(v["sequence"].get());
@@ -747,4 +747,56 @@ MacrosPage::MacrosPage(json& set, QWidget* parent) : QWidget(parent)
layout->addLayout(buttonLayout);
layout->addStretch(1);
setLayout(layout);
+}
+
+ClientPage::ClientPage(json& set, QWidget* parent) : QWidget(parent)
+{
+ QSettings settings;
+
+ QGroupBox* configGroup = new QGroupBox(tr("Game Client"));
+
+ // ------------------Client Log
+ QLabel* cliLabel = new QLabel(tr("Client Log location:"));
+
+ QLineEdit* fedit = new QLineEdit;
+ fedit->setText(settings.value(PTA_CONFIG_CLIENTLOG_PATH, QString()).toString());
+
+ connect(fedit, &QLineEdit::textChanged, [=, &set](const QString& text) {
+ if (!text.isEmpty())
+ {
+ set[PTA_CONFIG_CLIENTLOG_PATH] = text.toStdString();
+ }
+ });
+
+ QPushButton* browseButton = new QPushButton(tr("Browse"));
+ connect(browseButton, &QAbstractButton::clicked, [=]() {
+ QString defpath = QDir::currentPath();
+
+ QString fname = QFileDialog::getOpenFileName(this, tr("Load Client.txt"), defpath, tr("Client.txt (Client.txt)"));
+
+ if (!fname.isNull())
+ {
+ QFileInfo info(fname);
+
+ if (info.exists())
+ {
+ fedit->setText(info.absoluteFilePath());
+ }
+ }
+ });
+
+ QHBoxLayout* cliLayout = new QHBoxLayout;
+ cliLayout->addWidget(cliLabel);
+ cliLayout->addWidget(fedit);
+ cliLayout->addWidget(browseButton);
+
+ QVBoxLayout* configLayout = new QVBoxLayout;
+ configLayout->addLayout(cliLayout);
+
+ configGroup->setLayout(configLayout);
+
+ QVBoxLayout* mainLayout = new QVBoxLayout;
+ mainLayout->addWidget(configGroup);
+ mainLayout->addStretch(1);
+ setLayout(mainLayout);
}
\ No newline at end of file
diff --git a/PTA/configpages.h b/PTA/configpages.h
index 74e0f36..116e200 100644
--- a/PTA/configpages.h
+++ b/PTA/configpages.h
@@ -48,3 +48,11 @@ class MacrosPage : public QWidget
public:
MacrosPage(json& set, QWidget* parent = 0);
};
+
+class ClientPage : public QWidget
+{
+ Q_OBJECT
+
+public:
+ ClientPage(json& set, QWidget* parent = 0);
+};
diff --git a/PTA/itemapi.cpp b/PTA/itemapi.cpp
index d86e579..d91a936 100644
--- a/PTA/itemapi.cpp
+++ b/PTA/itemapi.cpp
@@ -99,14 +99,14 @@ ItemAPI::ItemAPI(QObject* parent) : QObject(parent)
json excj = json::parse(edat.toStdString());
- for (auto& e : excj["excludes"])
+ for (const auto& e : excj["excludes"])
{
c_excludes.insert(e.get());
}
}
else if (synchronizedGetJSON(QNetworkRequest(u_pta_excludes), data))
{
- for (auto& e : data["excludes"])
+ for (const auto& e : data["excludes"])
{
c_excludes.insert(e.get());
}
@@ -127,11 +127,11 @@ ItemAPI::ItemAPI(QObject* parent) : QObject(parent)
auto& stt = data["result"];
- for (auto& type : stt)
+ for (const auto& type : stt)
{
auto& el = type["entries"];
- for (auto& et : el)
+ for (const auto& et : el)
{
// Cut the key for multiline mods
std::string::size_type nl;
@@ -162,11 +162,11 @@ ItemAPI::ItemAPI(QObject* parent) : QObject(parent)
auto& itm = data["result"];
- for (auto& type : itm)
+ for (const auto& type : itm)
{
- auto& el = type["entries"];
+ const auto& el = type["entries"];
- for (auto& et : el)
+ for (const auto& et : el)
{
if (et.contains("name"))
{
@@ -213,7 +213,7 @@ ItemAPI::ItemAPI(QObject* parent) : QObject(parent)
throw std::runtime_error("Failed to download base item data");
}
- for (auto& [k, o] : data.items())
+ for (const auto& [k, o] : data.items())
{
std::string typeName = o["name"].get();
std::string itemClass = o["item_class"].get();
@@ -242,7 +242,7 @@ ItemAPI::ItemAPI(QObject* parent) : QObject(parent)
throw std::runtime_error("Failed to download mod type data");
}
- for (auto& [k, o] : data.items())
+ for (const auto& [k, o] : data.items())
{
std::string modname = o["name"].get();
std::string modtype = o["generation_type"].get();
@@ -329,14 +329,14 @@ ItemAPI::ItemAPI(QObject* parent) : QObject(parent)
json wlr = json::parse(wdat.toStdString());
- for (auto& e : wlr["data"])
+ for (const auto& e : wlr["data"])
{
c_weaponLocals.insert(e.get());
}
}
else if (synchronizedGetJSON(QNetworkRequest(u_pta_weaponlocals), data))
{
- for (auto& e : data["data"])
+ for (const auto& e : data["data"])
{
c_weaponLocals.insert(e.get());
}
@@ -357,14 +357,14 @@ ItemAPI::ItemAPI(QObject* parent) : QObject(parent)
json alr = json::parse(adat.toStdString());
- for (auto& e : alr["data"])
+ for (const auto& e : alr["data"])
{
c_armourLocals.insert(e.get());
}
}
else if (synchronizedGetJSON(QNetworkRequest(u_pta_armourlocals), data))
{
- for (auto& e : data["data"])
+ for (const auto& e : data["data"])
{
c_armourLocals.insert(e.get());
}
@@ -386,9 +386,9 @@ ItemAPI::ItemAPI(QObject* parent) : QObject(parent)
json dcr = json::parse(ddat.toStdString());
- for (auto [entry, list] : dcr.items())
+ for (const auto [entry, list] : dcr.items())
{
- for (auto value : list["unused"])
+ for (const auto value : list["unused"])
{
c_discriminators[entry].insert(value.get());
}
@@ -396,9 +396,9 @@ ItemAPI::ItemAPI(QObject* parent) : QObject(parent)
}
else if (synchronizedGetJSON(QNetworkRequest(u_pta_disc), data))
{
- for (auto [entry, list] : data.items())
+ for (const auto [entry, list] : data.items())
{
- for (auto value : list["unused"])
+ for (const auto value : list["unused"])
{
c_discriminators[entry].insert(value.get());
}
@@ -431,7 +431,7 @@ ItemAPI::ItemAPI(QObject* parent) : QObject(parent)
throw std::runtime_error("Cannot open currency.json");
}
- for (auto& [k, v] : c_currencyMap.items())
+ for (const auto& [k, v] : c_currencyMap.items())
{
c_currencyCodes.insert(v.get());
}
@@ -527,7 +527,7 @@ socket_filters_t ItemAPI::readSockets(QString prop)
ss.links = socks.length();
}
- for (auto& s : socks)
+ for (const auto& s : socks)
{
if ("R" == s)
{
@@ -1643,13 +1643,13 @@ PItem* ItemAPI::parse(QString itemText)
// Process special/pseudo rules
if (item->filters.size())
{
- for (auto [key, fil] : item->filters.items())
+ for (const auto [key, fil] : item->filters.items())
{
if (c_pseudoRules.contains(key))
{
- auto& rules = c_pseudoRules[key];
+ const auto& rules = c_pseudoRules[key];
- for (auto& r : rules)
+ for (const auto& r : rules)
{
std::string pid = r["id"].get();
@@ -1661,7 +1661,7 @@ PItem* ItemAPI::parse(QString itemText)
ps_entry["value"] = json::array();
- for (auto v : fil["value"])
+ for (const auto v : fil["value"])
{
if (v.is_number_float())
{
@@ -1749,7 +1749,7 @@ QString ItemAPI::toJson(PItem* item)
{
j["influences"] = json::array();
- for (auto i : item->f_misc.influences)
+ for (const auto i : item->f_misc.influences)
{
std::string inf = i;
inf[0] = toupper(inf[0]);
@@ -2347,7 +2347,7 @@ void ItemAPI::advancedPriceCheck(std::shared_ptr item)
if (misc.contains("influences"))
{
- for (auto [key, value] : misc["influences"].items())
+ for (const auto [key, value] : misc["influences"].items())
{
if (!key.empty() && value.get())
{
diff --git a/PTA/itemapi.h b/PTA/itemapi.h
index d99ec89..e970cbf 100644
--- a/PTA/itemapi.h
+++ b/PTA/itemapi.h
@@ -23,8 +23,8 @@ class ItemAPI : public QObject
public:
ItemAPI(QObject* parent = nullptr);
- json getLeagues() { return m_leagues; }
- QString getLeague();
+ const json getLeagues() { return m_leagues; }
+ QString getLeague();
PItem* parse(QString itemText);
@@ -35,8 +35,8 @@ class ItemAPI : public QObject
void openWiki(std::shared_ptr item);
signals:
- void humour(QString msg);
- void priceCheckFinished(std::shared_ptr item, QString results);
+ void humour(const QString& msg);
+ void priceCheckFinished(std::shared_ptr item, const QString& results);
private:
int readPropInt(QString prop);
diff --git a/PTA/macrohandler.cpp b/PTA/macrohandler.cpp
index fddd581..e4f768a 100644
--- a/PTA/macrohandler.cpp
+++ b/PTA/macrohandler.cpp
@@ -1,16 +1,23 @@
#include "macrohandler.h"
+#include "clientmonitor.h"
#include "pta_types.h"
#include "putil.h"
#include
#include
#include
+#include
#include
#include
-MacroHandler::MacroHandler(QObject* parent) : QObject(parent)
+using namespace std::placeholders;
+
+MacroHandler::MacroHandler(ClientMonitor* client, QObject* parent) : QObject(parent), m_client(client)
{
+ // Setup variables
+ m_variables.insert("last_whisper", std::bind(&ClientMonitor::getLastWhisperer, m_client));
+
QSettings settings;
auto macstr = settings.value(PTA_CONFIG_CUSTOM_MACROS).toString().toStdString();
@@ -29,7 +36,7 @@ void MacroHandler::setMacros(json macrolist)
m_macrolist = macrolist;
- for (auto& [k, v] : macrolist.items())
+ for (const auto& [k, v] : macrolist.items())
{
auto key = QString::fromStdString(k);
auto seq = QString::fromStdString(v["sequence"].get());
@@ -91,6 +98,50 @@ void MacroHandler::insertChatCommand(std::vector& keystrokes, std::string
insertKeyPress(keystrokes, VK_RETURN);
}
+bool MacroHandler::processChatCommand(std::string& command)
+{
+ QString qcmd = QString::fromStdString(command);
+
+ QRegularExpression re("!(\\w+)!");
+ QRegularExpressionMatch match = re.match(qcmd);
+
+ if (match.hasMatch())
+ {
+ if (!m_client->enabled())
+ {
+ qWarning() << "Client features unavailable. Please set Client.txt path in settings to enable.";
+ emit humour(tr("Client features unavailable. Please set Client.txt path in settings to enable."));
+
+ return false;
+ }
+
+ QString var = match.captured(1);
+
+ if (!m_variables.contains(var))
+ {
+ qWarning() << "Command variable" << var << "not found.";
+ emit humour(tr("Command variable not found. See log for more details."));
+
+ return false;
+ }
+
+ QString getvar = m_variables[var]();
+
+ if (getvar.isEmpty())
+ {
+ qWarning() << "Failed to retrieve variable" << var << ". Variable returned empty string.";
+ emit humour(tr("Failed to retrieve variable. See log for more details."));
+ return false;
+ }
+
+ qcmd.replace(re, getvar);
+ }
+
+ command = qcmd.toStdString();
+
+ return true;
+}
+
void MacroHandler::sendChatCommand(std::string command)
{
std::vector keystroke;
@@ -132,7 +183,10 @@ void MacroHandler::handleMacro(QString key)
{
case MACRO_TYPE_CHAT:
{
- sendChatCommand(command);
+ if (processChatCommand(command))
+ {
+ sendChatCommand(command);
+ }
break;
}
diff --git a/PTA/macrohandler.h b/PTA/macrohandler.h
index 212108a..591ff2f 100644
--- a/PTA/macrohandler.h
+++ b/PTA/macrohandler.h
@@ -2,33 +2,45 @@
#include
+#include
+
#include
#include
using json = nlohmann::json;
+class ClientMonitor;
+
class MacroHandler : public QObject
{
Q_OBJECT
public:
- MacroHandler(QObject* parent = nullptr);
+ MacroHandler(ClientMonitor* client, QObject* parent = nullptr);
void setMacros(json macrolist);
void clearMacros();
+signals:
+ void humour(const QString& msg);
+
public slots:
void handleForegroundChange(bool isPoe);
private:
void insertKeyPress(std::vector& keystrokes, WORD key);
void insertChatCommand(std::vector& keystrokes, std::string command);
+ bool processChatCommand(std::string& command);
void sendChatCommand(std::string command);
private slots:
void handleMacro(QString key);
private:
+ QMap> m_variables;
+
+ ClientMonitor* m_client;
+
json m_macrolist;
std::vector> m_macros;
};
\ No newline at end of file
diff --git a/PTA/pta.cpp b/PTA/pta.cpp
index a94d504..d701b4f 100644
--- a/PTA/pta.cpp
+++ b/PTA/pta.cpp
@@ -25,7 +25,12 @@ namespace
}
PTA::PTA(LogWindow* log, QWidget* parent) :
- QMainWindow(parent), m_logWindow(log), m_inputhandler(this), m_macrohandler(this), m_netmanager(new QNetworkAccessManager(this))
+ QMainWindow(parent),
+ m_logWindow(log),
+ m_inputhandler(this),
+ m_clientmonitor(this),
+ m_macrohandler(&m_clientmonitor, this),
+ m_netmanager(new QNetworkAccessManager(this))
{
if (nullptr == m_logWindow)
{
@@ -110,7 +115,7 @@ void PTA::showToolTip(QString message)
QToolTip::showText(QCursor::pos() + QPoint(5, 20), message);
}
-void PTA::showPriceResults(std::shared_ptr item, QString results)
+void PTA::showPriceResults(std::shared_ptr item, const QString& results)
{
#ifndef NDEBUG
qDebug() << "Prices copied to clipboard";
@@ -189,9 +194,12 @@ void PTA::createActions()
void PTA::setupFunctionality()
{
+ // UI
connect(m_api, &ItemAPI::humour, this, &PTA::showToolTip);
connect(m_api, &ItemAPI::priceCheckFinished, this, &PTA::showPriceResults);
+ connect(&m_macrohandler, &MacroHandler::humour, this, &PTA::showToolTip);
+
// Hotkeys
QSettings settings;
@@ -322,7 +330,7 @@ void PTA::saveSettings(int result)
QSettings settings;
- for (auto& [k, v] : results.items())
+ for (const auto& [k, v] : results.items())
{
if (v.is_boolean())
{
@@ -455,6 +463,13 @@ void PTA::saveSettings(int result)
m_macrohandler.setMacros(v);
}
+
+ if (k == PTA_CONFIG_CLIENTLOG_PATH)
+ {
+ QString logpath = QString::fromStdString(v.get());
+
+ m_clientmonitor.setPath(logpath);
+ }
}
}
diff --git a/PTA/pta.h b/PTA/pta.h
index 99866bc..548a82a 100644
--- a/PTA/pta.h
+++ b/PTA/pta.h
@@ -2,6 +2,7 @@
#include "ui_pta.h"
+#include "clientmonitor.h"
#include "macrohandler.h"
#include
@@ -55,7 +56,7 @@ class PTA : public QMainWindow
public slots:
void showToolTip(QString message);
- void showPriceResults(std::shared_ptr item, QString results);
+ void showPriceResults(std::shared_ptr item, const QString& results);
protected:
virtual void closeEvent(QCloseEvent* event) override;
@@ -107,6 +108,9 @@ private slots:
std::unique_ptr m_advancedKey;
std::unique_ptr m_wikiKey;
+ // Client Monitor
+ ClientMonitor m_clientmonitor;
+
// Macros
MacroHandler m_macrohandler;
diff --git a/PTA/pta.qrc b/PTA/pta.qrc
index 1f7354f..106f4e2 100644
--- a/PTA/pta.qrc
+++ b/PTA/pta.qrc
@@ -9,5 +9,6 @@
Resources/splash.png
Resources/logo.ico
Resources/macros.png
+ Resources/client.png
diff --git a/PTA/pta_types.h b/PTA/pta_types.h
index 4befbb7..acbbd2f 100644
--- a/PTA/pta_types.h
+++ b/PTA/pta_types.h
@@ -44,6 +44,8 @@ constexpr auto PTA_CONFIG_PREFILL_BASE = "pricecheck/prefillbase";
constexpr auto PTA_CONFIG_CUSTOM_MACROS = "macro/list";
+constexpr auto PTA_CONFIG_CLIENTLOG_PATH = "client/path";
+
// defaults
constexpr auto PTA_CONFIG_DEFAULT_PRICE_TEMPLATE = "templates/price/index.html";
constexpr auto PTA_CONFIG_DEFAULT_TEMPLATE_WIDTH = 600;
diff --git a/PTA/statdialog.cpp b/PTA/statdialog.cpp
index 7c08537..c3d52e7 100644
--- a/PTA/statdialog.cpp
+++ b/PTA/statdialog.cpp
@@ -255,7 +255,7 @@ StatDialog::StatDialog(PItem* item)
if (!item->f_misc.influences.empty())
{
- for (auto i : item->f_misc.influences)
+ for (const auto i : item->f_misc.influences)
{
QString influence = QString::fromStdString(i);
QString influcap = influence;