RCON: removed qt based tool

master
Martin Gerhardy 2020-09-20 20:48:58 +02:00
parent 0d8d00a98b
commit 662657d18c
78 changed files with 2 additions and 4285 deletions

View File

@ -150,7 +150,7 @@ jobs:
run: |
mkdir build
cd build
cmake .. -DRCON=OFF -DCMAKE_BUILD_TYPE=Release
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build .
cmake --install . --component voxedit --prefix voxedit-install
cmake --install . --component mapview --prefix mapview-install

View File

@ -48,7 +48,6 @@ option(UNITTESTS "Builds with tests" ON)
option(VISUALTESTS "Builds with visual tests" ON)
option(DISABLE_UNITY "Disable the fast unity build" ON)
option(TOOLS "Builds with tools" ON)
option(RCON "Builds with rcon tool - also needs TOOLS to be active" ON)
option(SERVER "Builds with server" ON)
option(CLIENT "Builds with client" ON)
option(VOXEDIT "Builds voxedit" ON)

View File

@ -17,7 +17,6 @@
* gtest
* opencl (optional)
* libuuid
* qt (for the rcon tool)
* mosquitto (optional)
Some of these dependencies might not be available as packages in your toolchain - most
@ -28,7 +27,7 @@ the higher priority.
```bash
apt-get install libglm-dev lua5.4 liblua5.4-dev libsdl2-dev postgresql-server-dev-all \
libpq-dev libenet-dev qt5-default qttools5-dev qttools5-dev-tools opencl-c-headers \
libpq-dev libenet-dev opencl-c-headers \
wayland-protocols pkg-config uuid-dev libsdl2-mixer-dev libuv1-dev
```

View File

@ -30,11 +30,6 @@ if (TOOLS)
else()
message(STATUS "Don't build noisetool")
endif()
if (RCON)
add_subdirectory(rcon)
else()
message(STATUS "Don't build rcon")
endif()
else()
message(STATUS "Don't build the tools")
endif()

View File

@ -1,87 +0,0 @@
/**
* @file
*/
#include "AIApplication.h"
#include "AIDebugger.h"
#include "AINodeStaticResolver.h"
#include "AIDebuggerWidget.h"
#include "Version.h"
#include "core/Trace.h"
#include <QList>
#include <QString>
#include <QToolBar>
#include <QMenuBar>
#include <QTranslator>
namespace ai {
namespace debug {
AIApplication::AIApplication(int argc, char** argv) :
QApplication(argc, argv) {
core_trace_init();
}
void AIApplication::init() {
#ifdef Q_WS_X11
QApplication::setGraphicsSystem(QLatin1String("raster"));
#endif
setOrganizationName("engine");
setOrganizationDomain("engine");
setApplicationName("rcon");
setApplicationVersion(VERSION);
#ifdef Q_WS_MAC
a.setAttribute(Qt::AA_DontShowIconsInMenus);
#endif
_resolver = new AINodeStaticResolver();
_debugger = createDebugger();
_widget = new AIDebuggerWidget(*_debugger, *_resolver, true);
_window.setCentralWidget(_widget);
QToolBar* _toolbar = _window.addToolBar("");
_toolbar->setMovable(false);
_toolbar->setFloatable(false);
_window.addToolBar(Qt::TopToolBarArea, _toolbar);
_widget->contributeToStatusBar(_window.statusBar());
_widget->contributeToToolBar(_toolbar);
QMenu* menuBar = _window.menuBar()->addMenu(tr("&File"));
_widget->contributeToFileMenu(menuBar);
_widget->contributeToHelpMenu(_window.menuBar()->addMenu(tr("&Help")));
_widget->contributeToSettingsMenu(_window.menuBar()->addMenu(tr("Settings")));
_window.showMaximized();
_window.show();
const QList<QString>& args = QCoreApplication::arguments();
if (args.size() == 3) {
const QString& hostname = args.at(1);
const short port = args.at(2).toShort();
qDebug() << "connect to " << hostname << " on port " << port;
_widget->connectToAIServer(hostname, port);
}
QTranslator qtTranslator;
qtTranslator.load("qt_" + QLocale::system().name(),
QLibraryInfo::location(QLibraryInfo::TranslationsPath));
installTranslator(&qtTranslator);
QTranslator simpleaiTranslator;
simpleaiTranslator.load(applicationName() + "_" + QLocale::system().name(), ":/data/");
installTranslator(&simpleaiTranslator);
}
AIApplication::~AIApplication() {
core_trace_shutdown();
delete _debugger;
delete _resolver;
delete _widget;
}
AIDebugger* AIApplication::createDebugger() {
return new AIDebugger(*_resolver);
}
}
}

View File

@ -1,41 +0,0 @@
/**
* @file
*/
#pragma once
#include <QApplication>
#include <QMainWindow>
namespace ai {
namespace debug {
class AIDebugger;
class AIDebuggerWidget;
class AINodeStaticResolver;
/**
* @brief Use this class to run the debugger as a stand-alone application.
*
* @note If you embed the debugger into an already exsting QT application, you of course
* don't need this.
*
* @ingroup Tools
*/
class AIApplication: public QApplication {
Q_OBJECT
protected:
AIDebugger* _debugger = nullptr;
AINodeStaticResolver* _resolver = nullptr;
AIDebuggerWidget* _widget = nullptr;
QMainWindow _window;
public:
AIApplication(int argc, char** argv);
virtual ~AIApplication();
virtual void init();
virtual AIDebugger* createDebugger();
};
}
}

View File

@ -1,354 +0,0 @@
/**
* @file
*/
#include "AIDebugger.h"
#include "AIDebuggerWidget.h"
#include "AINodeStaticResolver.h"
#include "MapView.h"
#include <QtCore>
#include <vector>
#include "Version.h"
#include "core/Trace.h"
#include "ai-shared/protocol/IProtocolHandler.h"
#include "ai-shared/protocol/AIStateMessage.h"
#include "ai-shared/protocol/AINamesMessage.h"
#include "ai-shared/protocol/AIPauseMessage.h"
#include "ai-shared/protocol/AISelectMessage.h"
#include "ai-shared/protocol/AIStepMessage.h"
#include "ai-shared/protocol/AIChangeMessage.h"
#include "ai-shared/protocol/AIAddNodeMessage.h"
#include "ai-shared/protocol/AIUpdateNodeMessage.h"
#include "ai-shared/protocol/AIDeleteNodeMessage.h"
#include "ai-shared/protocol/AINamesMessage.h"
#include "ai-shared/protocol/AIStubTypes.h"
#include "ai-shared/protocol/AICharacterDetailsMessage.h"
#include "ai-shared/protocol/AICharacterStaticMessage.h"
#include "ai-shared/protocol/ProtocolMessageFactory.h"
#include "ai-shared/protocol/ProtocolHandlerRegistry.h"
namespace ai {
namespace debug {
class StateHandler: public ai::ProtocolHandler<ai::AIStateMessage> {
private:
AIDebugger& _aiDebugger;
public:
StateHandler (AIDebugger& aiDebugger) :
_aiDebugger(aiDebugger) {
}
void execute(const ai::ClientId& /*clientId*/, const ai::AIStateMessage* msg) override {
_aiDebugger.setEntities(msg->getStates());
emit _aiDebugger.onEntitiesUpdated();
}
};
class CharacterHandler: public ai::ProtocolHandler<ai::AICharacterDetailsMessage> {
private:
AIDebugger& _aiDebugger;
public:
CharacterHandler (AIDebugger& aiDebugger) :
_aiDebugger(aiDebugger) {
}
void execute(const ai::ClientId& /*clientId*/, const ai::AICharacterDetailsMessage* msg) override {
_aiDebugger.setCharacterDetails(msg->getCharacterId(), msg->getAggro(), msg->getNode());
emit _aiDebugger.onSelected();
}
};
class CharacterStaticHandler: public ai::ProtocolHandler<ai::AICharacterStaticMessage> {
private:
AIDebugger& _aiDebugger;
public:
CharacterStaticHandler (AIDebugger& aiDebugger) :
_aiDebugger(aiDebugger) {
}
void execute(const ai::ClientId& /*clientId*/, const ai::AICharacterStaticMessage* msg) override {
_aiDebugger.addCharacterStaticData(*msg);
emit _aiDebugger.onSelected();
}
};
class NamesHandler: public ai::ProtocolHandler<ai::AINamesMessage> {
private:
AIDebugger& _aiDebugger;
public:
NamesHandler (AIDebugger& aiDebugger) :
_aiDebugger(aiDebugger) {
}
void execute(const ai::ClientId& /*clientId*/, const ai::AINamesMessage* msg) override {
_aiDebugger.setNames(msg->getNames());
emit _aiDebugger.onNamesReceived();
}
};
class PauseHandler: public ai::ProtocolHandler<ai::AIPauseMessage> {
private:
AIDebugger& _aiDebugger;
public:
PauseHandler (AIDebugger& aiDebugger) :
_aiDebugger(aiDebugger) {
}
void execute(const ai::ClientId& /*clientId*/, const ai::AIPauseMessage* msg) override {
const bool pause = msg->isPause();
_aiDebugger._pause = pause;
emit _aiDebugger.onPause(pause);
}
};
AIDebugger::AIDebugger(AINodeStaticResolver& resolver) :
_stateHandler(new StateHandler(*this)), _characterHandler(new CharacterHandler(*this)), _characterStaticHandler(
new CharacterStaticHandler(*this)), _pauseHandler(new PauseHandler(*this)), _namesHandler(new NamesHandler(*this)), _nopHandler(
new ai::NopHandler()), _selectedId(AI_NOTHING_SELECTED), _socket(this), _pause(false), _resolver(resolver) {
connect(&_socket, SIGNAL(readyRead()), SLOT(readTcpData()));
connect(&_socket, SIGNAL(disconnected()), SLOT(onDisconnect()));
ai::ProtocolHandlerRegistry& r = ai::ProtocolHandlerRegistry::get();
r.registerHandler(ai::PROTO_STATE, _stateHandler);
r.registerHandler(ai::PROTO_CHARACTER_DETAILS, _characterHandler);
r.registerHandler(ai::PROTO_CHARACTER_STATIC, _characterStaticHandler);
r.registerHandler(ai::PROTO_PAUSE, _pauseHandler);
r.registerHandler(ai::PROTO_NAMES, _namesHandler);
r.registerHandler(ai::PROTO_PING, _nopHandler);
}
AIDebugger::~AIDebugger() {
disconnectFromAIServer();
delete _stateHandler;
delete _characterHandler;
delete _characterStaticHandler;
delete _pauseHandler;
delete _namesHandler;
}
bool AIDebugger::isSelected(const ai::AIStateWorld& ai) const {
return _selectedId == ai.getId();
}
void AIDebugger::setCharacterDetails(const ai::CharacterId& id, const ai::AIStateAggro& aggro, const ai::AIStateNode& node) {
_selectedId = id;
_aggro = aggro.getAggro();
_node = node;
_attributes.clear();
const ai::AIStateWorld& state = _entities.value(id);
const ai::CharacterAttributes& attributes = state.getAttributes();
for (auto i = attributes.begin(); i != attributes.end(); ++i) {
_attributes[QString(i->first.c_str())] = QString(i->second.c_str());
}
}
void AIDebugger::addCharacterStaticData(const ai::AICharacterStaticMessage& msg) {
const core::DynamicArray<ai::AIStateNodeStatic>& data = msg.getStaticNodeData();
_resolver.set(data);
}
const ai::CharacterId& AIDebugger::getSelected() const {
return _selectedId;
}
void AIDebugger::togglePause() {
const bool newPauseMode = !_pause;
writeMessage(ai::AIPauseMessage(newPauseMode));
}
void AIDebugger::select(ai::CharacterId id) {
qDebug() << "select " << id;
writeMessage(ai::AISelectMessage(id));
}
void AIDebugger::select(const ai::AIStateWorld& ai) {
const ai::CharacterId id = ai.getId();
select(id);
}
bool AIDebugger::writeMessage(const ai::IProtocolMessage& msg) {
if (_socket.state() != QAbstractSocket::ConnectedState) {
return false;
}
// serialize into streamcontainer to get the final size
ai::streamContainer out;
msg.serialize(out);
// now put the serialized message into the byte array
QByteArray temp;
QDataStream data(&temp, QIODevice::ReadWrite);
// add the framing size int
const uint32_t size = out.size();
ai::streamContainer sizeC;
ai::IProtocolMessage::addInt(sizeC, size);
for (ai::streamContainer::iterator i = sizeC.begin(); i != sizeC.end(); ++i) {
const uint8_t byte = *i;
data << byte;
}
// add the real message
for (ai::streamContainer::iterator i = out.begin(); i != out.end(); ++i) {
const uint8_t byte = *i;
data << byte;
}
// now write everything to the socket
_socket.write(temp);
_socket.flush();
return true;
}
void AIDebugger::unselect() {
writeMessage(ai::AISelectMessage(AI_NOTHING_SELECTED));
_selectedId = AI_NOTHING_SELECTED;
_aggro.clear();
_node = ai::AIStateNode();
_attributes.clear();
emit onSelected();
qDebug() << "unselect entity";
}
void AIDebugger::step() {
writeMessage(ai::AIStepMessage(1L));
}
void AIDebugger::reset() {
writeMessage(ai::AIResetMessage());
}
void AIDebugger::change(const QString& name) {
writeMessage(ai::AIChangeMessage(name.toStdString().c_str()));
}
void AIDebugger::updateNode(int32_t nodeId, const QVariant& name, const QVariant& type, const QVariant& condition) {
writeMessage(ai::AIUpdateNodeMessage(nodeId, _selectedId, name.toString().toStdString().c_str(), type.toString().toStdString().c_str(), condition.toString().toStdString().c_str()));
}
void AIDebugger::deleteNode(int32_t nodeId) {
writeMessage(ai::AIDeleteNodeMessage(nodeId, _selectedId));
}
void AIDebugger::addNode(int32_t parentNodeId, const QVariant& name, const QVariant& type, const QVariant& condition) {
writeMessage(ai::AIAddNodeMessage(parentNodeId, _selectedId, name.toString().toStdString().c_str(),
type.toString().toStdString().c_str(),
condition.toString().toStdString().c_str()));
}
bool AIDebugger::connectToAIServer(const QString& hostname, short port) {
disconnectFromAIServer();
qDebug() << "connect to server: " << hostname << ":" << port;
_socket.connectToHost(hostname, port, QAbstractSocket::ReadWrite, QAbstractSocket::AnyIPProtocol);
if (_socket.waitForConnected()) {
qDebug() << "Connection established " << _socket.state();
return true;
}
const QAbstractSocket::SocketError socketError = _socket.error();
switch (socketError) {
case QAbstractSocket::RemoteHostClosedError:
qDebug() << "The connection was closed by the host";
break;
case QAbstractSocket::HostNotFoundError:
qDebug() << "The host was not found. Please check the host name and port settings";
break;
case QAbstractSocket::ConnectionRefusedError:
qDebug() << "The connection was refused by the peer";
break;
default:
qDebug() << "Socket error: " << socketError;
break;
}
return false;
}
bool AIDebugger::disconnectFromAIServer() {
qDebug() << "disconnect from server";
_socket.disconnectFromHost();
_socket.waitForDisconnected();
_socket.close();
return true;
}
void AIDebugger::onDisconnect() {
qDebug() << "disconnect from server: " << _socket.state();
{
_pause = false;
emit onPause(_pause);
}
{
_selectedId = AI_NOTHING_SELECTED;
_aggro.clear();
_attributes.clear();
_node = ai::AIStateNode();
emit onSelected();
}
if (!_names.empty()) {
_names.clear();
emit onNamesReceived();
}
if (!_entities.empty()) {
_entities.clear();
emit onEntitiesUpdated();
}
}
void AIDebugger::readTcpData() {
while (_socket.bytesAvailable() > 0) {
const QByteArray& data = _socket.readAll();
// read everything that is currently available from the socket
// and store it in our buffer
const int n = data.count();
for (int i = 0; i < n; ++i) {
_stream.push_back(data[i]);
}
ai::ProtocolMessageFactory& mf = ai::ProtocolMessageFactory::get();
for (;;) {
if (!mf.isNewMessageAvailable(_stream)) {
break;
}
// don't free this - preallocated memory that is reused
ai::IProtocolMessage* msg = mf.create(_stream);
if (msg == nullptr) {
qDebug() << "unknown server message - disconnecting";
disconnectFromAIServer();
break;
}
ai::ProtocolHandlerRegistry& r = ai::ProtocolHandlerRegistry::get();
ai::IProtocolHandler* handler = r.getHandler(*msg);
if (handler != nullptr) {
handler->execute(1, *msg);
} else {
qDebug() << "no handler for " << msg->getId();
disconnectFromAIServer();
break;
}
}
}
}
MapView* AIDebugger::createMapWidget() {
return new MapView(*this);
}
void AIDebugger::setNames(const core::DynamicArray<core::String>& names) {
core_trace_scoped(SetNames);
_names.clear();
_names.reserve(names.size());
for (const core::String& name : names) {
_names << QString(name.c_str());
}
}
void AIDebugger::setEntities(const core::DynamicArray<ai::AIStateWorld>& entities) {
core_trace_scoped(SetEntities);
_entities.clear();
for (const ai::AIStateWorld& state : entities) {
_entities.insert(state.getId(), state);
}
if (_selectedId == AI_NOTHING_SELECTED) {
return;
}
if (_entities.contains(_selectedId)) {
return;
}
// TODO: this doesn't work for some reason
// unselect();
}
}
}

View File

@ -1,154 +0,0 @@
/**
* @file
*/
#pragma once
#include "ai-shared/protocol/IProtocolHandler.h"
#include "ai-shared/protocol/AICharacterStaticMessage.h"
#include "ai-shared/protocol/AICharacterDetailsMessage.h"
#include <utility>
#include <QTcpSocket>
#include <QSettings>
#include <QFile>
#include <QStringList>
#include <QMap>
#include <QHash>
namespace ai {
namespace debug {
class MapView;
class AIDebuggerWidget;
class AINodeStaticResolver;
class PauseHandler;
/**
* @brief This is the remote debugger for the ai entities.
*
* You can extend this class and override AIDebugger::createMapWidget to create our own @c MapView instance
* to render additional details to your characters or even the map the entities are spawned on.
*/
class AIDebugger: public QObject {
Q_OBJECT
friend class PauseHandler;
public:
typedef QHash<ai::CharacterId, ai::AIStateWorld> Entities;
typedef Entities::const_iterator EntitiesIter;
typedef QMap<QString, QString> CharacterAttributesMap;
protected:
typedef Entities::iterator Iter;
// all the entities that are send by the ai debug server
Entities _entities;
// the network protocol message handlers
ai::IProtocolHandler *_stateHandler;
ai::IProtocolHandler *_characterHandler;
ai::IProtocolHandler *_characterStaticHandler;
ai::IProtocolHandler *_pauseHandler;
ai::IProtocolHandler *_namesHandler;
ai::IProtocolHandler *_nopHandler;
// The buffer where we store our network data until we can read one complete protocol message.
ai::streamContainer _stream;
// the current selected entity id
ai::CharacterId _selectedId;
// the aggro list of the current selected entity
core::DynamicArray<ai::AIStateAggroEntry> _aggro;
// the behaviour tree states of the current selected entity
ai::AIStateNode _node;
// the attributes of the current selected entity
CharacterAttributesMap _attributes;
// the socket of the ai debug server
QTcpSocket _socket;
bool _pause;
QStringList _names;
AINodeStaticResolver& _resolver;
bool writeMessage(const ai::IProtocolMessage& msg);
private slots:
void readTcpData();
void onDisconnect();
public:
AIDebugger(AINodeStaticResolver& resolver);
virtual ~AIDebugger();
/**
* @return The list of ai controlled entities
*/
const Entities& getEntities() const;
void setEntities(const core::DynamicArray<ai::AIStateWorld>& entities);
void setCharacterDetails(const ai::CharacterId& id, const ai::AIStateAggro& aggro, const ai::AIStateNode& node);
void addCharacterStaticData(const ai::AICharacterStaticMessage& msg);
void setNames(const core::DynamicArray<core::String>& names);
const QStringList& getNames() const;
/**
* @return The behaviour tree node that is assigned to the selected entity
*/
const ai::AIStateNode& getNode() const;
/**
* @return The key/value pairs of attributes that are assigned on the server side to the selected ai entity
*/
const CharacterAttributesMap& getAttributes() const;
const core::DynamicArray<ai::AIStateAggroEntry>& getAggro() const;
/**
* @brief Start the debugger - call this from your main method
*/
int run();
bool connectToAIServer(const QString& hostname, short port);
bool disconnectFromAIServer();
bool isSelected(const ai::AIStateWorld& ai) const;
const ai::CharacterId& getSelected() const;
void select(const ai::AIStateWorld& ai);
void select(ai::CharacterId id);
void togglePause();
void unselect();
void step();
void reset();
void change(const QString& name);
void updateNode(int32_t nodeId, const QVariant& name, const QVariant& type, const QVariant& condition);
void deleteNode(int32_t nodeId);
void addNode(int32_t parentNodeId, const QVariant& name, const QVariant& type, const QVariant& condition);
/**
* @brief override this if you would like to create your own @c MapView implementation that renders
* for example more details of your map.
*/
virtual MapView* createMapWidget();
signals:
void onPause(bool pause);
void disconnected();
// signal that is triggered whenever the entity details for the current selected entity arrived
void onSelected();
// new names list was received
void onNamesReceived();
// entities on the map were updated
void onEntitiesUpdated();
};
inline const core::DynamicArray<ai::AIStateAggroEntry>& AIDebugger::getAggro() const {
return _aggro;
}
inline const ai::AIStateNode& AIDebugger::getNode() const {
return _node;
}
inline const AIDebugger::CharacterAttributesMap& AIDebugger::getAttributes() const {
return _attributes;
}
inline const AIDebugger::Entities& AIDebugger::getEntities() const {
return _entities;
}
inline const QStringList& AIDebugger::getNames() const {
return _names;
}
}
}

View File

@ -1,100 +0,0 @@
find_package(Qt5Widgets)
find_package(Qt5Network)
find_package(Qt5Gui)
if (${Qt5Widgets_FOUND} AND ${Qt5Network_FOUND} AND ${Qt5Gui_FOUND})
project(rcon)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
qt5_add_resources(rcon_RESOURCES data/resources.qrc)
set(SRCS
gui/action/Action.h
gui/action/AddAction.h
gui/action/DeleteAction.h
gui/AICompressorProxy.h
gui/dialog/AddDialog.h
gui/dialog/IDialog.h
gui/dialog/ConnectDialog.h
gui/dialog/SettingsDialog.h
gui/widget/IComboBox.h
gui/AINodeStaticResolver.h
gui/view/map/MapItem.h
gui/view/map/MapView.h
gui/view/tree/TreeViewCommon.h
gui/view/tree/BehaviourTreeModel.h
gui/view/tree/NodeTreeView.h
gui/view/tree/BehaviourTreeModelItem.h
gui/view/tree/NodeTreeItem.h
gui/view/list/EntityListModel.h
gui/view/list/EntityList.h
gui/view/aggro/AggroTable.h
gui/view/aggro/AggroTableModel.h
gui/view/state/StateTableModel.h
gui/view/state/StateTable.h
gui/AIDebuggerWidget.h
AIApplication.h
AIDebugger.h
common/Settings.h
Version.h
Main.cpp
AIApplication.cpp
AIDebugger.cpp
rcon/RconAIApplication.h
rcon/RconAIDebugger.h
rcon/RconMapItem.h
rcon/RconMapView.h
gui/AIDebuggerWidget.cpp
gui/AINodeStaticResolver.cpp
gui/dialog/AddDialog.cpp
gui/dialog/ConnectDialog.cpp
gui/dialog/SettingsDialog.cpp
gui/dialog/IDialog.cpp
gui/view/list/EntityList.cpp
gui/view/list/EntityListModel.cpp
gui/view/map/MapView.cpp
gui/view/map/MapItem.cpp
gui/view/tree/BehaviourTreeModel.cpp
gui/view/tree/BehaviourTreeModelItem.cpp
gui/view/tree/NodeTreeItem.cpp
gui/view/tree/NodeTreeView.cpp
gui/view/tree/TreeViewCommon.cpp
gui/view/state/StateTable.cpp
gui/view/state/StateTableModel.cpp
gui/view/aggro/AggroTable.cpp
gui/view/aggro/AggroTableModel.cpp
gui/widget/IComboBox.cpp
data/resources.qrc
)
set_property(GLOBAL PROPERTY ${PROJECT_NAME}_NOUNITY TRUE)
engine_add_executable(TARGET ${PROJECT_NAME} SRCS ${SRCS} WINDOWED)
engine_target_link_libraries(TARGET ${PROJECT_NAME} DEPENDENCIES ai-shared shared)
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${AUTOGEN_TARGETS_FOLDER}
${AUTOMOC_TARGETS_FOLDER}
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/gui
${CMAKE_CURRENT_SOURCE_DIR}/gui/action
${CMAKE_CURRENT_SOURCE_DIR}/gui/dialog
${CMAKE_CURRENT_SOURCE_DIR}/gui/widget
${CMAKE_CURRENT_SOURCE_DIR}/gui/view/map
${CMAKE_CURRENT_SOURCE_DIR}/gui/view/tree
${CMAKE_CURRENT_SOURCE_DIR}/gui/view/aggro
${CMAKE_CURRENT_SOURCE_DIR}/gui/view/state
${CMAKE_CURRENT_SOURCE_DIR}/gui/view/list
${CMAKE_CURRENT_SOURCE_DIR}/common
)
qt5_use_modules(${PROJECT_NAME} Widgets Gui Network)
engine_target_link_libraries(TARGET ${PROJECT_NAME} DEPENDENCIES network)
message(STATUS "Build the rcon")
else()
message(WARNING "Could not findl qt5 modules for network gui and widget support")
endif() # NO QT

View File

@ -1,20 +0,0 @@
/**
* @file
*/
#include "rcon/RconAIApplication.h"
#include "rcon/RconAIDebugger.h"
#include "AIDebuggerWidget.h"
#ifdef AI_PROFILER
#include <google/profiler.h>
#endif
int main(int argc, char **argv) {
#ifdef AI_PROFILER
ProfilerStart("simpleai-debugger.prof");
#endif
rcon::RconAIApplication app(argc, argv);
app.init();
return rcon::RconAIApplication::exec();
}

View File

@ -1,6 +0,0 @@
/**
* @file
*/
#pragma once
#define VERSION "0.1"

View File

@ -1 +0,0 @@
IDI_ICON1 ICON DISCARDABLE "simpleai-debugger.ico"

View File

@ -1,102 +0,0 @@
#pragma once
#include <QSettings>
class Settings {
private:
QSettings _settings;
public:
static QSettings& getSettings() {
static Settings s;
return s._settings;
}
static QString getHostname(const QString& defaultVal) {
return getSettings().value("connect/hostname", defaultVal).toString();
}
static int getPort(int defaultVal) {
return getSettings().value("connect/port", defaultVal).toInt();
}
static void setHostname(const QString& val) {
getSettings().setValue("connect/hostname", val);
}
static void setPort(int val) {
getSettings().setValue("connect/port", val);
}
static int getGridInterval(int defaultVal = 100) {
return getSettings().value("mapview/gridinterval", defaultVal).toInt();
}
static void setGridInterval(int val) {
getSettings().setValue("mapview/gridinterval", val);
}
static float getItemSize(float defaultVal = 30.0) {
return getSettings().value("mapview/itemsize", defaultVal).toFloat();
}
static void setItemSize(float val) {
getSettings().setValue("mapview/itemsize", val);
}
static QColor getGridColor(const QColor& defaultVal = QColor(80, 80, 80)) {
return getSettings().value("mapview/gridcolor", defaultVal).value<QColor>();
}
static QColor getBackgroundColor(const QColor& defaultVal = QColor(50, 50, 50)) {
return getSettings().value("mapview/bgcolor", defaultVal).value<QColor>();
}
static QColor getNameColor(const QColor& defaultVal = QColor(255, 255, 255)) {
return getSettings().value("mapview/namecolor", defaultVal).value<QColor>();
}
static bool getGrid(bool defaultVal = true) {
return getSettings().value("mapview/showgrid", defaultVal).toBool();
}
static QString getNameAttribute(const QString& defaultVal) {
return getSettings().value("mapview/nameattribute", defaultVal).toString();
}
static void setNameAttribute(const QString& attribute) {
return getSettings().setValue("mapview/nameattribute", attribute);
}
static QString getGroupAttribute(const QString& defaultVal) {
return getSettings().value("mapview/groupattribute", defaultVal).toString();
}
static void setGroupAttribute(const QString& attribute) {
return getSettings().setValue("mapview/groupattribute", attribute);
}
static bool getCenterOnSelection(bool defaultVal = false) {
return getSettings().value("mapview/centeronselection", defaultVal).toBool();
}
static void setCenterOnSelection(bool val) {
getSettings().setValue("mapview/centeronselection", val);
}
static void setGridColor(const QColor& val) {
getSettings().setValue("mapview/gridcolor", val);
}
static void setBackgroundColor(const QColor& val) {
getSettings().setValue("mapview/bgcolor", val);
}
static void setNameColor(const QColor& val) {
getSettings().setValue("mapview/namecolor", val);
}
static void setGrid(bool val) {
getSettings().setValue("mapview/showgrid", val);
}
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 956 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 903 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 904 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 901 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,19 +0,0 @@
<RCC>
<qresource prefix="/">
<file>images/splash.png</file>
<file>images/disconnect.png</file>
<file>images/connect.png</file>
<file>images/step.png</file>
<file>images/reset.png</file>
<file>images/continue.png</file>
<file>images/exit.png</file>
<file>images/about.png</file>
<file>images/pause.png</file>
<file>images/zoomin.png</file>
<file>images/zoomout.png</file>
<file>images/switch.png</file>
<file>images/node.png</file>
<file>images/selector.png</file>
<file>images/priorityselector.png</file>
</qresource>
</RCC>

View File

@ -1,61 +0,0 @@
/**
* @file
*/
#pragma once
#include <QApplication>
#include <vector>
#include <string>
class CompressorProxy: public QObject {
Q_OBJECT
private:
inline bool emitCheck(bool & flag) {
flag = true;
QCoreApplication::sendPostedEvents(this, QEvent::MetaCall); // recurse
bool result = flag;
flag = false;
return result;
}
bool _selected;
bool _entitiesUpdated;
bool _namesReceived;
private slots:
void selected() {
if (!emitCheck(_selected)) {
return;
}
emit onSelected();
}
void entitiesUpdated() {
if (!emitCheck(_entitiesUpdated)) {
return;
}
emit onEntitiesUpdated();
}
void namesReceived() {
if (!emitCheck(_namesReceived)) {
return;
}
emit onNamesReceived();
}
signals:
// signal that is triggered whenever the entity details for the current selected entity arrived
void onSelected();
// new names list was received
void onNamesReceived();
// entities on the map were updated
void onEntitiesUpdated();
public:
// No default constructor, since the proxy must be a child of the
// target object.
explicit CompressorProxy(QObject * parentObj) :
QObject(parentObj), _selected(false), _entitiesUpdated(false), _namesReceived(false) {
}
};

View File

@ -1,452 +0,0 @@
/**
* @file
*/
#include <QMenuBar>
#include <QMessageBox>
#include <QStatusBar>
#include <QSplitter>
#include <QTreeView>
#include <QDesktopServices>
#include <QPushButton>
#include <QUrl>
#include <QHeaderView>
#include "core/Trace.h"
#include "AIDebugger.h"
#include "AIDebuggerWidget.h"
#include "ConnectDialog.h"
#include "StateTable.h"
#include "EntityList.h"
#include "AggroTable.h"
#include "MapView.h"
#include "NodeTreeView.h"
#include "AddAction.h"
#include "DeleteAction.h"
#include "SettingsDialog.h"
#include "Settings.h"
namespace ai {
namespace debug {
AIDebuggerWidget::AIDebuggerWidget(AIDebugger& debugger, AINodeStaticResolver& resolver, bool standalone) :
_resolver(resolver), _model(debugger, resolver), _debugger(debugger), _proxy(this), _standalone(standalone) {
createView();
createActions();
_statusBarLabel = new QLabel(tr("not connected"));
_selectedLabel = new QLabel(tr("nothing selected"));
connect(_namesComboBox, SIGNAL(currentIndexChanged(const QString &)), SLOT(change(const QString &)));
connect(&_debugger, SIGNAL(onPause(bool)), SLOT(setPause(bool)));
connect(&_debugger, SIGNAL(disconnected()), SLOT(onDisconnect()));
connect(&_debugger, SIGNAL(onSelected()), &_proxy, SLOT(selected()), Qt::QueuedConnection);
connect(&_debugger, SIGNAL(onNamesReceived()), &_proxy, SLOT(namesReceived()), Qt::QueuedConnection);
connect(&_debugger, SIGNAL(onEntitiesUpdated()), &_proxy, SLOT(entitiesUpdated()), Qt::QueuedConnection);
connect(&_proxy, SIGNAL(onSelected()), this, SLOT(onSelected()));
connect(&_proxy, SIGNAL(onEntitiesUpdated()), this, SLOT(onEntitiesUpdated()));
connect(&_proxy, SIGNAL(onNamesReceived()), this, SLOT(onNamesReceived()));
}
AIDebuggerWidget::~AIDebuggerWidget() {
delete _nodeTree;
delete _entityList;
delete _statusBarLabel;
delete _selectedLabel;
delete _stateTable;
delete _aggroTable;
delete _pauseAction;
delete _quitAction;
delete _resetAction;
delete _stepAction;
delete _connectAction;
delete _disconnectAction;
delete _aboutAction;
delete _documentationAction;
delete _bugAction;
delete _settingsAction;
delete _namesComboBox;
delete _tree;
}
void AIDebuggerWidget::onEntitiesUpdated() {
_entityList->updateEntityList();
_mapWidget->updateMapView();
}
void AIDebuggerWidget::onSelected() {
core_trace_scoped(OnSelected);
if (_model.editMode()) {
_model.abortEditMode();
}
const ai::CharacterId& id = _debugger.getSelected();
if (id == -1) {
_selectedLabel->setText(tr("nothing selected"));
} else {
_selectedLabel->setText(tr("selected %1").arg(id));
}
_model.setRootNode(const_cast<AIStateNode*>(&_debugger.getNode()));
_stateTable->updateStateTable();
_nodeTree->updateTreeWidget();
_tree->expandAll();
_aggroTable->updateAggroTable();
if (Settings::getCenterOnSelection()) {
_mapWidget->center(id);
} else {
_mapWidget->makeVisible(id);
}
}
void AIDebuggerWidget::onNamesReceived() {
core_trace_scoped(OnNamesReceived);
const QString name = _namesComboBox->currentText();
_namesComboBox->clear();
const QStringList& names = _debugger.getNames();
if (names.empty()) {
_namesComboBox->insertItem(0, tr("None"));
_namesComboBox->setEnabled(false);
} else {
_namesComboBox->insertItems(0, names);
_namesComboBox->setEnabled(true);
}
const int index = _namesComboBox->findText(name);
if (index == -1) {
if (!names.empty()) {
_namesComboBox->setCurrentIndex(0);
}
return;
}
_namesComboBox->setCurrentIndex(index);
}
void AIDebuggerWidget::contributeToStatusBar(QStatusBar* statusBar) {
statusBar->addWidget(_statusBarLabel);
statusBar->addWidget(_selectedLabel);
}
void AIDebuggerWidget::contributeToToolBar(QToolBar* toolBar) {
toolBar->addAction(_connectAction);
toolBar->addAction(_pauseAction);
toolBar->addAction(_stepAction);
toolBar->addAction(_resetAction);
}
void AIDebuggerWidget::contributeToFileMenu(QMenu *fileMenu) {
fileMenu->addAction(_connectAction);
fileMenu->addAction(_disconnectAction);
if (_standalone) {
fileMenu->addAction(_quitAction);
}
}
void AIDebuggerWidget::contributeToHelpMenu(QMenu *helpMenu) {
helpMenu->addAction(_documentationAction);
helpMenu->addAction(_bugAction);
helpMenu->addAction(_aboutAction);
}
void AIDebuggerWidget::contributeToSettingsMenu(QMenu *settingsMenu) {
settingsMenu->addAction(_settingsAction);
}
void AIDebuggerWidget::removeFromSettingsMenu(QMenu *settingsMenu) {
settingsMenu->removeAction(_settingsAction);
}
void AIDebuggerWidget::removeFromStatusBar(QStatusBar* statusBar) {
statusBar->removeWidget(_statusBarLabel);
statusBar->removeWidget(_selectedLabel);
}
void AIDebuggerWidget::removeFromToolBar(QToolBar* toolBar) {
toolBar->removeAction(_connectAction);
toolBar->removeAction(_pauseAction);
toolBar->removeAction(_stepAction);
toolBar->removeAction(_resetAction);
}
void AIDebuggerWidget::removeFromFileMenu(QMenu *fileMenu) {
fileMenu->removeAction(_connectAction);
fileMenu->removeAction(_disconnectAction);
if (_standalone) {
fileMenu->removeAction(_quitAction);
}
}
void AIDebuggerWidget::removeFromHelpMenu(QMenu *helpMenu) {
helpMenu->removeAction(_bugAction);
helpMenu->removeAction(_documentationAction);
helpMenu->removeAction(_aboutAction);
}
void AIDebuggerWidget::createView() {
QVBoxLayout *boxLayout = new QVBoxLayout();
QSplitter* splitter = new QSplitter(Qt::Orientation::Vertical);
splitter->addWidget(createTopWidget());
splitter->addWidget(createBottomWidget());
boxLayout->addWidget(splitter);
setLayout(boxLayout);
}
QWidget *AIDebuggerWidget::createTopWidget() {
QSplitter *splitter = new QSplitter();
_mapWidget = _debugger.createMapWidget();
_entityFilter = new QLineEdit();
_entityList = new EntityList(_debugger, _entityFilter);
_namesComboBox = new QComboBox();
_namesComboBox->setFixedWidth(_entityList->width());
_namesComboBox->setInsertPolicy(QComboBox::InsertAlphabetically);
_namesComboBox->addItem(tr("None"));
splitter->addWidget(_mapWidget);
QVBoxLayout *vbox = new QVBoxLayout();
vbox->setMargin(0);
vbox->addWidget(_namesComboBox);
vbox->addWidget(_entityFilter);
vbox->addWidget(_entityList);
QWidget *widget = new QWidget();
widget->setFixedWidth(_entityList->width());
widget->setLayout(vbox);
splitter->addWidget(widget);
return splitter;
}
void AIDebuggerWidget::onDeleteNode(int nodeId) {
_debugger.deleteNode(nodeId);
}
void AIDebuggerWidget::onAddNode(int parentNodeId, const QVariant& name, const QVariant& type, const QVariant& condition) {
_debugger.addNode(parentNodeId, name, type, condition);
}
void AIDebuggerWidget::showContextMenu(const QPoint &contextMenuPos) {
const QModelIndex& index = _tree->indexAt(contextMenuPos);
BehaviourTreeModelItem* item = _model.item(index);
if (item == nullptr) {
qDebug() << "No item found for index: " << index;
return;
}
const AIStateNode* node = item->node();
QMenu *contextMenu = new QMenu(this);
AddAction* actionAdd = new AddAction(node->getNodeId(), this);
connect(actionAdd, SIGNAL(triggered(int, const QVariant&, const QVariant&, const QVariant&)), this,
SLOT(onAddNode(int, const QVariant&, const QVariant&, const QVariant&)));
QAction* actionDelete = new DeleteAction(node->getNodeId(), this);
connect(actionDelete, SIGNAL(triggered(int)), this, SLOT(onDeleteNode(int)));
contextMenu->addAction(actionAdd);
contextMenu->addAction(actionDelete);
contextMenu->popup(_tree->viewport()->mapToGlobal(contextMenuPos));
}
QWidget *AIDebuggerWidget::createTreePanelWidget() {
QWidget* treePanel = new QWidget();
treePanel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
_nodeTree = new NodeTreeView(_debugger, _resolver);
_nodeTree->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
_nodeTree->setVisible(false);
_tree = new QTreeView();
_tree->setUniformRowHeights(true);
_tree->setAlternatingRowColors(true);
_tree->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
_tree->setModel(&_model);
_tree->setContextMenuPolicy(Qt::CustomContextMenu);
connect(_tree, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(showContextMenu(const QPoint &)));
QHeaderView *header = _tree->header();
header->setStretchLastSection(false);
header->setSectionResizeMode(QHeaderView::Interactive);
#if 0
header->setSectionResizeMode(COL_STATE, QHeaderView::ResizeToContents);
header->setSectionResizeMode(COL_LASTRUN, QHeaderView::ResizeToContents);
#endif
QPushButton *toggle = new QPushButton(QIcon(":/images/switch.png"), "");
toggle->setFlat(true);
toggle->setCheckable(true);
toggle->setFixedSize(16, 16);
toggle->setToolTip(tr("Switch between tree views"));
connect(toggle, SIGNAL(released()), this, SLOT(toggleTreeView()));
toggle->raise();
QGridLayout *treeLayout = new QGridLayout();
treeLayout->setColumnStretch(0, 10);
treeLayout->setRowStretch(0, 10);
treeLayout->addWidget(_nodeTree, 0, 0);
treeLayout->addWidget(_tree, 0, 0);
treeLayout->addWidget(toggle, 0, 0, Qt::AlignRight | Qt::AlignTop);
treePanel->setLayout(treeLayout);
return treePanel;
}
QWidget *AIDebuggerWidget::createBottomWidget() {
QSplitter *splitter = new QSplitter();
_aggroTable = new AggroTable(_debugger);
_stateTable = new StateTable(_debugger);
QWidget* treePanel = createTreePanelWidget();
splitter->addWidget(treePanel);
splitter->setStretchFactor(splitter->indexOf(treePanel), 5);
splitter->addWidget(_aggroTable);
splitter->setStretchFactor(splitter->indexOf(_aggroTable), 1);
splitter->addWidget(_stateTable);
splitter->setStretchFactor(splitter->indexOf(_stateTable), 1);
return splitter;
}
void AIDebuggerWidget::onDisconnect() {
_statusBarLabel->setText(tr("not connected"));
}
void AIDebuggerWidget::change(const QString &name) {
_name = name;
_debugger.change(name);
}
void AIDebuggerWidget::setPause(bool pause) {
if (pause) {
_pauseAction->setIcon(QIcon(":/images/continue.png"));
} else {
_pauseAction->setIcon(QIcon(":/images/pause.png"));
}
}
void AIDebuggerWidget::requestStep() {
_debugger.step();
}
void AIDebuggerWidget::requestReset() {
_debugger.reset();
}
void AIDebuggerWidget::requestPause() {
_debugger.togglePause();
}
void AIDebuggerWidget::connectToAIServer(const QString& hostname, short port) {
if (_debugger.connectToAIServer(hostname, port)) {
_statusBarLabel->setText(tr("connected to %1:%2").arg(hostname).arg(port));
} else {
_statusBarLabel->setText(tr("connection to %1:%2 failed").arg(hostname).arg(port));
}
}
void AIDebuggerWidget::quitApplication() {
QApplication::quit();
}
void AIDebuggerWidget::disconnectFromAIServer() {
_debugger.disconnectFromAIServer();
}
void AIDebuggerWidget::connectToAIServer() {
ConnectDialog d;
const int state = d.run();
if (state != QDialog::Accepted) {
_statusBarLabel->setText(tr("not connected"));
return;
}
const short port = d.getPort();
const QString& hostname = d.getHostname();
connectToAIServer(hostname, port);
}
void AIDebuggerWidget::about() {
QMessageBox::about(this, tr("About"), tr("AI debug visualization for libsimpleai.<br />Grab the latest version at <a href=\"https://github.com/mgerhardy/simpleai\">github</a>"));
}
void AIDebuggerWidget::documentation() {
QDesktopServices::openUrl(QUrl("https://github.com/mgerhardy/simpleai/wiki"));
}
void AIDebuggerWidget::settings() {
SettingsDialog d;
d.run();
}
void AIDebuggerWidget::bug() {
QDesktopServices::openUrl(QUrl("https://github.com/mgerhardy/simpleai/issues"));
}
void AIDebuggerWidget::toggleTreeView() {
if (_nodeTree->isVisible()) {
_nodeTree->setVisible(false);
_tree->setVisible(true);
} else {
_nodeTree->setVisible(true);
_tree->setVisible(false);
}
}
void AIDebuggerWidget::createActions() {
_disconnectAction = new QAction(tr("Disconnect"), this);
_disconnectAction->setShortcuts(QKeySequence::Close);
_disconnectAction->setStatusTip(tr("Disconnect from AI server"));
_disconnectAction->setIcon(QIcon(":/images/disconnect.png"));
connect(_disconnectAction, SIGNAL(triggered()), this, SLOT(disconnectFromAIServer()));
_connectAction = new QAction(tr("C&onnect"), this);
_connectAction->setShortcuts(QKeySequence::Open);
_connectAction->setStatusTip(tr("Connect to AI server"));
_connectAction->setIcon(QIcon(":/images/connect.png"));
connect(_connectAction, SIGNAL(triggered()), this, SLOT(connectToAIServer()));
_quitAction = new QAction(tr("Quit"), this);
connect(_quitAction, SIGNAL(triggered()), this, SLOT(quitApplication()));
_pauseAction = new QAction(tr("Pause"), this);
_pauseAction->setStatusTip(tr("Freeze the ai controlled entities"));
_pauseAction->setIcon(QIcon(":/images/pause.png"));
connect(_pauseAction, SIGNAL(triggered()), this, SLOT(requestPause()));
_stepAction = new QAction(tr("Step"), this);
_stepAction->setStatusTip(tr("Performs one step while ai is in pause mode"));
_stepAction->setIcon(QIcon(":/images/step.png"));
connect(_stepAction, SIGNAL(triggered()), this, SLOT(requestStep()));
_resetAction = new QAction(tr("Reset"), this);
_resetAction->setStatusTip(tr("Resets the states of the ai"));
_resetAction->setIcon(QIcon(":/images/reset.png"));
connect(_resetAction, SIGNAL(triggered()), this, SLOT(requestReset()));
_aboutAction = new QAction(tr("&About"), this);
_aboutAction->setStatusTip(tr("Show the application's About box"));
_aboutAction->setIcon(QIcon(":/images/about.png"));
connect(_aboutAction, SIGNAL(triggered()), this, SLOT(about()));
_documentationAction = new QAction(tr("&Documentation"), this);
_documentationAction->setStatusTip(tr("Open the libsimpleai documentation"));
_documentationAction->setIcon(QIcon(":/images/docs.png"));
connect(_documentationAction, SIGNAL(triggered()), this, SLOT(documentation()));
_bugAction = new QAction(tr("&Report a bug"), this);
_bugAction->setStatusTip(tr("Report a bug"));
_bugAction->setIcon(QIcon(":/images/bug.png"));
connect(_bugAction, SIGNAL(triggered()), this, SLOT(bug()));
_settingsAction = new QAction(tr("Settings"), this);
_settingsAction->setStatusTip(tr("Settings"));
_settingsAction->setIcon(QIcon(":/images/settings.png"));
connect(_settingsAction, SIGNAL(triggered()), this, SLOT(settings()));
}
QLabel *AIDebuggerWidget::createLabel(const QString &text) const {
QLabel *label = new QLabel(text);
label->setAlignment(Qt::AlignCenter);
label->setMargin(2);
label->setFrameStyle(QFrame::Box | QFrame::Sunken);
return label;
}
}
}

View File

@ -1,149 +0,0 @@
/**
* @file
*/
#pragma once
#include <QtGui>
#include <QLabel>
#include <QToolBar>
#include <QStatusBar>
#include <QComboBox>
#include <QMenu>
#include <QLineEdit>
#include <QTreeView>
#include "BehaviourTreeModel.h"
#include "AICompressorProxy.h"
#include "AINodeStaticResolver.h"
#include "BehaviourTreeModelItem.h"
namespace ai {
namespace debug {
class StateTable;
class EntityList;
class AggroTable;
class NodeTreeView;
class MapView;
class AIDebugger;
/**
* @brief The widget that represents the whole ai debugger
*/
class AIDebuggerWidget: public QWidget {
Q_OBJECT
public:
/**
* @param[in] standalone If this is @c true, the widget will e.g. contribute a quit action to the file menu.
* If this false, the widget will behave as if it would be part of an already existing appliation.
*/
AIDebuggerWidget(AIDebugger& debugger, AINodeStaticResolver& resolver, bool standalone);
virtual ~AIDebuggerWidget();
void connectToAIServer(const QString& hostname, short port);
/**
* @brief Call this with an existing @c QStatusBar to add additional ai debugger information to it
*/
void contributeToStatusBar(QStatusBar* statusBar);
/**
* @brief Call this with an existing @c QToolbar to add addition ai debugger buttons to it
*/
void contributeToToolBar(QToolBar* toolBar);
/**
* @brief Call this with an existing @c QToolbar to add addition ai debugger entries to it
*/
void contributeToFileMenu(QMenu *fileMenu);
/**
* @brief Call this with an existing @c QToolbar to add addition ai debugger entries to it
*/
void contributeToHelpMenu(QMenu *helpMenu);
/**
* @brief Call this with an existing @c QToolbar to add addition ai debugger entries to it
*/
void contributeToSettingsMenu(QMenu *settingsMenu);
/**
* @brief If you let the ai debugger contribute to the status bar, call if to remove the contribution
*/
void removeFromStatusBar(QStatusBar* statusBar);
/**
* @brief If you let the ai debugger contribute to the tool bar, call if to remove the contribution
*/
void removeFromToolBar(QToolBar* toolBar);
/**
* @brief If you let the ai debugger contribute to the file menu, call if to remove the contribution
*/
void removeFromFileMenu(QMenu *fileMenu);
/**
* @brief If you let the ai debugger contribute to the help menu, call if to remove the contribution
*/
void removeFromHelpMenu(QMenu *helpMenu);
/**
* @brief If you let the ai debugger contribute to the settings menu, call if to remove the contribution
*/
void removeFromSettingsMenu(QMenu *settingsMenu);
private slots:
void about();
void documentation();
void bug();
void settings();
void toggleTreeView();
void connectToAIServer();
void disconnectFromAIServer();
void quitApplication();
void requestPause();
void requestStep();
void requestReset();
void setPause(bool pause);
void change(const QString &);
void onNamesReceived();
void onDisconnect();
void onEntitiesUpdated();
void onSelected();
void showContextMenu(const QPoint& contextMenuPos);
void onDeleteNode(int nodeId);
void onAddNode(int parentNodeId, const QVariant& name, const QVariant& type, const QVariant& condition);
private:
void createView();
void createActions();
QWidget *createTopWidget();
QWidget *createBottomWidget();
QWidget *createTreePanelWidget();
QLabel *createLabel(const QString &text) const;
NodeTreeView *_nodeTree;
StateTable *_stateTable;
MapView *_mapWidget;
EntityList *_entityList;
QLineEdit *_entityFilter;
AggroTable *_aggroTable;
QAction *_connectAction;
QAction *_disconnectAction;
QAction *_pauseAction;
QAction *_quitAction;
QAction *_stepAction;
QAction *_resetAction;
QAction *_aboutAction;
QAction *_documentationAction;
QAction *_bugAction;
QAction *_settingsAction;
QLabel *_statusBarLabel;
QLabel *_selectedLabel;
QComboBox *_namesComboBox;
QTreeView *_tree;
AINodeStaticResolver& _resolver;
BehaviourTreeModel _model;
AIDebugger& _debugger;
QString _name;
CompressorProxy _proxy;
bool _standalone;
};
}
}

View File

@ -1,37 +0,0 @@
/**
* @file
*/
#include "AINodeStaticResolver.h"
#include "ai-shared/protocol/AIStubTypes.h"
#include "core/Log.h"
namespace ai {
namespace debug {
namespace anon {
static const ai::AIStateNodeStatic UNKNOWN(-1, "unknown", "unknown", "unknown", "unknown", "unknown");
}
AINodeStaticResolver::AINodeStaticResolver() {
}
void AINodeStaticResolver::set(const core::DynamicArray<ai::AIStateNodeStatic>& data) {
_data = data;
_hash.clear();
for (const ai::AIStateNodeStatic& s : _data) {
_hash.put(s.getId(), &s);
}
Log::debug("received %i entities", (int)_hash.size());
}
const ai::AIStateNodeStatic& AINodeStaticResolver::get(int32_t id) const {
const ai::AIStateNodeStatic* s;
if (_hash.get(id, s)) {
Log::debug("entry for %i wasn't found", id);
return anon::UNKNOWN;
}
return *s;
}
}
}

View File

@ -1,28 +0,0 @@
/**
* @file
*/
#pragma once
#include "core/collection/DynamicArray.h"
#include "core/collection/Map.h"
namespace ai {
class AIStateNodeStatic;
}
namespace ai {
namespace debug {
class AINodeStaticResolver {
private:
core::DynamicArray<ai::AIStateNodeStatic> _data;
core::Map<int32_t, const ai::AIStateNodeStatic*> _hash;
public:
AINodeStaticResolver();
void set(const core::DynamicArray<ai::AIStateNodeStatic>& data);
const ai::AIStateNodeStatic& get(int32_t id) const;
};
}
}

View File

@ -1,38 +0,0 @@
/**
* @file
*/
#pragma once
#include <QAction>
#include <QDialog>
#include <QWidgetAction>
#include <QMenu>
namespace ai {
namespace debug {
class Action: public QAction {
Q_OBJECT
protected:
Action(const QString& title, QObject* parentObj = nullptr) :
QAction(title, parentObj) {
}
Action(QObject* parentObj = nullptr) :
QAction(parentObj) {
}
// embedd stuff like e.g. a color dialog into a menu
void setPopupDialog(QDialog* dialog) {
QWidgetAction* action = new QWidgetAction(nullptr);
action->setDefaultWidget(dialog);
QMenu* actionMenu = new QMenu();
actionMenu->addAction(action);
connect(actionMenu, SIGNAL(aboutToShow()), dialog, SLOT(show()));
connect(dialog, SIGNAL(finished(int)), actionMenu, SLOT(hide()));
setMenu(actionMenu);
}
};
}
}

View File

@ -1,33 +0,0 @@
/**
* @file
*/
#include "Action.h"
#include "AddDialog.h"
namespace ai {
namespace debug {
class BehaviourTreeModelItem;
class AddAction: public Action {
Q_OBJECT
private:
int _parentId;
private slots:
void onTriggered() {
AddDialog d;
d.run();
emit triggered(_parentId, d.getName(), d.getType(), d.getCondition());
}
public:
AddAction(int parentId, QObject* parentObj) :
Action(tr("Add node"), parentObj), _parentId(parentId) {
connect(this, SIGNAL(triggered()), this, SLOT(onTriggered()));
}
signals:
void triggered(int parentId, const QVariant& name, const QVariant& type, const QVariant& condition);
};
}
}

View File

@ -1,30 +0,0 @@
/**
* @file
*/
#include "Action.h"
#include "AddDialog.h"
namespace ai {
namespace debug {
class BehaviourTreeModelItem;
class DeleteAction: public Action {
Q_OBJECT
private:
int _nodeId;
private slots:
void onTriggered() {
emit triggered(_nodeId);
}
public:
DeleteAction(int nodeId, QObject* parentObj) :
Action(tr("Delete node"), parentObj), _nodeId(nodeId) {
connect(this, SIGNAL(triggered()), this, SLOT(onTriggered()));
}
signals:
void triggered(int nodeId);
};
}
}

View File

@ -1,42 +0,0 @@
/**
* @file
*/
#include "AddDialog.h"
#include <QGridLayout>
#include <QLabel>
AddDialog::AddDialog() :
IDialog(tr("Create new node")), _nameText(nullptr), _typeText(nullptr), _conditionText(nullptr), _group(
nullptr) {
}
AddDialog::~AddDialog() {
delete _nameText;
delete _typeText;
delete _conditionText;
delete _group;
}
void AddDialog::addMainWidgets(QBoxLayout& boxLayout) {
_group = new QGroupBox(tr("Node"));
QGridLayout *innerLayout = new QGridLayout;
_nameText = new QLineEdit("NewNode");
innerLayout->addWidget(new QLabel(tr("Name")), 0, 0);
innerLayout->addWidget(_nameText, 0, 1);
_typeText = new QLineEdit("PrioritySelector");
innerLayout->addWidget(new QLabel(tr("Type")), 1, 0);
innerLayout->addWidget(_typeText, 1, 1);
_conditionText = new QLineEdit("True");
innerLayout->addWidget(new QLabel(tr("Condition")), 2, 0);
innerLayout->addWidget(_conditionText, 2, 1);
_group->setLayout(innerLayout);
boxLayout.addWidget(_group);
}
void AddDialog::onApply() {
_condition = _conditionText->text();
_name = _nameText->text();
_type = _typeText->text();
close();
}

View File

@ -1,41 +0,0 @@
/**
* @file
*/
#pragma once
#include "IDialog.h"
#include <QLineEdit>
#include <QGroupBox>
class AddDialog: public IDialog {
Q_OBJECT
private:
QLineEdit *_nameText;
QLineEdit *_typeText;
QLineEdit *_conditionText;
QGroupBox *_group;
QString _name;
QString _type;
QString _condition;
public:
AddDialog();
virtual ~AddDialog();
virtual void onApply() override;
virtual void addMainWidgets(QBoxLayout& boxLayout) override;
inline const QString& getName() const {
return _name;
}
inline const QString& getType() const {
return _type;
}
inline const QString& getCondition() const {
return _condition;
}
};

View File

@ -1,40 +0,0 @@
/**
* @file
*/
#include "ConnectDialog.h"
#include <QSettings>
#include "Settings.h"
ConnectDialog::ConnectDialog(const QString& defaultHostname, short defaultPort) :
IDialog(tr("Connect to AI server")), _hostnameText(nullptr), _portText(nullptr), _group(
nullptr) {
_hostname = Settings::getHostname(defaultHostname);
_port = Settings::getPort(defaultPort);
}
ConnectDialog::~ConnectDialog() {
delete _hostnameText;
delete _portText;
delete _group;
}
void ConnectDialog::addMainWidgets(QBoxLayout& boxLayout) {
_group = new QGroupBox(tr("Server"));
QVBoxLayout *vboxLayout = new QVBoxLayout;
_hostnameText = new QLineEdit(_hostname);
vboxLayout->addWidget(_hostnameText);
_portText = new QLineEdit(QString::number(_port));
vboxLayout->addWidget(_portText);
_group->setLayout(vboxLayout);
boxLayout.addWidget(_group);
}
void ConnectDialog::onApply() {
_hostname = _hostnameText->text();
_port = _portText->text().toShort();
Settings::setHostname(_hostname);
Settings::setPort(_portText->text().toInt());
close();
}

View File

@ -1,34 +0,0 @@
/**
* @file
*/
#pragma once
#include "IDialog.h"
#include <QLineEdit>
#include <QGroupBox>
// TODO: implement history of last connects
class ConnectDialog: public IDialog {
Q_OBJECT
private:
QLineEdit *_hostnameText;
QLineEdit *_portText;
QGroupBox *_group;
QString _hostname;
short _port;
public:
ConnectDialog(const QString& defaultHostname = "0.0.0.0", short defaultPort = 12345);
virtual ~ConnectDialog();
virtual void onApply() override;
virtual void addMainWidgets(QBoxLayout& boxLayout) override;
inline const QString& getHostname() const {
return _hostname;
}
inline short getPort() const {
return _port;
}
};

View File

@ -1,57 +0,0 @@
/**
* @file
*/
#include "IDialog.h"
#include <QGroupBox>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
IDialog::IDialog(const QString &title, int flags) :
QDialog(nullptr, 0), _title(title), _applyButton(
nullptr), _closeButton(nullptr), _buttonLayout(nullptr), _buttons(
nullptr), _mainLayout(nullptr), _flags(flags) {
setWindowTitle(_title);
setModal(false);
}
IDialog::~IDialog() {
if ((_flags & DIALOG_NO_APPLY_BUTTON) == 0) {
delete _applyButton;
}
delete _closeButton;
delete _buttonLayout;
delete _buttons;
delete _mainLayout;
}
int IDialog::run() {
_buttonLayout = new QHBoxLayout;
_buttons = new QWidget;
addButtons(*_buttonLayout);
_buttons->setLayout(_buttonLayout);
_mainLayout = new QVBoxLayout;
addMainWidgets(*_mainLayout);
_mainLayout->addSpacing(12);
_mainLayout->addWidget(_buttons);
_mainLayout->addStretch(1);
setLayout(_mainLayout);
show();
exec();
return result();
}
void IDialog::addButtons(QBoxLayout& boxLayout) {
if ((_flags & DIALOG_NO_APPLY_BUTTON) == 0) {
_applyButton = new QPushButton(tr("Apply"));
connect(_applyButton, SIGNAL(clicked()), this, SLOT(apply()));
boxLayout.addWidget(_applyButton);
}
_closeButton = new QPushButton(tr("Close"));
connect(_closeButton, SIGNAL(clicked()), this, SLOT(reject()));
boxLayout.addWidget(_closeButton);
}

View File

@ -1,45 +0,0 @@
/**
* @file
*/
#pragma once
#include <QDialog>
#include <QBoxLayout>
#include <QDebug>
#include <QList>
#define DIALOG_NO_APPLY_BUTTON (1 << 0)
class IDialog: public QDialog {
Q_OBJECT
protected:
QString _title;
QPushButton *_applyButton;
QPushButton *_closeButton;
QHBoxLayout *_buttonLayout;
QWidget *_buttons;
QVBoxLayout *_mainLayout;
int _flags;
private slots:
void apply() {
onApply();
emit accept();
}
public:
IDialog(const QString &title, int flags = 0);
virtual ~IDialog();
const QString& getTitle() const;
int run();
virtual void addButtons(QBoxLayout& layout);
virtual void onApply() {}
virtual void addMainWidgets(QBoxLayout& /* layout */) = 0;
};
inline const QString& IDialog::getTitle() const {
return _title;
}

View File

@ -1,112 +0,0 @@
/**
* @file
*/
#include "SettingsDialog.h"
#include "Settings.h"
#include "ai-shared/common/CharacterAttributes.h"
#include <QColorDialog>
#include <QGroupBox>
#include <QLabel>
#include <QCheckBox>
#include <QIntValidator>
#include <QLineEdit>
namespace ai {
namespace debug {
SettingsDialog::SettingsDialog() :
IDialog(tr("Settings"), DIALOG_NO_APPLY_BUTTON) {
}
QGroupBox* SettingsDialog::createMapView() {
QGroupBox* mapView = new QGroupBox(tr("Map view"), this);
QGridLayout* gridLayout = new QGridLayout(this);
// TODO: find something better for the colors
QColorDialog *bgColor = new QColorDialog(this);
bgColor->setWindowFlags(Qt::SubWindow);
bgColor->setOptions(QColorDialog::DontUseNativeDialog | QColorDialog::NoButtons);
bgColor->setCurrentColor(Settings::getBackgroundColor());
connect(bgColor, &QColorDialog::currentColorChanged, this, Settings::setBackgroundColor);
QCheckBox* showGrid = new QCheckBox(this);
showGrid->setChecked(Settings::getGrid());
connect(showGrid, &QCheckBox::stateChanged, this, &SettingsDialog::setShowGrid);
QCheckBox* centerOnSelection = new QCheckBox(this);
centerOnSelection->setChecked(Settings::getCenterOnSelection());
connect(centerOnSelection, &QCheckBox::stateChanged, this, &SettingsDialog::setCenterOnSelection);
QLineEdit* gridInterval = new QLineEdit(this);
gridInterval->setText(QString::number(Settings::getGridInterval()));
gridInterval->setValidator(new QIntValidator(0, 10000, gridInterval));
connect(gridInterval, &QLineEdit::textChanged, this, &SettingsDialog::setGridInterval);
QLineEdit* nameAttribute = new QLineEdit(this);
nameAttribute->setText(Settings::getNameAttribute(attributes::NAME));
connect(nameAttribute, &QLineEdit::textChanged, this, &SettingsDialog::setNameAttribute);
QLineEdit* itemSize = new QLineEdit(this);
itemSize->setText(QString::number(Settings::getItemSize()));
itemSize->setValidator(new QDoubleValidator(1.0, 100.0, 1, itemSize));
connect(itemSize, &QLineEdit::textChanged, this, &SettingsDialog::setItemSize);
int row = 0;
gridLayout->addWidget(new QLabel(tr("Show grid"), this), row, 0, Qt::AlignTop);
gridLayout->addWidget(showGrid, row, 1);
++row;
gridLayout->addWidget(new QLabel(tr("Grid interval"), this), row, 0, Qt::AlignTop);
gridLayout->addWidget(gridInterval, row, 1);
++row;
gridLayout->addWidget(new QLabel(tr("Item size"), this), row, 0, Qt::AlignTop);
gridLayout->addWidget(itemSize, row, 1);
++row;
gridLayout->addWidget(new QLabel(tr("Name attribute"), this), row, 0, Qt::AlignTop);
gridLayout->addWidget(nameAttribute, row, 1);
++row;
gridLayout->addWidget(new QLabel(tr("Background"), this), row, 0, Qt::AlignTop);
gridLayout->addWidget(bgColor, row, 1);
++row;
gridLayout->addWidget(new QLabel(tr("Center on selection"), this), row, 0, Qt::AlignTop);
gridLayout->addWidget(centerOnSelection, row, 1);
++row;
mapView->setLayout(gridLayout);
return mapView;
}
void SettingsDialog::setGridInterval(const QString& value) {
Settings::setGridInterval(value.toInt());
}
void SettingsDialog::setItemSize(const QString& value) {
Settings::setItemSize(value.toFloat());
}
void SettingsDialog::setShowGrid(int value) {
Settings::setGrid(value != 0);
}
void SettingsDialog::setCenterOnSelection(int value) {
Settings::setCenterOnSelection(value != 0);
}
void SettingsDialog::setNameAttribute(const QString& attribute) {
Settings::setNameAttribute(attribute);
}
void SettingsDialog::addMainWidgets(QBoxLayout& boxLayout) {
QSettings settings;
QGroupBox* mapView = createMapView();
boxLayout.addWidget(mapView);
}
}
}

View File

@ -1,30 +0,0 @@
/**
* @file
*/
#pragma once
#include "IDialog.h"
#include <QGroupBox>
namespace ai {
namespace debug {
class SettingsDialog : public IDialog {
Q_OBJECT
private:
QGroupBox* createMapView();
private slots:
void setShowGrid(int value);
void setGridInterval(const QString& value);
void setItemSize(const QString& value);
void setCenterOnSelection(int value);
void setNameAttribute(const QString& attribute);
public:
SettingsDialog();
void addMainWidgets(QBoxLayout& layout) override;
};
}
}

View File

@ -1,45 +0,0 @@
/**
* @file
*/
#include "AggroTable.h"
#include <QHeaderView>
namespace ai {
namespace debug {
AggroTable::AggroTable(AIDebugger& debugger) :
_model(debugger, this), _debugger(debugger) {
_proxyModel.setSourceModel(&_model);
setModel(&_proxyModel);
setAlternatingRowColors(true);
resizeColumnsToContents();
setSortingEnabled(false);
setSelectionMode(QAbstractItemView::NoSelection);
verticalHeader()->hide();
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
for (int c = 0; c < horizontalHeader()->count(); ++c) {
horizontalHeader()->setSectionResizeMode(c, QHeaderView::Stretch);
}
connect(selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), this, SLOT(selectEntity(QModelIndex,QModelIndex)));
}
AggroTable::~AggroTable() {
}
void AggroTable::updateAggroTable() {
_model.update();
}
void AggroTable::selectEntity(const QModelIndex &current, const QModelIndex &previous) {
Q_UNUSED(previous);
if (!current.isValid()) {
return;
}
const QModelIndex index = _proxyModel.mapToSource(current);
const core::DynamicArray<AIStateAggroEntry>& aggro = _debugger.getAggro();
const AIStateAggroEntry& aggroState = aggro[index.row()];
_debugger.select(aggroState.id);
}
}
}

View File

@ -1,34 +0,0 @@
/**
* @file
*/
#pragma once
#include <QTableView>
#include <QSortFilterProxyModel>
#include "AIDebugger.h"
#include "AggroTableModel.h"
namespace ai {
namespace debug {
/**
* @brief Shows a key value pair of values for the selected entity
*/
class AggroTable: public QTableView {
Q_OBJECT
private:
AggroTableModel _model;
QSortFilterProxyModel _proxyModel;
AIDebugger& _debugger;
private slots:
void selectEntity(const QModelIndex &current, const QModelIndex &previous);
public:
AggroTable(AIDebugger& debugger);
virtual ~AggroTable();
void updateAggroTable();
};
}
}

View File

@ -1,65 +0,0 @@
/**
* @file
*/
#include "AggroTableModel.h"
#include <QColor>
namespace ai {
namespace debug {
AggroTableModel::AggroTableModel(const AIDebugger& debugger, QTableView *tableView) :
QAbstractTableModel(nullptr), _debugger(debugger), _parent(tableView) {
}
AggroTableModel::~AggroTableModel() {
}
void AggroTableModel::update() {
beginResetModel();
endResetModel();
}
int AggroTableModel::rowCount(const QModelIndex & /*parent*/) const {
const core::DynamicArray<AIStateAggroEntry>& aggro = _debugger.getAggro();
return aggro.size();
}
int AggroTableModel::columnCount(const QModelIndex & /*parent*/) const {
return 2;
}
QVariant AggroTableModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (orientation != Qt::Horizontal) {
return QVariant();
}
if (role == Qt::DisplayRole) {
switch (section) {
case 0:
return tr("ID");
case 1:
return tr("Aggro");
default:
break;
}
}
return QVariant();
}
QVariant AggroTableModel::data(const QModelIndex &mdlIndex, int role) const {
const core::DynamicArray<AIStateAggroEntry>& aggro = _debugger.getAggro();
if (role == Qt::DisplayRole) {
switch (mdlIndex.column()) {
case 0:
return aggro[mdlIndex.row()].id;
case 1:
return aggro[mdlIndex.row()].aggro;
default:
break;
}
}
return QVariant();
}
}
}

View File

@ -1,45 +0,0 @@
/**
* @file
*/
#pragma once
#include <QAbstractTableModel>
#include <QTableView>
#include "AIDebugger.h"
namespace ai {
namespace debug {
class AggroTableModel: public QAbstractTableModel {
Q_OBJECT
private:
const AIDebugger& _debugger;
QTableView* _parent;
public:
AggroTableModel(const AIDebugger& debugger, QTableView *tableView);
~AggroTableModel();
inline const AIDebugger::Entities& getEntities() const {
return _debugger.getEntities();
}
inline const AIStateWorld* getEntity(const QModelIndex &mdlIndex) const {
const int size = getEntities().size();
if (size > mdlIndex.row() && mdlIndex.row() >= 0) {
return &getEntities().values().at(mdlIndex.row());
}
return nullptr;
}
void update();
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role) const override;
};
}
}

View File

@ -1,47 +0,0 @@
/**
* @file
*/
#include "EntityList.h"
#include "AIDebugger.h"
#include <QHeaderView>
namespace ai {
namespace debug {
EntityList::EntityList(AIDebugger& debugger, QLineEdit* entityFilter) :
_model(debugger, this), _debugger(debugger), _entityFilter(entityFilter) {
setFixedWidth(200);
_proxyModel.setSourceModel(&_model);
setModel(&_proxyModel);
setAlternatingRowColors(true);
setSortingEnabled(false);
setSelectionMode(QAbstractItemView::SingleSelection);
setSelectionBehavior(QAbstractItemView::SelectRows);
setEditTriggers(QAbstractItemView::NoEditTriggers);
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
verticalHeader()->hide();
horizontalHeader()->setStretchLastSection(true);
connect(_entityFilter, SIGNAL(textChanged(QString)), &_proxyModel, SLOT(setFilterWildcard(QString)));
connect(selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), this, SLOT(selectEntity(QModelIndex,QModelIndex)));
}
EntityList::~EntityList() {
}
void EntityList::updateEntityList() {
_model.update();
}
void EntityList::selectEntity(const QModelIndex &current, const QModelIndex &previous) {
Q_UNUSED(previous);
if (!current.isValid()) {
return;
}
const QModelIndex index = _proxyModel.mapToSource(current);
const AIStateWorld& worldState = _model.getEntities().at(index.row());
_debugger.select(worldState);
}
}
}

View File

@ -1,37 +0,0 @@
/**
* @file
*/
#pragma once
#include <QTableView>
#include <QSortFilterProxyModel>
#include <QLineEdit>
#include "EntityListModel.h"
namespace ai {
namespace debug {
class AIDebugger;
/**
* @brief Shows a list of all entities that are handled on the server we are connected to
*/
class EntityList: public QTableView {
Q_OBJECT
private:
EntityListModel _model;
QSortFilterProxyModel _proxyModel;
AIDebugger& _debugger;
QLineEdit* _entityFilter;
private slots:
void selectEntity(const QModelIndex &current, const QModelIndex &previous);
public:
EntityList(AIDebugger& debugger, QLineEdit *entityFilter);
virtual ~EntityList();
void updateEntityList();
};
}
}

View File

@ -1,87 +0,0 @@
/**
* @file
*/
#include "EntityListModel.h"
#include "AIDebugger.h"
#include <QColor>
namespace ai {
namespace debug {
EntityListModel::EntityListModel(AIDebugger& debugger, QTableView *tableView) :
QAbstractTableModel(tableView), _debugger(debugger), _parent(tableView) {
}
EntityListModel::~EntityListModel() {
}
QModelIndex EntityListModel::characterIndex(CharacterId id) const {
int row = 0;
for (const AIStateWorld& state : _list) {
if (state.getId() == id) {
return createIndex(row, 0);
}
++row;
}
qDebug() << "Could not find entity " << id << " in the model";
return QModelIndex();
}
void EntityListModel::update() {
beginResetModel();
_list = _debugger.getEntities().values();
// TODO: sort list - not model
endResetModel();
}
int EntityListModel::rowCount(const QModelIndex & /*parent*/) const {
return _list.size();
}
int EntityListModel::columnCount(const QModelIndex & /*parent*/) const {
return 1;
}
QVariant EntityListModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (orientation != Qt::Horizontal) {
return QVariant();
}
if (section != 0) {
return QVariant();
}
if (role == Qt::DisplayRole) {
return tr("Entities");
}
if (role == Qt::ToolTipRole) {
return tr("The character id");
}
return QVariant();
}
QVariant EntityListModel::data(const QModelIndex &mdlIndex, int role) const {
const AIStateWorld& state = _list.at(mdlIndex.row());
if (role == Qt::DisplayRole) {
if (mdlIndex.column() == 0) {
const CharacterAttributes& attributes = state.getAttributes();
auto name = attributes.find(attributes::NAME);
if (name != attributes.end()) {
return QString(name->second.c_str()).append(" (%1)").arg(state.getId());
}
return state.getId();
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0))
} else if (role == Qt::BackgroundRole) {
#else
} else if (role == Qt::BackgroundColorRole) {
#endif
if (_debugger.isSelected(state)) {
return QColor(Qt::gray);
}
}
return QVariant();
}
}
}

View File

@ -1,40 +0,0 @@
/**
* @file
*/
#pragma once
#include <QAbstractTableModel>
#include <QTableView>
#include "ai-shared/protocol/AIStubTypes.h"
namespace ai {
namespace debug {
class AIDebugger;
class EntityListModel: public QAbstractTableModel {
Q_OBJECT
private:
AIDebugger& _debugger;
QTableView* _parent;
QList<AIStateWorld> _list;
public:
EntityListModel(AIDebugger& debugger, QTableView *tableView);
~EntityListModel();
inline const QList<AIStateWorld>& getEntities() const {
return _list;
}
void update();
QModelIndex characterIndex(CharacterId id) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role) const override;
};
}
}

View File

@ -1,108 +0,0 @@
/**
* @file
*/
#include "MapItem.h"
#include "AIDebugger.h"
#include "Settings.h"
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <QFont>
#include <QDebug>
#include <math.h>
#include <QVector2D>
namespace ai {
namespace debug {
namespace {
const float OrientationScale = 2.0F;
}
MapItem::MapItem(QGraphicsItem* parent, const AIStateWorld& state, AIDebugger& aiDebugger) :
QGraphicsItemGroup(parent), _state(state), _aiDebugger(aiDebugger) {
setFlag(QGraphicsItem::ItemIsSelectable);
_body = new QGraphicsEllipseItem(0.0, 0.0, 0.0, 0.0, this);
_direction = new QGraphicsLineItem(0.0, 0.0, 0.0, 0.0, this);
_nameItem = new QGraphicsTextItem(_body);
_nameItem->setDefaultTextColor(Settings::getNameColor());
addToGroup(_body);
addToGroup(_direction);
addToGroup(_nameItem);
setAcceptHoverEvents(true);
setAcceptedMouseButtons(Qt::AllButtons);
}
MapItem::~MapItem() {
}
void MapItem::updateState(const AIStateWorld& state) {
const qreal size = Settings::getItemSize();
setPos((qreal)state.getPosition().x, (qreal)state.getPosition().z);
_body->setRect(-size / 2.0, -size / 2.0, size, size);
const CharacterAttributes& attributes = _state.getAttributes();
const QString& nameAttribute = Settings::getNameAttribute(attributes::NAME);
auto name = attributes.find(nameAttribute.toStdString().c_str());
if (name != attributes.end()) {
const QString nameStr(name->second.c_str());
setToolTip(nameStr);
_nameItem->setPlainText(nameStr);
} else {
const QString& nameStr = QString::number(_state.getId());
setToolTip(nameStr);
_nameItem->setPlainText(nameStr);
}
const bool selected = _aiDebugger.isSelected(state);
if (selected) {
static const QPen pen(QColor::fromRgb(255, 0, 0, 255));
_body->setPen(pen);
} else {
static const QPen pen(QColor::fromRgb(0, 0, 0, 255));
_body->setPen(pen);
}
// TODO: get color from settings
QColor color = QColor::fromRgb(200, 200, 0, 255);
_body->setBrush(color);
QVector2D end(::cosf(state.getOrientation()) * OrientationScale, ::sinf(state.getOrientation()) * OrientationScale);
end.normalize();
const qreal center = size / 2.0;
_direction->setLine(0.0, 0.0, center * end.x(), center * end.y());
const QString& groupAttribute = Settings::getGroupAttribute(attributes::GROUP);
auto groupIter = attributes.find(groupAttribute.toStdString().c_str());
if (groupIter != attributes.end()) {
// TODO: get color from settings
QColor colorGroup = QColor::fromRgb(200, 200, 0, 255);
const int groupId = atoi(groupIter->second.c_str());
if (groupId > 0) {
const int b = groupId * 113 % 255;
const int component = groupId % 3;
switch (component) {
case 0:
colorGroup.setRed(b);
break;
case 1:
colorGroup.setGreen(b);
break;
default:
colorGroup.setBlue(b);
break;
}
}
_body->setBrush(colorGroup);
}
}
void MapItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) {
QGraphicsItem::mouseDoubleClickEvent(event);
_aiDebugger.select(_state);
update();
}
}
}

View File

@ -1,42 +0,0 @@
/**
* @file
*/
#pragma once
#include <QGraphicsItemGroup>
#include <QGraphicsTextItem>
#include <QGraphicsEllipseItem>
#include <QGraphicsLineItem>
#include <QStyleOptionGraphicsItem>
#include "ai-shared/protocol/AIStubTypes.h"
namespace ai {
namespace debug {
class AIDebugger;
/**
* @brief Represents one entity in the world
*
* @note If you want to show additional details for an entity, see the @c MapView class
*/
class MapItem: public QGraphicsItemGroup {
protected:
const AIStateWorld _state;
AIDebugger& _aiDebugger;
QGraphicsEllipseItem *_body;
QGraphicsLineItem *_direction;
QGraphicsTextItem *_nameItem;
public:
MapItem(QGraphicsItem* parent, const AIStateWorld& state, AIDebugger& aiDebugger);
virtual ~MapItem();
virtual void updateState(const AIStateWorld& state);
protected:
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
};
}
}

View File

@ -1,149 +0,0 @@
/**
* @file
*/
#include "MapView.h"
#include "AIDebugger.h"
#include "MapItem.h"
#include "Settings.h"
#include "core/Trace.h"
namespace ai {
namespace debug {
MapView::MapView(AIDebugger& debugger) :
_debugger(debugger) {
_scene.setItemIndexMethod(QGraphicsScene::NoIndex);
setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
setCacheMode(QGraphicsView::CacheBackground);
setRenderHint(QPainter::Antialiasing, false);
setDragMode(ScrollHandDrag);
setInteractive(true);
setScene(&_scene);
}
MapView::~MapView() {
_scene.clear();
}
void MapView::scalingTime(qreal /*unused*/) {
const qreal factor = 1.0 + qreal(_numScheduledScalings) / 300.0;
scale(factor, factor);
}
void MapView::wheelEvent(QWheelEvent * wheelEventPtr) {
const int numDegrees = wheelEventPtr->delta() / 8;
const int numSteps = numDegrees / 15;
_numScheduledScalings += numSteps;
QTimeLine *anim = new QTimeLine(350, this);
anim->setUpdateInterval(20);
anim->setProperty("numsteps", QVariant::fromValue(numSteps));
connect(anim, SIGNAL(valueChanged(qreal)), SLOT(scalingTime(qreal)));
connect(anim, SIGNAL(finished()), SLOT(animFinished()));
anim->start();
}
void MapView::animFinished() {
const int reduce = sender()->property("numsteps").toInt();
_numScheduledScalings -= reduce;
sender()->~QObject();
}
MapItem* MapView::createMapItem(const AIStateWorld& state) {
return new MapItem(nullptr, state, _debugger);
}
MapItem* MapView::createOrUpdateMapItem(const AIStateWorld& state) {
core_trace_scoped(CreateOrUpdateMapItem);
auto i = _items.find(state.getId());
MapItem* item;
if (i == _items.end()) {
item = createMapItem(state);
} else {
item = i.value();
}
item->updateState(state);
if (_debugger.isSelected(state)) {
item->setZValue(std::numeric_limits<qreal>::max());
} else {
item->setZValue((qreal)state.getPosition().y);
}
if (i != _items.end()) {
return item;
}
_scene.addItem(item);
_items[state.getId()] = item;
return item;
}
void MapView::drawBackground(QPainter* painter, const QRectF& rectf) {
QGraphicsView::drawBackground(painter, rectf);
const QColor& color = Settings::getBackgroundColor();
painter->fillRect(rectf, QBrush(color));
if (!Settings::getGrid()) {
return;
}
const QColor& gridColor = Settings::getGridColor();
QPen linePen(gridColor, 1, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin);
linePen.setCosmetic(true);
painter->setPen(linePen);
const int gridInterval = Settings::getGridInterval();
const qreal left = static_cast<int>(rectf.left())
- (static_cast<int>(rectf.left()) % gridInterval);
const qreal top = static_cast<int>(rectf.top())
- (static_cast<int>(rectf.top()) % gridInterval);
QVarLengthArray<QLineF, 100> linesX;
for (qreal _x = left; _x < rectf.right(); _x += gridInterval) {
linesX.append(QLineF(_x, rectf.top(), _x, rectf.bottom()));
}
QVarLengthArray<QLineF, 100> linesY;
for (qreal _y = top; _y < rectf.bottom(); _y += gridInterval) {
linesY.append(QLineF(rectf.left(), _y, rectf.right(), _y));
}
painter->drawLines(linesX.data(), linesX.size());
painter->drawLines(linesY.data(), linesY.size());
}
void MapView::updateMapView() {
core_trace_scoped(UpdateMapView);
QHash<ai::CharacterId, MapItem*> copy(_items);
const AIDebugger::Entities& e = _debugger.getEntities();
for (AIDebugger::EntitiesIter i = e.begin(); i != e.end(); ++i) {
copy.remove(i->getId());
createOrUpdateMapItem(*i);
}
// remove the remaining entities - they are no longer part of the snapshot
for (auto i = copy.begin(); i != copy.end(); ++i) {
_scene.removeItem(i.value());
_items.remove(i.key());
}
}
bool MapView::center(CharacterId id) {
auto i = _items.find(id);
if (i == _items.end()) {
return false;
}
centerOn(i.value());
return true;
}
bool MapView::makeVisible(CharacterId id) {
auto i = _items.find(id);
if (i == _items.end()) {
return false;
}
ensureVisible(i.value());
return true;
}
}
}

View File

@ -1,56 +0,0 @@
/**
* @file
*/
#pragma once
#include "ai-shared/protocol/AIStubTypes.h"
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QWheelEvent>
#include <QTimeLine>
namespace ai {
namespace debug {
class AIDebugger;
class MapItem;
/**
* @brief The view that renders your map with all the ai controlled entities.
*
* @note If you want to render additional details to an entity, extend this class to override
* MapView::createMapItem and provide your own @c MapItem there.
*/
class MapView: public QGraphicsView {
Q_OBJECT
protected:
QGraphicsScene _scene;
AIDebugger& _debugger;
QHash<ai::CharacterId, MapItem*> _items;
int _numScheduledScalings = 0;
private slots:
void scalingTime(qreal x);
void animFinished();
public:
MapView(AIDebugger& debugger);
virtual ~MapView();
virtual void wheelEvent(QWheelEvent * wheelEventPtr) override;
virtual void updateMapView();
virtual bool center(CharacterId id);
virtual bool makeVisible(CharacterId id);
virtual MapItem* createOrUpdateMapItem(const AIStateWorld& state);
/**
* @brief Creates a @c MapItem and allows you to create your own instances to render extra details
*/
virtual MapItem* createMapItem(const AIStateWorld& state);
virtual void drawBackground(QPainter* painter, const QRectF& rect) override;
};
}
}

View File

@ -1,34 +0,0 @@
/**
* @file
*/
#include "StateTable.h"
#include "AIDebugger.h"
#include <QHeaderView>
namespace ai {
namespace debug {
StateTable::StateTable(AIDebugger& debugger) :
_model(debugger) {
_proxyModel.setSourceModel(&_model);
setModel(&_proxyModel);
setAlternatingRowColors(true);
resizeColumnsToContents();
setSortingEnabled(true);
setSelectionMode(QAbstractItemView::NoSelection);
verticalHeader()->hide();
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
for (int c = 0; c < horizontalHeader()->count(); ++c) {
horizontalHeader()->setSectionResizeMode(c, QHeaderView::Stretch);
}
}
StateTable::~StateTable() {
}
void StateTable::updateStateTable() {
_model.update();
}
}
}

View File

@ -1,31 +0,0 @@
/**
* @file
*/
#pragma once
#include <QTableView>
#include <QSortFilterProxyModel>
#include "StateTableModel.h"
namespace ai {
namespace debug {
class AIDebugger;
/**
* @brief Shows a key value pair of values for the selected entity
*/
class StateTable: public QTableView {
private:
StateTableModel _model;
QSortFilterProxyModel _proxyModel;
public:
StateTable(AIDebugger& debugger);
virtual ~StateTable();
void updateStateTable();
};
}
}

View File

@ -1,71 +0,0 @@
/**
* @file
*/
#include "StateTableModel.h"
#include "core/Trace.h"
#include <QColor>
namespace ai {
namespace debug {
StateTableModel::StateTableModel(const AIDebugger& debugger) :
QAbstractTableModel(nullptr), _debugger(debugger) {
}
StateTableModel::~StateTableModel() {
}
void StateTableModel::update() {
core_trace_scoped(StateTableModelUpdate);
beginResetModel();
const AIDebugger::CharacterAttributesMap& a = _debugger.getAttributes();
_list.clear();
for (auto i = a.begin(); i != a.end(); ++i) {
_list << i.key();
}
endResetModel();
}
int StateTableModel::rowCount(const QModelIndex & /*parent*/) const {
return _list.size();
}
int StateTableModel::columnCount(const QModelIndex & /*parent*/) const {
return 2;
}
QVariant StateTableModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (orientation != Qt::Horizontal) {
return QVariant();
}
if (role == Qt::DisplayRole) {
switch (section) {
case 0:
return tr("Key");
case 1:
return tr("Value");
default:
break;
}
}
return QVariant();
}
QVariant StateTableModel::data(const QModelIndex &mdlIndex, int role) const {
const QString& key = _list[mdlIndex.row()];
if (role == Qt::DisplayRole) {
switch (mdlIndex.column()) {
case 0:
return key;
case 1:
return _debugger.getAttributes().value(key);
default:
break;
}
}
return QVariant();
}
}
}

View File

@ -1,46 +0,0 @@
/**
* @file
*/
#pragma once
#include <QAbstractTableModel>
#include <QList>
#include <string>
#include "AIDebugger.h"
namespace ai {
namespace debug {
class StateTableModel: public QAbstractTableModel {
Q_OBJECT
private:
const AIDebugger& _debugger;
QList<QString> _list;
public:
StateTableModel(const AIDebugger& debugger);
~StateTableModel();
inline const AIDebugger::Entities& getEntities() const {
return _debugger.getEntities();
}
inline const AIStateWorld* getEntity(const QModelIndex &mdlIndex) const {
const int size = getEntities().size();
if (size > mdlIndex.row() && mdlIndex.row() >= 0) {
return &getEntities().values().at(mdlIndex.row());
}
return nullptr;
}
void update();
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role) const override;
};
}
}

View File

@ -1,193 +0,0 @@
/**
* @file
*/
#include "BehaviourTreeModel.h"
#include "BehaviourTreeModelItem.h"
#include "AIDebugger.h"
#include <QIcon>
#include <QDebug>
namespace ai {
namespace debug {
BehaviourTreeModel::BehaviourTreeModel(AIDebugger& debugger, AINodeStaticResolver& resolver, QObject *objParent) :
QAbstractItemModel(objParent), _rootItem(nullptr), _resolver(resolver), _debugger(debugger), _allowUpdate(true) {
connect(this, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(onDataChanged(const QModelIndex&, const QModelIndex&)));
}
BehaviourTreeModel::~BehaviourTreeModel() {
delete _rootItem;
}
void BehaviourTreeModel::onDataChanged(const QModelIndex& topLeft, const QModelIndex& /*bottomRight*/) {
BehaviourTreeModelItem *nodeItem = item(topLeft);
if (nodeItem == nullptr) {
qDebug() << "No item found at: " << topLeft;
return;
}
const QVariant& name = nodeItem->data(COL_NAME);
const QVariant& type = nodeItem->data(COL_TYPE);
const QVariant& condition = nodeItem->data(COL_CONDITION);
_debugger.updateNode(nodeItem->node()->getNodeId(), name, type, condition);
nodeItem->resetEdit();
}
QModelIndex BehaviourTreeModel::index(int row, int column, const QModelIndex &parentIndex) const {
if (!hasIndex(row, column, parentIndex)) {
return QModelIndex();
}
BehaviourTreeModelItem *parentItem;
if (!parentIndex.isValid()) {
parentItem = _rootItem;
} else {
parentItem = item(parentIndex);
}
BehaviourTreeModelItem *childItem = parentItem->child(row);
if (childItem != nullptr) {
return createIndex(row, column, childItem);
}
return QModelIndex();
}
QModelIndex BehaviourTreeModel::parent(const QModelIndex &mdlIndex) const {
if (!mdlIndex.isValid()) {
return QModelIndex();
}
BehaviourTreeModelItem *childItem = item(mdlIndex);
BehaviourTreeModelItem *parentItem = childItem->parent();
if (parentItem == nullptr || parentItem == _rootItem) {
return QModelIndex();
}
return createIndex(parentItem->row(), 0, parentItem);
}
int BehaviourTreeModel::rowCount(const QModelIndex &parentIndex) const {
BehaviourTreeModelItem *parentItem;
if (parentIndex.column() > 0) {
return 0;
}
if (!parentIndex.isValid()) {
parentItem = _rootItem;
} else {
parentItem = item(parentIndex);
}
if (parentItem == nullptr) {
return 0;
}
return parentItem->childCount();
}
int BehaviourTreeModel::columnCount(const QModelIndex &parentIndex) const {
if (parentIndex.isValid()) {
return item(parentIndex)->columnCount();
}
if (_rootItem == nullptr) {
return 0;
}
return _rootItem->columnCount();
}
QVariant BehaviourTreeModel::data(const QModelIndex &mdlIndex, int role) const {
if (!mdlIndex.isValid()) {
return QVariant();
}
BehaviourTreeModelItem *nodeItem = item(mdlIndex);
if (nodeItem == nullptr) {
return QVariant();
}
if (role == Qt::DecorationRole) {
if (mdlIndex.column() == COL_NAME) {
return nodeItem->icon();
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0))
} else if (role == Qt::ForegroundRole) {
#else
} else if (role == Qt::TextColorRole) {
#endif
return nodeItem->color();
}
if (role == Qt::EditRole && _allowUpdate) {
qDebug() << "start editing";
_allowUpdate = false;
}
if (role == Qt::DisplayRole || role == Qt::EditRole) {
return nodeItem->data(mdlIndex.column());
}
if (role == Qt::ToolTipRole) {
return nodeItem->tooltip(mdlIndex.column());
}
return QVariant();
}
bool BehaviourTreeModel::submit() {
if (!_allowUpdate) {
emit behaviourUpdated();
qDebug() << "end editing";
}
_allowUpdate = true;
return QAbstractItemModel::submit();
}
Qt::ItemFlags BehaviourTreeModel::flags(const QModelIndex &mdlIndex) const {
if (!mdlIndex.isValid()) {
return Qt::ItemIsEnabled;
}
Qt::ItemFlags itemflags = QAbstractItemModel::flags(mdlIndex);
switch (mdlIndex.column()) {
case COL_NAME:
case COL_TYPE:
case COL_CONDITION:
itemflags |= Qt::ItemIsEditable;
}
return itemflags;
}
bool BehaviourTreeModel::setData(const QModelIndex &mdlIndex, const QVariant &value, int role) {
if (mdlIndex.isValid() && role == Qt::EditRole) {
_rootItem->child(mdlIndex.row())->setData(mdlIndex.column(), value);
emit dataChanged(mdlIndex, mdlIndex);
return true;
}
return false;
}
QVariant BehaviourTreeModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (orientation == Qt::Horizontal && role == Qt::DisplayRole && _rootItem != nullptr) {
return _rootItem->headerData(section);
}
return QVariant();
}
bool BehaviourTreeModel::setRootNode(AIStateNode* node) {
if (!_allowUpdate) {
return false;
}
beginResetModel();
if (_rootItem != nullptr) {
delete _rootItem;
_rootItem = nullptr;
}
if (node->getNodeId() != -1) {
_rootItem = new BehaviourTreeModelItem(node, _resolver);
}
endResetModel();
return true;
}
}
}

View File

@ -1,64 +0,0 @@
/**
* @file
*/
#pragma once
#include "AINodeStaticResolver.h"
#include <QAbstractItemModel>
namespace ai {
class AIStateNode;
namespace debug {
class AIDebugger;
class BehaviourTreeModelItem;
class BehaviourTreeModel: public QAbstractItemModel {
Q_OBJECT
private:
BehaviourTreeModelItem *_rootItem;
AINodeStaticResolver& _resolver;
AIDebugger& _debugger;
mutable bool _allowUpdate;
public:
explicit BehaviourTreeModel(AIDebugger& debugger, AINodeStaticResolver& resolver, QObject *parent = nullptr);
~BehaviourTreeModel();
inline BehaviourTreeModelItem* item(const QModelIndex& mdlIndex) const {
if (!mdlIndex.isValid())
return nullptr;
return static_cast<BehaviourTreeModelItem*>(mdlIndex.internalPointer());
}
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &mdlIndex) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setRootNode(AIStateNode* node);
inline bool editMode() const {
return !_allowUpdate;
}
inline void abortEditMode() {
_allowUpdate = true;
}
public slots:
bool submit() override;
void onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
signals:
void behaviourUpdated();
};
}
}

View File

@ -1,147 +0,0 @@
/**
* @file
*/
#include "BehaviourTreeModelItem.h"
#include "TreeViewCommon.h"
#include "AINodeStaticResolver.h"
#include "core/Enum.h"
#include <QFile>
namespace ai {
namespace debug {
BehaviourTreeModelItem::BehaviourTreeModelItem(AIStateNode* stateNodePtr, AINodeStaticResolver& resolver, BehaviourTreeModelItem* modelItem) :
_node(stateNodePtr), _staticNodeData(resolver.get(stateNodePtr->getNodeId())), _parent(modelItem) {
if (_parent == nullptr) {
_rows.push_back(new BehaviourTreeModelItem(_node, resolver, this));
} else {
for (const AIStateNode& stateNode : _node->getChildren()) {
_rows.push_back(new BehaviourTreeModelItem(const_cast<AIStateNode*>(&stateNode), resolver, this));
}
}
const QString type = QString(_staticNodeData.getType().c_str()).toLower();
const QString path = ":/images/" + type + ".png";
if (QFile::exists(path)) {
_icon = QIcon(path);
} else if (type.contains("selector")) {
_icon = QIcon(":/images/selector.png");
} else {
_icon = QIcon(":/images/node.png");
}
}
BehaviourTreeModelItem::~BehaviourTreeModelItem() {
qDeleteAll(_rows);
}
void BehaviourTreeModelItem::resetEdit() {
_editedName = "";
_editedCondition = "";
_editedType = "";
}
BehaviourTreeModelItem* BehaviourTreeModelItem::child(int rowIndex) {
return _rows.value(rowIndex);
}
QVariant BehaviourTreeModelItem::color() const {
const TreeNodeStatus status = _node->getStatus();
switch (status) {
case TreeNodeStatus::UNKNOWN:
case TreeNodeStatus::CANNOTEXECUTE:
return QColor(Qt::gray);
case TreeNodeStatus::RUNNING:
case TreeNodeStatus::FINISHED:
return QColor(Qt::darkGreen);
case TreeNodeStatus::FAILED:
case TreeNodeStatus::EXCEPTION:
return QColor(Qt::red);
case TreeNodeStatus::MAX_TREENODESTATUS:
break;
}
return QVariant();
}
int BehaviourTreeModelItem::row() {
if (_parent != nullptr) {
return _parent->_rows.indexOf(this);
}
return 0;
}
QIcon BehaviourTreeModelItem::icon() const {
return _icon;
}
QString BehaviourTreeModelItem::tooltip(int column) const {
if (column == COL_NAME) {
return QString(_staticNodeData.getType().c_str());
}
if (column == COL_CONDITION) {
return QString(_staticNodeData.getCondition().c_str());
}
return QString();
}
void BehaviourTreeModelItem::setData(int column, const QVariant& editedData) {
switch (column) {
case COL_NAME:
_editedName = editedData.toString();
break;
case COL_TYPE:
_editedType = editedData.toString();
break;
case COL_CONDITION:
_editedCondition = editedData.toString();
break;
}
}
QVariant BehaviourTreeModelItem::headerData(int column) const {
switch (column) {
case COL_NAME:
return QObject::tr("Name");
case COL_TYPE:
return QObject::tr("Type");
case COL_CONDITION:
return QObject::tr("Condition");
case COL_STATE:
return QObject::tr("State");
case COL_LASTRUN:
return QObject::tr("Last run");
}
return QVariant();
}
QVariant BehaviourTreeModelItem::data(int column) const {
switch (column) {
case COL_NAME:
if (!_editedName.isEmpty()) {
return _editedName;
}
return QString(_staticNodeData.getName().c_str());
case COL_TYPE:
if (!_editedType.isEmpty()) {
return _editedType;
}
return QString(_staticNodeData.getType().c_str());
case COL_CONDITION:
if (!_editedCondition.isEmpty()) {
return _editedCondition;
}
return QString(_node->getCondition().c_str());
case COL_STATE: {
const TreeNodeStatus status = _node->getStatus();
if (status >= TreeNodeStatus::UNKNOWN && status < TreeNodeStatus::MAX_TREENODESTATUS) {
return stateNames[core::enumVal(status)];
}
return stateNames[core::enumVal(TreeNodeStatus::UNKNOWN)];
}
case COL_LASTRUN:
return QString::number(_node->getLastRun() / 1000);
}
return QVariant();
}
}
}

View File

@ -1,74 +0,0 @@
/**
* @file
*/
#pragma once
#include <QVariant>
#include <QIcon>
#include "ai-shared/protocol/AIStubTypes.h"
namespace ai {
namespace debug {
enum {
COL_NAME,
COL_TYPE,
COL_CONDITION,
COL_STATE,
COL_LASTRUN,
COL_MAX
};
class AINodeStaticResolver;
class BehaviourTreeModelItem {
private:
AIStateNode* _node;
const AIStateNodeStatic& _staticNodeData;
QList<BehaviourTreeModelItem*> _rows;
BehaviourTreeModelItem* _parent;
QIcon _icon;
QString _editedType;
QString _editedName;
QString _editedCondition;
public:
BehaviourTreeModelItem(AIStateNode* stateNodePtr, AINodeStaticResolver& resolver, BehaviourTreeModelItem* modelItem = nullptr);
virtual ~BehaviourTreeModelItem();
void setData(int column, const QVariant& data);
void resetEdit();
QVariant headerData(int column) const;
QVariant data(int column) const;
QString tooltip(int column) const;
QIcon icon() const;
QVariant color() const;
inline int columnCount() const {
return COL_MAX;
}
inline int childCount() const {
return _rows.size();
}
int row();
BehaviourTreeModelItem* child(int row);
inline BehaviourTreeModelItem* parent() {
return _parent;
}
inline AIStateNode* node() {
return _node;
}
};
}
}

View File

@ -1,169 +0,0 @@
/**
* @file
*/
#include "NodeTreeItem.h"
#include "AIDebugger.h"
#include "TreeViewCommon.h"
#include "core/Enum.h"
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <QFont>
#include <QDateTime>
namespace ai {
namespace debug {
namespace {
const qreal padding = 1;
const qreal fontSize = 10;
const QColor backgroundColor = QColor::fromRgb(32, 32, 32, 64);
const QColor runningBackgroundColor = QColor::fromRgb(255, 0, 0, 128);
const QFont font("Times", fontSize);
const QFontMetrics fontMetrics(font);
}
NodeTreeItem::NodeTreeItem (QGraphicsItem* parentGraphicsItem, const AIStateNode& node, const AIStateNodeStatic& staticNodeData, NodeTreeItem* parent, int itemHeight, int horizontalSpacing, int verticalSpacing) :
QGraphicsItem(parentGraphicsItem), _node(node), _parent(parent), _height(itemHeight), _horizontalSpacing(
horizontalSpacing), _verticalSpacing(verticalSpacing) {
_condition = QString(_node.getCondition().c_str());
_name = QString(staticNodeData.getName().c_str());
_type = QString(staticNodeData.getType().c_str());
#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0))
const int nameWidth = fontMetrics.horizontalAdvance(_name);
const int conditionWidth = fontMetrics.horizontalAdvance(_condition);
#else
const int nameWidth = fontMetrics.width(_name);
const int conditionWidth = fontMetrics.width(_condition);
#endif
_width = core_max(130, core_max(nameWidth, conditionWidth));
_lineHeight = fontMetrics.lineSpacing();
}
NodeTreeItem::~NodeTreeItem () {
}
void NodeTreeItem::init() {
setOffset(QPointF(100.0F, fullSize().height() / 2.0F - _height));
}
void NodeTreeItem::setOffset (const QPointF& offset) {
_offset = offset;
if (_parent == nullptr){
setPos(_offset);
} else {
setPos(_parent->pos() + _offset);
}
float yOffset = 0.0F;
foreach (NodeTreeItem* node, _children) {
const float halfHeight = node->fullSize().height() / 2.0F;
yOffset += halfHeight;
float heightOffset = -_size.height() / 2.0F + yOffset;
yOffset += halfHeight + _verticalSpacing;
const QPointF offsetF(_width + _horizontalSpacing, heightOffset);
node->setOffset(offsetF);
}
}
QRectF NodeTreeItem::boundingRect () const {
return _size;
}
QRectF NodeTreeItem::fullSize() {
if (!_size.isEmpty()) {
return _size;
}
_size = QRectF(0.0F, 0.0F, _width + _horizontalSpacing, _height + _verticalSpacing);
if (_children.empty()) {
return _size;
}
QRectF total;
foreach (NodeTreeItem* node, _children) {
QRectF childDimension = node->fullSize();
childDimension.translate(node->_width + _horizontalSpacing, total.height() + childDimension.height() / 2.0F);
total |= childDimension;
}
total.moveTo(total.x(), _size.center().y() - total.height() / 2.0F);
_size |= total;
return _size;
}
QPointF NodeTreeItem::getChildPos (NodeTreeItem* child) const {
const QPointF& childPos = child->pos() - pos();
const float childRelX = childPos.x();
const float childRelY = childPos.y() + child->boundingRect().center().y();
return QPointF(childRelX, childRelY);
}
void NodeTreeItem::paint (QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
Q_UNUSED(option);
Q_UNUSED(widget);
//painter->setClipRect(option->exposedRect);
const qreal lod = QStyleOptionGraphicsItem::levelOfDetailFromTransform(painter->worldTransform());
const bool running = _node.isRunning();
QBrush b = painter->brush();
if (running) {
painter->setBrush(runningBackgroundColor);
} else {
painter->setBrush(backgroundColor);
}
painter->drawRect(0.0F, 0.0F, _width, _height);
if (!_children.empty()) {
// location of the vertical line
const float seperatorX = _width + _horizontalSpacing / 2.0F;
// draw the (right) vertical line for connecting the parent with the separator vertical line
painter->drawLine(_width, _height / 2.0F, seperatorX, _height / 2.0F);
foreach (NodeTreeItem* child, _children) {
const QPointF& childPos = getChildPos(child);
// draw the (left) vertical line for connecting the separator with the children's left side
painter->drawLine(seperatorX, childPos.y(), childPos.x(), childPos.y());
}
// draw the vertical connection line
if (_children.size() >= 2) {
const QPointF& firstChildPos = getChildPos(_children.first());
const QPointF& lastChildPos = getChildPos(_children.last());
painter->drawLine(seperatorX, firstChildPos.y(), seperatorX, lastChildPos.y());
}
}
painter->setBrush(b);
if (lod < 0.4) {
return;
}
painter->setFont(font);
painter->save();
const int radius = 4;
QRect rect(padding + 2 * radius, padding, _width - 2 * padding - 2 * radius, _height - 2 * padding);
painter->drawText(rect, _name);
rect.setY(rect.y() + _lineHeight);
const TreeNodeStatus status = _node.getStatus();
const QString stateString = stateNames[core::enumVal(status)];
const int64_t lastRun = _node.getLastRun();
painter->drawText(rect, stateString);
QPoint center(padding + radius, padding + radius);
rect.setY(rect.y() + _lineHeight);
painter->drawText(rect, _condition);
int seconds;
if (lastRun == -1) {
seconds = 255;
} else {
seconds = lastRun / 1000;
}
QColor activityColor(core_max(0, 255 - seconds), 0, 0, 255);
painter->setBrush(activityColor);
painter->drawEllipse(center, radius, radius);
painter->restore();
}
}
}

View File

@ -1,57 +0,0 @@
/**
* @file
*/
#pragma once
#include <QColor>
#include <QPainter>
#include <QGraphicsItem>
#include <QStyleOptionGraphicsItem>
#include <QFontMetrics>
#include "ai-shared/protocol/AIStubTypes.h"
namespace ai {
namespace debug {
class AIDebugger;
// TODO use QGraphicsPathItem
class NodeTreeItem : public QGraphicsItem {
protected:
AIStateNode _node;
NodeTreeItem* _parent;
QList<NodeTreeItem*> _children;
QString _condition;
QString _name;
QString _type;
int _width;
int _height;
int _lineHeight;
int _horizontalSpacing;
int _verticalSpacing;
QRectF _size;
QPointF _offset;
QRectF fullSize();
inline QPointF getChildPos (NodeTreeItem* child) const;
void setOffset(const QPointF& offset);
QRectF boundingRect() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
public:
NodeTreeItem (QGraphicsItem* parentGraphicsItem, const AIStateNode& node, const AIStateNodeStatic& staticNodeData, NodeTreeItem* parent, int height, int horizontalSpacing, int verticalSpacing);
virtual ~NodeTreeItem ();
void init();
inline NodeTreeItem* getParent() { return _parent; }
inline const AIStateNode& getNode() { return _node; }
inline int width() const { return _width; }
inline int height() const { return _height; }
inline int getSize() const { return _node.getChildren().size(); }
inline void addChildren(NodeTreeItem* node) { _children.append(node); }
};
}
}

View File

@ -1,81 +0,0 @@
/**
* @file
*/
#include "NodeTreeView.h"
#include "AINodeStaticResolver.h"
#include "AIDebugger.h"
namespace {
const int horizontalSpacing = 40;
const int verticalSpacing = 10;
const int nodeHeight = 60;
}
namespace ai {
namespace debug {
NodeTreeView::NodeTreeView(AIDebugger& debugger, AINodeStaticResolver& resolver, QWidget* objParent) :
QGraphicsView(objParent), _debugger(debugger), _scene(this), _resolver(resolver) {
_scene.setItemIndexMethod(QGraphicsScene::NoIndex);
// because the connection lines are not included in the bounding box...
setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
setCacheMode(QGraphicsView::CacheBackground);
setRenderHint(QPainter::Antialiasing, false);
setDragMode(ScrollHandDrag);
setScene(&_scene);
}
NodeTreeView::~NodeTreeView() {
setScene(nullptr);
}
void NodeTreeView::updateTreeWidget() {
_scene.clear();
const ai::CharacterId& id = _debugger.getSelected();
if (id == -1) {
return;
}
const AIStateNode& node = _debugger.getNode();
NodeTreeItem* item = buildTreeItems(node, nullptr);
item->init();
_scene.setSceneRect(QRectF());
}
NodeTreeItem* NodeTreeView::buildTreeItems(const AIStateNode& node, NodeTreeItem* nodeTreeParent) {
NodeTreeItem* thisNode = new NodeTreeItem(nullptr, node, _resolver.get(node.getNodeId()), nodeTreeParent, nodeHeight, horizontalSpacing, verticalSpacing);
_scene.addItem(thisNode);
const core::DynamicArray<AIStateNode>& childrenNodes = node.getChildren();
for (auto i = childrenNodes.begin(); i != childrenNodes.end(); ++i) {
NodeTreeItem* childNode = buildTreeItems(*i, thisNode);
thisNode->addChildren(childNode);
}
return thisNode;
}
void NodeTreeView::scalingTime(qreal /*unused*/) {
const qreal factor = 1.0 + qreal(_numScheduledScalings) / 300.0;
scale(factor, factor);
}
void NodeTreeView::wheelEvent(QWheelEvent * wheelEventPtr) {
const int numDegrees = wheelEventPtr->delta() / 8;
const int numSteps = numDegrees / 15;
_numScheduledScalings += numSteps;
QTimeLine *anim = new QTimeLine(350, this);
anim->setUpdateInterval(20);
anim->setProperty("numsteps", QVariant::fromValue(numSteps));
connect(anim, SIGNAL(valueChanged(qreal)), SLOT(scalingTime(qreal)));
connect(anim, SIGNAL(finished()), SLOT(animFinished()));
anim->start();
}
void NodeTreeView::animFinished() {
const int reduce = sender()->property("numsteps").toInt();
_numScheduledScalings -= reduce;
sender()->~QObject();
}
}
}

View File

@ -1,45 +0,0 @@
/**
* @file
*/
#pragma once
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QWheelEvent>
#include <QTimeLine>
#include "NodeTreeItem.h"
namespace ai {
namespace debug {
class AINodeStaticResolver;
class AIDebugger;
/**
* @brief Shows the behaviour tree for the current selected entity
*/
class NodeTreeView: public QGraphicsView {
Q_OBJECT
private:
AIDebugger& _debugger;
QGraphicsScene _scene;
AINodeStaticResolver& _resolver;
NodeTreeItem* buildTreeItems(const AIStateNode& node, NodeTreeItem* parent);
int _numScheduledScalings = 0;
private slots:
void scalingTime(qreal x);
void animFinished();
public:
NodeTreeView(AIDebugger& debugger, AINodeStaticResolver& resolver, QWidget* parent = nullptr);
virtual ~NodeTreeView();
void wheelEvent(QWheelEvent * wheelEventPtr) override;
void updateTreeWidget();
};
}
}

View File

@ -1,24 +0,0 @@
/**
* @file
*/
#include "TreeViewCommon.h"
#include "ai-shared/common/TreeNodeStatus.h"
#include "core/Enum.h"
namespace ai {
namespace debug {
#define E(x) #x
const char *stateNames[] = {
E(UNKNOWN),
E(CANNOTEXECUTE),
E(RUNNING),
E(FINISHED),
E(FAILED),
E(EXCEPTION)
};
#undef E
static_assert(sizeof(stateNames) / sizeof(*stateNames) == core::enumVal(TreeNodeStatus::MAX_TREENODESTATUS), "State names don't match");
}
}

View File

@ -1,12 +0,0 @@
/**
* @file
*/
#pragma once
namespace ai {
namespace debug {
extern const char *stateNames[];
}
}

View File

@ -1,125 +0,0 @@
/**
* @file
*/
#include "IComboBox.h"
#include <QEvent>
#include <QMouseEvent>
#include <QPainter>
#include <QPen>
#include <QAbstractItemView>
DetailsClickFilter::DetailsClickFilter(QLabel* label, IComboBox* comboBox) :
_label(label), _comboBox(comboBox) {
}
bool DetailsClickFilter::eventFilter(QObject* watched, QEvent* mouseButtonPressEvent) {
if (watched != _label) {
return false;
}
if (mouseButtonPressEvent->type() != QEvent::MouseButtonPress) {
return false;
}
const QMouseEvent* const me = static_cast<const QMouseEvent*>(mouseButtonPressEvent);
_clickedPoint = me->pos();
_comboBox->onChangeDetails();
if (_label->pixmap() == nullptr) {
return false;
}
QImage tmp(_label->pixmap()->toImage());
QPainter painter(&tmp);
QPen paintpen(Qt::red);
paintpen.setWidth(4);
painter.setPen(paintpen);
painter.drawPoint(_clickedPoint);
_label->setPixmap(QPixmap::fromImage(tmp));
_comboBox->onClickedDetails(_clickedPoint);
return false;
}
const QPoint& DetailsClickFilter::getClickedPoint() const {
return _clickedPoint;
}
IComboBox::IComboBox(const QString& title, const QString& detailImageSubdir, int flags, QWidget* objParent) :
QWidget(objParent), _proxy(&_comboBox), _detailsClickFilter(&_details, this),
_flags(flags), _detailImageSubdir(detailImageSubdir) {
setLayout(&_vLayout);
if ((_flags & COMBOBOX_DETAILS) != 0) {
connect(&_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(changeDetails()));
_vLayout.addWidget(&_details);
_details.installEventFilter(&_detailsClickFilter);
}
_vLayout.addWidget(&_container);
_container.setLayout(&_hLayout);
_hLayout.addWidget(&_comboBox, 1);
#if 0
QSizePolicy sp = _comboBox.view()->sizePolicy();
sp.setHorizontalPolicy(QSizePolicy::Minimum);
_comboBox.view()->setSizePolicy(sp);
#endif
_comboBox.setDuplicatesEnabled(false);
_comboBox.setEditable(true);
_proxy.setSourceModel(_comboBox.model());
_comboBox.model()->setParent(&_proxy);
_comboBox.setModel(&_proxy);
_refresh = new QPushButton(QIcon(":/images/refresh.png"), "");
_refresh->setToolTip(title);
if ((_flags & COMBOBOX_NO_REFRESH) == 0) {
connect(_refresh, SIGNAL(clicked()), this, SLOT(onRefresh()));
_hLayout.addWidget(_refresh);
}
}
IComboBox::~IComboBox() {
delete _refresh;
}
void IComboBox::clear() {
_comboBox.clear();
_uniqueSet.clear();
}
void IComboBox::changeDetails() {
onChangeDetails();
}
void IComboBox::sort() {
_comboBox.model()->sort(0);
}
bool IComboBox::insert(int key, const QString& value) {
const QString unique = QString::number(key) + "-" + value;
if (!_uniqueSet.contains(unique)) {
_comboBox.addItem(value, QVariant(key));
_uniqueSet.insert(unique);
return true;
}
return false;
}
void IComboBox::onRefresh() {
clear();
load();
sort();
}
void IComboBox::onChangeDetails() {
if (_detailImageSubdir.isEmpty()) {
return;
}
const int id = getId();
const QString& imagePath(_detailImageSubdir + QDir::separator() + id + ".png");
const QPixmap pixmap(imagePath);
_details.setPixmap(pixmap);
}
int IComboBox::getId() const {
if (_comboBox.count() == 0 || _comboBox.currentIndex() == -1) {
return -1;
}
const int id = _comboBox.itemData(_comboBox.currentIndex()).toString().toInt();
return id;
}

View File

@ -1,66 +0,0 @@
/**
* @file
*/
#pragma once
#include <QComboBox>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QSet>
#include <QDir>
#include <QSortFilterProxyModel>
#define COMBOBOX_NO_REFRESH (1 << 0)
#define COMBOBOX_DETAILS (1 << 1)
class IComboBox;
class DetailsClickFilter: public QObject {
Q_OBJECT
protected:
QLabel* _label;
IComboBox* _comboBox;
QPoint _clickedPoint;
public:
DetailsClickFilter(QLabel* label, IComboBox* comboBox);
bool eventFilter(QObject* watched, QEvent* event) override;
const QPoint& getClickedPoint () const;
};
class IComboBox: public QWidget {
Q_OBJECT
protected:
QWidget _container;
QComboBox _comboBox;
QSortFilterProxyModel _proxy;
QHBoxLayout _hLayout;
QVBoxLayout _vLayout;
QLabel _details;
DetailsClickFilter _detailsClickFilter;
QPushButton *_refresh;
int _flags;
QSet<QString> _uniqueSet;
QString _detailImageSubdir;
private slots:
void onRefresh();
void changeDetails();
public:
IComboBox(const QString& title, const QString& detailImageSubdir, int flags = 0, QWidget* parent = nullptr);
virtual ~IComboBox();
// inserts an object and eliminate duplicates, returns true if the item was really inserted
bool insert(int key, const QString& value);
void sort();
void clear();
// loads the data from the given server. Before calling this manually you should also invoke @c clear
virtual void load() = 0;
virtual void onChangeDetails();
virtual void onClickedDetails(const QPoint& /* pos */) {}
// returns the id of the current selected item
int getId() const;
};

View File

@ -1,31 +0,0 @@
#pragma once
#include "AIApplication.h"
#include "RconAIDebugger.h"
#include "AIDebuggerWidget.h"
namespace rcon {
class RconAIApplication: public ai::debug::AIApplication {
private:
using Super = ai::debug::AIApplication;
public:
RconAIApplication(int argc, char** argv) :
Super(argc, argv) {
}
void init() override {
Super::init();
const QList<QString>& args = QCoreApplication::arguments();
if (args.size() != 3) {
qDebug() << "connect to 127.0.0.1 on port 11338";
_widget->connectToAIServer("127.0.0.1", 11338);
}
}
ai::debug::AIDebugger* createDebugger() override {
return new RconAIDebugger(*_resolver);
}
};
}

View File

@ -1,22 +0,0 @@
#pragma once
#include "AIDebugger.h"
#include "RconMapView.h"
namespace rcon {
class RconAIDebugger: public ai::debug::AIDebugger {
private:
using Super = ai::debug::AIDebugger;
protected:
ai::debug::MapView* createMapWidget() override {
return new RconMapView(*this);
}
public:
RconAIDebugger(ai::debug::AINodeStaticResolver& resolver) :
Super(resolver) {
}
};
}

View File

@ -1,93 +0,0 @@
#pragma once
#include "MapItem.h"
#include <QGraphicsEllipseItem>
#include <QGraphicsLineItem>
#include "shared/ProtocolEnum.h"
#include "Settings.h"
#include "core/Trace.h"
namespace rcon {
// TODO: render FIELDOFVIEW and HEALTH
class RconMapItem: public ai::debug::MapItem {
private:
using Super = ai::debug::MapItem;
QGraphicsEllipseItem *_visibilityCircle;
QGraphicsEllipseItem *_attackCircle;
QGraphicsLineItem *_healthBar;
QGraphicsLineItem *_healthBarMax;
QLineF _healthLine;
public:
RconMapItem(QGraphicsItem* parent, const ai::AIStateWorld& state,
ai::debug::AIDebugger& aiDebugger) :
Super(parent, state, aiDebugger) {
_visibilityCircle = new QGraphicsEllipseItem(0.0, 0.0, 0.0, 0.0, this);
_visibilityCircle->setPen(QColor::fromRgb(255, 255, 0));
addToGroup(_visibilityCircle);
const qreal size = Settings::getItemSize() / 2.0;
_healthLine = QLineF(-size, 0.0, size, 0.0);
_healthBarMax = new QGraphicsLineItem(_healthLine, this);
_healthBar = new QGraphicsLineItem(_healthLine, this);
QPen pen(Qt::green);
pen.setWidth(10);
_healthBar->setPen(pen);
QPen penMax(Qt::red);
penMax.setWidth(10);
_healthBarMax->setPen(penMax);
addToGroup(_healthBarMax);
addToGroup(_healthBar);
_attackCircle = new QGraphicsEllipseItem(0.0, 0.0, 0.0, 0.0, this);
_attackCircle->setPen(QColor::fromRgb(180, 0, 0));
addToGroup(_attackCircle);
}
virtual void updateState(const ai::AIStateWorld& state) override {
core_trace_scoped(UpdateState);
Super::updateState(state);
// format of those attributes is %f/%f
updateAttrib(network::AttribType::VIEWDISTANCE, _visibilityCircle);
updateAttrib(network::AttribType::ATTACKRANGE, _attackCircle);
updateCurrentAndMax(network::AttribType::HEALTH, _healthBar, _healthBarMax);
}
void updateCurrentAndMax(network::AttribType type, QGraphicsLineItem* curItem, QGraphicsLineItem* maxItem) {
const ai::CharacterAttributes& attributes = _state.getAttributes();
const char *attribName = network::EnumNameAttribType(type);
auto i = attributes.find(attribName);
if (i != attributes.end()) {
curItem->show();
maxItem->show();
const QString curAndMax(i->second.c_str());
QStringList values = curAndMax.split("/");
const double curVal = values[0].toDouble();
const double maxVal = values[1].toDouble();
const double percentage = curVal / maxVal;
const QLineF& maxLine = maxItem->line();
const QLineF newCurLine(maxLine.x1(), maxLine.y1(), maxLine.x2() * percentage, maxLine.y1());
curItem->setLine(newCurLine);
} else {
curItem->hide();
maxItem->hide();
}
}
void updateAttrib(network::AttribType type, QGraphicsEllipseItem* item) {
const ai::CharacterAttributes& attributes = _state.getAttributes();
const char *attribName = network::EnumNameAttribType(type);
auto i = attributes.find(attribName);
if (i != attributes.end()) {
item->show();
const double val = ::atof(i->second.c_str());
item->setRect(-val / 2.0, -val / 2.0, val, val);
} else {
item->hide();
}
}
};
}

View File

@ -1,23 +0,0 @@
#pragma once
#include "MapView.h"
#include "RconMapItem.h"
namespace rcon {
class RconAIDebugger;
class RconMapView: public ai::debug::MapView {
private:
using Super = ai::debug::MapView;
public:
RconMapView(RconAIDebugger& debugger) :
Super((ai::debug::AIDebugger&)debugger) {
}
ai::debug::MapItem* createMapItem(const ai::AIStateWorld& state) {
return new RconMapItem(nullptr, state, _debugger);
}
};
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB