diff --git a/app/app.pro b/app/app.pro
index e92f201f66..806a970d50 100644
--- a/app/app.pro
+++ b/app/app.pro
@@ -60,7 +60,9 @@ HEADERS += \
src/spinslider.h \
src/doubleprogressdialog.h \
src/colorslider.h \
- src/checkupdatesdialog.h
+ src/checkupdatesdialog.h \
+ src/consolewindow.h \
+ src/asciipreviewdialog.h
SOURCES += \
src/main.cpp \
@@ -89,7 +91,9 @@ SOURCES += \
src/spinslider.cpp \
src/doubleprogressdialog.cpp \
src/colorslider.cpp \
- src/checkupdatesdialog.cpp
+ src/checkupdatesdialog.cpp \
+ src/consolewindow.cpp \
+ src/asciipreviewdialog.cpp
FORMS += \
ui/mainwindow2.ui \
@@ -111,7 +115,9 @@ FORMS += \
ui/timelinepage.ui \
ui/filespage.ui \
ui/toolspage.ui \
- ui/toolboxwidget.ui
+ ui/toolboxwidget.ui \
+ ui/consolewindow.ui \
+ ui/asciipreviewdialog.ui
diff --git a/app/data/app.qrc b/app/data/app.qrc
index 78158697ef..0aeeff4323 100644
--- a/app/data/app.qrc
+++ b/app/data/app.qrc
@@ -56,6 +56,7 @@
icons/new/arrow-horizontal.png
icons/new/arrow-selectmove.png
icons/new/arrow-vertical.png
+ audio/electric-city.mp3
icons/onion-blue.png
diff --git a/app/data/audio/electric-city.mp3 b/app/data/audio/electric-city.mp3
new file mode 100644
index 0000000000..53bd07f3bb
Binary files /dev/null and b/app/data/audio/electric-city.mp3 differ
diff --git a/app/src/asciipreviewdialog.cpp b/app/src/asciipreviewdialog.cpp
new file mode 100644
index 0000000000..c77e0fbf30
--- /dev/null
+++ b/app/src/asciipreviewdialog.cpp
@@ -0,0 +1,30 @@
+#include "asciipreviewdialog.h"
+#include "ui_asciipreviewdialog.h"
+
+#include
+
+AsciiPreviewDialog::AsciiPreviewDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::AsciiPreviewDialog),
+ mMetrics(QFont("Courier New", 13))
+{
+ ui->setupUi(this);
+
+ ui->verticalLayout->setSizeConstraint(QLayout::SetFixedSize);
+}
+
+AsciiPreviewDialog::~AsciiPreviewDialog()
+{
+ delete ui;
+}
+
+void AsciiPreviewDialog::setText(QString s)
+{
+ ui->textDisplay->setText(s);
+ qDebug() << mMetrics.size(0, s);
+}
+
+void AsciiPreviewDialog::setPageNumber(int n)
+{
+ setWindowTitle(tr("Previewing Page #%1").arg(n));
+}
diff --git a/app/src/asciipreviewdialog.h b/app/src/asciipreviewdialog.h
new file mode 100644
index 0000000000..3d8513c733
--- /dev/null
+++ b/app/src/asciipreviewdialog.h
@@ -0,0 +1,26 @@
+#ifndef ASCIIPREVIEWDIALOG_H
+#define ASCIIPREVIEWDIALOG_H
+
+#include
+
+namespace Ui {
+class AsciiPreviewDialog;
+}
+
+class AsciiPreviewDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit AsciiPreviewDialog(QWidget *parent = nullptr);
+ ~AsciiPreviewDialog();
+
+ void setText(QString s);
+ void setPageNumber(int n);
+
+private:
+ Ui::AsciiPreviewDialog *ui;
+ QFontMetrics mMetrics;
+};
+
+#endif // ASCIIPREVIEWDIALOG_H
diff --git a/app/src/consolewindow.cpp b/app/src/consolewindow.cpp
new file mode 100644
index 0000000000..434f99ddda
--- /dev/null
+++ b/app/src/consolewindow.cpp
@@ -0,0 +1,681 @@
+#include "consolewindow.h"
+#include "ui_consolewindow.h"
+
+#include
+#include
+#include
+
+#include "mainwindow2.h"
+#include "ui_mainwindow2.h"
+#include "editor.h"
+#include "toolmanager.h"
+#include "pointerevent.h"
+#include "viewmanager.h"
+#include "playbackmanager.h"
+#include "colormanager.h"
+#include "layermanager.h"
+#include "layercamera.h"
+#include "layerbitmap.h"
+#include "asciiimage.h"
+#include "asciipreviewdialog.h"
+
+ConsoleWindow::ConsoleWindow(QWidget *parent) :
+ QMainWindow(parent),
+ ui(new Ui::ConsoleWindow)
+{
+ ui->setupUi(this);
+
+ // Actions
+ connect(ui->prompt, &QLineEdit::returnPressed, this, &ConsoleWindow::runCommand);
+ ui->prompt->installEventFilter(this);
+
+ // Styling
+ ui->prompt->setAttribute(Qt::WA_MacShowFocusRect, 0);
+ ui->prompt->setFocus();
+
+ mMainWindow = new MainWindow2(this);
+ mMainWindow->mEditor->color()->setColor(Qt::black);
+ LayerManager *lm = mMainWindow->mEditor->layers();
+ mCamLayer = lm->createCameraLayer("ASCII Camera");
+ mCamLayer->setViewRect(QRect(mMainWindow->mEditor->view()->mapScreenToCanvas(QPoint(0, 0)).toPoint(), QSize(50, 50)));
+ LayerCamera *curCam;
+ while ((curCam = static_cast(lm->getLastCameraLayer())) != mCamLayer)
+ {
+ lm->deleteLayer(lm->getIndex(curCam));
+ }
+ mDrawingLayer = lm->createBitmapLayer("ASCII Bitmap");
+ lm->setCurrentLayer(mDrawingLayer);
+
+ mPreviewDialog = new AsciiPreviewDialog(this);
+ connect(mMainWindow->mEditor, &Editor::currentFrameChanged, this, &ConsoleWindow::frameChanged);
+
+ // Play music
+ QMediaPlaylist *playlist = new QMediaPlaylist();
+ playlist->addMedia(QUrl("qrc:/audio/electric-city.mp3"));
+ playlist->setPlaybackMode(QMediaPlaylist::Loop);
+ mSpeaker = new QMediaPlayer();
+ mSpeaker->setPlaylist(playlist);
+ mSpeaker->play();
+}
+
+ConsoleWindow::~ConsoleWindow()
+{
+ delete ui;
+}
+
+bool ConsoleWindow::eventFilter(QObject *watched, QEvent *event)
+{
+ if (watched == ui->prompt)
+ {
+ if (event->type() == QEvent::FocusOut)
+ {
+ // Keep focus on prompt
+ ui->prompt->setFocus();
+ }
+ }
+ return false;
+}
+
+void ConsoleWindow::runCommand()
+{
+ // Handle exiting the splash screen
+ if (mIsOnSplash)
+ {
+ // Remove title
+ ui->title->hide();
+ // Remove logo
+ ui->console->clear();
+
+ // Clear prompt
+ ui->prompt->setText("");
+ // Make prompt editable
+ ui->prompt->setReadOnly(false);
+
+ // Show help text
+ printLook("");
+ print(tr("Hint: If you are unsure of what to do, type HELP below and hit enter."));
+
+ // Prevent this code from being run again
+ mIsOnSplash = false;
+ return;
+ }
+
+ // Get command
+ QString command = ui->prompt->text().trimmed();
+ // Clean command for robustness
+ command = command.toLower().replace(QRegularExpression("[^A-Za-z 0-9\\.\\-]"), "");
+ // Echo to output
+ ui->console->appendPlainText("> " + command);
+ // Clear prompt
+ ui->prompt->setText("");
+
+ // Take action!
+
+ if (command == tr("help"))
+ {
+ printHelp();
+ }
+ else if (command == tr("look"))
+ {
+ printLook("");
+ }
+ else if (command.startsWith(tr("look ")))
+ {
+ printLook(command.mid(tr("look ").size()));
+ }
+ else if (command == tr("close") || command == tr("quit"))
+ {
+ // Prevents sound glitching
+ mSpeaker->pause();
+
+ // Quit application
+ close();
+ }
+ else if ((QStringList() << tr("equip") << tr("pick") << tr("pickup") << tr("pick up") << tr("grab")).contains(command))
+ {
+ print(tr("What do you want to %1? To specify, use the action: %2