RCON: removed qt based tool
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
```
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
/**
|
||||
* @file
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#define VERSION "0.1"
|
|
@ -1 +0,0 @@
|
|||
IDI_ICON1 ICON DISCARDABLE "simpleai-debugger.ico"
|
|
@ -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);
|
||||
}
|
||||
};
|
Before Width: | Height: | Size: 657 B |
Before Width: | Height: | Size: 956 B |
Before Width: | Height: | Size: 840 B |
Before Width: | Height: | Size: 903 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 904 B |
Before Width: | Height: | Size: 402 B |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 901 B |
Before Width: | Height: | Size: 514 B |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.6 KiB |
|
@ -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>
|
|
@ -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) {
|
||||
}
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
};
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
};
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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 ¤t, 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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 ¤t, const QModelIndex &previous);
|
||||
public:
|
||||
AggroTable(AIDebugger& debugger);
|
||||
virtual ~AggroTable();
|
||||
|
||||
void updateAggroTable();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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 ¤t, 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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 ¤t, const QModelIndex &previous);
|
||||
public:
|
||||
EntityList(AIDebugger& debugger, QLineEdit *entityFilter);
|
||||
virtual ~EntityList();
|
||||
|
||||
void updateEntityList();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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); }
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/**
|
||||
* @file
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace ai {
|
||||
namespace debug {
|
||||
|
||||
extern const char *stateNames[];
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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) {
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
Before Width: | Height: | Size: 3.3 KiB |