commit
ddfafb89b7
@ -0,0 +1,76 @@ |
|||||||
|
# This file is used to ignore files which are generated |
||||||
|
# ---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
*~ |
||||||
|
*.autosave |
||||||
|
*.a |
||||||
|
*.core |
||||||
|
*.moc |
||||||
|
*.o |
||||||
|
*.obj |
||||||
|
*.orig |
||||||
|
*.rej |
||||||
|
*.so |
||||||
|
*.so.* |
||||||
|
*_pch.h.cpp |
||||||
|
*_resource.rc |
||||||
|
*.qm |
||||||
|
.#* |
||||||
|
*.*# |
||||||
|
core |
||||||
|
!core/ |
||||||
|
tags |
||||||
|
.DS_Store |
||||||
|
.directory |
||||||
|
*.debug |
||||||
|
Makefile* |
||||||
|
*.prl |
||||||
|
*.app |
||||||
|
moc_*.cpp |
||||||
|
ui_*.h |
||||||
|
qrc_*.cpp |
||||||
|
Thumbs.db |
||||||
|
*.res |
||||||
|
*.rc |
||||||
|
/.qmake.cache |
||||||
|
/.qmake.stash |
||||||
|
|
||||||
|
# qtcreator generated files |
||||||
|
*.pro.user* |
||||||
|
CMakeLists.txt.user* |
||||||
|
|
||||||
|
# xemacs temporary files |
||||||
|
*.flc |
||||||
|
|
||||||
|
# Vim temporary files |
||||||
|
.*.swp |
||||||
|
|
||||||
|
# Visual Studio generated files |
||||||
|
*.ib_pdb_index |
||||||
|
*.idb |
||||||
|
*.ilk |
||||||
|
*.pdb |
||||||
|
*.sln |
||||||
|
*.suo |
||||||
|
*.vcproj |
||||||
|
*vcproj.*.*.user |
||||||
|
*.ncb |
||||||
|
*.sdf |
||||||
|
*.opensdf |
||||||
|
*.vcxproj |
||||||
|
*vcxproj.* |
||||||
|
|
||||||
|
# MinGW generated files |
||||||
|
*.Debug |
||||||
|
*.Release |
||||||
|
|
||||||
|
# Python byte code |
||||||
|
*.pyc |
||||||
|
|
||||||
|
# Binaries |
||||||
|
# -------- |
||||||
|
*.dll |
||||||
|
*.exe |
||||||
|
|
||||||
|
# Build directories |
||||||
|
build* |
@ -0,0 +1,136 @@ |
|||||||
|
cmake_minimum_required(VERSION 3.14) |
||||||
|
|
||||||
|
project(EuroFuckSimulator LANGUAGES CXX) |
||||||
|
|
||||||
|
set(CMAKE_AUTOUIC ON) |
||||||
|
set(CMAKE_AUTOMOC ON) |
||||||
|
set(CMAKE_AUTORCC ON) |
||||||
|
set(CMAKE_CXX_STANDARD 17) |
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON) |
||||||
|
|
||||||
|
#add_compile_options(-fsanitize=address) |
||||||
|
#add_link_options(-fsanitize=address) |
||||||
|
|
||||||
|
include(ExternalProject) |
||||||
|
include(FetchContent) |
||||||
|
|
||||||
|
#if (OPT_MINGW_STD_THREADS) |
||||||
|
# FetchContent_Declare( |
||||||
|
# mingw-std-threads |
||||||
|
# GIT_REPOSITORY https://github.com/meganz/mingw-std-threads.git |
||||||
|
# GIT_TAG master |
||||||
|
# ) |
||||||
|
# FetchContent_MakeAvailable(mingw-std-threads) |
||||||
|
#endif() |
||||||
|
|
||||||
|
if (OPT_QT_JSON_SETTINGS) |
||||||
|
FetchContent_Declare( |
||||||
|
QtJsonSettings |
||||||
|
GIT_REPOSITORY https://github.com/jimj316/QtJsonSettings.git |
||||||
|
GIT_TAG main |
||||||
|
) |
||||||
|
FetchContent_MakeAvailable(QtJsonSettings) |
||||||
|
add_compile_definitions(WITH_QT_JSON_SETTINGS) |
||||||
|
endif() |
||||||
|
|
||||||
|
find_package(ixwebsocket CONFIG REQUIRED) |
||||||
|
find_package(nlohmann_json 3.2.0 REQUIRED) |
||||||
|
find_package(ZLIB REQUIRED) |
||||||
|
find_package(Threads REQUIRED) |
||||||
|
|
||||||
|
file(GLOB BUTTPLUG_SRC_FILES |
||||||
|
"libraries/buttplugCpp/src/*.cpp" |
||||||
|
"libraries/buttplugCpp/include/*.h" |
||||||
|
) |
||||||
|
|
||||||
|
file(GLOB_RECURSE SCS_SDK_FILES |
||||||
|
"libraries/scs-sdk/include/*.h" |
||||||
|
) |
||||||
|
|
||||||
|
#file(GLOB ACCORDION_FILES |
||||||
|
# "libraries/accordion/src/*" |
||||||
|
#) |
||||||
|
|
||||||
|
find_package(QT NAMES Qt5 REQUIRED COMPONENTS Core Gui Widgets Charts LinguistTools) |
||||||
|
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Gui Widgets Charts LinguistTools) |
||||||
|
|
||||||
|
set(TS_FILES EuroFuckSimulator_en_GB.ts) |
||||||
|
|
||||||
|
include_directories( |
||||||
|
# libraries/accordion/src/ |
||||||
|
libraries/buttplugCpp/include/ |
||||||
|
libraries/scs-sdk/include/ |
||||||
|
src/ |
||||||
|
) |
||||||
|
|
||||||
|
if (OPT_QT_JSON_SETTINGS) |
||||||
|
include_directories( |
||||||
|
${QtJsonSettings_SOURCE_DIR} |
||||||
|
) |
||||||
|
endif() |
||||||
|
|
||||||
|
set (SOURCE_FILES |
||||||
|
src/EuroFuckSimulator_global.h |
||||||
|
src/eurofucksimulator.cpp |
||||||
|
src/eurofucksimulator.h |
||||||
|
src/telemetry.h |
||||||
|
src/qapphost.h |
||||||
|
src/qapphost.cpp |
||||||
|
src/rulemodule.h |
||||||
|
src/rulemodule.cpp |
||||||
|
|
||||||
|
src/gui/gui.h |
||||||
|
src/gui/gui.cpp |
||||||
|
src/gui/gui.ui |
||||||
|
src/gui/fancygraph.h |
||||||
|
src/gui/fancygraph.cpp |
||||||
|
src/gui/fancyslider.h |
||||||
|
src/gui/fancyslider.cpp |
||||||
|
) |
||||||
|
|
||||||
|
if (OPT_BUILD_TEST_EXE) |
||||||
|
message("Building test executable.") |
||||||
|
add_executable(EuroFuckSimulator |
||||||
|
src/main.cpp |
||||||
|
${SOURCE_FILES} |
||||||
|
|
||||||
|
${TS_FILES} |
||||||
|
${BUTTPLUG_SRC_FILES} |
||||||
|
${SCS_SDK_FILES} |
||||||
|
# ${ACCORDION_FILES} |
||||||
|
) |
||||||
|
else() |
||||||
|
add_library(EuroFuckSimulator SHARED |
||||||
|
src/telemeter.cpp |
||||||
|
${SOURCE_FILES} |
||||||
|
|
||||||
|
${TS_FILES} |
||||||
|
${BUTTPLUG_SRC_FILES} |
||||||
|
${SCS_SDK_FILES} |
||||||
|
# ${ACCORDION_FILES} |
||||||
|
) |
||||||
|
endif() |
||||||
|
|
||||||
|
target_link_libraries(EuroFuckSimulator PRIVATE |
||||||
|
Qt${QT_VERSION_MAJOR}::Core |
||||||
|
Qt${QT_VERSION_MAJOR}::Gui |
||||||
|
Qt${QT_VERSION_MAJOR}::Widgets |
||||||
|
Qt${QT_VERSION_MAJOR}::Charts |
||||||
|
ixwebsocket::ixwebsocket |
||||||
|
nlohmann_json::nlohmann_json |
||||||
|
|
||||||
|
) |
||||||
|
|
||||||
|
if (OPT_QT_JSON_SETTINGS) |
||||||
|
target_link_libraries(EuroFuckSimulator PRIVATE |
||||||
|
QtJsonSettings |
||||||
|
) |
||||||
|
endif() |
||||||
|
|
||||||
|
target_compile_definitions(EuroFuckSimulator PRIVATE EUROFUCKSIMULATOR_LIBRARY) |
||||||
|
|
||||||
|
if(COMMAND qt_create_translation) |
||||||
|
qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES}) |
||||||
|
else() |
||||||
|
qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES}) |
||||||
|
endif() |
@ -0,0 +1,3 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<!DOCTYPE TS> |
||||||
|
<TS version="2.1" language="en_GB"></TS> |
@ -0,0 +1,12 @@ |
|||||||
|
#ifndef EUROFUCKSIMULATOR_GLOBAL_H |
||||||
|
#define EUROFUCKSIMULATOR_GLOBAL_H |
||||||
|
|
||||||
|
#include <QtCore/qglobal.h> |
||||||
|
|
||||||
|
#if defined(EUROFUCKSIMULATOR_LIBRARY) |
||||||
|
# define EUROFUCKSIMULATOR_EXPORT Q_DECL_EXPORT |
||||||
|
#else |
||||||
|
# define EUROFUCKSIMULATOR_EXPORT Q_DECL_IMPORT |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif // EUROFUCKSIMULATOR_GLOBAL_H
|
@ -0,0 +1,16 @@ |
|||||||
|
#include "eurofucksimulator.h" |
||||||
|
|
||||||
|
#include <iostream> |
||||||
|
#include <QDebug> |
||||||
|
#include <QApplication> |
||||||
|
|
||||||
|
#define _USE_MATH_DEFINES |
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
EuroFuckSimulator::EuroFuckSimulator() |
||||||
|
:QObject() |
||||||
|
{ |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
@ -0,0 +1,53 @@ |
|||||||
|
#ifndef EUROFUCKSIMULATOR_H |
||||||
|
#define EUROFUCKSIMULATOR_H |
||||||
|
|
||||||
|
#include "EuroFuckSimulator_global.h" |
||||||
|
|
||||||
|
#include <functional> |
||||||
|
|
||||||
|
#include "telemetry.h" |
||||||
|
#include "gui/gui.h" |
||||||
|
|
||||||
|
class EUROFUCKSIMULATOR_EXPORT EuroFuckSimulator: public QObject |
||||||
|
{ |
||||||
|
Q_OBJECT; |
||||||
|
private: |
||||||
|
|
||||||
|
Gui* gui; |
||||||
|
|
||||||
|
|
||||||
|
// speed module
|
||||||
|
double speedSineX = 0.0; |
||||||
|
double speedMaxMPH = 70.0; |
||||||
|
double speedWaveFreq = 2.5; |
||||||
|
double speedWaveAmp = 0.25; |
||||||
|
double speedWeight = 0.75; |
||||||
|
|
||||||
|
// rpm module
|
||||||
|
double rpmRate = 1500.0; // RPM for full power
|
||||||
|
double rpmWeight = 0.25; |
||||||
|
|
||||||
|
// punishment module
|
||||||
|
double punishmentTimeRate = 1.0 / 500.0; // punishment time (seconds) per euro fined
|
||||||
|
double punishmentMultiplier = 0.1; //
|
||||||
|
|
||||||
|
// job reward module
|
||||||
|
double rewardTimeRate = 30.0 / 1500.0; // reward time (seconds) per euro earned
|
||||||
|
|
||||||
|
// distance reward module
|
||||||
|
// double
|
||||||
|
|
||||||
|
// crash module?
|
||||||
|
double crashTime = 0.5; // time the motor runs at full power after a crash
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
public: |
||||||
|
EuroFuckSimulator(); |
||||||
|
~EuroFuckSimulator() = default; |
||||||
|
|
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
#endif // EUROFUCKSIMULATOR_H
|
@ -0,0 +1,79 @@ |
|||||||
|
#include "fancygraph.h" |
||||||
|
#include "qevent.h" |
||||||
|
|
||||||
|
#include <QTimer> |
||||||
|
#include <QPainter> |
||||||
|
#include <QPainterPath> |
||||||
|
#include <QDateTime> |
||||||
|
#include <cmath> |
||||||
|
|
||||||
|
FancyGraph::FancyGraph(QWidget *parent) |
||||||
|
: QWidget{parent} |
||||||
|
{ |
||||||
|
timer = new QTimer(this); |
||||||
|
connect(timer, &QTimer::timeout, this, QOverload<>::of(&FancyGraph::repaint)); |
||||||
|
timer->start(1000/60); |
||||||
|
} |
||||||
|
|
||||||
|
QPointF FancyGraph::dataToScreen(QPointF dataPoint, QRectF paintZone) |
||||||
|
{ |
||||||
|
double time = QDateTime::currentMSecsSinceEpoch() / 1000.0; |
||||||
|
|
||||||
|
QPointF screenPoint; |
||||||
|
|
||||||
|
screenPoint.setX(paintZone.left() + (1-((time - dataPoint.x())/historyLength)) * paintZone.width()); |
||||||
|
screenPoint.setY(paintZone.top() + (1-dataPoint.y()) * paintZone.height()); |
||||||
|
|
||||||
|
return screenPoint; |
||||||
|
} |
||||||
|
|
||||||
|
void FancyGraph::paintEvent(QPaintEvent* event) |
||||||
|
{ |
||||||
|
QRectF paintZone = rect().adjusted(6,6,-6,-6); |
||||||
|
QColor colour = QColor(217,0,74); |
||||||
|
|
||||||
|
QPainter painter(this); |
||||||
|
painter.setRenderHint(QPainter::Antialiasing); |
||||||
|
// painter.setRenderHint(QPainter::HighQualityAntialiasing);
|
||||||
|
painter.setClipRegion(event->region()); |
||||||
|
// painter.begin(this);/
|
||||||
|
|
||||||
|
double time = QDateTime::currentMSecsSinceEpoch() / 1000.0; |
||||||
|
|
||||||
|
// trim data points from too far in the past
|
||||||
|
while (!points.empty() && points.first().x() < time - (historyLength * 1.1)) |
||||||
|
{ |
||||||
|
points.removeFirst(); |
||||||
|
} |
||||||
|
|
||||||
|
if (!points.empty()) |
||||||
|
{ |
||||||
|
QPainterPath path; |
||||||
|
path.moveTo(dataToScreen(points.first(), paintZone)); |
||||||
|
for (QPointF point: points) |
||||||
|
{ |
||||||
|
path.lineTo(dataToScreen(point, paintZone)); |
||||||
|
} |
||||||
|
|
||||||
|
// path.addEllipse(end, 3, 3);
|
||||||
|
|
||||||
|
painter.setPen(QPen(QBrush(colour), 2.5, Qt::PenStyle::SolidLine, Qt::PenCapStyle::RoundCap, Qt::RoundJoin)); |
||||||
|
painter.drawPath(path); |
||||||
|
|
||||||
|
QPointF end = dataToScreen(points.last(), paintZone); |
||||||
|
end.setX(paintZone.right()); |
||||||
|
painter.setBrush(QBrush(colour)); |
||||||
|
painter.drawEllipse(end, 3, 3); |
||||||
|
} |
||||||
|
|
||||||
|
// painter.end();
|
||||||
|
} |
||||||
|
|
||||||
|
void FancyGraph::addPoint(double value) |
||||||
|
{ |
||||||
|
if (!std::isnan(value)) |
||||||
|
{ |
||||||
|
QPointF point(QDateTime::currentMSecsSinceEpoch() / 1000.0, value); |
||||||
|
points.append(point); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
#ifndef FANCYGRAPH_H |
||||||
|
#define FANCYGRAPH_H |
||||||
|
|
||||||
|
#include <QObject> |
||||||
|
#include <QWidget> |
||||||
|
#include <QTimer> |
||||||
|
#include <QPainter> |
||||||
|
#include <QPointF> |
||||||
|
|
||||||
|
class FancyGraph : public QWidget |
||||||
|
{ |
||||||
|
Q_OBJECT |
||||||
|
|
||||||
|
private: |
||||||
|
QTimer* timer; |
||||||
|
|
||||||
|
QList<QPointF> points; |
||||||
|
|
||||||
|
double historyLength = 10.0; // secs
|
||||||
|
|
||||||
|
QPointF dataToScreen(QPointF dataPoint, QRectF paintZone); |
||||||
|
public: |
||||||
|
explicit FancyGraph(QWidget *parent = nullptr); |
||||||
|
|
||||||
|
void paintEvent(QPaintEvent *event) override; |
||||||
|
|
||||||
|
public slots: |
||||||
|
void addPoint(double value); |
||||||
|
|
||||||
|
signals: |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
#endif // FANCYGRAPH_H
|
@ -0,0 +1,56 @@ |
|||||||
|
#include "fancyslider.h" |
||||||
|
#include "qboxlayout.h" |
||||||
|
|
||||||
|
#include <cmath> |
||||||
|
|
||||||
|
FancySlider::FancySlider(QWidget *parent) |
||||||
|
: QWidget{parent} |
||||||
|
{ |
||||||
|
QHBoxLayout* hbox = new QHBoxLayout(this); |
||||||
|
hbox->setMargin(0); |
||||||
|
hbox->setContentsMargins(0, 0, 0, 0); |
||||||
|
setLayout(hbox); |
||||||
|
|
||||||
|
slider = new QSlider(); |
||||||
|
slider->setOrientation(Qt::Orientation::Horizontal); |
||||||
|
slider->setMaximum(sliderResolution); |
||||||
|
|
||||||
|
box = new QDoubleSpinBox(); |
||||||
|
label = new QLabel(); |
||||||
|
|
||||||
|
layout()->addWidget(slider); |
||||||
|
layout()->addWidget(box); |
||||||
|
layout()->addWidget(label); |
||||||
|
|
||||||
|
connect(box, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &FancySlider::setValue); |
||||||
|
connect(slider, &QSlider::valueChanged, this, [&](int v) |
||||||
|
{ |
||||||
|
setValue(((v / (double)sliderResolution) * (max - min)) + min); |
||||||
|
}); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void FancySlider::setValue(double v) |
||||||
|
{ |
||||||
|
if (v != value) |
||||||
|
{ |
||||||
|
value = v; |
||||||
|
updateValues(); |
||||||
|
|
||||||
|
emit valueChanged(value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void FancySlider::updateValues() |
||||||
|
{ |
||||||
|
box->setMaximum(max); |
||||||
|
box->setMinimum(min); |
||||||
|
if (value != box->value()) |
||||||
|
box->setValue(value); |
||||||
|
|
||||||
|
slider->setValue(((value - min) / (max - min)) * sliderResolution); |
||||||
|
|
||||||
|
box->setSingleStep((max-min) / sliderResolution); |
||||||
|
box->setDecimals(std::ceil(-std::log10(box->singleStep()))); |
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
#ifndef FANCYSLIDER_H |
||||||
|
#define FANCYSLIDER_H |
||||||
|
|
||||||
|
#include <QWidget> |
||||||
|
#include <QSlider> |
||||||
|
#include <QSpinBox> |
||||||
|
#include <QLabel> |
||||||
|
|
||||||
|
class FancySlider : public QWidget |
||||||
|
{ |
||||||
|
Q_OBJECT; |
||||||
|
|
||||||
|
Q_PROPERTY(double value READ getValue WRITE setValue NOTIFY valueChanged); |
||||||
|
public: |
||||||
|
explicit FancySlider(QWidget *parent = nullptr); |
||||||
|
|
||||||
|
double getValue() {return value;} |
||||||
|
double getMin() {return min;} |
||||||
|
double getMax() {return max;} |
||||||
|
|
||||||
|
private: |
||||||
|
QSlider* slider; |
||||||
|
QDoubleSpinBox* box; |
||||||
|
QLabel* label; |
||||||
|
|
||||||
|
void updateValues(); |
||||||
|
|
||||||
|
double value; |
||||||
|
double min = 0.0; |
||||||
|
double max = 100.0; |
||||||
|
|
||||||
|
int sliderResolution = 1000.0; |
||||||
|
|
||||||
|
public slots: |
||||||
|
Q_SLOT void setValue(double value); |
||||||
|
void setMin(double newMin) {min = newMin; updateValues();} |
||||||
|
void setMax(double newMax) {max = newMax; updateValues();} |
||||||
|
|
||||||
|
signals: |
||||||
|
void valueChanged(double value); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
#endif // FANCYSLIDER_H
|
@ -0,0 +1,348 @@ |
|||||||
|
#include "gui.h" |
||||||
|
#include "qdebug.h" |
||||||
|
#include "ui_gui.h" |
||||||
|
|
||||||
|
#include "gui/fancyslider.h" |
||||||
|
|
||||||
|
#include <QDateTime> |
||||||
|
#include <QScreen> |
||||||
|
|
||||||
|
Gui::Gui(QWidget *parent) : |
||||||
|
QMainWindow(parent), |
||||||
|
ui(new Ui::Gui) |
||||||
|
{ |
||||||
|
ui->setupUi(this); |
||||||
|
|
||||||
|
QScreen *screen = QGuiApplication::primaryScreen(); |
||||||
|
QRect screenGeometry = screen->geometry(); |
||||||
|
setGeometry(screenGeometry.right() - width(), screenGeometry.bottom() - height(), width(), height()); |
||||||
|
|
||||||
|
file = new QFile("efs.log"); |
||||||
|
if (file->open(QIODevice::Truncate | QIODevice::WriteOnly)) |
||||||
|
{ |
||||||
|
stream = new QTextStream(file); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
throw new std::runtime_error("Failed to open log file."); |
||||||
|
} |
||||||
|
|
||||||
|
logLine("Starting up!"); |
||||||
|
|
||||||
|
client = new Client("ws://127.0.0.1", 12345); |
||||||
|
client->connect(callbackFunctionPtr); |
||||||
|
client->requestDeviceList(); |
||||||
|
|
||||||
|
logLine("Client started!"); |
||||||
|
|
||||||
|
logLine("Started OK!"); |
||||||
|
setWindowFlag(Qt::WindowStaysOnTopHint, true); |
||||||
|
// setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | );
|
||||||
|
|
||||||
|
modules.append(&speedMod); |
||||||
|
connect(ui->speedEnabledBox, &QCheckBox::stateChanged, &speedMod, &SpeedModule::setEnabled); |
||||||
|
connect(&speedMod, &SpeedModule::EnabledChanged, ui->speedEnabledBox, &QCheckBox::setChecked); |
||||||
|
connect(ui->speedTopSlider, &FancySlider::valueChanged, &speedMod, &SpeedModule::setTopSpeed); |
||||||
|
connect(&speedMod, &SpeedModule::TopSpeedChanged, ui->speedTopSlider, &FancySlider::setValue); |
||||||
|
ui->speedTopSlider->setMax(100.0); |
||||||
|
connect(ui->speedBumpFreqSlider, &FancySlider::valueChanged, &speedMod, &SpeedModule::setWaveFrequency); |
||||||
|
connect(ui->speedBumpFreqSlider, &FancySlider::valueChanged, this, [](double v){qDebug() << "Wave freq " << v;}); |
||||||
|
connect(&speedMod, &SpeedModule::WaveFrequencyChanged, ui->speedBumpFreqSlider, &FancySlider::setValue); |
||||||
|
ui->speedBumpFreqSlider->setMax(5.0); |
||||||
|
ui->speedBumpFreqSlider->setMin(0.1); |
||||||
|
connect(ui->speedBumpAmpSlider, &FancySlider::valueChanged, &speedMod, &SpeedModule::setWaveAmplitude); |
||||||
|
connect(&speedMod, &SpeedModule::WaveAmplitudeChanged, ui->speedBumpAmpSlider, &FancySlider::setValue); |
||||||
|
ui->speedBumpAmpSlider->setMax(0.5); |
||||||
|
connect(ui->speedOutputPowerSlider, &FancySlider::valueChanged, &speedMod, &SpeedModule::setMaxOutput); |
||||||
|
connect(&speedMod, &SpeedModule::MaxOutputChanged, ui->speedOutputPowerSlider, &FancySlider::setValue); |
||||||
|
ui->speedOutputPowerSlider->setMax(1.0); |
||||||
|
|
||||||
|
modules.append(&rpmMod); |
||||||
|
connect(ui->rpmEnabledBox, &QCheckBox::stateChanged, &rpmMod, &RPMModule::setEnabled); |
||||||
|
connect(&rpmMod, &RPMModule::EnabledChanged, ui->rpmEnabledBox, &QCheckBox::setChecked); |
||||||
|
connect(ui->rpmTopSlider, &FancySlider::valueChanged, &rpmMod, &RPMModule::setTopRPM); |
||||||
|
connect(&rpmMod, &RPMModule::TopRPMChanged, ui->rpmTopSlider, &FancySlider::setValue); |
||||||
|
ui->rpmTopSlider->setMax(3000.0); |
||||||
|
connect(ui->rpmBottomSlider, &FancySlider::valueChanged, &rpmMod, &RPMModule::setBottomRPM); |
||||||
|
connect(&rpmMod, &RPMModule::BottomRPMChanged, ui->rpmBottomSlider, &FancySlider::setValue); |
||||||
|
ui->rpmBottomSlider->setMax(1000.0); |
||||||
|
connect(ui->rpmOutputPowerSlider, &FancySlider::valueChanged, &rpmMod, &RPMModule::setMaxOutput); |
||||||
|
connect(&rpmMod, &RPMModule::MaxOutputChanged, ui->rpmOutputPowerSlider, &FancySlider::setValue); |
||||||
|
ui->rpmOutputPowerSlider->setMax(1.0); |
||||||
|
|
||||||
|
modules.append(&crimeMod); |
||||||
|
connect(ui->crimeEnableBox, &QCheckBox::stateChanged, &crimeMod, &CrimeModule::setEnabled); |
||||||
|
connect(&crimeMod, &CrimeModule::EnabledChanged, ui->crimeEnableBox, &QCheckBox::setChecked); |
||||||
|
connect(ui->crimeRateSlider, &FancySlider::valueChanged, &crimeMod, &CrimeModule::setSecondsPerEuro); |
||||||
|
connect(&crimeMod, &CrimeModule::SecondsPerEuroChanged, ui->crimeRateSlider, &FancySlider::setValue); |
||||||
|
ui->crimeRateSlider->setMax(100.0); |
||||||
|
connect(ui->crimePowerMultiplier, &FancySlider::valueChanged, &crimeMod, &CrimeModule::setPowerMultiplier); |
||||||
|
connect(&crimeMod, &CrimeModule::PowerMultiplierChanged, ui->crimePowerMultiplier, &FancySlider::setValue); |
||||||
|
ui->crimePowerMultiplier->setMax(2.0); |
||||||
|
|
||||||
|
modules.append(&jobMod); |
||||||
|
connect(ui->jobRewardEnableBox, &QCheckBox::stateChanged, &jobMod, &JobModule::setEnabled); |
||||||
|
connect(&jobMod, &JobModule::EnabledChanged, ui->jobRewardEnableBox, &QCheckBox::setChecked); |
||||||
|
connect(ui->jobRewardMoneyRateSlider, &FancySlider::valueChanged, &jobMod, &JobModule::setSecondsPerEuro); |
||||||
|
connect(&jobMod, &JobModule::SecondsPerEuroChanged, ui->jobRewardMoneyRateSlider, &FancySlider::setValue); |
||||||
|
ui->jobRewardMoneyRateSlider->setMax(10); |
||||||
|
connect(ui->jobRewardXPRateSlider, &FancySlider::valueChanged, &jobMod, &JobModule::setSecondsPerXP); |
||||||
|
connect(&jobMod, &JobModule::SecondsPerXPChanged, ui->jobRewardXPRateSlider, &FancySlider::setValue); |
||||||
|
ui->jobRewardXPRateSlider->setMax(30); |
||||||
|
connect(ui->jobRewardPowerBoostSlider, &FancySlider::valueChanged, &jobMod, &JobModule::setPowerBoost); |
||||||
|
connect(&jobMod, &JobModule::PowerBoostChanged, ui->jobRewardPowerBoostSlider, &FancySlider::setValue); |
||||||
|
ui->jobRewardPowerBoostSlider->setMax(1.0 ); |
||||||
|
|
||||||
|
modules.append(&distMod); |
||||||
|
connect(ui->distanceRewardEnabledBox, &QCheckBox::stateChanged, &distMod, &JobModule::setEnabled); |
||||||
|
connect(&distMod, &JobModule::EnabledChanged, ui->distanceRewardEnabledBox, &QCheckBox::setChecked); |
||||||
|
connect(ui->distanceRewardDistSlider, &FancySlider::valueChanged, &distMod, &DistanceModule::setDistance); |
||||||
|
connect(&distMod, &DistanceModule::DistanceChanged, ui->distanceRewardDistSlider, &FancySlider::setValue); |
||||||
|
ui->distanceRewardDistSlider->setMax(50); |
||||||
|
connect(ui->distanceRewardRandSlider, &FancySlider::valueChanged, &distMod, &DistanceModule::setRandomness); |
||||||
|
connect(&distMod, &DistanceModule::RandomnessChanged, ui->distanceRewardRandSlider, &FancySlider::setValue); |
||||||
|
ui->distanceRewardRandSlider->setMax(1); |
||||||
|
connect(ui->distanceRewardDurationSlider, &FancySlider::valueChanged, &distMod, &DistanceModule::setDuration); |
||||||
|
connect(&distMod, &DistanceModule::DurationChanged, ui->distanceRewardDurationSlider, &FancySlider::setValue); |
||||||
|
ui->distanceRewardDurationSlider->setMax(60.0); |
||||||
|
connect(ui->distanceRewardPowerBoostSlider, &FancySlider::valueChanged, &distMod, &DistanceModule::setPowerBoost); |
||||||
|
connect(&distMod, &DistanceModule::PowerBoostChanged, ui->distanceRewardPowerBoostSlider, &FancySlider::setValue); |
||||||
|
ui->distanceRewardPowerBoostSlider->setMax(1.0); |
||||||
|
|
||||||
|
loadSettings(); |
||||||
|
|
||||||
|
for (RuleModule* module: modules) |
||||||
|
{ |
||||||
|
connect(module, &RuleModule::statusUpdate, this, &Gui::rulesStatusUpdate); |
||||||
|
} |
||||||
|
|
||||||
|
ui->tabWidget->setCurrentIndex(0); |
||||||
|
|
||||||
|
timer = new QTimer(this); |
||||||
|
connect(timer, &QTimer::timeout, this, &Gui::update); |
||||||
|
timer->start(UPDATE_INTERVAL_MS); |
||||||
|
} |
||||||
|
|
||||||
|
Gui::~Gui() |
||||||
|
{ |
||||||
|
delete ui; |
||||||
|
} |
||||||
|
|
||||||
|
//void Gui::doubleConnect(QObject* objectA, QObject* objectB, const QMetaMethod aSignal, const QMetaMethod aSlot, const QMetaMethod bSignal, const QMetaMethod bSlot )
|
||||||
|
//{
|
||||||
|
// connect(objectA, aSignal, objectB, bSlot);
|
||||||
|
// connect(objectB, bSignal, objectA, aSlot);
|
||||||
|
//}
|
||||||
|
|
||||||
|
void Gui::loadSettings() |
||||||
|
{ |
||||||
|
|
||||||
|
QSettings settings; |
||||||
|
qDebug() << "Loading settings from " << settings.fileName(); |
||||||
|
for (RuleModule* module : modules) |
||||||
|
{ |
||||||
|
module->load(settings); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Gui::saveSettings() |
||||||
|
{ |
||||||
|
QSettings settings; |
||||||
|
for (RuleModule* module : modules) |
||||||
|
{ |
||||||
|
module->save(settings); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void Gui::shutdown() |
||||||
|
{ |
||||||
|
logLine("Shutting down!"); |
||||||
|
emit shuttingDown(); |
||||||
|
if (client->clientConnected) |
||||||
|
{ |
||||||
|
logLine("Disconnecting from client!"); |
||||||
|
client->stopAllDevices(); |
||||||
|
logLine("Devices stopped."); |
||||||
|
client->disconnect(); |
||||||
|
logLine("Client disconnected."); |
||||||
|
} |
||||||
|
|
||||||
|
stream->flush(); |
||||||
|
file->close(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void Gui::closeEvent(QCloseEvent* event) |
||||||
|
{ |
||||||
|
// event->ignore();
|
||||||
|
saveSettings(); |
||||||
|
shutdown(); |
||||||
|
QApplication::quit(); |
||||||
|
event->accept(); |
||||||
|
} |
||||||
|
|
||||||
|
void Gui::updateStatus() |
||||||
|
{ |
||||||
|
QString statusHTML = "<html><head/><body>"; |
||||||
|
QString connectionStatus = "<p>"; |
||||||
|
QString hintText = ""; |
||||||
|
if (client->clientConnected) |
||||||
|
{ |
||||||
|
connectionStatus += "Connected to Intiface!"; |
||||||
|
if (client->getDevices().size() == 0) |
||||||
|
{ |
||||||
|
connectionStatus += " No devices available."; |
||||||
|
hintText += "Put your device in pairing mode, then press 'Devices / Start Scanning' in Intiface Central."; |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
connectionStatus += "Can't connect to Intiface!"; |
||||||
|
hintText += "Make sure <a href=\"https://intiface.com/central/\">Intiface Central</a> is installed and running.<br/>If Intiface is on another device, enter the address (ws://...) on the 'Config' tab."; |
||||||
|
} |
||||||
|
connectionStatus += "</p>"; |
||||||
|
|
||||||
|
statusHTML += connectionStatus; |
||||||
|
if (!hintText.isEmpty()) |
||||||
|
{ |
||||||
|
statusHTML += "<p><em>" + hintText + "</em></p>"; |
||||||
|
} |
||||||
|
|
||||||
|
if (!rulesStatusText.isEmpty()) |
||||||
|
{ |
||||||
|
statusHTML += "<p>" + rulesStatusText + "</p>"; |
||||||
|
} |
||||||
|
|
||||||
|
statusHTML += "</body></html>"; |
||||||
|
ui->statusLabel->setText(statusHTML); |
||||||
|
} |
||||||
|
|
||||||
|
void Gui::update() |
||||||
|
{ |
||||||
|
QMutexLocker lk(&telemetryMutex); |
||||||
|
|
||||||
|
long time = latestTelemetry.timestamp; |
||||||
|
if (time == 0) |
||||||
|
{ |
||||||
|
time = QDateTime::currentMSecsSinceEpoch() * 1000; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
long deltaUS = lastTime > 0 ? time - lastTime : 0; |
||||||
|
double delta = deltaUS / (1000.0 * 1000.0); |
||||||
|
|
||||||
|
double deltaDistance = latestTelemetry.speed * delta; |
||||||
|
distanceCounter += deltaDistance; |
||||||
|
|
||||||
|
lastTime = time; |
||||||
|
|
||||||
|
TelemetryState tele; |
||||||
|
if (!paused) |
||||||
|
tele = latestTelemetry; |
||||||
|
|
||||||
|
tele.distance = distanceCounter; |
||||||
|
|
||||||
|
double power = 0.0; |
||||||
|
|
||||||
|
for (RuleModule* module: modules) |
||||||
|
{ |
||||||
|
power = module->process(tele, power, delta); |
||||||
|
} |
||||||
|
|
||||||
|
power = std::clamp(power, 0.0, 1.0); |
||||||
|
|
||||||
|
if (client->clientConnected && !paused) |
||||||
|
{ |
||||||
|
|
||||||
|
std::vector<DeviceClass> devices = client->getDevices(); |
||||||
|
for (DeviceClass device : devices) |
||||||
|
{ |
||||||
|
logLine(QString("Power to %2: %1").arg(power).arg(QString::fromStdString(device.deviceName))); |
||||||
|
client->sendScalar(device, power); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
updateStatus(); |
||||||
|
|
||||||
|
ui->distanceLabel->setText(QString("%1").arg(tele.distance / 1000.0, 0, 'f', 3)); |
||||||
|
ui->outputGraph->addPoint(power); |
||||||
|
} |
||||||
|
|
||||||
|
void Gui::setPause(bool pause) |
||||||
|
{ |
||||||
|
paused = pause; |
||||||
|
if (paused) |
||||||
|
logLine("Game paused."); |
||||||
|
else |
||||||
|
logLine("Game unpaused."); |
||||||
|
} |
||||||
|
|
||||||
|
void Gui::logLine(QString line) |
||||||
|
{ |
||||||
|
qDebug() << line; |
||||||
|
// *stream << line << "\n";
|
||||||
|
// stream->flush();
|
||||||
|
} |
||||||
|
|
||||||
|
void Gui::receiveTelemetry(TelemetryState tele) |
||||||
|
{ |
||||||
|
if (!paused) |
||||||
|
{ |
||||||
|
// logLine(QString("speed %1 rpm %2").arg(tele.speed).arg(tele.rpm));
|
||||||
|
QMutexLocker lk(&telemetryMutex); |
||||||
|
|
||||||
|
latestTelemetry = tele; |
||||||
|
|
||||||
|
ui->speedLabel->setText(QString("%1").arg(tele.speed * 3.6, 4, 'f', 1)); |
||||||
|
ui->rpmLabel->setText(QString("%1").arg(tele.rpm, 0, 'f', 0)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Gui::rulesStatusUpdate(QString status) |
||||||
|
{ |
||||||
|
rulesStatusText = status; |
||||||
|
QTimer::singleShot(1000, [this](){ rulesStatusText = ""; }); |
||||||
|
} |
||||||
|
|
||||||
|
void Gui::gameplayEvent(QString event, QMap<QString, QVariant> attributes) |
||||||
|
{ |
||||||
|
qDebug() << "Game event: " << event << ", atts: " << attributes; |
||||||
|
for (RuleModule* module: modules) |
||||||
|
{ |
||||||
|
module->gameEvent(event, attributes); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Gui::callbackFunction(const mhl::Messages msg) |
||||||
|
{ |
||||||
|
if (msg.messageType == mhl::MessageTypes::DeviceList) |
||||||
|
{ |
||||||
|
logLine("Device List callback"); |
||||||
|
} |
||||||
|
if (msg.messageType == mhl::MessageTypes::DeviceAdded) |
||||||
|
{ |
||||||
|
logLine("Device Added callback"); |
||||||
|
} |
||||||
|
if (msg.messageType == mhl::MessageTypes::ServerInfo) |
||||||
|
{ |
||||||
|
logLine("Server Info callback"); |
||||||
|
} |
||||||
|
if (msg.messageType == mhl::MessageTypes::DeviceRemoved) |
||||||
|
{ |
||||||
|
logLine("Device Removed callback"); |
||||||
|
} |
||||||
|
if (msg.messageType == mhl::MessageTypes::SensorReading) |
||||||
|
{ |
||||||
|
logLine("Sensor Reading callback"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Gui::on_jobCompleteTestButton_pressed() |
||||||
|
{ |
||||||
|
QMap<QString, QVariant> atts; |
||||||
|
atts["earned.xp"] = QVariant::fromValue(125); |
||||||
|
atts["revenue"] = QVariant::fromValue(2041); |
||||||
|
gameplayEvent("job.delivered", atts); |
||||||
|
} |
||||||
|
|
@ -0,0 +1,90 @@ |
|||||||
|
#ifndef GUI_H |
||||||
|
#define GUI_H |
||||||
|
|
||||||
|
#include <QMainWindow> |
||||||
|
#include <QFile> |
||||||
|
#include <QTextStream> |
||||||
|
#include <QTimer> |
||||||
|
#include <QMutex> |
||||||
|
#include <QThread> |
||||||
|
#include <QCloseEvent> |
||||||
|
#include <QList> |
||||||
|
#include <QMetaMethod> |
||||||
|
|
||||||
|
#include "../rulemodule.h" |
||||||
|
#include "telemetry.h" |
||||||
|
|
||||||
|
#include "../libraries/buttplugCpp/include/buttplugclient.h" |
||||||
|
|
||||||
|
const QString CANT_CONNECT_MESSAGE = "<html><head/><body><p>Can't connect to Intiface server!</p><p>Make sure that <a href=\"https://intiface.com/central/\"><span style=\" text-decoration: underline; color:#8ab4f8;\">Intiface Central</span></a> is installed and running.</p><p><span style=\" font-style:italic;\">[ If Intiface is on a different device, enter the address (ws://...) on the 'Config' tab. ]</span></p></body></html>"; |
||||||
|
|
||||||
|
namespace Ui { |
||||||
|
class Gui; |
||||||
|
} |
||||||
|
|
||||||
|
class Gui : public QMainWindow |
||||||
|
{ |
||||||
|
Q_OBJECT |
||||||
|
|
||||||
|
static const int UPDATE_INTERVAL_MS = 100; |
||||||
|
|
||||||
|
public: |
||||||
|
explicit Gui(QWidget *parent = nullptr); |
||||||
|
~Gui(); |
||||||
|
|
||||||
|
void loadSettings(); |
||||||
|
void saveSettings(); |
||||||
|
void shutdown(); |
||||||
|
void logLine(QString line); |
||||||
|
|
||||||
|
void receiveTelemetry(TelemetryState tele); |
||||||
|
void gameplayEvent(QString event, QMap<QString, QVariant> attributes); |
||||||
|
void setPause(bool pause); |
||||||
|
|
||||||
|
virtual void closeEvent(QCloseEvent* event) override; |
||||||
|
|
||||||
|
protected slots: |
||||||
|
void update(); |
||||||
|
void updateStatus(); |
||||||
|
void rulesStatusUpdate(QString status); |
||||||
|
|
||||||
|
signals: |
||||||
|
void shuttingDown(); |
||||||
|
|
||||||
|
private slots: |
||||||
|
void on_jobCompleteTestButton_pressed(); |
||||||
|
|
||||||
|
private: |
||||||
|
Ui::Gui* ui; |
||||||
|
|
||||||
|
Client* client; |
||||||
|
QFile* file; |
||||||
|
QTextStream* stream; |
||||||
|
QTimer* timer; |
||||||
|
|
||||||
|
QMutex telemetryMutex; |
||||||
|
TelemetryState latestTelemetry; |
||||||
|
double distanceCounter = 0.0; |
||||||
|
bool paused = true; |
||||||
|
|
||||||
|
scs_timestamp_t lastTime = 0; |
||||||
|
|
||||||
|
QList<RuleModule*> modules; |
||||||
|
SpeedModule speedMod; |
||||||
|
RPMModule rpmMod; |
||||||
|
CrimeModule crimeMod; |
||||||
|
JobModule jobMod; |
||||||
|
DistanceModule distMod; |
||||||
|
|
||||||
|
QString rulesStatusText; |
||||||
|
|
||||||
|
void callbackFunction(const mhl::Messages msg); |
||||||
|
std::function<void(const mhl::Messages)> callbackFunctionPtr = [this](const mhl::Messages msg){callbackFunction(msg);}; |
||||||
|
|
||||||
|
// static void doubleConnect(QObject* objectA, QObject* objectB, const QMetaMethod aSignal, const QMetaMethod aSlot, const QMetaMethod bSignal, const QMetaMethod bSlot );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
#endif // GUI_H
|
@ -0,0 +1,556 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<ui version="4.0"> |
||||||
|
<class>Gui</class> |
||||||
|
<widget class="QMainWindow" name="Gui"> |
||||||
|
<property name="geometry"> |
||||||
|
<rect> |
||||||
|
<x>0</x> |
||||||
|
<y>0</y> |
||||||
|
<width>460</width> |
||||||
|
<height>498</height> |
||||||
|
</rect> |
||||||
|
</property> |
||||||
|
<property name="windowTitle"> |
||||||
|
<string>Euro Fuck Simulator</string> |
||||||
|
</property> |
||||||
|
<widget class="QWidget" name="centralwidget"> |
||||||
|
<layout class="QVBoxLayout" name="verticalLayout"> |
||||||
|
<item> |
||||||
|
<widget class="FancyGraph" name="outputGraph" native="true"> |
||||||
|
<property name="minimumSize"> |
||||||
|
<size> |
||||||
|
<width>0</width> |
||||||
|
<height>96</height> |
||||||
|
</size> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item> |
||||||
|
<layout class="QGridLayout" name="gridLayout"> |
||||||
|
<item row="0" column="0"> |
||||||
|
<widget class="QLabel" name="label_4"> |
||||||
|
<property name="text"> |
||||||
|
<string>Speed (KPH)</string> |
||||||
|
</property> |
||||||
|
<property name="alignment"> |
||||||
|
<set>Qt::AlignCenter</set> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="0" column="1"> |
||||||
|
<widget class="QLabel" name="label_5"> |
||||||
|
<property name="text"> |
||||||
|
<string>RPM</string> |
||||||
|
</property> |
||||||
|
<property name="alignment"> |
||||||
|
<set>Qt::AlignCenter</set> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="0" column="2"> |
||||||
|
<widget class="QLabel" name="label_6"> |
||||||
|
<property name="text"> |
||||||
|
<string>Distance (KM)</string> |
||||||
|
</property> |
||||||
|
<property name="alignment"> |
||||||
|
<set>Qt::AlignCenter</set> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="1" column="0"> |
||||||
|
<widget class="QLabel" name="speedLabel"> |
||||||
|
<property name="font"> |
||||||
|
<font> |
||||||
|
<family>Monospace</family> |
||||||
|
<pointsize>14</pointsize> |
||||||
|
<bold>true</bold> |
||||||
|
</font> |
||||||
|
</property> |
||||||
|
<property name="frameShape"> |
||||||
|
<enum>QFrame::Panel</enum> |
||||||
|
</property> |
||||||
|
<property name="frameShadow"> |
||||||
|
<enum>QFrame::Raised</enum> |
||||||
|
</property> |
||||||
|
<property name="text"> |
||||||
|
<string>69.0</string> |
||||||
|
</property> |
||||||
|
<property name="alignment"> |
||||||
|
<set>Qt::AlignCenter</set> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="1" column="1"> |
||||||
|
<widget class="QLabel" name="rpmLabel"> |
||||||
|
<property name="font"> |
||||||
|
<font> |
||||||
|
<family>Monospace</family> |
||||||
|
<pointsize>14</pointsize> |
||||||
|
<bold>true</bold> |
||||||
|
</font> |
||||||
|
</property> |
||||||
|
<property name="frameShape"> |
||||||
|
<enum>QFrame::Panel</enum> |
||||||
|
</property> |
||||||
|
<property name="frameShadow"> |
||||||
|
<enum>QFrame::Raised</enum> |
||||||
|
</property> |
||||||
|
<property name="text"> |
||||||
|
<string>420</string> |
||||||
|
</property> |
||||||
|
<property name="alignment"> |
||||||
|
<set>Qt::AlignCenter</set> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="1" column="2"> |
||||||
|
<widget class="QLabel" name="distanceLabel"> |
||||||
|
<property name="font"> |
||||||
|
<font> |
||||||
|
<family>Monospace</family> |
||||||
|
<pointsize>14</pointsize> |
||||||
|
<bold>true</bold> |
||||||
|
</font> |
||||||
|
</property> |
||||||
|
<property name="frameShape"> |
||||||
|
<enum>QFrame::Panel</enum> |
||||||
|
</property> |
||||||
|
<property name="frameShadow"> |
||||||
|
<enum>QFrame::Raised</enum> |
||||||
|
</property> |
||||||
|
<property name="text"> |
||||||
|
<string>3621</string> |
||||||
|
</property> |
||||||
|
<property name="alignment"> |
||||||
|
<set>Qt::AlignCenter</set> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
</layout> |
||||||
|
</item> |
||||||
|
<item> |
||||||
|
<widget class="QTabWidget" name="tabWidget"> |
||||||
|
<property name="currentIndex"> |
||||||
|
<number>1</number> |
||||||
|
</property> |
||||||
|
<widget class="QWidget" name="tab"> |
||||||
|
<attribute name="title"> |
||||||
|
<string>Status</string> |
||||||
|
</attribute> |
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3"> |
||||||
|
<item> |
||||||
|
<widget class="QLabel" name="statusLabel"> |
||||||
|
<property name="text"> |
||||||
|
<string><html><head/><body><p><span style=" font-style:italic;">(Starting up, just a moment...)</span></p></body></html></string> |
||||||
|
</property> |
||||||
|
<property name="alignment"> |
||||||
|
<set>Qt::AlignCenter</set> |
||||||
|
</property> |
||||||
|
<property name="wordWrap"> |
||||||
|
<bool>true</bool> |
||||||
|
</property> |
||||||
|
<property name="openExternalLinks"> |
||||||
|
<bool>true</bool> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
</layout> |
||||||
|
</widget> |
||||||
|
<widget class="QWidget" name="tab_3"> |
||||||
|
<attribute name="title"> |
||||||
|
<string>Rules</string> |
||||||
|
</attribute> |
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5"> |
||||||
|
<property name="spacing"> |
||||||
|
<number>6</number> |
||||||
|
</property> |
||||||
|
<item> |
||||||
|
<widget class="QScrollArea" name="scrollArea"> |
||||||
|
<property name="widgetResizable"> |
||||||
|
<bool>true</bool> |
||||||
|
</property> |
||||||
|
<widget class="QWidget" name="scrollAreaWidgetContents"> |
||||||
|
<property name="geometry"> |
||||||
|
<rect> |
||||||
|
<x>0</x> |
||||||
|
<y>-276</y> |
||||||
|
<width>404</width> |
||||||
|
<height>525</height> |
||||||
|
</rect> |
||||||
|
</property> |
||||||
|
<layout class="QFormLayout" name="formLayout_3"> |
||||||
|
<item row="0" column="0"> |
||||||
|
<widget class="QLabel" name="label_3"> |
||||||
|
<property name="font"> |
||||||
|
<font> |
||||||
|
<bold>true</bold> |
||||||
|
</font> |
||||||
|
</property> |
||||||
|
<property name="text"> |
||||||
|
<string>Speed to Power</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="0" column="1"> |
||||||
|
<widget class="QCheckBox" name="speedEnabledBox"> |
||||||
|
<property name="toolTip"> |
||||||
|
<string><html><head/><body><p>Maps the truck's speed to output power.</p><p>Has an optional &quot;speed bump&quot; feature that wiggles up and down as you travel along.</p></body></html></string> |
||||||
|
</property> |
||||||
|
<property name="text"> |
||||||
|
<string>Enabled</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="1" column="0"> |
||||||
|
<widget class="QLabel" name="label_7"> |
||||||
|
<property name="text"> |
||||||
|
<string>Top Speed</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="1" column="1"> |
||||||
|
<widget class="FancySlider" name="speedTopSlider" native="true"> |
||||||
|
<property name="toolTip"> |
||||||
|
<string><html><head/><body><p>The speed (in KPH) needed for maximum output power.</p><p>The highest speed limit on any road is 90KPH.</p><p>Most trucks struggle to get over 80KPH with a trailer attached.</p></body></html></string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="2" column="0"> |
||||||
|
<widget class="QLabel" name="label_8"> |
||||||
|
<property name="text"> |
||||||
|
<string>Speed Bump Frequency</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="2" column="1"> |
||||||
|
<widget class="FancySlider" name="speedBumpFreqSlider" native="true"> |
||||||
|
<property name="toolTip"> |
||||||
|
<string><html><head/><body><p>The frequency of the &quot;speed bump&quot; wave at maximum speed, in Hz (cycles per second).</p><p>The waves get slower as you slow down, with the effect that the bumps happen at the same distance down the road.</p></body></html></string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="3" column="0"> |
||||||
|
<widget class="QLabel" name="label_9"> |
||||||
|
<property name="text"> |
||||||
|
<string>Speed Bump Amplitude</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="3" column="1"> |
||||||
|
<widget class="FancySlider" name="speedBumpAmpSlider" native="true"> |
||||||
|
<property name="toolTip"> |
||||||
|
<string><html><head/><body><p>The amplitude (size) of the speed bump waves. 1.0 is 100%.</p></body></html></string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="4" column="0"> |
||||||
|
<widget class="QLabel" name="label_15"> |
||||||
|
<property name="text"> |
||||||
|
<string>Output Power</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="4" column="1"> |
||||||
|
<widget class="FancySlider" name="speedOutputPowerSlider" native="true"> |
||||||
|
<property name="toolTip"> |
||||||
|
<string><html><head/><body><p>The total output power when travelling at top speed. 1.0 is 100%.</p><p>Use this to mix the output with other modules, or limit the power so other things are more noticable!</p></body></html></string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="5" column="0"> |
||||||
|
<widget class="QLabel" name="label_10"> |
||||||
|
<property name="font"> |
||||||
|
<font> |
||||||
|
<bold>true</bold> |
||||||
|
</font> |
||||||
|
</property> |
||||||
|
<property name="text"> |
||||||
|
<string>RPM to Power</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="5" column="1"> |
||||||
|
<widget class="QCheckBox" name="rpmEnabledBox"> |
||||||
|
<property name="toolTip"> |
||||||
|
<string><html><head/><body><p>Maps the engine RPM to output power. Feel the vroom!</p></body></html></string> |
||||||
|
</property> |
||||||
|
<property name="text"> |
||||||
|
<string>Enabled</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="6" column="0"> |
||||||
|
<widget class="QLabel" name="label_12"> |
||||||
|
<property name="text"> |
||||||
|
<string>Top RPM</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="6" column="1"> |
||||||
|
<widget class="FancySlider" name="rpmTopSlider" native="true"> |
||||||
|
<property name="toolTip"> |
||||||
|
<string><html><head/><body><p>The engine RPM needed for maximum output.</p><p>Most truck engines cap out at 2000RPM, and change gears at 500RPM.</p></body></html></string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="7" column="0"> |
||||||
|
<widget class="QLabel" name="label_11"> |
||||||
|
<property name="text"> |
||||||
|
<string>Bottom RPM</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="7" column="1"> |
||||||
|
<widget class="FancySlider" name="rpmBottomSlider" native="true"> |
||||||
|
<property name="toolTip"> |
||||||
|
<string><html><head/><body><p>The engine RPM for zero output. Use this to make nothing happen when the engine is idling or changing gear.</p><p><br/></p><p>Most truck engines idle at 500RPM.</p><p><br/></p></body></html></string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="8" column="0"> |
||||||
|
<widget class="QLabel" name="label_13"> |
||||||
|
<property name="text"> |
||||||
|
<string>Output Power</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="8" column="1"> |
||||||
|
<widget class="FancySlider" name="rpmOutputPowerSlider" native="true"> |
||||||
|
<property name="toolTip"> |
||||||
|
<string><html><head/><body><p>The total output power when at top RPM. 1.0 is 100%.</p><p>Use this to mix the output with other modules, or limit the power so other things are more noticable!</p></body></html></string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="9" column="0"> |
||||||
|
<widget class="QLabel" name="label_14"> |
||||||
|
<property name="font"> |
||||||
|
<font> |
||||||
|
<bold>true</bold> |
||||||
|
</font> |
||||||
|
</property> |
||||||
|
<property name="text"> |
||||||
|
<string>Punish for Crimes</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="9" column="1"> |
||||||
|
<widget class="QCheckBox" name="crimeEnableBox"> |
||||||
|
<property name="toolTip"> |
||||||
|
<string><html><head/><body><p>Punishes you for comitting crimes by slashing the output power for a period of time.</p><p>The duration depends on how much you got fined.</p></body></html></string> |
||||||
|
</property> |
||||||
|
<property name="text"> |
||||||
|
<string>Enabled</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="10" column="0"> |
||||||
|
<widget class="QLabel" name="label_16"> |
||||||
|
<property name="text"> |
||||||
|
<string>Seconds per €100</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="10" column="1"> |
||||||
|
<widget class="FancySlider" name="crimeRateSlider" native="true"> |
||||||
|
<property name="toolTip"> |
||||||
|
<string><html><head/><body><p>How long you get punished (in seconds) for each €100 fined.</p><p>The fine for most offenses is between €100 and €800, although some can go as high as €5000!</p><p>With a value of 30 that's between 30 seconds and 4 minutes.</p></body></html></string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="11" column="0"> |
||||||
|
<widget class="QLabel" name="label_17"> |
||||||
|
<property name="text"> |
||||||
|
<string>Power Multiplier</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="11" column="1"> |
||||||
|
<widget class="FancySlider" name="crimePowerMultiplier" native="true"> |
||||||
|
<property name="toolTip"> |
||||||
|
<string><html><head/><body><p>How much the output gets multiplied by while the punishment is active; For example, 0.1 means that only 10% makes it through. Use 0.0 for no output at all.</p><p>Note: if the value is above 1.0 you're actually getting <span style=" font-style:italic;">rewarded</span> for breaking the law...</p></body></html></string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="12" column="0"> |
||||||
|
<widget class="QLabel" name="label_18"> |
||||||
|
<property name="font"> |
||||||
|
<font> |
||||||
|
<bold>true</bold> |
||||||
|
</font> |
||||||
|
</property> |
||||||
|
<property name="text"> |
||||||
|
<string>Reward for Completion</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="12" column="1"> |
||||||
|
<widget class="QCheckBox" name="jobRewardEnableBox"> |
||||||
|
<property name="toolTip"> |
||||||
|
<string><html><head/><body><p>Rewards you for finishing a job by pushing the output power up for a period of time, depending on how much money and XP you earned.</p><p>The output will ramp up to a peak very quickly, then fade away over time.</p></body></html></string> |
||||||
|
</property> |
||||||
|
<property name="text"> |
||||||
|
<string>Enabled</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="13" column="0"> |
||||||
|
<widget class="QLabel" name="label_20"> |
||||||
|
<property name="text"> |
||||||
|
<string>Seconds per €100</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="13" column="1"> |
||||||
|
<widget class="FancySlider" name="jobRewardMoneyRateSlider" native="true"> |
||||||
|
<property name="toolTip"> |
||||||
|
<string><html><head/><body><p>How many seconds the reward lasts for each €100 earned.</p><p>Jobs can earn from €500 to over €10,000; with a value of 2, the range will be 10 seconds to 3 minutes!</p><p>This stacks from the reward from the XP earned (below).</p></body></html></string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="14" column="0"> |
||||||
|
<widget class="QLabel" name="label_21"> |
||||||
|
<property name="text"> |
||||||
|
<string>Seconds per 100XP</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="14" column="1"> |
||||||
|
<widget class="FancySlider" name="jobRewardXPRateSlider" native="true"> |
||||||
|
<property name="toolTip"> |
||||||
|
<string><html><head/><body><p>How long the reward lasts for each 100XP earned.</p><p>Jobs can earn between 200XP and 1000XP depending on your performance.</p></body></html></string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="15" column="0"> |
||||||
|
<widget class="QLabel" name="label_25"> |
||||||
|
<property name="text"> |
||||||
|
<string>Power Boost</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="15" column="1"> |
||||||
|
<widget class="FancySlider" name="jobRewardPowerBoostSlider" native="true"/> |
||||||
|
</item> |
||||||
|
<item row="16" column="0"> |
||||||
|
<widget class="QLabel" name="label"> |
||||||
|
<property name="font"> |
||||||
|
<font> |
||||||
|
<bold>true</bold> |
||||||
|
</font> |
||||||
|
</property> |
||||||
|
<property name="text"> |
||||||
|
<string>Distance Rewards</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="16" column="1"> |
||||||
|
<widget class="QCheckBox" name="distanceRewardEnabledBox"> |
||||||
|
<property name="text"> |
||||||
|
<string>Enabled</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="17" column="0"> |
||||||
|
<widget class="QLabel" name="label_22"> |
||||||
|
<property name="text"> |
||||||
|
<string>Average Distance</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="17" column="1"> |
||||||
|
<widget class="FancySlider" name="distanceRewardDistSlider" native="true"/> |
||||||
|
</item> |
||||||
|
<item row="18" column="0"> |
||||||
|
<widget class="QLabel" name="label_23"> |
||||||
|
<property name="text"> |
||||||
|
<string>Randomness</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="18" column="1"> |
||||||
|
<widget class="FancySlider" name="distanceRewardRandSlider" native="true"/> |
||||||
|
</item> |
||||||
|
<item row="19" column="0"> |
||||||
|
<widget class="QLabel" name="label_26"> |
||||||
|
<property name="text"> |
||||||
|
<string>Duration</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="19" column="1"> |
||||||
|
<widget class="FancySlider" name="distanceRewardDurationSlider" native="true"/> |
||||||
|
</item> |
||||||
|
<item row="20" column="0"> |
||||||
|
<widget class="QLabel" name="label_27"> |
||||||
|
<property name="text"> |
||||||
|
<string>Power Boost</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="20" column="1"> |
||||||
|
<widget class="FancySlider" name="distanceRewardPowerBoostSlider" native="true"/> |
||||||
|
</item> |
||||||
|
</layout> |
||||||
|
</widget> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
</layout> |
||||||
|
</widget> |
||||||
|
<widget class="QWidget" name="tab_2"> |
||||||
|
<attribute name="title"> |
||||||
|
<string>Config</string> |
||||||
|
</attribute> |
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_4"> |
||||||
|
<item> |
||||||
|
<layout class="QFormLayout" name="formLayout"> |
||||||
|
<item row="0" column="0"> |
||||||
|
<widget class="QLabel" name="label_2"> |
||||||
|
<property name="text"> |
||||||
|
<string>Intiface Address</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="0" column="1"> |
||||||
|
<widget class="QLineEdit" name="lineEdit"> |
||||||
|
<property name="text"> |
||||||
|
<string>ws://127.0.0.1:12345</string> |
||||||
|
</property> |
||||||
|
<property name="placeholderText"> |
||||||
|
<string>ws://127.0.0.1:12345</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
<item row="1" column="1"> |
||||||
|
<widget class="QPushButton" name="jobCompleteTestButton"> |
||||||
|
<property name="text"> |
||||||
|
<string>Job Complete</string> |
||||||
|
</property> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
</layout> |
||||||
|
</item> |
||||||
|
</layout> |
||||||
|
</widget> |
||||||
|
</widget> |
||||||
|
</item> |
||||||
|
</layout> |
||||||
|
</widget> |
||||||
|
<widget class="QStatusBar" name="statusbar"/> |
||||||
|
</widget> |
||||||
|
<customwidgets> |
||||||
|
<customwidget> |
||||||
|
<class>FancyGraph</class> |
||||||
|
<extends>QWidget</extends> |
||||||
|
<header>gui/fancygraph.h</header> |
||||||
|
<container>1</container> |
||||||
|
</customwidget> |
||||||
|
<customwidget> |
||||||
|
<class>FancySlider</class> |
||||||
|
<extends>QWidget</extends> |
||||||
|
<header>gui/fancyslider.h</header> |
||||||
|
<container>1</container> |
||||||
|
</customwidget> |
||||||
|
</customwidgets> |
||||||
|
<resources/> |
||||||
|
<connections/> |
||||||
|
</ui> |
@ -0,0 +1,47 @@ |
|||||||
|
|
||||||
|
#include <exception> |
||||||
|
|
||||||
|
#include "gui/gui.h" |
||||||
|
#include "qapphost.h" |
||||||
|
#include "QTemporaryDir" |
||||||
|
|
||||||
|
QAppHost* host; |
||||||
|
Gui* gui; |
||||||
|
|
||||||
|
void boot() |
||||||
|
{ |
||||||
|
if (gui == nullptr) |
||||||
|
{ |
||||||
|
gui = new Gui(); |
||||||
|
TelemetryState tele; |
||||||
|
tele.timestamp = 0; |
||||||
|
tele.speed = 9.72222222222; |
||||||
|
tele.rpm = 1000; |
||||||
|
tele.gear = 7; |
||||||
|
gui->setPause(false); |
||||||
|
gui->receiveTelemetry(tele); |
||||||
|
gui->setVisible(true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
int main(int argc, char** argv) |
||||||
|
{ |
||||||
|
|
||||||
|
try |
||||||
|
{ |
||||||
|
if (host == nullptr) |
||||||
|
{ |
||||||
|
host = new QAppHost(&boot); |
||||||
|
// std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
} |
||||||
|
|
||||||
|
host->waitForThread(); |
||||||
|
} |
||||||
|
catch (std::exception ex) |
||||||
|
{ |
||||||
|
std::cerr << "Killed by exception: " << ex.what() << std::endl; |
||||||
|
return SCS_RESULT_generic_error; |
||||||
|
} |
||||||
|
|
||||||
|
std::cout << "Execution finished." << std::endl; |
||||||
|
} |
@ -0,0 +1,100 @@ |
|||||||
|
#include "qapphost.h" |
||||||
|
|
||||||
|
#include <QDebug> |
||||||
|
#include <iostream> |
||||||
|
#include <QTimer> |
||||||
|
#include <thread> |
||||||
|
#ifdef WITH_QT_JSON_SETTINGS |
||||||
|
#include <qt_json_settings.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
int argc = 1; |
||||||
|
char* argv[] = {"SharedLibrary", nullptr}; |
||||||
|
char** nargv = nullptr; |
||||||
|
|
||||||
|
QAppHost::QAppHost(std::function<void(void)> callback) |
||||||
|
{ |
||||||
|
callbackFunction = callback; |
||||||
|
std::cout << "QAppHost::QAppHost()" << std::endl; |
||||||
|
if (thread == nullptr) |
||||||
|
{ |
||||||
|
thread = new std::thread(&QAppHost::threadRun, this); |
||||||
|
std::cout << "Host thread created." << std::endl; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//QAppHost::QAppHost(int& nargc, char** args)
|
||||||
|
//{
|
||||||
|
// argc = nargc;
|
||||||
|
// nargv = args;
|
||||||
|
// std::cout << "QAppHost::QAppHost()" << std::endl;
|
||||||
|
// if (thread == nullptr)
|
||||||
|
// {
|
||||||
|
// thread = new std::thread(&QAppHost::threadRun, this);
|
||||||
|
// std::cout << "Host thread created." << std::endl;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//QAppHost::~QAppHost()
|
||||||
|
//{
|
||||||
|
// qDebug() << "QAppHost::~QAppHost()";
|
||||||
|
// if (app != nullptr)
|
||||||
|
// {
|
||||||
|
// delete app;
|
||||||
|
// qDebug() << "App deleted.";
|
||||||
|
// }
|
||||||
|
// if (thread != nullptr)
|
||||||
|
// {
|
||||||
|
// delete thread;
|
||||||
|
// qDebug() << "Thread deleted.";
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
void QAppHost::waitForThread() |
||||||
|
{ |
||||||
|
if (thread != nullptr && thread->joinable()) |
||||||
|
thread->join(); |
||||||
|
} |
||||||
|
|
||||||
|
void QAppHost::threadRun() |
||||||
|
{ |
||||||
|
std::cout << "QAppHost::threadRun()" << std::endl; |
||||||
|
|
||||||
|
if (QApplication::instance() == nullptr) |
||||||
|
{ |
||||||
|
app = new QApplication(argc, nargv == nullptr ? argv : nargv); |
||||||
|
std::cout << "App created." << std::endl; |
||||||
|
|
||||||
|
QCoreApplication::setOrganizationName("Catboy Labs"); |
||||||
|
QCoreApplication::setOrganizationDomain("mysoft.com"); |
||||||
|
QCoreApplication::setApplicationName("Euro Fuck Simulator"); |
||||||
|
#ifdef WITH_QT_JSON_SETTINGS |
||||||
|
QtJsonSettings::Initialize(); |
||||||
|
#endif |
||||||
|
|
||||||
|
QTimer::singleShot(250, callbackFunction); |
||||||
|
std::cout << "Boot timer created." << std::endl; |
||||||
|
|
||||||
|
app->exec(); |
||||||
|
qDebug() << "App finished executing."; |
||||||
|
} |
||||||
|
|
||||||
|
qDebug() << "QAppHost::threadRun() finished"; |
||||||
|
} |
||||||
|
|
||||||
|
void QAppHost::shutdown() |
||||||
|
{ |
||||||
|
qDebug() << "QAppHost::shutdown()"; |
||||||
|
if (QApplication::instance() != nullptr) |
||||||
|
{ |
||||||
|
QApplication::instance()->quit(); |
||||||
|
qDebug() << "App quit."; |
||||||
|
} |
||||||
|
// if (thread != nullptr && thread->joinable())
|
||||||
|
// {
|
||||||
|
// qDebug() << "Thread joining...";
|
||||||
|
// thread->join();
|
||||||
|
// qDebug() << "Thread joined.";
|
||||||
|
// }
|
||||||
|
qDebug() << "QAppHost::shutdown() finished"; |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
#ifndef QAPPHOST_H |
||||||
|
#define QAPPHOST_H |
||||||
|
|
||||||
|
#include <QApplication> |
||||||
|
#include <thread> |
||||||
|
#include <functional> |
||||||
|
|
||||||
|
class QAppHost |
||||||
|
{ |
||||||
|
private: |
||||||
|
QApplication* app; |
||||||
|
std::thread* thread; |
||||||
|
std::function<void(void)> callbackFunction; |
||||||
|
|
||||||
|
public: |
||||||
|
explicit QAppHost(std::function<void(void)> callback); |
||||||
|
// QAppHost(int& argc, char** argv);
|
||||||
|
~QAppHost() = default; |
||||||
|
|
||||||
|
void waitForThread(); |
||||||
|
void threadRun(); |
||||||
|
void shutdown(); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
#endif // QAPPHOST_H
|
@ -0,0 +1,209 @@ |
|||||||
|
#include "rulemodule.h" |
||||||
|
|
||||||
|
#include <QMetaObject> |
||||||
|
#include <QMetaProperty> |
||||||
|
#include <QDebug> |
||||||
|
#include <QRandomGenerator> |
||||||
|
|
||||||
|
#include "math.h" |
||||||
|
#include "common/scssdk_telemetry_common_gameplay_events.h" |
||||||
|
|
||||||
|
double peakedSine(double x, double peakTime = 0.2) |
||||||
|
{ |
||||||
|
double stretchedX; |
||||||
|
if (x < peakTime) |
||||||
|
stretchedX = x / (peakTime*2.0); |
||||||
|
else |
||||||
|
stretchedX = 0.5+( ( (x-peakTime)/(1-peakTime) )*0.5 ); |
||||||
|
|
||||||
|
double ret = 1.0-((1.0+(std::cos(stretchedX * M_PI * 2.0)))/2.0); |
||||||
|
qDebug() << "peakedSine(" << x << ") =" << ret; |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
RuleModule::RuleModule(QObject *parent) |
||||||
|
: QObject{parent} |
||||||
|
{ |
||||||
|
// const QMetaObject* meta = metaObject();
|
||||||
|
// for (int idx = 1; idx < meta->propertyCount(); idx++)
|
||||||
|
// {
|
||||||
|
// QMetaProperty prop = meta->property(idx);
|
||||||
|
// connect(this, prop.notifySignal(), this, [&]());
|
||||||
|
// }
|
||||||
|
} |
||||||
|
|
||||||
|
void RuleModule::save(QSettings& settings) const |
||||||
|
{ |
||||||
|
const QMetaObject* meta = metaObject(); |
||||||
|
settings.beginGroup(objectName()); |
||||||
|
for (int idx = 1; idx < meta->propertyCount(); idx++) |
||||||
|
{ |
||||||
|
QMetaProperty prop = meta->property(idx); |
||||||
|
QVariant value = prop.read(this); |
||||||
|
qDebug() << "Saving settings: " << objectName() << "/" << prop.name() << " = " << value; |
||||||
|
settings.setValue(prop.name(), value); |
||||||
|
} |
||||||
|
settings.endGroup(); |
||||||
|
} |
||||||
|
|
||||||
|
void RuleModule::load(QSettings& settings) |
||||||
|
{ |
||||||
|
const QMetaObject* meta = metaObject(); |
||||||
|
settings.beginGroup(objectName()); |
||||||
|
for (int idx = 1; idx < meta->propertyCount(); idx++) |
||||||
|
{ |
||||||
|
QMetaProperty prop = meta->property(idx); |
||||||
|
QVariant value = settings.value(prop.name(), prop.read(this)); |
||||||
|
qDebug() << "Loading settings: " << objectName() << "/" << prop.name() << " = " << value; |
||||||
|
prop.write(this, value); |
||||||
|
emit prop.notifySignal(); |
||||||
|
} |
||||||
|
settings.endGroup(); |
||||||
|
} |
||||||
|
|
||||||
|
double SpeedModule::process(const TelemetryState& telemetry, double previous, double delta) |
||||||
|
{ |
||||||
|
double ret = previous; |
||||||
|
if (Enabled && TopSpeed > 0) |
||||||
|
{ |
||||||
|
double speedN = std::abs(telemetry.speed * 3.6) / TopSpeed; |
||||||
|
sine_x += delta * speedN; |
||||||
|
double sine = std::sin(sine_x * WaveFrequency * M_PI * 2); |
||||||
|
double power = std::clamp(speedN + (sine * speedN * WaveAmplitude), 0.0, 1.0); |
||||||
|
ret += power * MaxOutput; |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
double RPMModule::process(const TelemetryState& telemetry, double previous, double delta) |
||||||
|
{ |
||||||
|
double ret = previous; |
||||||
|
if (Enabled && (TopRPM - BottomRPM) > 0) |
||||||
|
{ |
||||||
|
double rpmN = (telemetry.rpm - BottomRPM) / (TopRPM - BottomRPM); |
||||||
|
ret += rpmN * MaxOutput; |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
void CrimeModule::gameEvent(const QString name, const QMap<QString, QVariant> attributes) |
||||||
|
{ |
||||||
|
if (name == SCS_TELEMETRY_GAMEPLAY_EVENT_player_fined) |
||||||
|
{ |
||||||
|
if (attributes.contains(SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_fine_amount)) |
||||||
|
{ |
||||||
|
uint64_t amount = attributes[SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_fine_amount].toULongLong(); |
||||||
|
timeout += amount * SecondsPerEuro; |
||||||
|
} |
||||||
|
if (attributes.contains(SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_fine_offence)) |
||||||
|
{ |
||||||
|
QString offence = attributes[SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_fine_offence].toString(); |
||||||
|
|
||||||
|
// You...
|
||||||
|
if (offence == "crash") |
||||||
|
whatYouDid = "killed someone"; |
||||||
|
else if (offence == "red_signal") |
||||||
|
whatYouDid = "ran a red light"; |
||||||
|
else if (offence == "avoid_sleeping") |
||||||
|
whatYouDid = "were caught being eepy"; |
||||||
|
else if (offence == "speeding_camera") |
||||||
|
whatYouDid = "got flashed"; |
||||||
|
else if (offence == "no_lights") |
||||||
|
whatYouDid = "forgot to press the headlight button twice"; |
||||||
|
// else if (offence == "speeding")
|
||||||
|
// whatYouDid = "";
|
||||||
|
// else if (offence == "avoid_weighing")
|
||||||
|
// whatYouDid = "";
|
||||||
|
else if (offence == "illegal_trailer") |
||||||
|
whatYouDid = "got found packing heat"; |
||||||
|
else if (offence == "avoid_inspection") |
||||||
|
whatYouDid = "missed penis inspection day"; |
||||||
|
// else if (offence == "illegal_border_crossing")
|
||||||
|
// whatYouDid = "";
|
||||||
|
// else if (offence == "hard_shoulder_violation")
|
||||||
|
// whatYouDid = "";
|
||||||
|
// else if (offence == "damaged_vehicle_usage")
|
||||||
|
// whatYouDid = "";
|
||||||
|
else if (offence == "generic") |
||||||
|
whatYouDid = "failed the vibe check"; |
||||||
|
else |
||||||
|
whatYouDid = "broke the law"; |
||||||
|
// ...! You'll be punished for X seconds!
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
double CrimeModule::process(const TelemetryState& telemetry, double previous, double delta) |
||||||
|
{ |
||||||
|
double ret = previous; |
||||||
|
if (Enabled) |
||||||
|
{ |
||||||
|
if (timeout > 0.0) |
||||||
|
{ |
||||||
|
timeout -= delta; |
||||||
|
ret *= PowerMultiplier; |
||||||
|
|
||||||
|
emit statusUpdate(QString("You %2! You'll be punished for %1 seconds!").arg(timeout, 0, 'f', 1).arg(whatYouDid)); |
||||||
|
} |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
void JobModule::gameEvent(const QString name, const QMap<QString, QVariant> attributes) |
||||||
|
{ |
||||||
|
if (name == SCS_TELEMETRY_GAMEPLAY_EVENT_job_delivered) |
||||||
|
{ |
||||||
|
totalTime = 0.0; |
||||||
|
if (attributes.contains(SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_revenue)) |
||||||
|
{ |
||||||
|
uint64_t amount = attributes[SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_revenue].toULongLong(); |
||||||
|
totalTime += (amount/100.0) * SecondsPerEuro; |
||||||
|
} |
||||||
|
if (attributes.contains(SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_earned_xp)) |
||||||
|
{ |
||||||
|
uint64_t amount = attributes[SCS_TELEMETRY_GAMEPLAY_EVENT_ATTRIBUTE_earned_xp].toULongLong(); |
||||||
|
totalTime += (amount/100.0) * SecondsPerXP; |
||||||
|
} |
||||||
|
timeout = totalTime; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
double JobModule::process(const TelemetryState& telemetry, double previous, double delta) |
||||||
|
{ |
||||||
|
double ret = previous; |
||||||
|
if (Enabled) |
||||||
|
{ |
||||||
|
if (timeout > 0.0) |
||||||
|
{ |
||||||
|
timeout -= delta; |
||||||
|
ret += peakedSine((totalTime-timeout)/totalTime, 0.1) * PowerBoost; |
||||||
|
|
||||||
|
emit statusUpdate(QString("Well done! You'll be rewarded for %1 seconds!").arg(timeout, 0, 'f', 1)); |
||||||
|
} |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
double DistanceModule::process(const TelemetryState& telemetry, double previous, double delta) |
||||||
|
{ |
||||||
|
double ret = previous; |
||||||
|
if (Enabled) |
||||||
|
{ |
||||||
|
if (telemetry.distance > nextReward) |
||||||
|
{ |
||||||
|
double random = (1.0 - Randomness) + (QRandomGenerator::global()->generateDouble() * 2.0 * Randomness); |
||||||
|
qDebug () << "Random factor: " << random; |
||||||
|
double distanceToNext = Distance * 1000.0 * random; |
||||||
|
timeout = Duration; |
||||||
|
nextReward += distanceToNext; |
||||||
|
qDebug() << "Next distance reward will be in " << distanceToNext << " metres, at " << nextReward; |
||||||
|
} |
||||||
|
if (timeout > 0.0) |
||||||
|
{ |
||||||
|
timeout -= delta; |
||||||
|
ret += peakedSine((Duration-timeout)/Duration, 0.4) * PowerBoost; |
||||||
|
emit statusUpdate(QString("You've driven so far! You'll be rewarded for %1 seconds!").arg(timeout, 0, 'f', 1)); |
||||||
|
} |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
@ -0,0 +1,124 @@ |
|||||||
|
#ifndef RULEMODULE_H |
||||||
|
#define RULEMODULE_H |
||||||
|
|
||||||
|
#include <QObject> |
||||||
|
#include <QSettings> |
||||||
|
|
||||||
|
#include "telemetry.h" |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// I'm sorry I'm just addicted to efficiency ok
|
||||||
|
#define FANCY_PROPERTY(Type,Name,Default) \ |
||||||
|
Q_PROPERTY(Type Name READ get##Name WRITE set##Name NOTIFY Name##Changed); \
|
||||||
|
protected: \
|
||||||
|
Type Name = Default; \
|
||||||
|
public: \
|
||||||
|
Type get##Name() { return Name; } \
|
||||||
|
Q_SIGNAL void Name##Changed(Type newValue); \
|
||||||
|
public slots: \
|
||||||
|
void set##Name(Type value) { bool changed = (Name != value); Name = value; if (changed) emit Name##Changed(Name); }; |
||||||
|
|
||||||
|
class RuleModule : public QObject |
||||||
|
{ |
||||||
|
Q_OBJECT; |
||||||
|
|
||||||
|
public: |
||||||
|
explicit RuleModule(QObject *parent = nullptr); |
||||||
|
|
||||||
|
virtual double process(const TelemetryState& telemetry, double previous, double delta) = 0; |
||||||
|
virtual void gameEvent(const QString name, const QMap<QString, QVariant> attributes) {}; |
||||||
|
|
||||||
|
void load(QSettings& settings); |
||||||
|
void save(QSettings& settings) const; |
||||||
|
|
||||||
|
FANCY_PROPERTY(bool, Enabled, false); |
||||||
|
|
||||||
|
signals: |
||||||
|
void statusUpdate(QString status); |
||||||
|
|
||||||
|
public slots: |
||||||
|
virtual void gameEvent(QString eventType, double param) {} |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
class SpeedModule: public RuleModule |
||||||
|
{ |
||||||
|
Q_OBJECT; |
||||||
|
public: |
||||||
|
SpeedModule() { setObjectName("speed"); } |
||||||
|
virtual double process(const TelemetryState& telemetry, double previous, double delta) override; |
||||||
|
|
||||||
|
FANCY_PROPERTY(double, TopSpeed, 65.0); |
||||||
|
FANCY_PROPERTY(double, MaxOutput, 1.0); |
||||||
|
FANCY_PROPERTY(double, WaveFrequency, 3.0); |
||||||
|
FANCY_PROPERTY(double, WaveAmplitude, 0.25); |
||||||
|
|
||||||
|
private: |
||||||
|
double sine_x = 0.0; |
||||||
|
}; |
||||||
|
|
||||||
|
class RPMModule: public RuleModule |
||||||
|
{ |
||||||
|
Q_OBJECT; |
||||||
|
public: |
||||||
|
RPMModule() {setObjectName("rpm");} |
||||||
|
virtual double process(const TelemetryState& telemetry, double previous, double delta) override; |
||||||
|
|
||||||
|
FANCY_PROPERTY(double, TopRPM, 2500.0); |
||||||
|
FANCY_PROPERTY(double, BottomRPM, 500.0); |
||||||
|
FANCY_PROPERTY(double, MaxOutput, 1.0); |
||||||
|
}; |
||||||
|
|
||||||
|
class CrimeModule: public RuleModule |
||||||
|
{ |
||||||
|
Q_OBJECT; |
||||||
|
public: |
||||||
|
CrimeModule() {setObjectName("crime");} |
||||||
|
virtual double process(const TelemetryState& telemetry, double previous, double delta) override; |
||||||
|
virtual void gameEvent(const QString name, const QMap<QString, QVariant> attributes) override; |
||||||
|
|
||||||
|
FANCY_PROPERTY(double, SecondsPerEuro, 30.0 / 400.0); |
||||||
|
FANCY_PROPERTY(double, PowerMultiplier, 0.1); |
||||||
|
|
||||||
|
private: |
||||||
|
double timeout = 0.0; |
||||||
|
QString whatYouDid = ""; |
||||||
|
}; |
||||||
|
|
||||||
|
class JobModule: public RuleModule |
||||||
|
{ |
||||||
|
Q_OBJECT; |
||||||
|
public: |
||||||
|
JobModule() {setObjectName("job");} |
||||||
|
virtual double process(const TelemetryState& telemetry, double previous, double delta) override; |
||||||
|
virtual void gameEvent(const QString name, const QMap<QString, QVariant> attributes) override; |
||||||
|
|
||||||
|
FANCY_PROPERTY(double, SecondsPerEuro, 30.0 / 7000.0); |
||||||
|
FANCY_PROPERTY(double, SecondsPerXP, 30.0 / 500.0); |
||||||
|
FANCY_PROPERTY(double, PowerBoost, 0.7); |
||||||
|
|
||||||
|
private: |
||||||
|
double totalTime = 0.0; |
||||||
|
double timeout = 0.0; |
||||||
|
}; |
||||||
|
|
||||||
|
class DistanceModule: public RuleModule |
||||||
|
{ |
||||||
|
Q_OBJECT; |
||||||
|
public: |
||||||
|
DistanceModule() {setObjectName("distance");} |
||||||
|
virtual double process(const TelemetryState& telemetry, double previous, double delta) override; |
||||||
|
|
||||||
|
FANCY_PROPERTY(double, Distance, 10.0); |
||||||
|
FANCY_PROPERTY(double, Randomness, 1.0); |
||||||
|
FANCY_PROPERTY(double, Duration, 30.0); |
||||||
|
FANCY_PROPERTY(double, PowerBoost, 0.7); |
||||||
|
|
||||||
|
private: |
||||||
|
double nextReward = 0.0; |
||||||
|
double timeout = 0.0; |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
#endif // RULEMODULE_H
|
@ -0,0 +1,284 @@ |
|||||||
|
// Windows stuff.
|
||||||
|
#ifdef _WIN32 |
||||||
|
# define WINVER 0x0500 |
||||||
|
# define _WIN32_WINNT 0x0500 |
||||||
|
# include <windows.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
// SDK
|
||||||
|
#include "../libraries/scs-sdk/include/scssdk_telemetry.h" |
||||||
|
#include "../libraries/scs-sdk/include/eurotrucks2/scssdk_eut2.h" |
||||||
|
#include "../libraries/scs-sdk/include/eurotrucks2/scssdk_telemetry_eut2.h" |
||||||
|
#include "../libraries/scs-sdk/include/amtrucks/scssdk_ats.h" |
||||||
|
#include "../libraries/scs-sdk/include/amtrucks/scssdk_telemetry_ats.h" |
||||||
|
|
||||||
|
#include "telemetry.h" |
||||||
|
#include "qapphost.h" |
||||||
|
//#include "eurofucksimulator.h"
|
||||||
|
#include "gui/gui.h" |
||||||
|
|
||||||
|
#include <QVector3D> |
||||||
|
#include <QMatrix4x4> |
||||||
|
#include <QQuaternion> |
||||||
|
|
||||||
|
//#include <QApplication>
|
||||||
|
//#include <pthread.h>
|
||||||
|
|
||||||
|
#define UNUSED(x) |
||||||
|
|
||||||
|
scs_timestamp_t last_timestamp = static_cast<scs_timestamp_t>(-1); |
||||||
|
TelemetryState telemetry; |
||||||
|
|
||||||
|
QAppHost* host = nullptr; |
||||||
|
//EuroFuckSimulator* eu = nullptr;
|
||||||
|
Gui* gui = nullptr; |
||||||
|
|
||||||
|
QVariant value_to_variant(scs_value_t value) |
||||||
|
{ |
||||||
|
QVariant ret; |
||||||
|
switch (value.type) |
||||||
|
{ |
||||||
|
case SCS_VALUE_TYPE_INVALID: |
||||||
|
{ |
||||||
|
ret = QVariant(); |
||||||
|
break; |
||||||
|
} |
||||||
|
case SCS_VALUE_TYPE_bool: { |
||||||
|
ret = QVariant::fromValue(value.value_bool.value); |
||||||
|
break; |
||||||
|
} |
||||||
|
case SCS_VALUE_TYPE_s32: { |
||||||
|
ret = QVariant::fromValue(static_cast<int>(value.value_s32.value)); |
||||||
|
break; |
||||||
|
} |
||||||
|
case SCS_VALUE_TYPE_u32: { |
||||||
|
ret = QVariant::fromValue(static_cast<unsigned>(value.value_u32.value)); |
||||||
|
break; |
||||||
|
} |
||||||
|
case SCS_VALUE_TYPE_s64: { |
||||||
|
ret = QVariant::fromValue(value.value_s64.value); |
||||||
|
break; |
||||||
|
} |
||||||
|
case SCS_VALUE_TYPE_u64: { |
||||||
|
ret = QVariant::fromValue(value.value_u64.value); |
||||||
|
break; |
||||||
|
} |
||||||
|
case SCS_VALUE_TYPE_float: { |
||||||
|
ret = QVariant::fromValue(value.value_float.value); |
||||||
|
break; |
||||||
|
} |
||||||
|
case SCS_VALUE_TYPE_double: { |
||||||
|
ret = QVariant::fromValue(value.value_double.value); |
||||||
|
break; |
||||||
|
} |
||||||
|
case SCS_VALUE_TYPE_fvector: { |
||||||
|
ret = QVariant::fromValue(QVector3D(value.value_fvector.x,value.value_fvector.y,value.value_fvector.z)); |
||||||
|
break; |
||||||
|
} |
||||||
|
case SCS_VALUE_TYPE_dvector: { |
||||||
|
ret = QVariant::fromValue(QVector3D(value.value_dvector.x,value.value_dvector.y,value.value_dvector.z)); |
||||||
|
break; |
||||||
|
} |
||||||
|
case SCS_VALUE_TYPE_euler: { |
||||||
|
ret = QVariant::fromValue(QQuaternion::fromEulerAngles(value.value_euler.heading * 360.0f,value.value_euler.pitch * 360.0f,value.value_euler.roll * 360.0f)); |
||||||
|
break; |
||||||
|
} |
||||||
|
// case SCS_VALUE_TYPE_fplacement: {
|
||||||
|
//// QMatrix4x4::QMatrix4x4
|
||||||
|
//// log_line(
|
||||||
|
//// "fplacement = (%f,%f,%f) h:%f p:%f r:%f",
|
||||||
|
//// value.value_fplacement.position.x,
|
||||||
|
//// value.value_fplacement.position.y,
|
||||||
|
//// value.value_fplacement.position.z,
|
||||||
|
//// value.value_fplacement.orientation.heading * 360.0f,
|
||||||
|
//// value.value_fplacement.orientation.pitch * 360.0f,
|
||||||
|
//// value.value_fplacement.orientation.roll * 360.0f
|
||||||
|
//// );
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case SCS_VALUE_TYPE_dplacement: {
|
||||||
|
//// log_line(
|
||||||
|
//// "dplacement = (%f,%f,%f) h:%f p:%f r:%f",
|
||||||
|
//// value.value_dplacement.position.x,
|
||||||
|
//// value.value_dplacement.position.y,
|
||||||
|
//// value.value_dplacement.position.z,
|
||||||
|
//// value.value_dplacement.orientation.heading * 360.0f,
|
||||||
|
//// value.value_dplacement.orientation.pitch * 360.0f,
|
||||||
|
//// value.value_dplacement.orientation.roll * 360.0f
|
||||||
|
//// );
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
case SCS_VALUE_TYPE_string: { |
||||||
|
ret = QVariant::fromValue(QString(value.value_string.value)); |
||||||
|
break; |
||||||
|
} |
||||||
|
default: { |
||||||
|
ret = QVariant(); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
QMap<QString, QVariant> attributes_to_map(const scs_named_value_t *const attributes) |
||||||
|
{ |
||||||
|
QMap<QString, QVariant> ret; |
||||||
|
for (const scs_named_value_t *current = attributes; current->name; ++current) |
||||||
|
{ |
||||||
|
QString name = QString::fromStdString(current->name); |
||||||
|
QVariant value = value_to_variant(current->value); |
||||||
|
ret[name] = value; |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
SCSAPI_VOID telemetry_frame_start(const scs_event_t UNUSED(event), const void *const event_info, const scs_context_t UNUSED(context)) |
||||||
|
{ |
||||||
|
const struct scs_telemetry_frame_start_t *const info = static_cast<const scs_telemetry_frame_start_t *>(event_info); |
||||||
|
|
||||||
|
int delta = info->paused_simulation_time - last_timestamp; |
||||||
|
telemetry.timestamp += (delta); |
||||||
|
// telemetry.time_delta = delta;
|
||||||
|
last_timestamp = info->paused_simulation_time; |
||||||
|
} |
||||||
|
|
||||||
|
SCSAPI_VOID telemetry_frame_end(const scs_event_t UNUSED(event), const void *const UNUSED(event_info), const scs_context_t UNUSED(context)) |
||||||
|
{ |
||||||
|
if (gui != nullptr) |
||||||
|
gui->receiveTelemetry(telemetry); |
||||||
|
} |
||||||
|
|
||||||
|
SCSAPI_VOID telemetry_pause(const scs_event_t event, const void *const UNUSED(event_info), const scs_context_t UNUSED(context)) |
||||||
|
{ |
||||||
|
bool output_paused = (event == SCS_TELEMETRY_EVENT_paused); |
||||||
|
if (gui != nullptr) |
||||||
|
gui->setPause(output_paused); |
||||||
|
} |
||||||
|
|
||||||
|
SCSAPI_VOID telemetry_configuration(const scs_event_t event, const void *const event_info, const scs_context_t UNUSED(context)) |
||||||
|
{ |
||||||
|
const struct scs_telemetry_configuration_t *const info = static_cast<const scs_telemetry_configuration_t *>(event_info); |
||||||
|
// qDebug() << attributes_to_map(info->attributes);
|
||||||
|
} |
||||||
|
|
||||||
|
SCSAPI_VOID telemetry_gameplay_event(const scs_event_t event, const void *const event_info, const scs_context_t UNUSED(context)) |
||||||
|
{ |
||||||
|
const struct scs_telemetry_gameplay_event_t *const info = static_cast<const scs_telemetry_gameplay_event_t *>(event_info); |
||||||
|
|
||||||
|
QMap<QString,QVariant> map = attributes_to_map(info->attributes); |
||||||
|
|
||||||
|
gui->gameplayEvent(QString::fromStdString(info->id), map); |
||||||
|
} |
||||||
|
|
||||||
|
SCSAPI_VOID telemetry_store_orientation(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) |
||||||
|
{ |
||||||
|
assert(context); |
||||||
|
TelemetryState *const state = static_cast<TelemetryState *>(context); |
||||||
|
|
||||||
|
// This callback was registered with the SCS_TELEMETRY_CHANNEL_FLAG_no_value flag
|
||||||
|
// so it is called even when the value is not available.
|
||||||
|
|
||||||
|
if (! value) { |
||||||
|
state->orientation_available = false; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
assert(value); |
||||||
|
assert(value->type == SCS_VALUE_TYPE_euler); |
||||||
|
state->orientation_available = true; |
||||||
|
state->heading = value->value_euler.heading * 360.0f; |
||||||
|
state->pitch = value->value_euler.pitch * 360.0f; |
||||||
|
state->roll = value->value_euler.roll * 360.0f; |
||||||
|
} |
||||||
|
|
||||||
|
SCSAPI_VOID telemetry_store_float(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) |
||||||
|
{ |
||||||
|
// The SCS_TELEMETRY_CHANNEL_FLAG_no_value flag was not provided during registration
|
||||||
|
// so this callback is only called when a valid value is available.
|
||||||
|
|
||||||
|
assert(value); |
||||||
|
assert(value->type == SCS_VALUE_TYPE_float); |
||||||
|
assert(context); |
||||||
|
*static_cast<float *>(context) = value->value_float.value; |
||||||
|
} |
||||||
|
|
||||||
|
SCSAPI_VOID telemetry_store_s32(const scs_string_t name, const scs_u32_t index, const scs_value_t *const value, const scs_context_t context) |
||||||
|
{ |
||||||
|
// The SCS_TELEMETRY_CHANNEL_FLAG_no_value flag was not provided during registration
|
||||||
|
// so this callback is only called when a valid value is available.
|
||||||
|
|
||||||
|
assert(value); |
||||||
|
assert(value->type == SCS_VALUE_TYPE_s32); |
||||||
|
assert(context); |
||||||
|
*static_cast<int *>(context) = value->value_s32.value; |
||||||
|
} |
||||||
|
|
||||||
|
void boot() |
||||||
|
{ |
||||||
|
if (gui == nullptr) |
||||||
|
{ |
||||||
|
gui = new Gui(); |
||||||
|
gui->setVisible(true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
SCSAPI_RESULT scs_telemetry_init(const scs_u32_t version, const scs_telemetry_init_params_t *const params) |
||||||
|
{ |
||||||
|
if (version != SCS_TELEMETRY_VERSION_1_01) { |
||||||
|
return SCS_RESULT_unsupported; |
||||||
|
} |
||||||
|
|
||||||
|
try |
||||||
|
{ |
||||||
|
if (host == nullptr) |
||||||
|
{ |
||||||
|
host = new QAppHost(&boot); |
||||||
|
} |
||||||
|
} |
||||||
|
catch (std::exception ex) |
||||||
|
{ |
||||||
|
std::cerr << "Killed by exception: " << ex.what() << std::endl; |
||||||
|
return SCS_RESULT_generic_error; |
||||||
|
} |
||||||
|
|
||||||
|
const scs_telemetry_init_params_v101_t *const version_params = static_cast<const scs_telemetry_init_params_v101_t *>(params); |
||||||
|
|
||||||
|
const bool events_registered = |
||||||
|
(version_params->register_for_event(SCS_TELEMETRY_EVENT_frame_start, telemetry_frame_start, nullptr) == SCS_RESULT_ok) && |
||||||
|
(version_params->register_for_event(SCS_TELEMETRY_EVENT_frame_end, telemetry_frame_end, nullptr) == SCS_RESULT_ok) && |
||||||
|
(version_params->register_for_event(SCS_TELEMETRY_EVENT_paused, telemetry_pause, nullptr) == SCS_RESULT_ok) && |
||||||
|
(version_params->register_for_event(SCS_TELEMETRY_EVENT_started, telemetry_pause, nullptr) == SCS_RESULT_ok); |
||||||
|
|
||||||
|
if (! events_registered) |
||||||
|
{ |
||||||
|
version_params->common.log(SCS_LOG_TYPE_error, "Unable to register event callbacks"); |
||||||
|
return SCS_RESULT_generic_error; |
||||||
|
} |
||||||
|
|
||||||
|
version_params->register_for_event(SCS_TELEMETRY_EVENT_configuration, telemetry_configuration, nullptr); |
||||||
|
version_params->register_for_event(SCS_TELEMETRY_EVENT_gameplay, telemetry_gameplay_event, nullptr); |
||||||
|
|
||||||
|
version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_speed, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry.speed); |
||||||
|
version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_engine_rpm, SCS_U32_NIL, SCS_VALUE_TYPE_float, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_float, &telemetry.rpm); |
||||||
|
version_params->register_for_channel(SCS_TELEMETRY_TRUCK_CHANNEL_engine_gear, SCS_U32_NIL, SCS_VALUE_TYPE_s32, SCS_TELEMETRY_CHANNEL_FLAG_none, telemetry_store_s32, &telemetry.gear); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return SCS_RESULT_ok; |
||||||
|
} |
||||||
|
|
||||||
|
SCSAPI_VOID scs_telemetry_shutdown(void) |
||||||
|
{ |
||||||
|
if (gui != nullptr) |
||||||
|
{ |
||||||
|
gui->setVisible(false); |
||||||
|
gui->saveSettings(); |
||||||
|
gui->shutdown(); |
||||||
|
delete gui; |
||||||
|
} |
||||||
|
if (host != nullptr) |
||||||
|
{ |
||||||
|
host->shutdown(); |
||||||
|
delete host; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
#ifndef TELEMETRY_H |
||||||
|
#define TELEMETRY_H |
||||||
|
|
||||||
|
#include "../libraries/scs-sdk/include/scssdk.h" |
||||||
|
|
||||||
|
struct TelemetryState |
||||||
|
{ |
||||||
|
scs_timestamp_t timestamp = 0; |
||||||
|
// int time_delta = 0;
|
||||||
|
// scs_timestamp_t raw_rendering_timestamp;
|
||||||
|
// scs_timestamp_t raw_simulation_timestamp;
|
||||||
|
// scs_timestamp_t raw_paused_simulation_timestamp;
|
||||||
|
|
||||||
|
bool orientation_available = false; |
||||||
|
float heading = 0; |
||||||
|
float pitch = 0; |
||||||
|
float roll = 0; |
||||||
|
|
||||||
|
float speed = 0; |
||||||
|
float rpm = 0; |
||||||
|
int gear = 0; |
||||||
|
double distance = 0; |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
//enum GameplayEvent
|
||||||
|
//{
|
||||||
|
|
||||||
|
//};
|
||||||
|
|
||||||
|
#endif // TELEMETRY_H
|
Loading…
Reference in new issue