Compare commits

...

7 Commits

10 changed files with 203 additions and 109 deletions

View File

@ -40,6 +40,18 @@
"name": "index",
"type": "quint16"
}]
},
"deselect": {
"return": "void",
"mut": true
},
"select": {
"return": "void",
"mut": true,
"arguments": [{
"name": "index",
"type": "quint16"
}]
}
},
"properties": {

View File

@ -1,7 +1,7 @@
#include "tycho.h"
MapModel::MapModel(QObject *parent) :
AbstractMapModel(parent),
MapModel::MapModel(Project *parent) :
AbstractMapModel(static_cast<QWidget *>(parent)),
ProjectModel()
{
dbgPrintFunc();
@ -12,11 +12,6 @@ MapModel::~MapModel()
dbgPrintFunc();
}
ProjectModelType MapModel::type() const
{
return ProjectModelType::Map;
}
bool MapModel::isDirty() const
{
return AbstractMapModel::isDirty();
@ -37,6 +32,22 @@ bool MapModel::saveAs(QString const &path) const
return AbstractMapModel::saveAs(path);
}
void MapModel::deselect()
{
AbstractMapModel::deselect();
emit deselected();
}
void MapModel::select(QModelIndex const &index)
{
auto idx = index.internalId();
AbstractMapModel::select(idx);
emit selected(idx);
}
QVariant MapModel::data(const QModelIndex &index, int role) const
{
switch(role) {

View File

@ -1,8 +1,8 @@
#include "tycho.h"
MapProps::MapProps(std::weak_ptr<MapModel> _mapModel, QWidget *parent) :
QDialog(parent),
mapModel(_mapModel)
MapProps::MapProps(Project *parent) :
QDialog(static_cast<QWidget *>(parent)),
mapModel(parent->getMapModel())
{
setupUi(this);
@ -12,8 +12,8 @@ MapProps::MapProps(std::weak_ptr<MapModel> _mapModel, QWidget *parent) :
bbox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
verticalLayout->addWidget(bbox);
connect(bbox, SIGNAL(accepted()), this, SLOT(accept()));
connect(bbox, SIGNAL(rejected()), this, SLOT(reject()));
connect(bbox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(bbox, &QDialogButtonBox::rejected, this, &QDialog::reject);
dbgPrintFunc();
}
@ -23,4 +23,9 @@ MapProps::~MapProps()
dbgPrintFunc();
}
void MapProps::accept()
{
done(QDialog::Accepted);
}
// EOF

View File

@ -1,8 +1,8 @@
#include "tycho.h"
MapView::MapView(std::weak_ptr<MapModel> _mapModel, QWidget *parent) :
QWidget(parent),
mapModel(_mapModel)
MapView::MapView(Project *parent) :
QWidget(static_cast<QWidget *>(parent)),
mapModel(parent->getMapModel())
{
setupUi(this);

View File

@ -22,7 +22,7 @@ Menu::~Menu()
void Menu::mapNew()
{
QScopedPointer proj{new Project(ProjectModelType::Map)};
QScopedPointer proj{new Project(ProjectType::Map)};
addProject(proj.take());
}
@ -40,7 +40,7 @@ void Menu::mapOpen()
"All files (*)"));
if(!fname.isEmpty()) {
QScopedPointer proj{new Project(ProjectModelType::Map)};
QScopedPointer proj{new Project(ProjectType::Map)};
if(proj->getModel()->open(fname)) {
addProject(proj.take());
@ -98,19 +98,22 @@ void Menu::openMapProperties()
{
auto proj = activeProject();
if(proj && proj->getModel()->type() == ProjectModelType::Map) {
MapProps props{proj->getMapModel(), proj};
if(proj && proj->getType() == ProjectType::Map) {
MapProps props{proj};
props.exec();
}
}
void Menu::updateActions()
{
auto proj = activeProject();
bool active = proj != nullptr;
std::optional<ProjectType> active;
actionClose->setEnabled(active);
actionMapProps->setEnabled(active);
if(auto proj = activeProject()) {
active = proj->getType();
}
actionClose->setEnabled(!!active);
actionMapProps->setEnabled(active == ProjectType::Map);
}
void Menu::closeEvent(QCloseEvent *event)

View File

@ -1,9 +1,26 @@
#include "tycho.h"
Project::Project(ProjectModelType type) :
static
ProjectModel *makeModel(Project *proj)
{
switch(proj->getType()) {
case ProjectType::Map: return new MapModel(proj);
}
}
static
ProjectView *makeView(Project *proj)
{
switch(proj->getType()) {
case ProjectType::Map: return new MapView(proj);
}
}
Project::Project(ProjectType _type) :
QMdiSubWindow(),
model(nullptr),
view(nullptr)
type(_type),
model(makeModel(this)),
view(makeView(this))
{
auto widget = new QWidget(this);
@ -12,15 +29,13 @@ Project::Project(ProjectModelType type) :
setWidget(widget);
setAttribute(Qt::WA_DeleteOnClose);
switch(type) {
case ProjectModelType::Map:
model.reset(new MapModel(this));
view = new MapView(getMapModel(), this);
break;
}
listView->setModel(dynamic_cast<QAbstractItemModel *>(model));
verticalLayout->insertWidget(0, dynamic_cast<QWidget *>(view));
listView->setModel(dynamic_cast<QAbstractItemModel *>(model.get()));
verticalLayout->insertWidget(0, view);
connect(listView,
SIGNAL(doubleClicked(QModelIndex const &)),
dynamic_cast<QObject *>(model),
SLOT(select(QModelIndex const &)));
dbgPrintFunc();
}
@ -30,14 +45,19 @@ Project::~Project()
dbgPrintFunc();
}
std::shared_ptr<ProjectModel> Project::getModel()
ProjectType Project::getType() const
{
return type;
}
ProjectModel *Project::getModel() const
{
return model;
}
std::shared_ptr<MapModel> Project::getMapModel()
MapModel *Project::getMapModel() const
{
return std::dynamic_pointer_cast<MapModel>(model);
return dynamic_cast<MapModel *>(model);
}
void Project::closeEvent(QCloseEvent *event)

View File

@ -3,7 +3,7 @@
#include <cstddef>
#include <cstdint>
#include <iostream>
#include <memory>
#include <optional>
#include <vector>
#include <QApplication>
@ -25,74 +25,92 @@
#include "../ui/ui_menu.h"
#include "../ui/ui_project.h"
// Types ---------------------------------------------------------------------|
class MapModel;
class MapProps;
class MapView;
class Menu;
class Project;
enum class ProjectModelType
enum class ProjectType
{
Map,
};
// Interfaces ----------------------------------------------------------------|
class ProjectModel
{
public:
virtual ~ProjectModel() {}
virtual ProjectModelType type() const = 0;
virtual bool isDirty() const = 0;
virtual bool open(QString const &path) = 0;
virtual bool save() const = 0;
virtual bool saveAs(QString const &path) const = 0;
};
class ProjectView
{
public:
virtual ~ProjectView() {}
};
// Implementations -----------------------------------------------------------|
class MapModel final : public AbstractMapModel, public ProjectModel
{
Q_OBJECT
public:
explicit MapModel(QObject *parent = nullptr);
explicit MapModel(Project *parent);
~MapModel() override;
ProjectModelType type() const override;
bool isDirty() const override;
bool open(QString const &path) override;
bool save() const override;
bool saveAs(QString const &path) const override;
public slots:
void deselect();
void select(QModelIndex const &index);
signals:
void deselected();
void selected(std::uint16_t index);
private:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole)
const override;
};
class MapView final : public QWidget, public ProjectView, private Ui::MapView
{
Q_OBJECT
public:
explicit MapView(Project *parent);
~MapView();
private:
MapModel *const mapModel;
};
// UI ------------------------------------------------------------------------|
class MapProps final : public QDialog, private Ui::MapProps
{
Q_OBJECT
public:
explicit MapProps(std::weak_ptr<MapModel> mapModel,
QWidget *parent = nullptr);
explicit MapProps(Project *parent);
~MapProps();
private:
std::weak_ptr<MapModel> mapModel;
};
class MapView final : public QWidget, private Ui::MapView
{
Q_OBJECT
public:
explicit MapView(std::weak_ptr<MapModel> mapModel,
QWidget *parent = nullptr);
~MapView();
void accept() override;
private:
std::weak_ptr<MapModel> mapModel;
MapModel *const mapModel;
};
class Menu final : public QMainWindow, private Ui::Menu
@ -116,8 +134,9 @@ protected:
void openLicense(QWidget *parent);
private:
Project *activeProject() const;
Project *activeProject() const;
QMdiSubWindow *activeSubWindow() const;
void addProject(Project *proj);
};
@ -126,35 +145,44 @@ class Project final : public QMdiSubWindow, private Ui::Project
Q_OBJECT
public:
explicit Project(ProjectModelType type);
explicit Project(ProjectType type);
~Project();
std::shared_ptr<ProjectModel> getModel();
std::shared_ptr<MapModel> getMapModel();
ProjectType getType() const;
ProjectModel *getModel() const;
MapModel *getMapModel() const;
protected:
void closeEvent(QCloseEvent *event) override;
private:
std::shared_ptr<ProjectModel> model;
QWidget *view;
ProjectType const type;
ProjectModel *const model;
ProjectView *const view;
};
// Functions -----------------------------------------------------------------|
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-security"
template<typename... VA>
static inline void dbgPrint([[maybe_unused]] char const *fmt,
[[maybe_unused]] VA &&...va)
static inline
void dbgPrint([[maybe_unused]] char const *fmt, [[maybe_unused]] VA &&...va)
{
#ifdef TYCHO_DEBUG_ASSERTIONS
qDebug(fmt, std::forward<VA>(va)...);
#endif
}
#pragma clang diagnostic pop
#define dbgPrintFunc() dbgPrint("%s", __func__)
static inline constexpr std::uint32_t fourCC(std::uint8_t a,
std::uint8_t b,
std::uint8_t c,
std::uint8_t d)
constexpr
std::uint32_t fourCC(std::byte a, std::byte b, std::byte c, std::byte d)
{
return (a << 24) | (b << 16) | (c << 8) | d;
}

View File

@ -38,7 +38,7 @@ impl AbstractMapModelTrait for AbstractMapModel
eprintln!("new AbstractMapModel");
}
Self{emit, map: EntryDataMap::default()}
Self{emit, map: EntryDataMap::default(), selected: None}
}
/// Returns the emitter of `self`.
@ -100,6 +100,17 @@ impl AbstractMapModelTrait for AbstractMapModel
/// Returns `true` if the file has been modified from its original state.
fn is_dirty(&self) -> bool {false}
fn deselect(&mut self) {self.selected = None;}
fn select(&mut self, index: u16)
{
if cfg!(debug_assertions) {
eprintln!("selecting map {}", index);
}
self.selected = Some(index);
}
}
impl Drop for AbstractMapModel
@ -113,8 +124,9 @@ impl Drop for AbstractMapModel
}
pub struct AbstractMapModel {
emit: AbstractMapModelEmitter,
map: EntryDataMap,
emit: AbstractMapModelEmitter,
map: EntryDataMap,
selected: Option<u16>,
}
// EOF

View File

@ -43,7 +43,7 @@
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEdit">
<widget class="QLineEdit" name="mapName">
<property name="toolTip">
<string>The name of the map. This is used in the level selection menu.</string>
</property>
@ -63,7 +63,7 @@
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="comboBox">
<widget class="QComboBox" name="textureSet">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -111,7 +111,7 @@
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="comboBox_2">
<widget class="QComboBox" name="landscape">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -154,7 +154,7 @@
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="spinBox">
<widget class="QSpinBox" name="musicNo">
<property name="enabled">
<bool>false</bool>
</property>
@ -187,7 +187,7 @@
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QCheckBox" name="checkBox_3">
<widget class="QCheckBox" name="carnage">
<property name="toolTip">
<string>The map can be played in the Carnage game mode.</string>
</property>
@ -197,7 +197,7 @@
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBox_4">
<widget class="QCheckBox" name="ktmwtb">
<property name="toolTip">
<string>The map can be played in the Kill The Man With The Ball game mode.</string>
</property>
@ -207,7 +207,7 @@
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBox_2">
<widget class="QCheckBox" name="coOperative">
<property name="toolTip">
<string>The map can be played in co-operative multi-player.</string>
</property>
@ -217,7 +217,7 @@
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox">
<widget class="QCheckBox" name="solo">
<property name="toolTip">
<string>The map can be played single-player.</string>
</property>
@ -227,7 +227,7 @@
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="checkBox_5">
<widget class="QCheckBox" name="kingOfTheHill">
<property name="toolTip">
<string>The map can be played in the King of the Hill game mode.</string>
</property>
@ -237,7 +237,7 @@
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="checkBox_6">
<widget class="QCheckBox" name="defense">
<property name="toolTip">
<string>The map can be played in the Defense game mode.</string>
</property>
@ -247,7 +247,7 @@
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="checkBox_7">
<widget class="QCheckBox" name="rugby">
<property name="toolTip">
<string>The map can be played in the Rugby game mode.</string>
</property>
@ -257,7 +257,7 @@
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="checkBox_8">
<widget class="QCheckBox" name="captureTheFlag">
<property name="toolTip">
<string>The map can be played in the Capture the Flag game mode.</string>
</property>
@ -279,7 +279,7 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QCheckBox" name="checkBox_17">
<widget class="QCheckBox" name="vacuum">
<property name="toolTip">
<string>This flag makes it so most weapons don't work, and the player's oxygen slowly depletes.</string>
</property>
@ -289,7 +289,7 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_18">
<widget class="QCheckBox" name="magnetic">
<property name="toolTip">
<string>This flag makes the motion sensor extremely glitchy.</string>
</property>
@ -299,7 +299,7 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_19">
<widget class="QCheckBox" name="rebellion">
<property name="toolTip">
<string>This flag strips the player's items and health, and makes S'pht friendly.</string>
</property>
@ -309,7 +309,7 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_20">
<widget class="QCheckBox" name="lowGravity">
<property name="toolTip">
<string>This flag lowers gravity significantly.</string>
</property>
@ -319,7 +319,7 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_21">
<widget class="QCheckBox" name="marathon1Glue">
<property name="toolTip">
<string>This flag makes glue polygons handle like they did in Marathon 1.</string>
</property>
@ -329,7 +329,7 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_22">
<widget class="QCheckBox" name="lavaFloor">
<property name="toolTip">
<string>This flag makes floors damage the player.</string>
</property>
@ -339,7 +339,7 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_23">
<widget class="QCheckBox" name="rebellionNoStrip">
<property name="toolTip">
<string>This flag makes S'pht friendly.</string>
</property>
@ -349,7 +349,7 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_24">
<widget class="QCheckBox" name="music">
<property name="toolTip">
<string>This flag enables music in the level.</string>
</property>
@ -359,7 +359,7 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_25">
<widget class="QCheckBox" name="terminalsStopTime">
<property name="toolTip">
<string>This flag makes reading terminals stop time in single-player.</string>
</property>
@ -369,7 +369,7 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_26">
<widget class="QCheckBox" name="m1MonsterLimits">
<property name="toolTip">
<string>This flag sets monster activation limits to Marathon 1's.</string>
</property>
@ -379,7 +379,7 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_27">
<widget class="QCheckBox" name="m1WeaponDifferences">
<property name="toolTip">
<string>This flag changes weapon pickups on Total Carnage and makes grenades low gravity.</string>
</property>
@ -402,7 +402,7 @@ Some of them also set the &quot;failure&quot; terminal state when the objective
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox_9">
<widget class="QCheckBox" name="extermination">
<property name="toolTip">
<string>The player must kill all monsters on the map, allowing for only up to 8 of them to still be alive.</string>
</property>
@ -412,7 +412,7 @@ Some of them also set the &quot;failure&quot; terminal state when the objective
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBox_10">
<widget class="QCheckBox" name="exploration">
<property name="toolTip">
<string>The player must stand in polygons marked as explorable.</string>
</property>
@ -422,7 +422,7 @@ Some of them also set the &quot;failure&quot; terminal state when the objective
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBox_12">
<widget class="QCheckBox" name="repair">
<property name="toolTip">
<string>The player must flip switches marked as repairable.</string>
</property>
@ -432,7 +432,7 @@ Some of them also set the &quot;failure&quot; terminal state when the objective
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="checkBox_11">
<widget class="QCheckBox" name="retrieval">
<property name="toolTip">
<string>The player must grab items marked as retreivable.</string>
</property>
@ -442,7 +442,7 @@ Some of them also set the &quot;failure&quot; terminal state when the objective
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="checkBox_13">
<widget class="QCheckBox" name="rescue">
<property name="toolTip">
<string>The player must keep half of the BoBs in the level alive. Failure will be set if more than half are killed.</string>
</property>
@ -452,7 +452,7 @@ Some of them also set the &quot;failure&quot; terminal state when the objective
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="checkBox_14">
<widget class="QCheckBox" name="m1Exploration">
<property name="toolTip">
<string>The player must look at polygons marked as explorable.</string>
</property>
@ -462,7 +462,7 @@ Some of them also set the &quot;failure&quot; terminal state when the objective
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="checkBox_15">
<widget class="QCheckBox" name="m1Rescue">
<property name="toolTip">
<string>The player must flip the last (by side index) switch marked as repairable.</string>
</property>
@ -472,7 +472,7 @@ Some of them also set the &quot;failure&quot; terminal state when the objective
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="checkBox_16">
<widget class="QCheckBox" name="m1Repair">
<property name="toolTip">
<string>The exact same as Repair, but uses the internal ID for Marathon 1 BoBs instead.</string>
</property>
@ -491,9 +491,9 @@ Some of them also set the &quot;failure&quot; terminal state when the objective
<resources/>
<connections>
<connection>
<sender>checkBox_24</sender>
<sender>music</sender>
<signal>toggled(bool)</signal>
<receiver>spinBox</receiver>
<receiver>musicNo</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
@ -507,9 +507,9 @@ Some of them also set the &quot;failure&quot; terminal state when the objective
</hints>
</connection>
<connection>
<sender>checkBox_24</sender>
<sender>music</sender>
<signal>toggled(bool)</signal>
<receiver>comboBox_2</receiver>
<receiver>landscape</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">

View File

@ -16,6 +16,9 @@
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QListView" name="listView">
<property name="statusTip">
<string>A list of all items in this project. Double-click an item to open it in the view.</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>