diff --git a/.gitignore b/.gitignore
index 1adb744..7d8fd4e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,7 @@ debug/
Makefile*
object_script*
+/android/gradle*
/Attorney_Online_remake_resource.rc
/attorney_online_remake_plugin_import.cpp
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..e2abad6
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,210 @@
+stages:
+ - build
+ - deploy
+ - publish
+
+cache:
+ key: ${CI_COMMIT_REF_SLUG}
+ paths:
+ - lib/
+
+before_script:
+ - echo Current working directory is $(pwd)
+
+build linux x86_64:
+ image: ubuntu
+ stage: build
+ tags:
+ - docker
+ - linux
+ script:
+ # Install dependencies
+ - apt-get update
+ - >
+ apt-get install --no-install-recommends -y qt5-default qtmultimedia5-dev
+ clang make git sudo curl ca-certificates pkg-config upx unzip
+
+ # Print versions
+ - qmake --version
+ - clang --version
+
+ # Extract BASS
+ #- mkdir bass
+ #- cd bass
+ #- curl http://www.un4seen.com/files/bass24-linux.zip -o bass.zip
+ #- unzip bass.zip
+ #- cp x64/libbass.so ../lib
+ #- curl http://www.un4seen.com/files/bassopus24-linux.zip -o bassopus.zip
+ #- unzip bassopus.zip
+ #- cp x64/libbassopus.so ../lib
+ #- cd ..
+
+ # Extract Discord RPC
+ - mkdir discord-rpc
+ - cd discord-rpc
+ - curl -L https://github.com/discordapp/discord-rpc/releases/download/v3.4.0/discord-rpc-linux.zip -o discord_rpc_linux.zip
+ - unzip discord_rpc_linux.zip
+ - cp discord-rpc/linux-dynamic/lib/libdiscord-rpc.so ../lib
+ - cd ..
+
+ # Extract QtApng
+ - mkdir qtapng
+ - cd qtapng
+ - curl -L https://github.com/Skycoder42/QtApng/releases/download/1.1.0-5/build_gcc_64_5.12.0.tar.xz -o apng.tar.xz
+ - tar -xvf apng.tar.xz
+ - cp gcc_64/plugins/imageformats/libqapng.so ../lib
+ - cd ..
+
+ # Build
+ - qmake -spec linux-clang "DEFINES += DISCORD QTAUDIO"
+ - make -j4
+
+ # Post-processing
+ - upx --lzma -9 --force bin/Attorney_Online
+ artifacts:
+ paths:
+ - bin/
+
+build windows i686:
+ image: ${CI_REGISTRY_IMAGE}/builder-windows-i686
+ stage: build
+ tags:
+ - docker
+ - linux
+ script:
+ # Install dependencies
+ - apt-get update
+ - apt-get install --no-install-recommends -y make curl ca-certificates upx unzip
+
+ # Extract BASS
+ - mkdir bass
+ - cd bass
+ - curl http://www.un4seen.com/files/bass24.zip -o bass.zip
+ - unzip bass.zip
+ - cp bass.dll ../lib
+ - curl http://www.un4seen.com/files/bassopus24.zip -o bassopus.zip
+ - unzip bassopus.zip
+ - cp bassopus.dll ../lib
+ - cd ..
+
+ # Build
+ - /opt/mxe/usr/${TARGET_SPEC}/qt5/bin/qmake "DEFINES += DISCORD BASSAUDIO"
+ - make -j4
+
+ # Post-processing
+ - upx --lzma -9 --force bin/Attorney_Online.exe
+ artifacts:
+ paths:
+ - bin/
+
+# Base folder
+.deploy_base: &deploy_base |
+ mkdir base
+ mkdir base/themes
+ cp -a ../base/themes/default base/themes/
+ cp -a ../base/config.ini base/config.sample.ini
+ cp -a ../base/serverlist.txt base/serverlist.sample.txt
+
+# Miscellaneous files
+.deploy_misc: &deploy_misc |
+ cp -a ../README.md README.md.txt
+ cp -a ../LICENSE.MIT LICENSE.txt
+
+deploy linux x86_64:
+ stage: deploy
+ dependencies:
+ - build linux x86_64
+ tags:
+ - docker
+ - linux
+ script:
+ - mkdir artifact
+ - cd artifact
+ - *deploy_base
+ - *deploy_misc
+
+ # Platform-specific
+ - cp -a ../lib/*.so .
+ - cp -a ../bin/Attorney_Online .
+ - echo "#!/bin/sh" >> ./run.sh
+ - echo "LD_LIBRARY_PATH=.:\$LD_LIBRARY_PATH ./Attorney_Online" >> ./run.sh
+ - chmod +x ./run.sh
+
+ # Zipping
+ # zip -r -9 -l Attorney_Online_$(git describe --tags)_linux_x86_64.zip .
+ - mkdir ../zip
+ - tar cavf ../zip/Attorney_Online_$(git describe --tags)_linux_x64.tar.xz *
+ - sha1sum ../zip/*
+ artifacts:
+ paths:
+ - zip/
+
+deploy windows i686:
+ image: ubuntu
+ stage: deploy
+ dependencies:
+ - build windows i686
+ tags:
+ - docker
+ - linux
+ script:
+ - apt-get update
+ - apt-get install --no-install-recommends -y zip git
+
+ - mkdir artifact
+ - cd artifact
+ - *deploy_base
+ - *deploy_misc
+
+ # Platform-specific
+ - cp -a ../lib/*.dll .
+ - cp -a ../bin/Attorney_Online.exe .
+
+ # Zipping
+ # -r: recursive; -9: max compression; -l: convert to CR LF
+ - mkdir ../zip
+ - zip -r -9 -l ../zip/Attorney_Online_$(git describe --tags)_windows_x86.zip .
+ - sha1sum ../zip/*
+ artifacts:
+ paths:
+ - zip/
+
+# How to publish to S3/Wasabi:
+# - Ensure the following variables have been populated in the CI/CD settings:
+# - S3_ACCESS_KEY, S3_SECRET_KEY
+# - S3_MANIFESTS, S3_ARCHIVES
+# - Select "Run Pipeline" on the Pipelines menu on GitLab.
+# - Select the version you want to release.
+# - Set the following variables:
+# - MANIFEST: program_winnt_i386.json
+# - ARCHIVE_FULL: ao-X.Y.Z-win32.zip
+# - VERSION: X.Y.Z
+# - EXECUTABLE: Attorney_Online_X.Y.Z.exe
+# - Run the pipeline to completion.
+# (See scripts/wasabi.sh)
+
+publish linux x86_64:
+ image: ubuntu
+ stage: publish
+ dependencies:
+ - deploy linux x86_64
+ when: manual
+ script:
+ - cd zip
+ - ../scripts/wasabi.sh
+ variables:
+ MANIFEST: program_linux_x86_64.json
+ ARTIFACT_SUFFIX: _linux_x64.tar.xz
+
+publish windows i686:
+ image: ubuntu
+ stage: publish
+ dependencies:
+ - deploy windows i686
+ when: manual
+ script:
+ - cd zip
+ - ../scripts/wasabi.sh
+ variables:
+ MANIFEST: program_winnt_i386.json
+ ARTIFACT_SUFFIX: _windows_x86.zip
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..36f31d5
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,12 @@
+language: cpp
+os: osx
+
+addons:
+ homebrew:
+ update: true
+ packages:
+ - qt5
+
+script:
+ - ./scripts/macos_build.sh
+ - ./scripts/macos_post_build.sh
diff --git a/Attorney_Online.pro b/Attorney_Online.pro
index c11b5fd..488ddd1 100644
--- a/Attorney_Online.pro
+++ b/Attorney_Online.pro
@@ -1,4 +1,4 @@
-QT += core gui widgets multimedia network
+QT += core gui widgets network
TARGET = Attorney_Online
TEMPLATE = app
@@ -12,11 +12,52 @@ MOC_DIR = $$PWD/build
SOURCES += $$files($$PWD/src/*.cpp)
HEADERS += $$files($$PWD/include/*.h)
-LIBS += -L$$PWD/lib -lbass -ldiscord-rpc
-CONFIG += c++11
+
+LIBS += -L$$PWD/lib
+
+#DEFINES += DISCORD
+
+contains(DEFINES, DISCORD) {
+LIBS += -ldiscord-rpc
+}
+
+#DEFINES += BASSAUDIO
+
+contains(DEFINES, BASSAUDIO) {
+LIBS += -lbass
+}
+
+#DEFINES += QTAUDIO
+
+contains(DEFINES, QTAUDIO) {
+QT += multimedia
+}
+
+macx:LIBS += -framework CoreFoundation -framework Foundation -framework CoreServices
+
+
+CONFIG += c++14
RESOURCES += resources.qrc
+TRANSLATIONS = resource/translations/ao_en.ts \
+ resource/translations/ao_jp.ts \
+ resource/translations/ao_de.ts \
+ resource/translations/ao_ru.ts \
+ resource/translations/ao_es.ts \
+ resource/translations/ao_pl.ts
+
win32:RC_ICONS = resource/logo.ico
macx:ICON = resource/logo.icns
+
+android:DISTFILES += \
+ android/AndroidManifest.xml \
+ android/build.gradle \
+ android/gradle/wrapper/gradle-wrapper.jar \
+ android/gradle/wrapper/gradle-wrapper.properties \
+ android/gradlew \
+ android/gradlew.bat \
+ android/res/values/libs.xml
+
+ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
diff --git a/README_BUILD.md b/README_BUILD.md
index 685699c..f7441b8 100644
--- a/README_BUILD.md
+++ b/README_BUILD.md
@@ -8,22 +8,40 @@ This program has five main dependencies
* Discord Rich Presence (https://github.com/discordapp/discord-rpc/releases)
* Qt Apng Plugin (https://github.com/Skycoder42/QtApng/releases)
+### Help
+
+If you're having issues with any of this, ask in the offical Discord: https://discord.gg/wWvQ3pw
+Alternatively, you can ask OmniTroid#4004 on Discord.
+
### How to build dynamically (the easy way)
+#### General preparation
+
What you want to do is first download the latest version of Qt from the first link. (get the prebuilt dynamic version)
-If you're on Ubuntu, go to the scripts/ folder and run configure_ubuntu.sh. This should fetch all the required dependencies automatically.
-If not, go to each one of the links above and find the right dynamic library for your platform:
-* Windows: .dll
-* Linux: .so
-* Mac: .dylib
+After going through the OS-specific steps below, compiling in Qt creator should work.
-And put them in BOTH lib/ and the repository root (lib/ is required for linking and root is required for runtime)
+#### Windows
-Launch Qt creator, open the .pro file and try running it. Ask in the Discord if you're having issues: https://discord.gg/wWvQ3pw
+If you're on Windows, you need to go find all the dependencies (see above) and put them in the lib/ folder.
+
+#### MacOS
+
+If you're on MacOS, you can simply go to terminal and run ./scripts/configure_macos.sh
+This will automatically fetch all the required dependencies. Additionally, if you need to create a standalone release, just run ./scripts/release_macos.sh
+This will make the .app bundle in bin/ able to execute as a standalone.
+
+#### Ubuntu
+
+If you're on Ubuntu, just go to terminal and run ./scripts/configure_ubuntu.sh
+This should fetch all the required dependencies automatically.
+
+#### Other Linux
+
+With some tweaks to the ubuntu script, it shouldn't be a big hassle to compile it on a modern linux. Look in the script and see what you may have to modify.
### How to build statically (the hard way)
-You're gonna have a bad time.
+You're gonna have a bad time.
Building statically means you can distribute the final program without needing to pack alongside a lot of dynamic libraries.
This is a tricky process and is not recommended unless you know what you're doing.
diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
new file mode 100644
index 0000000..0792ce8
--- /dev/null
+++ b/android/AndroidManifest.xml
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/build.gradle b/android/build.gradle
new file mode 100644
index 0000000..3087d08
--- /dev/null
+++ b/android/build.gradle
@@ -0,0 +1,62 @@
+buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.5.0'
+ }
+}
+
+repositories {
+ google()
+ jcenter()
+}
+
+apply plugin: 'com.android.application'
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
+}
+
+android {
+ /*******************************************************
+ * The following variables:
+ * - androidBuildToolsVersion,
+ * - androidCompileSdkVersion
+ * - qt5AndroidDir - holds the path to qt android files
+ * needed to build any Qt application
+ * on Android.
+ *
+ * are defined in gradle.properties file. This file is
+ * updated by QtCreator and androiddeployqt tools.
+ * Changing them manually might break the compilation!
+ *******************************************************/
+
+ compileSdkVersion androidCompileSdkVersion.toInteger()
+
+ buildToolsVersion '28.0.3'
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
+ aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
+ res.srcDirs = [qt5AndroidDir + '/res', 'res']
+ resources.srcDirs = ['resources']
+ renderscript.srcDirs = ['src']
+ assets.srcDirs = ['assets']
+ jniLibs.srcDirs = ['libs']
+ }
+ }
+
+ lintOptions {
+ abortOnError false
+ }
+
+ // Do not compress Qt binary resources file
+ aaptOptions {
+ noCompress 'rcc'
+ }
+}
diff --git a/android/project.properties b/android/project.properties
new file mode 100644
index 0000000..a08f37e
--- /dev/null
+++ b/android/project.properties
@@ -0,0 +1 @@
+target=android-21
\ No newline at end of file
diff --git a/android/res/drawable-ldpi/icon.png b/android/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..f53fe30
Binary files /dev/null and b/android/res/drawable-ldpi/icon.png differ
diff --git a/android/res/values/libs.xml b/android/res/values/libs.xml
new file mode 100644
index 0000000..6b1a4a2
--- /dev/null
+++ b/android/res/values/libs.xml
@@ -0,0 +1,22 @@
+
+
+
+ https://download.qt.io/ministro/android/qt5/qt-5.14
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/include/aoapplication.h b/include/aoapplication.h
index f1c4190..280b731 100644
--- a/include/aoapplication.h
+++ b/include/aoapplication.h
@@ -22,6 +22,10 @@
#include
#include
#include
+#include
+#ifdef QTAUDIO
+#include
+#endif
#include
#include
@@ -189,6 +193,9 @@ public:
// Returns the audio device used for the client.
QString get_audio_output_device();
+ #ifdef QTAUDIO
+ QAudioDeviceInfo QtAudioDevice;
+ #endif
// Returns whether the user would like to have custom shownames on by default.
bool get_showname_enabled_by_default();
@@ -211,6 +218,9 @@ public:
//Returns the dimensions of widget with specified identifier from p_file
pos_size_type get_element_dimensions(QString p_identifier, QString p_file);
+ //Returns the name of the font with p_identifier from p_file
+ QString get_font_name(QString p_identifier, QString p_file);
+
//Returns the value of font_size with p_identifier from p_file
int get_font_size(QString p_identifier, QString p_file);
@@ -332,7 +342,6 @@ public:
private:
const int RELEASE = 2;
const int MAJOR_VERSION = 7;
- const int MINOR_VERSION = 2;
QString current_theme = "default";
diff --git a/include/aoblipplayer.h b/include/aoblipplayer.h
index aebba77..44ca48b 100644
--- a/include/aoblipplayer.h
+++ b/include/aoblipplayer.h
@@ -1,13 +1,19 @@
#ifndef AOBLIPPLAYER_H
#define AOBLIPPLAYER_H
+#if defined(BASSAUDIO)
#include "bass.h"
+#elif defined(QTAUDIO)
+#include
+#endif
+
#include "aoapplication.h"
#include
#include
#include
+
class AOBlipPlayer
{
public:
@@ -15,16 +21,22 @@ public:
void set_blips(QString p_sfx);
void blip_tick();
- void set_volume(int p_volume);
+ void set_volume(qreal p_volume);
int m_cycle = 0;
private:
QWidget *m_parent;
AOApplication *ao_app;
+ qreal m_volume;
- int m_volume;
+ void set_volume_internal(qreal p_volume);
+
+ #if defined(BASSAUDIO)
HSTREAM m_stream_list[5];
+ #elif defined(QTAUDIO)
+ QSoundEffect m_blips;
+ #endif
};
#endif // AOBLIPPLAYER_H
diff --git a/include/aoevidencebutton.h b/include/aoevidencebutton.h
index 27fb84b..80b747c 100644
--- a/include/aoevidencebutton.h
+++ b/include/aoevidencebutton.h
@@ -35,8 +35,10 @@ protected:
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
void mouseDoubleClickEvent(QMouseEvent *e);
+ /*
void dragLeaveEvent(QMouseEvent *e);
void dragEnterEvent(QMouseEvent *e);
+ */
signals:
void evidence_clicked(int p_id);
diff --git a/include/aomusicplayer.h b/include/aomusicplayer.h
index a88cb79..71ae604 100644
--- a/include/aomusicplayer.h
+++ b/include/aomusicplayer.h
@@ -1,7 +1,11 @@
#ifndef AOMUSICPLAYER_H
#define AOMUSICPLAYER_H
+#if defined(BASSAUDIO)
#include "bass.h"
+#elif defined(QTAUDIO)
+#include
+#endif
#include "aoapplication.h"
#include
@@ -32,5 +36,37 @@ private:
HSTREAM m_stream;
};
+#elif defined(QTAUDIO)
+class AOMusicPlayer : public QObject
+{
+public:
+ AOMusicPlayer(QWidget *parent, AOApplication *p_ao_app);
+ ~AOMusicPlayer();
+
+ void play(QString p_song);
+ void set_volume(int p_value);
+
+private:
+ QMediaPlayer m_player;
+ QWidget *m_parent;
+ AOApplication *ao_app;
+
+ int m_volume = 0;
+};
+#else
+class AOMusicPlayer : public QObject
+{
+public:
+ AOMusicPlayer(QWidget *parent, AOApplication *p_ao_app);
+ ~AOMusicPlayer();
+
+ void play(QString p_song);
+ void set_volume(int p_value);
+
+private:
+ QWidget *m_parent;
+ AOApplication *ao_app;
+};
+#endif
#endif // AOMUSICPLAYER_H
diff --git a/include/aooptionsdialog.h b/include/aooptionsdialog.h
index 53bd309..233d079 100644
--- a/include/aooptionsdialog.h
+++ b/include/aooptionsdialog.h
@@ -2,7 +2,13 @@
#define AOOPTIONSDIALOG_H
#include "aoapplication.h"
+
+#ifdef BASSAUDIO
#include "bass.h"
+#elif defined QTAUDIO
+#include
+#include
+#endif
#include
#include
@@ -57,6 +63,8 @@ private:
QCheckBox *ui_discord_cb;
QLabel *ui_epilepsy_lbl;
QCheckBox *ui_epilepsy_cb;
+ QLabel *ui_language_label;
+ QComboBox *ui_language_combobox;
QWidget *ui_callwords_tab;
QWidget *ui_callwords_widget;
diff --git a/include/aosfxplayer.h b/include/aosfxplayer.h
index 39bea0d..5b4d2ec 100644
--- a/include/aosfxplayer.h
+++ b/include/aosfxplayer.h
@@ -1,7 +1,12 @@
#ifndef AOSFXPLAYER_H
#define AOSFXPLAYER_H
+#if defined(BASSAUDIO)
#include "bass.h"
+#elif defined(QTAUDIO)
+#include
+#endif
+
#include "aoapplication.h"
#include
@@ -17,15 +22,22 @@ public:
void play(QString p_sfx, QString p_char = "", QString shout = "");
void stop();
- void set_volume(int p_volume);
+ void set_volume(qreal p_volume);
void setLooping(bool is_looping);
+
private:
QWidget *m_parent;
AOApplication *ao_app;
-
- int m_volume = 0;
+ qreal m_volume = 0;
bool looping_sfx = false;
+
+ void set_volume_internal(qreal p_volume);
+
+ #if defined(BASSAUDIO)
HSTREAM m_stream;
+ #elif defined(QTAUDIO)
+ QSoundEffect m_sfx;
+ #endif
};
#endif // AOSFXPLAYER_H
diff --git a/include/courtroom.h b/include/courtroom.h
index e00e454..587d963 100644
--- a/include/courtroom.h
+++ b/include/courtroom.h
@@ -10,6 +10,7 @@
#include "aomovie.h"
#include "aocharmovie.h"
#include "aomusicplayer.h"
+#include "aooptionsdialog.h"
#include "aosfxplayer.h"
#include "aoblipplayer.h"
#include "aoevidencebutton.h"
@@ -36,7 +37,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -60,6 +60,8 @@
#include
#include
#include
+#include
+
#include
class AOApplication;
@@ -558,6 +560,7 @@ private:
void construct_char_select();
void set_char_select();
void set_char_select_page();
+ void char_clicked(int n_char);
void put_button_in_place(int starting, int chars_on_this_page);
void filter_character_list();
@@ -673,14 +676,12 @@ private slots:
void on_char_select_left_clicked();
void on_char_select_right_clicked();
- void on_char_search_changed(const QString& newtext);
- void on_char_taken_clicked(int newstate);
- void on_char_passworded_clicked(int newstate);
+ void on_char_search_changed();
+ void on_char_taken_clicked();
+ void on_char_passworded_clicked();
void on_spectator_clicked();
- void char_clicked(int n_char);
-
void on_switch_area_music_clicked();
void on_casing_clicked();
diff --git a/include/discord_rich_presence.h b/include/discord_rich_presence.h
index e7ecc6e..4115b51 100644
--- a/include/discord_rich_presence.h
+++ b/include/discord_rich_presence.h
@@ -5,6 +5,8 @@
#include
#include
#include
+#include
+
#include
#include
@@ -16,6 +18,8 @@ namespace AttorneyOnline {
class Discord
{
+ Q_DECLARE_TR_FUNCTIONS(Discord)
+
private:
const char* APPLICATION_ID = "399779271737868288";
std::string server_name, server_id;
diff --git a/include/lobby.h b/include/lobby.h
index 3a34fb8..06cd798 100644
--- a/include/lobby.h
+++ b/include/lobby.h
@@ -75,6 +75,8 @@ private:
QProgressBar *ui_progress_bar;
AOButton *ui_cancel;
+ QModelIndex last_model;
+
void set_size_and_pos(QWidget *p_widget, QString p_identifier);
public slots:
void fucking_threading_goddamn_it(QString fuckshitassgoddamnfuck);
@@ -90,6 +92,7 @@ private slots:
void on_connect_released();
void on_about_clicked();
void on_server_list_clicked(QModelIndex p_model);
+ void on_server_list_doubleclicked(QModelIndex p_model);
void on_chatfield_return_pressed();
};
diff --git a/include/networkmanager.h b/include/networkmanager.h
index e28abfd..08b10db 100644
--- a/include/networkmanager.h
+++ b/include/networkmanager.h
@@ -44,13 +44,11 @@ public:
QString ms_nosrv_hostname = "master.aceattorneyonline.com";
#endif
- const int ms_port = 27016;
+ const quint16 ms_port = 27016;
const int timeout_milliseconds = 2000;
- const int ms_reconnect_delay_ms = 7000;
-
- // kind of arbitrary max buffer size
- const size_t buffer_max_size = 16384;
+ // in seconds
+ const int ms_reconnect_delay = 7;
bool ms_partial_packet = false;
QString ms_temp_packet = "";
diff --git a/resource/translations/ao_de.qm b/resource/translations/ao_de.qm
new file mode 100644
index 0000000..6fd250d
Binary files /dev/null and b/resource/translations/ao_de.qm differ
diff --git a/resource/translations/ao_de.ts b/resource/translations/ao_de.ts
new file mode 100644
index 0000000..1c0675b
--- /dev/null
+++ b/resource/translations/ao_de.ts
@@ -0,0 +1,982 @@
+
+
+
+
+ AOApplication
+
+
+ Disconnected from server.
+ Vom Server getrennt.
+
+
+
+ Error connecting to master server. Will try again in %1 seconds.
+ Fehler bei der Verbindung zum Master Server. Erneuter Versuch in %1 Sekunden.
+
+
+ Error connecting to master server. Will try again in %n seconds.
+ Fehler bei der Verbindung zum Master Server. Erneuter Versuch in %n Sekunden.
+
+
+
+ There was an error connecting to the master server.
+We deploy multiple master servers to mitigate any possible downtime, but the client appears to have exhausted all possible methods of finding and connecting to one.
+Please check your Internet connection and firewall, and please try again.
+ Es gab einen Fehler beim Verbinden zum Master Server.
+Wir verwenden mehrere Master Server um Ausfälle zu verhindern, jedoch hat der Client alle Möglichkeiten einen zu finden erschöpft.
+Bitte prüfe deine Internetverbindung und Firewall, und versuche es erneut.
+
+
+
+ Outdated version! Your version: %1
+Please go to aceattorneyonline.com to update.
+ Version zu alt! Deine Version: %1
+Bitte besuche aceattorneyonline.com für ein Update.
+
+
+
+ You have been exiled from AO.
+Have a nice day.
+ Du wurdest von AO befreit.
+Schönen Urlaub.
+
+
+
+ Attorney Online 2
+ Attorney Online 2
+
+
+
+ Loading
+ Laden
+
+
+
+ Loading evidence:
+%1/%2
+ Lade Beweisstücke:
+%1/%2
+
+
+
+
+ Loading music:
+%1/%2
+ Lade Musik:
+%1/%2
+
+
+
+ Loading chars:
+%1/%2
+ Lade Charaktere:
+%1/%2
+
+
+
+ You have been kicked from the server.
+Reason: %1
+ Du wurdest von diesem Server geschmissen.
+Grund: %1
+
+
+
+ You have been banned from the server.
+Reason: %1
+ Du wurdest von diesem Server verbannt.
+Grund: %1
+
+
+
+ You are banned on this server.
+Reason: %1
+ Du bist von diesem Server verbannt.
+Grund: %1
+
+
+ You have been kicked from the server.
+Reason:
+ Du wurdest von diesem Server geschmissen.
+Grund:
+
+
+ You are banned on this server.
+Reason:
+ Du wurdest von diesem Server verbannt.
+Grund:
+
+
+ You have been kicked.
+ Du wurdest rausgeschmissen.
+
+
+ You are banned on this server.
+ Du wurdest verbannt.
+
+
+
+ AOCaseAnnouncerDialog
+
+
+ Case Announcer
+ Fallansager
+
+
+
+ Case title:
+ Fallname:
+
+
+
+ Defense needed
+ Verteidiger benötigt
+
+
+
+ Prosecution needed
+ Kläger benötigt
+
+
+
+ Judge needed
+ Richter benötigt
+
+
+
+ Jurors needed
+ Jury benötigt
+
+
+
+ Stenographer needed
+ Stenograph benötigt
+
+
+
+ AOOptionsDialog
+
+
+ Settings
+ Einstellungen
+
+
+
+ Gameplay
+ Spiel
+
+
+
+ Theme:
+ Theme:
+
+
+
+ Sets the theme used in-game. If the new theme changes the lobby's look as well, you'll need to reload the lobby for the changes to take effect, such as by joining a server and leaving it.
+ Setzt das Theme. Wenn das neue Theme auch das Aussehen der Lobby verändert, must du diese neu laden um die Änderungen zu sehen.
+
+
+
+ Log goes downwards:
+ Verlauf geht nach unten:
+
+
+
+ If ticked, new messages will appear at the bottom (like the OOC chatlog). The traditional (AO1) behaviour is equivalent to this being unticked.
+ Wenn angehakt werden neue Nachrichten unten erscheinen (wie beim OOC). Das traditionelle (AO1) Verhalten wäre nicht angehakt.
+
+
+
+ Log length:
+ Länge:
+
+
+
+ The amount of messages the IC chatlog will keep before deleting older messages. A value of 0 or below counts as 'infinite'.
+ Die Menge an Nachrichten die aufgehoben werden bevor alte gelöscht werden. 0 bedeutet unendlich.
+
+
+
+ Default username:
+ Standard Benutzername:
+
+
+
+ Your OOC name will be automatically set to this value when you join a server.
+ Dein OOC Name wird automatisch auf dies gesetzt.
+
+
+
+ Custom shownames:
+ Eigener Anzeigename:
+
+
+
+ Gives the default value for the in-game 'Custom shownames' tickbox, which in turn determines whether the client should display custom in-character names.
+ Standardwert für die Anzeigename Box, welche den In-Charakter Namen bestimmt.
+
+
+
+ Backup MS:
+ Rückfall MS:
+
+
+
+ If the built-in server lookups fail, the game will try the address given here and use it as a backup master server address.
+ Wenn dereingebaute Master Server fehlschlägt, wird das Spiel diesen hier verwenden.
+
+
+
+ Discord:
+ Discord:
+
+
+
+ Allows others on Discord to see what server you are in, what character are you playing, and how long you have been playing for.
+ Erlaubt anderen auf Discord zu sehen auf welchem Server du spielst, welchen Charakter du spielst und wie lange.
+
+
+
+ Language:
+ Sprache:
+
+
+
+ Sets the language if you don't want to use your system language.
+ Setzte die Sprache falls du nicht die Systemsprache verwenden möchtest.
+
+
+
+ Callwords
+ Alarmwörter
+
+
+
+ <html><head/><body>Enter as many callwords as you would like. These are case insensitive. Make sure to leave every callword in its own line!<br>Do not leave a line with a space at the end -- you will be alerted everytime someone uses a space in their messages.</body></html>
+ <html><head/><body>Gib so viele Alarmwörter ein wie du möchtest. Groß/Kleinschreibung ist egal. Für jede Wort nur eine Zeile!<br>Bitte keine leere Zeile am Ende -- du bekommst sonst bei jeder Nachricht einen Alarm.</body></html>
+
+
+
+ Audio
+ Audio
+
+
+
+ Audio device:
+ Audiogerät:
+
+
+
+ Sets the audio device for all sounds.
+ Setzt das Audiogerät für all Geräusche.
+
+
+
+ Music:
+ Musik:
+
+
+
+ Sets the music's default volume.
+ Setzt die Musiklautstärke.
+
+
+
+ SFX:
+ SFX:
+
+
+
+ Sets the SFX's default volume. Interjections and actual sound effects count as 'SFX'.
+ Setzt die Lautstärke der Soundeffekte wie Einsprüche und die Geräusche der Charaktere.
+
+
+
+ Blips:
+ Blips:
+
+
+
+ Sets the volume of the blips, the talking sound effects.
+ Setzt die Lautstärke der Blips, das ist das Geräusch das die Charaktere beim Reden machen.
+
+
+
+ Blip rate:
+ Bliprate:
+
+
+
+ Sets the delay between playing the blip sounds.
+ Setzt die Pause zwischen einzelnen Blips.
+
+
+
+ Blank blips:
+ Leere Blips:
+
+
+
+ If true, the game will play a blip sound even when a space is 'being said'.
+ Wenn angehakt wird das Spiel auch bei einem Leerzeichen einen Blip machen.
+
+
+
+ Casing
+ Fälle
+
+
+
+ This server supports case alerts.
+ Dieser Server unterstützt Fallalarme.
+
+
+
+ This server does not support case alerts.
+ Dieser Server unterstützt Fallalarme nicht.
+
+
+
+ Pretty self-explanatory.
+ Eigentlich selbsterklärend.
+
+
+
+ Casing:
+ Fälle:
+
+
+
+ If checked, you will get alerts about case announcements.
+ Wenn angehakt wirst du benachrichtigt wenn ein Fall angekündigt wird.
+
+
+
+ Defense:
+ Verteidigung:
+
+
+
+ If checked, you will get alerts about case announcements if a defense spot is open.
+ Wenn angehakt wirst du benachrichtigt wenn ein Verteidiger benötigt wird.
+
+
+
+ Prosecution:
+ Kläger:
+
+
+
+ If checked, you will get alerts about case announcements if a prosecutor spot is open.
+ Wenn angehakt wirst du benachrichtigt wenn ein Kläger benötigt wird.
+
+
+
+ Judge:
+ Richter:
+
+
+
+ If checked, you will get alerts about case announcements if the judge spot is open.
+ Wenn angehakt wirst du benachrichtigt wenn ein Richter benötigt wird.
+
+
+
+ Juror:
+ Jury:
+
+
+
+ If checked, you will get alerts about case announcements if a juror spot is open.
+ Wenn angehakt wirst du benachrichtigt wenn eine Jury benötigt wird.
+
+
+
+ Stenographer:
+ Stenograph:
+
+
+
+ If checked, you will get alerts about case announcements if a stenographer spot is open.
+ Wenn angehakt wirst du benachrichtigt wenn ein Stenograph benötigt wird.
+
+
+
+ CM:
+ CM:
+
+
+
+ If checked, you will appear amongst the potential CMs on the server.
+ Wenn angehakt wirst du als potentielle CM angezeigt.
+
+
+
+ Hosting cases:
+ Fallleitung:
+
+
+
+ If you're a CM, enter what cases you are willing to host.
+ Wenn du CM bist, gib ein welche Fälle du spielen möchtest.
+
+
+
+ Courtroom
+
+
+ Password
+ Passwort
+
+
+
+ Spectator
+ Zuschauer
+
+
+
+
+ Search
+ Suche
+
+
+
+ Passworded
+ Gesperrt
+
+
+
+ Taken
+ Benutzt
+
+
+
+ Generating chars:
+%1/%2
+ Generiere Charaktere:
+%1/%2
+
+
+ Generating chars:
+
+ Generiere Charaktere:
+
+
+
+ Could not find %1
+ Konnte %1 nicht finden.
+
+
+
+ Showname
+ Anzeigename
+
+
+
+ Message
+ Nachricht
+
+
+
+ Name
+ Name
+
+
+
+ Pre
+ Vor
+
+
+
+ Flip
+ Spiegeln
+
+
+
+ Guard
+ Wache
+
+
+
+
+ Casing
+ Fall
+
+
+
+ Shownames
+ Anzeigenamen
+
+
+
+ No Interrupt
+ Keine Unterbrechung
+
+
+
+ White
+ Weiß
+
+
+
+ Green
+ Grün
+
+
+
+ Red
+ Rot
+
+
+
+ Orange
+ Orange
+
+
+
+ Blue
+ Blau
+
+
+
+ Yellow
+ Gelb
+
+
+
+ Music
+ Musik
+
+
+
+ Sfx
+ Sfx
+
+
+
+ Blips
+ Blips
+
+
+
+ Log limit
+ Verlaufsgrenze
+
+
+
+
+ Server
+ Server
+
+
+
+ Change character
+ Charakter ändern
+
+
+
+ Reload theme
+ Aussehen neu laden
+
+
+
+ Call mod
+ Moderator rufen
+
+
+
+ Settings
+ Einstellungen
+
+
+
+ A/M
+ A/M
+
+
+
+ Preanim
+ Voranimation
+
+
+
+ Back to Lobby
+ Zurück zur Lobby
+
+
+ You have been banned.
+ Du wurdest verbannt.
+
+
+ %1 has played a song: %2
+ %1 hat ein Lied gespielt: %2
+
+
+
+ Rainbow
+ Regenbogen
+
+
+
+ Pink
+ Pink
+
+
+
+ Cyan
+ Cyan
+
+
+
+ % offset
+ % Abstand
+
+
+
+ You were granted the Guard button.
+ Dir wurde der Wache Knopf gegeben.
+
+
+ This does nohing, but there you go.
+ Dies bewirkt nichts, aber egal.
+
+
+
+ This does nothing, but there you go.
+ Dies bewirkt nichts, aber egal.
+
+
+
+ You opened the settings menu.
+ Du hast die Einstellungen geöffnet.
+
+
+
+ You will now pair up with
+ Du wirst nun mit
+
+
+
+ if they also choose your character in return.
+ gepaart, wenn der andere dies auch tut.
+
+
+
+ You are no longer paired with anyone.
+ Du bist nicht mehr gepaart.
+
+
+
+ Are you sure you typed that well? The char ID could not be recognised.
+ Hast du dich vertippt? Die ID konnte nicht erkannt werden.
+
+
+
+ You have set your offset to
+ Dein Abstand ist auf
+
+
+
+ Your offset must be between -100% and 100%!
+ Der Abstand muss zwischen -100% und 100% liegen!
+
+
+
+ That offset does not look like one.
+ Das sieht nicht wie ein Abstand aus.
+
+
+
+ You switched your music and area list.
+ Du hast zwischen Musik- und Gebitsliste umgeschaltet.
+
+
+
+ You have forcefully enabled features that the server may not support. You may not be able to talk IC, or worse, because of this.
+ Du hast Funktionen erzwungen die der Server eventuell nicht unterstützt. Möglicherweise wirst du nicht mehr sprechen können.
+
+
+
+ Your pre-animations interrupt again.
+ Deine Voranimation unterbrechen nun Text.
+
+
+
+ Your pre-animations will not interrupt text.
+ Deine Voranimation unterbrechen Text nicht.
+
+
+
+ Couldn't open chatlog.txt to write into.
+ Konnte chatlog.txt nicht öffnen.
+
+
+
+ The IC chatlog has been saved.
+ Der IC Verlauf wurde gespeichert.
+
+
+
+ You don't have a `base/cases/` folder! It was just made for you, but seeing as it WAS just made for you, it's likely the case file you're looking for can't be found in there.
+ Du hattest keinen 'base/cases' Ordner! Ich hab ihn nun angelegt aber bedenke das er leer sein wird.
+
+
+
+ You need to give a filename to load (extension not needed)! Make sure that it is in the `base/cases/` folder, and that it is a correctly formatted ini.
+Cases you can load: %1
+ Du musst einen Dateinamen angeben (ohne .ini). Stelle sicher das er im 'base/cases' Ordner ist und das er korrekt formatiert ist.
+Verfügbare Fälle: %1
+
+
+
+ Case made by %1.
+ Fall von %1.
+
+
+
+ Navigate to %1 for the CM doc.
+ Gehe zu %1 für das CM Dokument.
+
+
+
+ Your case "%1" was loaded!
+ Dein Fall "%1" wurde geladen!
+
+
+ You need to give a filename to load (extension not needed)! Make sure that it is in the `base/cases/` folder, and that it is a correctly formatted ini.
+Cases you can load:
+ Du musst einen Dateinamen angeben (ohne .ini). Stelle sicher das er im 'base/cases' Ordner ist und das er korrekt formatiert ist.
+Verfügbare Fälle:
+
+
+
+ Too many arguments to load a case! You only need one filename, without extension.
+ Zu viele Argumente! Du brauchst nur den Dateinamen, ohne Erweiterung.
+
+
+ Case made by
+ Fall von
+
+
+ Navigate to
+ Gehe zu
+
+
+ for the CM doc.
+ für das CM Dokument.
+
+
+ Your case "
+ Dein Fall "
+
+
+ " was loaded!
+ " wurde geladen!
+
+
+
+ You don't have a `base/cases/` folder! It was just made for you, but seeing as it WAS just made for you, it's likely that you somehow deleted it.
+ Du hattest keinen 'base/cases' Ordner! Ich hab ihn nun angelegt aber bedenke das er leer sein wird.
+
+
+
+ You need to give a filename to save (extension not needed) and the courtroom status!
+ Du musst einen Dateinamen (ohne Erweiterung) angebenn, sowie den Gebietsstatus!
+
+
+
+ Too many arguments to save a case! You only need a filename without extension and the courtroom status!
+ Zu viele Argumente! Du brauchst nur den Dateinamen, ohne Erweiterung sowie den Gebietsstatus!
+
+
+
+ Succesfully saved, edit doc and cmdoc link on the ini!
+ Erfolgreich gespeichert!
+
+
+
+ Master
+ Master
+
+
+
+ Reason:
+ Grund:
+
+
+
+ Call Moderator
+ Moderator rufen
+
+
+
+
+ Error
+ Fehler
+
+
+
+ You must provide a reason.
+ Du musst einen Grund angeben.
+
+
+
+ The message is too long.
+ Die Nachricht ist zu lang.
+
+
+ Choose..
+ Wähle..
+
+
+
+ Choose...
+ Wähle...
+
+
+
+ Images (*.png)
+ Bilder (*.png)
+
+
+
+ Add new evidence...
+ Neues Beweisstück...
+
+
+
+ Discord
+
+ Objection!
+ Einspruch!
+
+
+ In Lobby
+ In Lobby
+
+
+ Idle
+ Untätig
+
+
+ In a Server
+ In einem Server
+
+
+ Playing as %1
+ Spielt als %1
+
+
+ Spectating
+ Zuschauend
+
+
+
+ Lobby
+
+
+ Attorney Online 2
+ Attorney Online 2
+
+
+
+ Name
+ Name
+
+
+
+ It doesn't look like your client is set up correctly.
+Did you download all resources correctly from tiny.cc/getao, including the large 'base' folder?
+ Dein Client ist nicht korrekt eingerichtet.
+Hast du ALLES von tiny.cc/getao heruntergeladen und entpackt, auch den großen 'base' Ordner?
+
+
+
+ Version: %1
+ Version: %1
+
+
+
+ Loading
+ Laden
+
+
+
+ Cancel
+ Abbrechen
+
+
+
+ <h2>Attorney Online %1</h2>The courtroom drama simulator<p><b>Source code:</b> <a href='https://github.com/AttorneyOnline/AO2-Client'>https://github.com/AttorneyOnline/AO2-Client</a><p><b>Major development:</b><br>OmniTroid, stonedDiscord, longbyte1, gameboyprinter, Cerapter<p><b>Special thanks:</b><br>Remy, Iamgoofball, Hibiki, Qubrick (webAO), Ruekasu (UI design), Draxirch (UI design), Unishred, Argoneus (tsuserver), Fiercy, Noevain, Cronnicossy
+ <h2>Attorney Online %1</h2>Der Gerichtsdrama Simulator<p><b>Quelltext:</b> <a href='https://github.com/AttorneyOnline/AO2-Client'>https://github.com/AttorneyOnline/AO2-Client</a><p><b>Leitende Entwicklung:</b><br>OmniTroid, stonedDiscord, longbyte1, gameboyprinter, Cerapter<p><b>Danksagungen:</b><br>Remy, Iamgoofball, Hibiki, Qubrick (webAO), Ruekasu (UI design), Draxirch (UI design), Unishred, Argoneus (tsuserver), Fiercy, Noevain, Cronnicossy
+
+
+
+ Online: %1/%2
+ Online: %1/%2
+
+
+ Attorney Online 2 is built using Qt 5.11.
+
+Lead development:
+longbyte1
+OmniTroid
+
+stonedDiscord
+Supporting development:
+Fiercy
+
+UI design:
+Ruekasu
+Draxirch
+
+Special thanks:
+Unishred
+Argoneus
+Noevain
+Cronnicossy
+ Attorney Online 2 wurde gemacht mit Qt 5.11.
+
+Leitende Entwicklung:
+longbyte1
+OmniTroid
+stonedDiscord
+
+Unterstützende Entwicklung:
+Fiercy
+
+UI Design:
+Ruekasu
+Draxirch
+
+Speziellen Dank:
+Unishred
+Argoneus
+Noevain
+Cronnicossy
+
+
+
+
+ Offline
+ Offline
+
+
+
+ debug_functions
+
+
+ Error: %1
+ Fehler: %1
+
+
+
+ Error
+ Fehler
+
+
+
+ Notice
+ Hinweis
+
+
+
diff --git a/resource/translations/ao_en.qm b/resource/translations/ao_en.qm
new file mode 100644
index 0000000..9dad8df
Binary files /dev/null and b/resource/translations/ao_en.qm differ
diff --git a/resource/translations/ao_en.ts b/resource/translations/ao_en.ts
new file mode 100644
index 0000000..b917a41
--- /dev/null
+++ b/resource/translations/ao_en.ts
@@ -0,0 +1,826 @@
+
+
+
+
+ AOApplication
+
+
+ Disconnected from server.
+
+
+
+
+ Error connecting to master server. Will try again in %1 seconds.
+
+
+
+
+ There was an error connecting to the master server.
+We deploy multiple master servers to mitigate any possible downtime, but the client appears to have exhausted all possible methods of finding and connecting to one.
+Please check your Internet connection and firewall, and please try again.
+
+
+
+
+ Outdated version! Your version: %1
+Please go to aceattorneyonline.com to update.
+
+
+
+
+ You have been exiled from AO.
+Have a nice day.
+
+
+
+
+ Attorney Online 2
+
+
+
+
+ Loading
+
+
+
+
+ Loading evidence:
+%1/%2
+
+
+
+
+
+ Loading music:
+%1/%2
+
+
+
+
+ Loading chars:
+%1/%2
+
+
+
+
+ You have been kicked from the server.
+Reason: %1
+
+
+
+
+ You have been banned from the server.
+Reason: %1
+
+
+
+
+ You are banned on this server.
+Reason: %1
+
+
+
+
+ AOCaseAnnouncerDialog
+
+
+ Case Announcer
+
+
+
+
+ Case title:
+
+
+
+
+ Defense needed
+
+
+
+
+ Prosecution needed
+
+
+
+
+ Judge needed
+
+
+
+
+ Jurors needed
+
+
+
+
+ Stenographer needed
+
+
+
+
+ AOOptionsDialog
+
+
+ Settings
+
+
+
+
+ Gameplay
+
+
+
+
+ Theme:
+
+
+
+
+ Sets the theme used in-game. If the new theme changes the lobby's look as well, you'll need to reload the lobby for the changes to take effect, such as by joining a server and leaving it.
+
+
+
+
+ Log goes downwards:
+
+
+
+
+ If ticked, new messages will appear at the bottom (like the OOC chatlog). The traditional (AO1) behaviour is equivalent to this being unticked.
+
+
+
+
+ Log length:
+
+
+
+
+ The amount of messages the IC chatlog will keep before deleting older messages. A value of 0 or below counts as 'infinite'.
+
+
+
+
+ Default username:
+
+
+
+
+ Your OOC name will be automatically set to this value when you join a server.
+
+
+
+
+ Custom shownames:
+
+
+
+
+ Gives the default value for the in-game 'Custom shownames' tickbox, which in turn determines whether the client should display custom in-character names.
+
+
+
+
+ Backup MS:
+
+
+
+
+ If the built-in server lookups fail, the game will try the address given here and use it as a backup master server address.
+
+
+
+
+ Discord:
+
+
+
+
+ Allows others on Discord to see what server you are in, what character are you playing, and how long you have been playing for.
+
+
+
+
+ Language:
+
+
+
+
+ Sets the language if you don't want to use your system language.
+
+
+
+
+ Callwords
+
+
+
+
+ <html><head/><body>Enter as many callwords as you would like. These are case insensitive. Make sure to leave every callword in its own line!<br>Do not leave a line with a space at the end -- you will be alerted everytime someone uses a space in their messages.</body></html>
+
+
+
+
+ Audio
+
+
+
+
+ Audio device:
+
+
+
+
+ Sets the audio device for all sounds.
+
+
+
+
+ Music:
+
+
+
+
+ Sets the music's default volume.
+
+
+
+
+ SFX:
+
+
+
+
+ Sets the SFX's default volume. Interjections and actual sound effects count as 'SFX'.
+
+
+
+
+ Blips:
+
+
+
+
+ Sets the volume of the blips, the talking sound effects.
+
+
+
+
+ Blip rate:
+
+
+
+
+ Sets the delay between playing the blip sounds.
+
+
+
+
+ Blank blips:
+
+
+
+
+ If true, the game will play a blip sound even when a space is 'being said'.
+
+
+
+
+ Casing
+
+
+
+
+ This server supports case alerts.
+
+
+
+
+ This server does not support case alerts.
+
+
+
+
+ Pretty self-explanatory.
+
+
+
+
+ Casing:
+
+
+
+
+ If checked, you will get alerts about case announcements.
+
+
+
+
+ Defense:
+
+
+
+
+ If checked, you will get alerts about case announcements if a defense spot is open.
+
+
+
+
+ Prosecution:
+
+
+
+
+ If checked, you will get alerts about case announcements if a prosecutor spot is open.
+
+
+
+
+ Judge:
+
+
+
+
+ If checked, you will get alerts about case announcements if the judge spot is open.
+
+
+
+
+ Juror:
+
+
+
+
+ If checked, you will get alerts about case announcements if a juror spot is open.
+
+
+
+
+ Stenographer:
+
+
+
+
+ If checked, you will get alerts about case announcements if a stenographer spot is open.
+
+
+
+
+ CM:
+
+
+
+
+ If checked, you will appear amongst the potential CMs on the server.
+
+
+
+
+ Hosting cases:
+
+
+
+
+ If you're a CM, enter what cases you are willing to host.
+
+
+
+
+ Courtroom
+
+
+ Password
+
+
+
+
+ Spectator
+
+
+
+
+
+ Search
+
+
+
+
+ Passworded
+
+
+
+
+ Taken
+
+
+
+
+ Generating chars:
+%1/%2
+
+
+
+
+ Showname
+
+
+
+
+ Message
+
+
+
+
+ Name
+
+
+
+
+ Pre
+
+
+
+
+ Flip
+
+
+
+
+ Guard
+
+
+
+
+
+ Casing
+
+
+
+
+ Shownames
+
+
+
+
+ No Interrupt
+
+
+
+
+ White
+
+
+
+
+ Green
+
+
+
+
+ Red
+
+
+
+
+ Orange
+
+
+
+
+ Blue
+
+
+
+
+ Yellow
+
+
+
+
+ This does nothing, but there you go.
+
+
+
+
+ You need to give a filename to load (extension not needed)! Make sure that it is in the `base/cases/` folder, and that it is a correctly formatted ini.
+Cases you can load: %1
+
+
+
+
+ Case made by %1.
+
+
+
+
+ Navigate to %1 for the CM doc.
+
+
+
+
+ Your case "%1" was loaded!
+
+
+
+
+
+ Server
+
+
+
+
+ Back to Lobby
+
+
+
+
+ Rainbow
+
+
+
+
+ Pink
+
+
+
+
+ Cyan
+
+
+
+
+ % offset
+
+
+
+
+ Music
+
+
+
+
+ Sfx
+
+
+
+
+ Blips
+
+
+
+
+ Log limit
+
+
+
+
+ Change character
+
+
+
+
+ Reload theme
+
+
+
+
+ Call mod
+
+
+
+
+ Settings
+
+
+
+
+ A/M
+
+
+
+
+ Preanim
+
+
+
+
+ You were granted the Guard button.
+
+
+
+
+ You opened the settings menu.
+
+
+
+
+ You will now pair up with
+
+
+
+
+ if they also choose your character in return.
+
+
+
+
+ You are no longer paired with anyone.
+
+
+
+
+ Are you sure you typed that well? The char ID could not be recognised.
+
+
+
+
+ You have set your offset to
+
+
+
+
+ Your offset must be between -100% and 100%!
+
+
+
+
+ That offset does not look like one.
+
+
+
+
+ You switched your music and area list.
+
+
+
+
+ You have forcefully enabled features that the server may not support. You may not be able to talk IC, or worse, because of this.
+
+
+
+
+ Your pre-animations interrupt again.
+
+
+
+
+ Your pre-animations will not interrupt text.
+
+
+
+
+ Couldn't open chatlog.txt to write into.
+
+
+
+
+ The IC chatlog has been saved.
+
+
+
+
+ You don't have a `base/cases/` folder! It was just made for you, but seeing as it WAS just made for you, it's likely the case file you're looking for can't be found in there.
+
+
+
+
+ Too many arguments to load a case! You only need one filename, without extension.
+
+
+
+
+ You don't have a `base/cases/` folder! It was just made for you, but seeing as it WAS just made for you, it's likely that you somehow deleted it.
+
+
+
+
+ You need to give a filename to save (extension not needed) and the courtroom status!
+
+
+
+
+ Too many arguments to save a case! You only need a filename without extension and the courtroom status!
+
+
+
+
+ Succesfully saved, edit doc and cmdoc link on the ini!
+
+
+
+
+ Master
+
+
+
+
+ Reason:
+
+
+
+
+ Call Moderator
+
+
+
+
+
+ Error
+
+
+
+
+ You must provide a reason.
+
+
+
+
+ The message is too long.
+
+
+
+
+ Choose...
+
+
+
+
+ Images (*.png)
+
+
+
+
+ Add new evidence...
+
+
+
+
+ Lobby
+
+
+ Attorney Online 2
+
+
+
+
+ Name
+
+
+
+
+ It doesn't look like your client is set up correctly.
+Did you download all resources correctly from tiny.cc/getao, including the large 'base' folder?
+
+
+
+
+ Version: %1
+
+
+
+
+ Loading
+
+
+
+
+ Cancel
+
+
+
+
+ <h2>Attorney Online %1</h2>The courtroom drama simulator<p><b>Source code:</b> <a href='https://github.com/AttorneyOnline/AO2-Client'>https://github.com/AttorneyOnline/AO2-Client</a><p><b>Major development:</b><br>OmniTroid, stonedDiscord, longbyte1, gameboyprinter, Cerapter<p><b>Special thanks:</b><br>Remy, Iamgoofball, Hibiki, Qubrick (webAO), Ruekasu (UI design), Draxirch (UI design), Unishred, Argoneus (tsuserver), Fiercy, Noevain, Cronnicossy
+
+
+
+
+ Online: %1/%2
+
+
+
+
+
+ Offline
+
+
+
+
+ debug_functions
+
+
+ Error: %1
+
+
+
+
+ Error
+
+
+
+
+ Notice
+
+
+
+
diff --git a/resource/translations/ao_es.qm b/resource/translations/ao_es.qm
new file mode 100644
index 0000000..56bad74
Binary files /dev/null and b/resource/translations/ao_es.qm differ
diff --git a/resource/translations/ao_es.ts b/resource/translations/ao_es.ts
new file mode 100644
index 0000000..9c14710
--- /dev/null
+++ b/resource/translations/ao_es.ts
@@ -0,0 +1,883 @@
+
+
+
+
+ AOApplication
+
+
+ Disconnected from server.
+ Desconectado del servidor.
+
+
+
+ Error connecting to master server. Will try again in %1 seconds.
+ Error al conectarse a la lista de servidores. Se intentará nuevamente en %1 segundos.
+
+
+
+ There was an error connecting to the master server.
+We deploy multiple master servers to mitigate any possible downtime, but the client appears to have exhausted all possible methods of finding and connecting to one.
+Please check your Internet connection and firewall, and please try again.
+ I translated master servers as "lista de servidores" because literally nobody will understand if i make the literal translation "servidor maestro". And in the end a master server is just a list of servers. Also removed the part about having multiple master servers, i just don't think the average user will understand this even if i do a good translation.
+ Hubo un error al obtener la lista de servidores. Verifique su conexión a Internet y firewall, y vuelva a intentarlo.
+
+
+
+ Outdated version! Your version: %1
+Please go to aceattorneyonline.com to update.
+ ¡Versión desactualizada! Su versión: %1 Vaya a aceattorneyonline.com para actualizar.
+
+
+
+ You have been exiled from AO.
+Have a nice day.
+ Has sido exiliado de AO.
+Que tengas un buen día.
+
+
+
+ Attorney Online 2
+ Attorney Online 2
+
+
+
+ Loading
+ Cargando
+
+
+
+ Loading evidence:
+%1/%2
+ Cargando evidencia:
+%1/%2
+
+
+
+
+ Loading music:
+%1/%2
+ Cargando música:
+%1/%2
+
+
+
+ Loading chars:
+%1/%2
+ Cargando personajes:
+%1/%2
+
+
+
+ You have been kicked from the server.
+Reason: %1
+ Has sido expulsado del servidor.
+Razón: %1
+
+
+
+ You have been banned from the server.
+Reason: %1
+ Has sido bloqueado de este servidor.
+Razón: %1
+
+
+
+ You are banned on this server.
+Reason: %1
+ Has sido bloqueado en este servidor.
+Razón: %1
+
+
+ You have been kicked from the server.
+Reason:
+ Has sido expulsado del servidor.
+Razón:
+
+
+ You are banned on this server.
+Reason:
+ Has sido bloqueado en este servidor.
+Razón:
+
+
+
+ AOCaseAnnouncerDialog
+
+
+ Case Announcer
+ Anunciar caso
+
+
+
+ Case title:
+ Título del caso:
+
+
+
+ Defense needed
+ Se necesita abogado
+
+
+
+ Prosecution needed
+ Se necesita fiscal
+
+
+
+ Judge needed
+ Se necesita juez
+
+
+
+ Jurors needed
+ Se necesita jurado
+
+
+
+ Stenographer needed
+ Se necesita taquígrafo
+
+
+
+ AOOptionsDialog
+
+
+ Settings
+ Ajustes
+
+
+
+ Gameplay
+ Jugabilidad
+
+
+
+ Theme:
+ Tema visual:
+
+
+
+ Sets the theme used in-game. If the new theme changes the lobby's look as well, you'll need to reload the lobby for the changes to take effect, such as by joining a server and leaving it.
+ Establece el tema visual utilizado en el juego. Si el nuevo tema también cambia el aspecto del lobby, deberá volver a cargar el lobby para que los cambios surtan efecto, como unirse a un servidor y volver al lobby.
+
+
+
+ Log goes downwards:
+ Invertir historial IC:
+
+
+
+ If ticked, new messages will appear at the bottom (like the OOC chatlog). The traditional (AO1) behaviour is equivalent to this being unticked.
+ Si está marcado, los nuevos mensajes aparecerán en la parte inferior (como el chat OOC).
+
+
+
+ Log length:
+ Limite del historial:
+
+
+
+ The amount of messages the IC chatlog will keep before deleting older messages. A value of 0 or below counts as 'infinite'.
+ La cantidad de mensajes que mantendrá el historial del chat IC antes de eliminar mensajes más antiguos. 0 significa 'infinito'.
+
+
+
+ Default username:
+ Usuario predeterminado:
+
+
+
+ Your OOC name will be automatically set to this value when you join a server.
+ Su nombre OOC se establecerá automáticamente a este cuando se una a un servidor.
+
+
+
+ Custom shownames:
+ Mostrar nombres:
+
+
+
+ Gives the default value for the in-game 'Custom shownames' tickbox, which in turn determines whether the client should display custom in-character names.
+ Activa la casilla 'Mostrar nombres' de forma predeterminada en el juego, que a su vez determina si el cliente debe mostrar nombres personalizados en los personajes.
+
+
+
+ Backup MS:
+ Master SV de respaldo:
+
+
+
+ If the built-in server lookups fail, the game will try the address given here and use it as a backup master server address.
+ Si la lista de servidores predeterminada falla, el juego probará la dirección proporcionada aquí.
+
+
+
+ Discord:
+ Discord:
+
+
+
+ Allows others on Discord to see what server you are in, what character are you playing, and how long you have been playing for.
+ Permite a otros en Discord ver en qué servidor estás, qué personaje juegas y cuánto tiempo has estado jugando.
+
+
+
+ Language:
+ Idioma:
+
+
+
+ Sets the language if you don't want to use your system language.
+ Establece el idioma si no desea utilizar el idioma de su sistema.
+
+
+
+ Callwords
+ Palabras clave
+
+
+
+ <html><head/><body>Enter as many callwords as you would like. These are case insensitive. Make sure to leave every callword in its own line!<br>Do not leave a line with a space at the end -- you will be alerted everytime someone uses a space in their messages.</body></html>
+ <html><head/><body>Ingrese tantas palabras de llamada como desee.<br>Esto no distingue entre mayúsculas y minúsculas. ¡Asegúrese de dejar cada palabra en su propia línea!<br>No deje una línea con un espacio al final; recibirá una alerta cada vez que alguien use un espacio en sus mensajes.</body></html>
+
+
+
+ Audio
+ Audio
+
+
+
+ Audio device:
+ Dispositivo:
+
+
+
+ Sets the audio device for all sounds.
+ Establece el dispositivo de audio.
+
+
+
+ Music:
+ Música:
+
+
+
+ Sets the music's default volume.
+ Establece el volumen predeterminado de la música.
+
+
+
+ SFX:
+ SFX:
+
+
+
+ Sets the SFX's default volume. Interjections and actual sound effects count as 'SFX'.
+ Establece el volumen predeterminado de SFX. Las interjecciones y los efectos de sonido reales cuentan como 'SFX'.
+
+
+
+ Blips:
+ Blips:
+
+
+
+ Sets the volume of the blips, the talking sound effects.
+ Establece el volumen de los blips, el sonido al hablar.
+
+
+
+ Blip rate:
+ Tasa de blips:
+
+
+
+ Sets the delay between playing the blip sounds.
+ Establece el retraso entre la reproducción de los sonidos blip.
+
+
+
+ Blank blips:
+ Blips en blanco:
+
+
+
+ If true, the game will play a blip sound even when a space is 'being said'.
+ Si está marcada, el juego reproducirá un sonido blip incluso cuando se 'dice' un espacio.
+
+
+
+ Casing
+ Caso
+
+
+
+ This server supports case alerts.
+ Este servidor admite alertas de casos.
+
+
+
+ This server does not support case alerts.
+ Este servidor no admite alertas de casos.
+
+
+
+ Pretty self-explanatory.
+ Bastante autoexplicativo.
+
+
+
+ Casing:
+ Caso:
+
+
+
+ If checked, you will get alerts about case announcements.
+ Si está marcado, recibirá anuncios de casos.
+
+
+
+ Defense:
+ Abogado:
+
+
+
+ If checked, you will get alerts about case announcements if a defense spot is open.
+ Si está marcado, recibirá alertas sobre anuncios de casos si hay un lugar de abogado libre.
+
+
+
+ Prosecution:
+ Fiscal:
+
+
+
+ If checked, you will get alerts about case announcements if a prosecutor spot is open.
+ Si está marcada, recibirá alertas sobre anuncios de casos si hay un puesto de fiscal libre.
+
+
+
+ Judge:
+ Juez:
+
+
+
+ If checked, you will get alerts about case announcements if the judge spot is open.
+ Si está marcado, recibirá alertas sobre anuncios de casos si el puesto de juez está libre.
+
+
+
+ Juror:
+ Jurado:
+
+
+
+ If checked, you will get alerts about case announcements if a juror spot is open.
+ Si está marcado, recibirá alertas sobre anuncios de casos si hay un puesto de jurado libre.
+
+
+
+ Stenographer:
+ Taquígrafo:
+
+
+
+ If checked, you will get alerts about case announcements if a stenographer spot is open.
+ Si está marcado, recibirá alertas sobre anuncios de casos si hay un lugar de taquígrafo libre.
+
+
+
+ CM:
+ CM:
+
+
+
+ If checked, you will appear amongst the potential CMs on the server.
+ Si está marcado, aparecerá entre los posibles CM en el servidor.
+
+
+
+ Hosting cases:
+ Casos:
+
+
+
+ If you're a CM, enter what cases you are willing to host.
+ Si eres un CM, ingresa qué casos estás dispuesto a organizar.
+
+
+
+ Courtroom
+
+
+ Password
+ Contraseña
+
+
+
+ Spectator
+ Espectador
+
+
+
+
+ Search
+ Buscar
+
+
+
+ Passworded
+ Contraseña
+
+
+
+ Taken
+ En uso
+
+
+
+ Generating chars:
+%1/%2
+ Generando personajes:
+%1/%2
+
+
+ Generating chars:
+
+ Generando personajes:
+
+
+
+
+ Showname
+
+
+
+
+ Message
+ Mensaje
+
+
+
+ Name
+ Nombre
+
+
+
+ Pre
+
+
+
+
+ Flip
+
+
+
+
+ Guard
+ Guardia
+
+
+
+
+ Casing
+ Caso
+
+
+
+ Shownames
+
+
+
+
+ No Interrupt
+
+
+
+
+ White
+ Blanco
+
+
+
+ Green
+ Verde
+
+
+
+ Red
+ Rojo
+
+
+
+ Orange
+ Naranja
+
+
+
+ Blue
+ Azul
+
+
+
+ Yellow
+ Amarillo
+
+
+
+ Rainbow
+ Arcoíris
+
+
+
+ Pink
+ Rosado
+
+
+
+ Cyan
+ Cian
+
+
+
+ % offset
+ % desplazamiento
+
+
+
+ Music
+
+
+
+
+ Sfx
+
+
+
+
+ Blips
+
+
+
+
+ Log limit
+
+
+
+
+ Change character
+
+
+
+
+ Reload theme
+
+
+
+
+ Call mod
+
+
+
+
+ Settings
+ Ajustes
+
+
+
+ A/M
+ A/M
+
+
+
+ Preanim
+
+
+
+
+ Back to Lobby
+ Volver al lobby
+
+
+
+ You were granted the Guard button.
+ Te ha sido otorgado el botón Guardia.
+
+
+
+ This does nothing, but there you go.
+ Esto no hace nada, pero ahí lo tienes.
+
+
+
+ You opened the settings menu.
+ Abriste el menú de configuración.
+
+
+
+ You will now pair up with
+ Ahora te emparejarás con
+
+
+
+ if they also choose your character in return.
+ si ellos también eligen a tu personaje a cambio.
+
+
+
+ You are no longer paired with anyone.
+ Ya no estás emparejado con nadie.
+
+
+
+ Are you sure you typed that well? The char ID could not be recognised.
+ ¿Estás seguro de que lo escribiste bien? El ID de personaje no pudo ser reconocido.
+
+
+
+ You have set your offset to
+ Ha configurado su desplazamiento en
+
+
+
+ Your offset must be between -100% and 100%!
+ ¡Su desplazamiento debe estar entre -100% y 100%!
+
+
+
+ That offset does not look like one.
+ Ese desplazamiento no se parece a uno.
+
+
+
+ You switched your music and area list.
+ Cambiaste tu lista de música y área.
+
+
+
+ You have forcefully enabled features that the server may not support. You may not be able to talk IC, or worse, because of this.
+ Ha habilitado forzosamente funciones que el servidor puede no admitir. Es posible que no pueda hablar IC, o peor, debido a esto.
+
+
+
+ Your pre-animations interrupt again.
+ Sus pre-animaciones interrumpen de nuevo.
+
+
+
+ Your pre-animations will not interrupt text.
+ Sus pre-animaciones no interrumpirán el texto.
+
+
+
+ Couldn't open chatlog.txt to write into.
+ No se pudo abrir chatlog.txt para escribir.
+
+
+
+ The IC chatlog has been saved.
+ El chat IC se ha guardado.
+
+
+
+ You don't have a `base/cases/` folder! It was just made for you, but seeing as it WAS just made for you, it's likely the case file you're looking for can't be found in there.
+ ¡No tienes una carpeta `base/cases /`! Ha sido creada para ti. Pero debido a que no existia la carpeta, tampoco habían casos guardados ahí.
+
+
+
+ You need to give a filename to load (extension not needed)! Make sure that it is in the `base/cases/` folder, and that it is a correctly formatted ini.
+Cases you can load: %1
+ ¡Debe dar un nombre de archivo para cargar (no se necesita extensión)! Asegúrese de que esté en la carpeta `base/cases/` y de que tenga el formato correcto.
+Casos que puede cargar: %1
+
+
+
+ Case made by %1.
+ Caso hecho por %1.
+
+
+
+ Navigate to %1 for the CM doc.
+ Navegue a %1 para el documento del CM.
+
+
+
+ Your case "%1" was loaded!
+ Su caso "%1" fue cargado!
+
+
+ You need to give a filename to load (extension not needed)! Make sure that it is in the `base/cases/` folder, and that it is a correctly formatted ini.
+Cases you can load:
+ ¡Debe dar un nombre de archivo para cargar (no se necesita extensión)! Asegúrese de que esté en la carpeta `base/cases/` y de que tenga el formato correcto.
+Casos que puede cargar:
+
+
+
+ Too many arguments to load a case! You only need one filename, without extension.
+ ¡Demasiados argumentos para cargar un caso! Solo necesita un nombre de archivo, sin extensión.
+
+
+ Case made by
+ Caso hecho por
+
+
+ Navigate to
+ Navegue a
+
+
+ for the CM doc.
+ para el documento de CM.
+
+
+ Your case "
+ Su caso "
+
+
+ " was loaded!
+ " fue cargado!
+
+
+
+ You don't have a `base/cases/` folder! It was just made for you, but seeing as it WAS just made for you, it's likely that you somehow deleted it.
+ ¡No tienes una carpeta `base/cases /`! Fue creada para ti.
+
+
+
+ You need to give a filename to save (extension not needed) and the courtroom status!
+ ¡Debe dar un nombre de archivo para guardar (no se necesita la extensión) y el estado de la sala del tribunal!
+
+
+
+ Too many arguments to save a case! You only need a filename without extension and the courtroom status!
+ why two exclamations, seems excesive.
+ ¡Demasiados argumentos para salvar un caso! Solo necesita un nombre de archivo sin extensión y el estado de la sala del tribunal.
+
+
+
+ Succesfully saved, edit doc and cmdoc link on the ini!
+ ¡Guardado con éxito, puede editar el doc y doc link en el archivo ini!
+
+
+
+ Master
+
+
+
+
+
+ Server
+
+
+
+
+ Reason:
+ Razón:
+
+
+
+ Call Moderator
+ Llamar Moderador
+
+
+
+
+ Error
+ Error
+
+
+
+ You must provide a reason.
+ Debes proporcionar una razón.
+
+
+
+ The message is too long.
+ El mensaje es muy largo.
+
+
+
+ Choose...
+ Elegir...
+
+
+
+ Images (*.png)
+ Imágenes (* .png)
+
+
+
+ Add new evidence...
+ Añadir nueva evidencia...
+
+
+
+ Lobby
+
+
+ Attorney Online 2
+ Attorney Online 2
+
+
+
+ Name
+ Nombre
+
+
+
+ It doesn't look like your client is set up correctly.
+Did you download all resources correctly from tiny.cc/getao, including the large 'base' folder?
+ No parece que su cliente esté configurado correctamente.
+¿Descargó todos los recursos correctamente desde tiny.cc/getao, incluida la gran carpeta 'base'?
+
+
+
+ Version: %1
+ Versión: %1
+
+
+
+ Loading
+ Cargando
+
+
+
+ Cancel
+ Cancelar
+
+
+
+ <h2>Attorney Online %1</h2>The courtroom drama simulator<p><b>Source code:</b> <a href='https://github.com/AttorneyOnline/AO2-Client'>https://github.com/AttorneyOnline/AO2-Client</a><p><b>Major development:</b><br>OmniTroid, stonedDiscord, longbyte1, gameboyprinter, Cerapter<p><b>Special thanks:</b><br>Remy, Iamgoofball, Hibiki, Qubrick (webAO), Ruekasu (UI design), Draxirch (UI design), Unishred, Argoneus (tsuserver), Fiercy, Noevain, Cronnicossy
+ <h2>Attorney Online %1</h2>El simulador de drama legal<p><b>Código fuente:</b> <a href='https://github.com/AttorneyOnline/AO2-Client'>https: //github.com/AttorneyOnline/AO2-Client</a><p><b>Desarrollo mayor:</b> <br>OmniTroid, stonedDiscord, longbyte1, gameboyprinter, Cerapter<p><b>Agradecimiento especial:</b><br>Remy, Iamgoofball, Hibiki, Qubrick (webAO), Ruekasu (diseño de interfaz de usuario), Draxirch (diseño de interfaz de usuario), Unishred, Argoneus (tsuserver), Fiercy, Noevain, Cronnicossy
+
+
+
+ Online: %1/%2
+ En línea: %1/%2
+
+
+
+
+ Offline
+ Fuera de línea
+
+
+
+ debug_functions
+
+
+ Error: %1
+ Error: %1
+
+
+
+ Error
+ Error
+
+
+
+ Notice
+ In spanish it would be "Aviso", but i believe it's going to be more useful for bug reports to not translate any debug string.
+ Notice
+
+
+
diff --git a/resource/translations/ao_jp.qm b/resource/translations/ao_jp.qm
new file mode 100644
index 0000000..e4fb562
Binary files /dev/null and b/resource/translations/ao_jp.qm differ
diff --git a/resource/translations/ao_jp.ts b/resource/translations/ao_jp.ts
new file mode 100644
index 0000000..4f8153a
--- /dev/null
+++ b/resource/translations/ao_jp.ts
@@ -0,0 +1,849 @@
+
+
+
+
+ AOApplication
+
+
+ Disconnected from server.
+
+
+
+
+ Error connecting to master server. Will try again in %1 seconds.
+
+
+
+
+ There was an error connecting to the master server.
+We deploy multiple master servers to mitigate any possible downtime, but the client appears to have exhausted all possible methods of finding and connecting to one.
+Please check your Internet connection and firewall, and please try again.
+
+
+
+
+ Outdated version! Your version: %1
+Please go to aceattorneyonline.com to update.
+
+
+
+
+ You have been exiled from AO.
+Have a nice day.
+
+
+
+
+ Attorney Online 2
+
+
+
+
+ Loading
+ ロード中
+
+
+
+ Loading evidence:
+%1/%2
+ 証拠がロード中: %1/%2
+
+
+
+
+ Loading music:
+%1/%2
+ 音楽がロード中: %1/%2
+
+
+
+ Loading chars:
+%1/%2
+ キャラがロード中: %1/%2
+
+
+
+ You have been kicked from the server.
+Reason: %1
+
+
+
+
+ You have been banned from the server.
+Reason: %1
+
+
+
+
+ You are banned on this server.
+Reason: %1
+
+
+
+
+ AOCaseAnnouncerDialog
+
+
+ Case Announcer
+
+
+
+
+ Case title:
+
+
+
+
+ Defense needed
+
+
+
+
+ Prosecution needed
+
+
+
+
+ Judge needed
+
+
+
+
+ Jurors needed
+
+
+
+
+ Stenographer needed
+
+
+
+
+ AOOptionsDialog
+
+
+ Settings
+
+
+
+
+ Gameplay
+
+
+
+
+ Theme:
+
+
+
+
+ Sets the theme used in-game. If the new theme changes the lobby's look as well, you'll need to reload the lobby for the changes to take effect, such as by joining a server and leaving it.
+
+
+
+
+ Log goes downwards:
+
+
+
+
+ If ticked, new messages will appear at the bottom (like the OOC chatlog). The traditional (AO1) behaviour is equivalent to this being unticked.
+
+
+
+
+ Log length:
+
+
+
+
+ The amount of messages the IC chatlog will keep before deleting older messages. A value of 0 or below counts as 'infinite'.
+
+
+
+
+ Default username:
+
+
+
+
+ Your OOC name will be automatically set to this value when you join a server.
+
+
+
+
+ Custom shownames:
+
+
+
+
+ Gives the default value for the in-game 'Custom shownames' tickbox, which in turn determines whether the client should display custom in-character names.
+
+
+
+
+ Backup MS:
+
+
+
+
+ If the built-in server lookups fail, the game will try the address given here and use it as a backup master server address.
+
+
+
+
+ Discord:
+
+
+
+
+ Allows others on Discord to see what server you are in, what character are you playing, and how long you have been playing for.
+
+
+
+
+ Language:
+
+
+
+
+ Sets the language if you don't want to use your system language.
+
+
+
+
+ Callwords
+
+
+
+
+ <html><head/><body>Enter as many callwords as you would like. These are case insensitive. Make sure to leave every callword in its own line!<br>Do not leave a line with a space at the end -- you will be alerted everytime someone uses a space in their messages.</body></html>
+
+
+
+
+ Audio
+
+
+
+
+ Audio device:
+
+
+
+
+ Sets the audio device for all sounds.
+
+
+
+
+ Music:
+ 音楽:
+
+
+
+ Sets the music's default volume.
+
+
+
+
+ SFX:
+ 効果音:
+
+
+
+ Sets the SFX's default volume. Interjections and actual sound effects count as 'SFX'.
+
+
+
+
+ Blips:
+ ブリップ:
+
+
+
+ Sets the volume of the blips, the talking sound effects.
+
+
+
+
+ Blip rate:
+
+
+
+
+ Sets the delay between playing the blip sounds.
+
+
+
+
+ Blank blips:
+
+
+
+
+ If true, the game will play a blip sound even when a space is 'being said'.
+
+
+
+
+ Casing
+
+
+
+
+ This server supports case alerts.
+
+
+
+
+ This server does not support case alerts.
+
+
+
+
+ Pretty self-explanatory.
+
+
+
+
+ Casing:
+
+
+
+
+ If checked, you will get alerts about case announcements.
+
+
+
+
+ Defense:
+
+
+
+
+ If checked, you will get alerts about case announcements if a defense spot is open.
+
+
+
+
+ Prosecution:
+
+
+
+
+ If checked, you will get alerts about case announcements if a prosecutor spot is open.
+
+
+
+
+ Judge:
+
+
+
+
+ If checked, you will get alerts about case announcements if the judge spot is open.
+
+
+
+
+ Juror:
+
+
+
+
+ If checked, you will get alerts about case announcements if a juror spot is open.
+
+
+
+
+ Stenographer:
+
+
+
+
+ If checked, you will get alerts about case announcements if a stenographer spot is open.
+
+
+
+
+ CM:
+
+
+
+
+ If checked, you will appear amongst the potential CMs on the server.
+
+
+
+
+ Hosting cases:
+
+
+
+
+ If you're a CM, enter what cases you are willing to host.
+
+
+
+
+ Courtroom
+
+
+ Password
+
+
+
+
+ Spectator
+ 観客
+
+
+
+
+ Search
+
+
+
+
+ Passworded
+
+
+
+
+ Taken
+
+
+
+
+ Generating chars:
+%1/%2
+
+
+
+
+ Showname
+
+
+
+
+ Message
+
+
+
+
+ Name
+ 名前
+
+
+
+ Pre
+ 前
+
+
+
+ Flip
+ フリップ
+
+
+
+ Guard
+ ガード
+
+
+
+
+ Casing
+
+
+
+
+ Shownames
+
+
+
+
+ No Interrupt
+
+
+
+
+ White
+ 白
+
+
+
+ Green
+ 緑
+
+
+
+ Red
+ 赤
+
+
+
+ Orange
+ オレンジ
+
+
+
+ Blue
+ 青
+
+
+
+ Yellow
+ 黄色
+
+
+
+ This does nothing, but there you go.
+
+
+
+
+ You need to give a filename to load (extension not needed)! Make sure that it is in the `base/cases/` folder, and that it is a correctly formatted ini.
+Cases you can load: %1
+
+
+
+
+ Case made by %1.
+
+
+
+
+ Navigate to %1 for the CM doc.
+
+
+
+
+ Your case "%1" was loaded!
+
+
+
+
+
+ Server
+
+
+
+
+ Back to Lobby
+ ロビーに戻る
+
+
+
+ Rainbow
+ 虹
+
+
+
+ Pink
+
+
+
+
+ Cyan
+
+
+
+
+ % offset
+
+
+
+
+ Music
+ 音楽
+
+
+
+ Sfx
+ 効果音
+
+
+
+ Blips
+ ブリップ
+
+
+
+ Log limit
+
+
+
+
+ Change character
+
+
+
+
+ Reload theme
+
+
+
+
+ Call mod
+
+
+
+
+ Settings
+
+
+
+
+ A/M
+
+
+
+
+ Preanim
+
+
+
+
+ You were granted the Guard button.
+
+
+
+
+ You opened the settings menu.
+
+
+
+
+ You will now pair up with
+
+
+
+
+ if they also choose your character in return.
+
+
+
+
+ You are no longer paired with anyone.
+
+
+
+
+ Are you sure you typed that well? The char ID could not be recognised.
+
+
+
+
+ You have set your offset to
+
+
+
+
+ Your offset must be between -100% and 100%!
+
+
+
+
+ That offset does not look like one.
+
+
+
+
+ You switched your music and area list.
+
+
+
+
+ You have forcefully enabled features that the server may not support. You may not be able to talk IC, or worse, because of this.
+
+
+
+
+ Your pre-animations interrupt again.
+
+
+
+
+ Your pre-animations will not interrupt text.
+
+
+
+
+ Couldn't open chatlog.txt to write into.
+
+
+
+
+ The IC chatlog has been saved.
+
+
+
+
+ You don't have a `base/cases/` folder! It was just made for you, but seeing as it WAS just made for you, it's likely the case file you're looking for can't be found in there.
+
+
+
+
+ Too many arguments to load a case! You only need one filename, without extension.
+
+
+
+
+ You don't have a `base/cases/` folder! It was just made for you, but seeing as it WAS just made for you, it's likely that you somehow deleted it.
+
+
+
+
+ You need to give a filename to save (extension not needed) and the courtroom status!
+
+
+
+
+ Too many arguments to save a case! You only need a filename without extension and the courtroom status!
+
+
+
+
+ Succesfully saved, edit doc and cmdoc link on the ini!
+
+
+
+
+ Master
+ マスター
+
+
+
+ Reason:
+
+
+
+
+ Call Moderator
+ モデレーターを呼ぶ
+
+
+
+
+ Error
+ エラー
+
+
+
+ You must provide a reason.
+
+
+
+
+ The message is too long.
+
+
+
+
+ Choose...
+ 選択...
+
+
+
+ Images (*.png)
+ イメージ (*.png)
+
+
+
+ Add new evidence...
+ 新しい証拠を付け加える...
+
+
+
+ Discord
+
+ Objection!
+ 意義あり!
+
+
+ In Lobby
+ ロビーでいる
+
+
+ Idle
+ 落ちている
+
+
+ In a Server
+ サーバーでいます
+
+
+ Spectating
+ 観客している
+
+
+
+ Lobby
+
+
+ Attorney Online 2
+
+
+
+
+ Name
+ 名前
+
+
+
+ It doesn't look like your client is set up correctly.
+Did you download all resources correctly from tiny.cc/getao, including the large 'base' folder?
+
+
+
+
+ Version: %1
+
+
+
+
+ Loading
+ ロード中
+
+
+
+ Cancel
+ キャンセル
+
+
+
+ <h2>Attorney Online %1</h2>The courtroom drama simulator<p><b>Source code:</b> <a href='https://github.com/AttorneyOnline/AO2-Client'>https://github.com/AttorneyOnline/AO2-Client</a><p><b>Major development:</b><br>OmniTroid, stonedDiscord, longbyte1, gameboyprinter, Cerapter<p><b>Special thanks:</b><br>Remy, Iamgoofball, Hibiki, Qubrick (webAO), Ruekasu (UI design), Draxirch (UI design), Unishred, Argoneus (tsuserver), Fiercy, Noevain, Cronnicossy
+
+
+
+
+ Online: %1/%2
+
+
+
+
+
+ Offline
+ オフライン
+
+
+
+ debug_functions
+
+
+ Error: %1
+ エラー: %1
+
+
+
+ Error
+ エラー
+
+
+
+ Notice
+
+
+
+
diff --git a/resource/translations/ao_pl.qm b/resource/translations/ao_pl.qm
new file mode 100644
index 0000000..cdc1393
Binary files /dev/null and b/resource/translations/ao_pl.qm differ
diff --git a/resource/translations/ao_pl.ts b/resource/translations/ao_pl.ts
new file mode 100644
index 0000000..307c604
--- /dev/null
+++ b/resource/translations/ao_pl.ts
@@ -0,0 +1,841 @@
+
+
+
+
+ AOApplication
+
+
+ Disconnected from server.
+ Odłączono od serwera.
+
+
+
+ Error connecting to master server. Will try again in %1 seconds.
+ Błąd podczas łączenia się do głównego serwera. Spróbuj ponownie za %1 sekundy.
+
+
+
+ There was an error connecting to the master server.
+We deploy multiple master servers to mitigate any possible downtime, but the client appears to have exhausted all possible methods of finding and connecting to one.
+Please check your Internet connection and firewall, and please try again.
+ Odkryto błąd podczas łączania się do głównego serwera.
+Używamy wielu głównych serwerów, aby zminimalizować każdą możliwą przerwę, ale klient wyczerpał jaką kolwiek możliwość znalazienia i połączenia się do któregoś.
+Proszę sprawdzić swoje połączenie internetowe oraz zaporę ogniową i spróbować ponownie.
+
+
+
+ Outdated version! Your version: %1
+Please go to aceattorneyonline.com to update.
+ Nieaktualna wersja! Twoja wersja: %1
+Proszę udać się do aceattorneyonline.com, aby zaktualizować.
+
+
+
+ You have been exiled from AO.
+Have a nice day.
+ Zostałeś wygnany z AO2.
+Życzę miłego dnia.
+
+
+
+ Attorney Online 2
+
+
+
+
+ Loading
+ Ładowanie
+
+
+
+ Loading evidence:
+%1/%2
+ Ładowanie dowodów:
+%1/%2
+
+
+
+
+ Loading music:
+%1/%2
+ Ładowanie muzyki:
+%1/%2
+
+
+
+ Loading chars:
+%1/%2
+ Ładowanie postaci:
+%1/%2
+
+
+
+ You have been kicked from the server.
+Reason: %1
+ Zostałeś wyrzucony z tego serwera.
+Powód: %1
+
+
+
+ You have been banned from the server.
+Reason: %1
+ Zostałeś zbanowany z tego serwera.
+Powód: %1
+
+
+
+ You are banned on this server.
+Reason: %1
+ Jesteś zbanowany na tym serwerze.
+Powód: %1
+
+
+
+ AOCaseAnnouncerDialog
+
+
+ Case Announcer
+ Ogłaszanie rozpraw
+
+
+
+ Case title:
+ Tytuł rozprawy:
+
+
+
+ Defense needed
+ Potrzebny obrońca
+
+
+
+ Prosecution needed
+ Potrzebny prokurator
+
+
+
+ Judge needed
+ Potrzebny sędzia
+
+
+
+ Jurors needed
+ Potrzebny ławnik
+
+
+
+ Stenographer needed
+ Potrzebny stenograf
+
+
+
+ AOOptionsDialog
+
+
+ Settings
+ Ustawienia
+
+
+
+ Gameplay
+ Rozgrywka
+
+
+
+ Theme:
+ Motyw:
+
+
+
+ Sets the theme used in-game. If the new theme changes the lobby's look as well, you'll need to reload the lobby for the changes to take effect, such as by joining a server and leaving it.
+ Ustawia motyw używany w grze. Jeżeli nowy motyw równiesz zmienia wygląd poczekalni, musisz odświeżyć poczekalnię, aby zmiany zaczęły działać, np. poprzez dołączenie do serwera i wyjście z niego.
+
+
+
+ Log goes downwards:
+ Dziennik idzie w dół:
+
+
+
+ If ticked, new messages will appear at the bottom (like the OOC chatlog). The traditional (AO1) behaviour is equivalent to this being unticked.
+ Jeżeli zaznaczone, nowe wiadomości zaczną się pojawiać na dole (tak jak na czacie OOC). Tradycyjne (AO1) zachowanie jest równoważne do tego bycia nie zaznaczonym.
+
+
+
+ Log length:
+ Długość dziennika:
+
+
+
+ The amount of messages the IC chatlog will keep before deleting older messages. A value of 0 or below counts as 'infinite'.
+ Ilość wiadomości, jakie czat IC będzie zostawiał zanim usunie starsze wiadomości. Wartośc 0 albo niżej, liczy się jako 'nieskończone'.
+
+
+
+ Default username:
+ Domyślna nazwa użytkownika:
+
+
+
+ Your OOC name will be automatically set to this value when you join a server.
+ Twoja nazwa OOC będzie ustawiana automatycznie do tej wartości, kiedy dołączysz na serwer.
+
+
+
+ Custom shownames:
+ Niestandardowe ksywki:
+
+
+
+ Gives the default value for the in-game 'Custom shownames' tickbox, which in turn determines whether the client should display custom in-character names.
+ Daje domyślną wartość przyciskowi wyboru 'Niestandardowe ksywki', który określa czy klient powinien pokazywać niestandardowe IC nazwy.
+
+
+
+ Backup MS:
+ Kopia zapasowa głównego serwera:
+
+
+
+ If the built-in server lookups fail, the game will try the address given here and use it as a backup master server address.
+ Jeśli wbudowane szukanie serwerów zawiedzie, gra spróbuje użyć adresu podanego tutaj i użyje go jako adresu zapasowego głównego serwera.
+
+
+
+ Discord:
+
+
+
+
+ Allows others on Discord to see what server you are in, what character are you playing, and how long you have been playing for.
+ Pozwala innym na Discordzie zobaczyć na jakim serwerze się znajdujesz, jaką postać używasz i jak długo grałeś.
+
+
+
+ Language:
+ Język:
+
+
+
+ Sets the language if you don't want to use your system language.
+ Ustawia język, jeśli nie chcesz używać języka systemowego.
+
+
+
+ Callwords
+ Zawołania
+
+
+
+ <html><head/><body>Enter as many callwords as you would like. These are case insensitive. Make sure to leave every callword in its own line!<br>Do not leave a line with a space at the end -- you will be alerted everytime someone uses a space in their messages.</body></html>
+ <html><head/><body>Wpisz tyle zawołań, ile dusza zapragnie. Wielkość liter nie ma znaczenia. Miej na uwadze, aby każde zawołanie było na swojej lini!<br>Nie zostawiaj spacji na końcu -- zostaniesz zaalarmowany za każdym razem, kiedy ktoś użyje spacji w swojej wiadomości.</body></html>
+
+
+
+ Audio
+ Dźwięk
+
+
+
+ Audio device:
+ Urządzenie dźwiękowe:
+
+
+
+ Sets the audio device for all sounds.
+ Ustawia urządzenie dźwiękowe na wszystkie dźwięki.
+
+
+
+ Music:
+ Muzyka:
+
+
+
+ Sets the music's default volume.
+ Ustawia domyślną głośność muzyki.
+
+
+
+ SFX:
+ Efekty dźwiękowe (SFX):
+
+
+
+ Sets the SFX's default volume. Interjections and actual sound effects count as 'SFX'.
+ Ustawia domyślną głośność efektów specjalnych (SFX). Wtrącenia się i same efekty specjalne są zaliczane jako 'SFX'.
+
+
+
+ Blips:
+ Blipy:
+
+
+
+ Sets the volume of the blips, the talking sound effects.
+ Ustawia głośność blipów, efektów dźwiękowych mówienia.
+
+
+
+ Blip rate:
+ Szybkość blipów:
+
+
+
+ Sets the delay between playing the blip sounds.
+ Ustawia opóźnienie pomiędzy graniem blipów.
+
+
+
+ Blank blips:
+ The 'blip' isn't an accurate polish representation of this english word.
+ Puste blipy:
+
+
+
+ If true, the game will play a blip sound even when a space is 'being said'.
+ Jeśli prawdziwe, gra zagra dźwięk blip za każdym razem spacja 'jest mówiona'.
+
+
+
+ Casing
+ Rozprawa
+
+
+
+ This server supports case alerts.
+ Ten serwer wspiera komunikaty rozpraw.
+
+
+
+ This server does not support case alerts.
+ Ten serwer nie wspiera komunikatów rozpraw.
+
+
+
+ Pretty self-explanatory.
+ Dosyć oczywiste.
+
+
+
+ Casing:
+ Rozprawy:
+
+
+
+ If checked, you will get alerts about case announcements.
+ Jeżeli zaznaczone, dostaniesz komunikaty o ogłoszeniach rozpraw.
+
+
+
+ Defense:
+ Obrona:
+
+
+
+ If checked, you will get alerts about case announcements if a defense spot is open.
+ Jeżeli zaznaczone, dostaniesz komunikaty o ogłoszeniach rozpraw, jeśli miejsce obrony jest otwarte.
+
+
+
+ Prosecution:
+ Prokuratura:
+
+
+
+ If checked, you will get alerts about case announcements if a prosecutor spot is open.
+ Jeżeli zaznaczone, dostaniesz komunikaty o ogłoszeniach rozpraw, jeśli miejsce prokuratury jest otwarte.
+
+
+
+ Judge:
+ Sędzia:
+
+
+
+ If checked, you will get alerts about case announcements if the judge spot is open.
+ Jeśli zaznaczone, dostaniesz komunikaty o ogłoszeniach rozpraw, jeśli miejsce sędzi jest otwarte.
+
+
+
+ Juror:
+ Ławnik:
+
+
+
+ If checked, you will get alerts about case announcements if a juror spot is open.
+ Jeśli zaznaczone, dostaniesz komunikaty o ogłoszeniach rozpraw, jeśli miejsce ławnika jest otwarte.
+
+
+
+ Stenographer:
+ Stenograf:
+
+
+
+ If checked, you will get alerts about case announcements if a stenographer spot is open.
+ Jeśli zaznaczone, dostaniesz komunikaty o ogłoszeniach rozpraw, jeśli miejsce stenografa jest otwarte.
+
+
+
+ CM:
+ Mistrz rozpraw (CM):
+
+
+
+ If checked, you will appear amongst the potential CMs on the server.
+ Jeśli zaznaczone, pojawisz się wśród potencjalnych mistrzów rozpraw (CM) na serwerze.
+
+
+
+ Hosting cases:
+ Hostowane rozprawy:
+
+
+
+ If you're a CM, enter what cases you are willing to host.
+ Jeśli jesteś mistrzem rozpraw (CM), wpisz jakie rozprawy jesteś chętny hostowania.
+
+
+
+ Courtroom
+
+
+ Password
+ Hasło
+
+
+
+ Spectator
+ Widz
+
+
+
+
+ Search
+ Wyszukaj
+
+
+
+ Passworded
+ Zahasłowany
+
+
+
+ Taken
+ Zajęty
+
+
+
+ Generating chars:
+%1/%2
+ Generowanie postaci:
+%1.%2
+
+
+
+ Showname
+ Ksywka
+
+
+
+ Message
+ Wiadomość
+
+
+
+ Name
+ Nazwa
+
+
+
+ Pre
+ przed-
+
+
+
+ Flip
+ Odwróć
+
+
+
+ Guard
+ Na Służbie (mod)
+
+
+
+
+ Casing
+ Rozprawa
+
+
+
+ Shownames
+ Ksywki
+
+
+
+ No Interrupt
+ Bez przerwy
+
+
+
+ White
+ Biały
+
+
+
+ Green
+ Zielony
+
+
+
+ Red
+ Czerwony
+
+
+
+ Orange
+ Pomarańczowy
+
+
+
+ Blue
+ Niebieski
+
+
+
+ Yellow
+ Żółty
+
+
+
+ This does nothing, but there you go.
+ To nic nie robi, ale proszę bardzo.
+
+
+
+ You need to give a filename to load (extension not needed)! Make sure that it is in the `base/cases/` folder, and that it is a correctly formatted ini.
+Cases you can load: %1
+ Musisz podać nazwę pliku, którego chcesz załadować (rozszerzenie nie potrzebne!) Upewnij się, że jest w folderze `base/cases/` i że jest to poprawnie sformatowane ini.
+Rozprawy które możesz załadować: %1
+
+
+
+ Case made by %1.
+ Rozprawa zrobiona przez %1.
+
+
+
+ Navigate to %1 for the CM doc.
+ Przejdź do %1, aby dojść do dokumentu CM.
+
+
+
+ Your case "%1" was loaded!
+ Twoja rozprawa "%1" została wczytana!
+
+
+
+
+ Server
+ Serwer
+
+
+
+ Back to Lobby
+ Powrót do poczekalni
+
+
+
+ Rainbow
+ Tęczowy
+
+
+
+ Pink
+ Różowy
+
+
+
+ Cyan
+ Turkusowy
+
+
+
+ % offset
+ % wyrówanie
+
+
+
+ Music
+ Muzyka
+
+
+
+ Sfx
+ Sfx
+
+
+
+ Blips
+ Blipy
+
+
+
+ Log limit
+ Limit dziennika
+
+
+
+ Change character
+ Zmiena postaci
+
+
+
+ Reload theme
+ Odśwież motyw
+
+
+
+ Call mod
+ Wezwij moda
+
+
+
+ Settings
+ Ustawienia
+
+
+
+ A/M
+ O meaning 'Obszar' and M meaning 'Muzyka'.
+ O/M
+
+
+
+ Preanim
+ przed-animacja
+
+
+
+ You were granted the Guard button.
+ Zostałeś obdarzonym przyciskiem Na Służbie.
+
+
+
+ You opened the settings menu.
+ Otworzyłeś opcje.
+
+
+
+ You will now pair up with
+ Będzie teraz w parze z
+
+
+
+ if they also choose your character in return.
+ jeżeli oni również wybiorą ciebie spowrotem.
+
+
+
+ You are no longer paired with anyone.
+ Nie jesteś już w parze z kimkolwiek.
+
+
+
+ Are you sure you typed that well? The char ID could not be recognised.
+ Czy jesteś pewien, że dobrze to napisałeś? ID postaci nie zostało rozpoznane.
+
+
+
+ You have set your offset to
+ Musisz ustawić swoje wyrównanie do
+
+
+
+ Your offset must be between -100% and 100%!
+ Twoje wyrównanie musi być między -100%, a 100%!
+
+
+
+ That offset does not look like one.
+ To wyrównanie nie wygląda na jedno.
+
+
+
+ You switched your music and area list.
+ Przełączyłeś swoją listę obszarów i muzyki.
+
+
+
+ You have forcefully enabled features that the server may not support. You may not be able to talk IC, or worse, because of this.
+ Włączyłeś funkcje, które ten serwer może nie wspierać. Możliwe że, nie możesz rozmawiać na czacie IC lub gorzej przez to.
+
+
+
+ Your pre-animations interrupt again.
+ Twoje przed-animacje przerywają tekst spowrotem.
+
+
+
+ Your pre-animations will not interrupt text.
+ Twoje przed-animacje nie będą przerywać tekstu.
+
+
+
+ Couldn't open chatlog.txt to write into.
+ Nie można było otworzyć chatlog.txt, aby zapisać do niego.
+
+
+
+ The IC chatlog has been saved.
+ Dziennik czatu IC został zapisany.
+
+
+
+ You don't have a `base/cases/` folder! It was just made for you, but seeing as it WAS just made for you, it's likely the case file you're looking for can't be found in there.
+ Nie masz folderu `base/cases/`! Został zrobiony tylko dla ciebie, ale widząc, że ZOSTAŁ zrobiony tylko dla ciebie, prawdopodobnie plik rozpraw, którego szukasz nie został znaleziony tutaj.
+
+
+
+ Too many arguments to load a case! You only need one filename, without extension.
+ Za dużo parametrów, aby załadować rozprawę! Potrzebujesz tylko jedną nazwę pliku, bez rozszerzenia nazwy pliku.
+
+
+
+ You don't have a `base/cases/` folder! It was just made for you, but seeing as it WAS just made for you, it's likely that you somehow deleted it.
+ Nie masz folderu `base/cases/`! Został zrobiony tylko dla ciebie, ale widząc, że ZOSTAŁ zrobiony tylko dla ciebie, prawdopodobnie jakoś usunąłeś go.
+
+
+
+ You need to give a filename to save (extension not needed) and the courtroom status!
+ Musisz podać nazwę pliku, aby go zapisać (rozszerzenie nie potrzebne) i status sali sądowej!
+
+
+
+ Too many arguments to save a case! You only need a filename without extension and the courtroom status!
+ Za dużo parametrów, aby zapisać rozprawę! Potrzebujesz tylko jedną nazwę pliku, bez rozszerzenia nazwy pliku i statusu sali sądowej!
+
+
+
+ Succesfully saved, edit doc and cmdoc link on the ini!
+ Zapisano pomyślnie, edytuj dokument i link cmdoc w .ini!
+
+
+
+ Master
+ Główny
+
+
+
+ Reason:
+ Powód:
+
+
+
+ Call Moderator
+ Wezwij Moderatora
+
+
+
+
+ Error
+ Błąd
+
+
+
+ You must provide a reason.
+ Musisz podać przyczynę.
+
+
+
+ The message is too long.
+ Ta wiadomość jest za długa.
+
+
+
+ Choose...
+ Wybierz...
+
+
+
+ Images (*.png)
+ Plik obrazu (*.png)
+
+
+
+ Add new evidence...
+ Dodaj nowe dowody...
+
+
+
+ Lobby
+
+
+ Attorney Online 2
+
+
+
+
+ Name
+ Nazwa
+
+
+
+ It doesn't look like your client is set up correctly.
+Did you download all resources correctly from tiny.cc/getao, including the large 'base' folder?
+ Wygłąda na to, że twój klient nie jest ustawiony poprawnie.
+Czy pobrałeś wszystkie zasoby poprawnie z tiny.cc/getao, włączając duży folder 'base'?
+
+
+
+ Version: %1
+ Wersja: %1
+
+
+
+ Loading
+ Ładowanie
+
+
+
+ Cancel
+ Anuluj
+
+
+
+ <h2>Attorney Online %1</h2>The courtroom drama simulator<p><b>Source code:</b> <a href='https://github.com/AttorneyOnline/AO2-Client'>https://github.com/AttorneyOnline/AO2-Client</a><p><b>Major development:</b><br>OmniTroid, stonedDiscord, longbyte1, gameboyprinter, Cerapter<p><b>Special thanks:</b><br>Remy, Iamgoofball, Hibiki, Qubrick (webAO), Ruekasu (UI design), Draxirch (UI design), Unishred, Argoneus (tsuserver), Fiercy, Noevain, Cronnicossy
+ <h2>Attorney Online: %1</h2>Symulator dramy sądowej<p><b>Kod żródłowy:</b> <a href='https://github.com/AttorneyOnline/AO2-Client'>https://github.com/AttorneyOnline/AO2-Client</a><p><b>Główny rozwój:</b><br>OmniTroid, stonedDiscord, longbyte1, gameboyprinter, Cerapter<p><b>Szczególne podziękowania:</b><br>Remy, Iamgoofball, Hibiki, Qubrick (webAO), Ruekasu (UI design), Draxirch (UI design), Unishred, Argoneus (tsuserver), Fiercy, Noevain, Cronnicossy
+
+
+
+ Online: %1/%2
+
+
+
+
+
+ Offline
+
+
+
+
+ debug_functions
+
+
+ Error: %1
+ Błąd: %1
+
+
+
+ Error
+ Błąd
+
+
+
+ Notice
+ Ogłoszenie
+
+
+
diff --git a/resource/translations/ao_ru.qm b/resource/translations/ao_ru.qm
new file mode 100644
index 0000000..19bf0a9
Binary files /dev/null and b/resource/translations/ao_ru.qm differ
diff --git a/resource/translations/ao_ru.ts b/resource/translations/ao_ru.ts
new file mode 100644
index 0000000..9714c9d
--- /dev/null
+++ b/resource/translations/ao_ru.ts
@@ -0,0 +1,855 @@
+
+
+
+
+ AOApplication
+
+
+ Disconnected from server.
+ Соединение с сервером прервано.
+
+
+
+ Error connecting to master server. Will try again in %1 seconds.
+ Ошибка соединения с главным сервером. Попытка пересоединения будет через %1 с.
+
+
+
+ There was an error connecting to the master server.
+We deploy multiple master servers to mitigate any possible downtime, but the client appears to have exhausted all possible methods of finding and connecting to one.
+Please check your Internet connection and firewall, and please try again.
+ Произошла ошибка соединения с главным сервером.
+Пожалуйста, проверьте ваши Интернет-соединение, настройки браундмауэра, и попробуйте перезайти.
+
+
+
+ Outdated version! Your version: %1
+Please go to aceattorneyonline.com to update.
+ Устаревшая версия! У вас установлена %1
+Проследуйте на сайт aceattorneyonline.com для обновления.
+
+
+
+ You have been exiled from AO.
+Have a nice day.
+ Из AO вас отправили в жизнь.
+Хорошего дня.
+
+
+
+ Attorney Online 2
+ Attorney Online 2
+
+
+
+ Loading
+ Загрузка
+
+
+
+ Loading evidence:
+%1/%2
+ Загрузка вещдоков:
+%1/%2
+
+
+
+
+ Loading music:
+%1/%2
+ Загрузка музыки:
+%1/%2
+
+
+
+ Loading chars:
+%1/%2
+ Загрузка персонажей:
+%1/%2
+
+
+
+ You have been kicked from the server.
+Reason: %1
+ Вас выпнули с сервера.
+Причина: %1
+
+
+
+ You have been banned from the server.
+Reason: %1
+
+
+
+
+ You are banned on this server.
+Reason: %1
+ Вас отправили в баню.
+Причина: %1
+
+
+ You have been kicked from the server.
+Reason:
+ Вас выпнули с сервера.
+Причина:
+
+
+ You are banned on this server.
+Reason:
+ Вас отправили в баню.
+Причина:
+
+
+
+ AOCaseAnnouncerDialog
+
+
+ Case Announcer
+ Материалы дела
+
+
+
+ Case title:
+ Название:
+
+
+
+ Defense needed
+ Сторона защиты
+
+
+
+ Prosecution needed
+ Сторона обвинения
+
+
+
+ Judge needed
+ Без судьи никак
+
+
+
+ Jurors needed
+ Суд присяжных
+
+
+
+ Stenographer needed
+ Нужен стенографист?
+
+
+
+ AOOptionsDialog
+
+
+ Settings
+ Настройки
+
+
+
+ Gameplay
+ Игра
+
+
+
+ Theme:
+ Тема:
+
+
+
+ Sets the theme used in-game. If the new theme changes the lobby's look as well, you'll need to reload the lobby for the changes to take effect, such as by joining a server and leaving it.
+ Устанавливает внешний вид игры. Может понадобиться перезайти на сервер.
+
+
+
+ Log goes downwards:
+ Портянку вниз:
+
+
+
+ If ticked, new messages will appear at the bottom (like the OOC chatlog). The traditional (AO1) behaviour is equivalent to this being unticked.
+ Отметьте галочку, если хотите, чтобы сообщения в игровом чате отображались снизу, а не сверху.
+
+
+
+ Log length:
+ Длина игрового чата:
+
+
+
+ The amount of messages the IC chatlog will keep before deleting older messages. A value of 0 or below counts as 'infinite'.
+ Количество сообщений, максимально хранимых в игровом чате. Значение, равное 0 или меньше, будет расценено как снятие такого ограничения.
+
+
+
+ Default username:
+ Никнейм по умолчанию:
+
+
+
+ Your OOC name will be automatically set to this value when you join a server.
+ Псевдоним, используемый при соединении с сервером. В основном, его видно в чате сервера.
+
+
+
+ Custom shownames:
+ Произвольные имена:
+
+
+
+ Gives the default value for the in-game 'Custom shownames' tickbox, which in turn determines whether the client should display custom in-character names.
+ Отображать произвольные имена персонажей, установленные самими игроками.
+
+
+
+ Backup MS:
+ Запасной ГС:
+
+
+
+ If the built-in server lookups fail, the game will try the address given here and use it as a backup master server address.
+ Отображать перечень серверов от главного сервера, указанного здесь, когда не удалось соединиться с первичным ГС.
+
+
+
+ Discord:
+ Discord:
+
+
+
+ Allows others on Discord to see what server you are in, what character are you playing, and how long you have been playing for.
+ Показать в Discord сервер, на котором вы играете, каким персонажем управляете и время игры.
+
+
+
+ Language:
+ Язык:
+
+
+
+ Sets the language if you don't want to use your system language.
+
+
+
+
+ Callwords
+ Позывные
+
+
+
+ <html><head/><body>Enter as many callwords as you would like. These are case insensitive. Make sure to leave every callword in its own line!<br>Do not leave a line with a space at the end -- you will be alerted everytime someone uses a space in their messages.</body></html>
+ <html><head/><body>Введите на отдельных строках свои позывные, при указании которых в сообщениях будет подан звуковой сигнал.</body></html>
+
+
+
+ Audio
+ Аудио
+
+
+
+ Audio device:
+ Устройство воспроизведения:
+
+
+
+ Sets the audio device for all sounds.
+ Куда вещать звук из игры.
+
+
+
+ Music:
+ Музыка:
+
+
+
+ Sets the music's default volume.
+ Громкость музыки по умолчанию.
+
+
+
+ SFX:
+ Звук. эффекты:
+
+
+
+ Sets the SFX's default volume. Interjections and actual sound effects count as 'SFX'.
+ Громкость звуковых эффектов по умолчанию.
+
+
+
+ Blips:
+ Сигналы:
+
+
+
+ Sets the volume of the blips, the talking sound effects.
+ Громкость сигналов, заменяющих голос, по умолчанию.
+
+
+
+ Blip rate:
+ Пер. сигналов:
+
+
+
+ Sets the delay between playing the blip sounds.
+ Период между сигналами, заменяющими голос, по умолчанию.
+
+
+
+ Blank blips:
+ Пустые сигналы:
+
+
+
+ If true, the game will play a blip sound even when a space is 'being said'.
+ Проигрывать сигналы даже для пробелов.
+
+
+
+ Casing
+ Заседание
+
+
+
+ This server supports case alerts.
+ Этот сервер поддерживает объявление заседания.
+
+
+
+ This server does not support case alerts.
+ Этот сервер не поддерживает объявление заседания.
+
+
+
+ Pretty self-explanatory.
+ Весьма доходчиво.
+
+
+
+ Casing:
+ Новое дело:
+
+
+
+ If checked, you will get alerts about case announcements.
+ При заведении дела вы получите уведомление.
+
+
+
+ Defense:
+ Защита:
+
+
+
+ If checked, you will get alerts about case announcements if a defense spot is open.
+ При заведении дела, в котором нужна сторона защиты, вы получите уведомление.
+
+
+
+ Prosecution:
+ Обвинение:
+
+
+
+ If checked, you will get alerts about case announcements if a prosecutor spot is open.
+ При заведении дела, в котором нужна сторона обвинения, вы получите уведомление.
+
+
+
+ Judge:
+ Судья:
+
+
+
+ If checked, you will get alerts about case announcements if the judge spot is open.
+ При заведении дела, в котором нужен судья, вы получите уведомление.
+
+
+
+ Juror:
+ Присяжный:
+
+
+
+ If checked, you will get alerts about case announcements if a juror spot is open.
+ При заведении дела, в котором нужны присяжные заседатели, вы получите уведомление.
+
+
+
+ Stenographer:
+ Стенографист:
+
+
+
+ If checked, you will get alerts about case announcements if a stenographer spot is open.
+ При заведении дела, в котором нужна стенография, вы получите уведомление.
+
+
+
+ CM:
+ ПД:
+
+
+
+ If checked, you will appear amongst the potential CMs on the server.
+ Отметьте, если вы хотите состоять в числе производителей дел.
+
+
+
+ Hosting cases:
+ ПД акт.:
+
+
+
+ If you're a CM, enter what cases you are willing to host.
+ Будучи производителем дела (ПД), вы можете войти в зону и заниматься её оркестровкой.
+
+
+
+ Courtroom
+
+
+ Password
+ Пароль
+
+
+
+ Spectator
+ Наблюдатель
+
+
+
+
+ Search
+ Поиск
+
+
+
+ Passworded
+ Ограничен паролем
+
+
+
+ Taken
+ Занят
+
+
+
+ Generating chars:
+%1/%2
+ Генерация персонажей:
+%1/%2
+
+
+ Generating chars:
+
+ Генерация персонажей:
+
+
+
+
+ Showname
+ Имя
+
+
+
+ Message
+ Сообщение
+
+
+
+ Name
+ Никнейм
+
+
+
+ Pre
+ Пред.
+
+
+
+ Flip
+ Разв.
+
+
+
+ Guard
+ Охрана
+
+
+
+
+ Casing
+ Дело
+
+
+
+ Shownames
+ Произв. имена
+
+
+
+ No Interrupt
+ Говорить сразу
+
+
+
+ White
+ Белый
+
+
+
+ Green
+ Зелëный
+
+
+
+ Red
+ Красный
+
+
+
+ Orange
+ Оранжевый
+
+
+
+ Blue
+ Синий
+
+
+
+ Yellow
+ Жëлтый
+
+
+
+ This does nothing, but there you go.
+ В общем-то, это ни на что не влияет...
+
+
+
+ You need to give a filename to load (extension not needed)! Make sure that it is in the `base/cases/` folder, and that it is a correctly formatted ini.
+Cases you can load: %1
+ Укажите имя файла с делом (без расширения) для загрузки. Убедитесь, что оно расположено в папке `base/cases`.
+Были найдены: %1
+
+
+
+ Case made by %1.
+ Дело завëл игрок: %1.
+
+
+
+ Navigate to %1 for the CM doc.
+ Перейдите к %1 для получения материалов дела.
+
+
+
+ Your case "%1" was loaded!
+ Дело под кодовым названием "%1" готово!
+
+
+
+
+ Server
+ Сервер
+
+
+
+ Back to Lobby
+ Назад в лобби
+
+
+
+ Rainbow
+ Радужный
+
+
+
+ Pink
+ Розовый
+
+
+
+ Cyan
+ Голубой
+
+
+
+ % offset
+ % сдвига
+
+
+
+ Music
+ Музыка
+
+
+
+ Sfx
+ Звук. эффекты
+
+
+
+ Blips
+ Сигналы
+
+
+
+ Log limit
+
+
+
+
+ Change character
+
+
+
+
+ Reload theme
+
+
+
+
+ Call mod
+
+
+
+
+ Settings
+ Настройки
+
+
+
+ A/M
+
+
+
+
+ Preanim
+
+
+
+
+ You were granted the Guard button.
+ Теперь у вас есть кнопка "Охрана".
+
+
+
+ You opened the settings menu.
+ Вы открыли меню настроек.
+
+
+
+ You will now pair up with
+ Вы встанете парой с персонажем по имени
+
+
+
+ if they also choose your character in return.
+ (если он выберет вас в ответ).
+
+
+
+ You are no longer paired with anyone.
+ Теперь вы не стоите в парах.
+
+
+
+ Are you sure you typed that well? The char ID could not be recognised.
+ Кажется, вам нужно поменять запрос: такой идентификатор персонажа не был найден.
+
+
+
+ You have set your offset to
+ Вы установили сдвиг персонажа на
+
+
+
+ Your offset must be between -100% and 100%!
+ Сдвиг персонажа должен быть между -100% и 100%!
+
+
+
+ That offset does not look like one.
+ Неверный сдвиг персонажа.
+
+
+
+ You switched your music and area list.
+ Вы переключили перечень зон и музыки.
+
+
+
+ You have forcefully enabled features that the server may not support. You may not be able to talk IC, or worse, because of this.
+ Из-за того, что вы включили не поддержимаемые сервером возможности, он может не принять ваши сообщения.
+
+
+
+ Your pre-animations interrupt again.
+ Персонаж будет говорить только после анимации.
+
+
+
+ Your pre-animations will not interrupt text.
+ Персонаж будет говорить и во время анимации.
+
+
+
+ Couldn't open chatlog.txt to write into.
+ Не могу открыть `chatlog.txt` для записи лога.
+
+
+
+ The IC chatlog has been saved.
+ Лог игрового чата сохранëн.
+
+
+
+ You don't have a `base/cases/` folder! It was just made for you, but seeing as it WAS just made for you, it's likely the case file you're looking for can't be found in there.
+ Файл с делом не найден. Если найдëте, положите его в папку `base/cases/`, которую мы для вас создали.
+
+
+
+ Too many arguments to load a case! You only need one filename, without extension.
+ Введите имя файла без расширения.
+
+
+
+ You don't have a `base/cases/` folder! It was just made for you, but seeing as it WAS just made for you, it's likely that you somehow deleted it.
+ Папка `base/cases/` отсутствует!
+
+
+
+ You need to give a filename to save (extension not needed) and the courtroom status!
+ Введите имя файла (без расширения) и предоставьте статус зоны.
+
+
+
+ Too many arguments to save a case! You only need a filename without extension and the courtroom status!
+ Убедитесь, что имя файла не содержит расширение.
+
+
+
+ Succesfully saved, edit doc and cmdoc link on the ini!
+ Сохранение прошло успешно!
+
+
+
+ Master
+ Мастер
+
+
+
+ Reason:
+ Причина:
+
+
+
+ Call Moderator
+ Позвать модератора
+
+
+
+
+ Error
+ Ошибка
+
+
+
+ You must provide a reason.
+ Укажите причину.
+
+
+
+ The message is too long.
+ Слишком длинный текст.
+
+
+
+ Choose...
+ Выбрать...
+
+
+
+ Images (*.png)
+ Изображения (*.png)
+
+
+
+ Add new evidence...
+ Добавить новую улику...
+
+
+
+ Lobby
+
+
+ Attorney Online 2
+ Attorney Online 2
+
+
+
+ Name
+ Никнейм
+
+
+
+ It doesn't look like your client is set up correctly.
+Did you download all resources correctly from tiny.cc/getao, including the large 'base' folder?
+ Не похоже, что ваш клиент установлен правильно.
+Скачали ли вы все ресурсы (tiny.cc/getao), включая огромную папку `base`?
+
+
+
+ Version: %1
+ Версия: %1
+
+
+
+ Loading
+ Загрузка
+
+
+
+ Cancel
+ Отмена
+
+
+
+ <h2>Attorney Online %1</h2>The courtroom drama simulator<p><b>Source code:</b> <a href='https://github.com/AttorneyOnline/AO2-Client'>https://github.com/AttorneyOnline/AO2-Client</a><p><b>Major development:</b><br>OmniTroid, stonedDiscord, longbyte1, gameboyprinter, Cerapter<p><b>Special thanks:</b><br>Remy, Iamgoofball, Hibiki, Qubrick (webAO), Ruekasu (UI design), Draxirch (UI design), Unishred, Argoneus (tsuserver), Fiercy, Noevain, Cronnicossy
+ <h2>Attorney Online %1</h2>Симулятор судебной драмы<p><b>Исходный код:</b> <a href='https://github.com/AttorneyOnline/AO2-Client'>https://github.com/AttorneyOnline/AO2-Client</a><p><b>Основной разработкой занимались:</b><br>OmniTroid, stonedDiscord, longbyte1, gameboyprinter, Cerapter<p><b>Особенная благодарность:</b><br>Remy, Iamgoofball, Hibiki, Qubrick (webAO), Ruekasu (дизайн интерфейса), Draxirch (дизайн интерфейса), Unishred, Argoneus (tsuserver), Fiercy, Noevain, Cronnicossy
+
+
+
+ Online: %1/%2
+ Онлайн: %1/%2
+
+
+
+
+ Offline
+ Вне сети
+
+
+
+ debug_functions
+
+
+ Error: %1
+ Ошибка: %1
+
+
+
+ Error
+ Ошибка
+
+
+
+ Notice
+ На заметку
+
+
+
diff --git a/resources.qrc b/resources.qrc
index bc9f322..fce01eb 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -2,5 +2,11 @@
resource/fonts/Ace-Attorney.ttfresource/logo.png
+ resource/translations/ao_de.qm
+ resource/translations/ao_en.qm
+ resource/translations/ao_jp.qm
+ resource/translations/ao_es.qm
+ resource/translations/ao_ru.qm
+ resource/translations/ao_pl.qm
diff --git a/scripts/.gitignore b/scripts/.gitignore
new file mode 100644
index 0000000..2f8a2b9
--- /dev/null
+++ b/scripts/.gitignore
@@ -0,0 +1,6 @@
+node_modules/
+
+# Cursed file
+package-lock.json
+
+s3_keys.sh
diff --git a/scripts/configure_macos.sh b/scripts/configure_macos.sh
new file mode 100755
index 0000000..af08966
--- /dev/null
+++ b/scripts/configure_macos.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+# This script fetches all build dependencies for MacOS
+# Tested on MacOS 10.14 (Mojave), Qt 5.13 and XCode 10.2
+
+# Exit on errors and unset variables
+set -eu
+
+ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/"
+
+cd ${ROOT_DIR}
+
+LIB_TARGET="../../lib"
+BASS_LINK="http://uk.un4seen.com/files/bass24-osx.zip"
+BASSOPUS_LINK="http://www.un4seen.com/files/bassopus24-osx.zip"
+DISCORD_RPC_LINK="https://github.com/discordapp/discord-rpc/releases/download/v3.4.0/discord-rpc-osx.zip"
+APNG_LINK="https://github.com/Skycoder42/QtApng/releases/download/1.1.2-2/qtapng_clang_64_5.13.0.tar.xz"
+
+# Easier if we don't need to worry about an existing tmp folder tbh smh
+# v Add a slash here for free tmp folder cleanup in true javascript community style
+rm -rf tmp
+mkdir tmp
+cd tmp
+
+curl -Ls ${BASS_LINK} -o bass.zip
+unzip -qq bass.zip
+cp libbass.dylib ${LIB_TARGET}
+
+curl -Ls ${BASSOPUS_LINK} -o bassopus.zip
+unzip -qq bassopus.zip
+cp libbassopus.dylib ${LIB_TARGET}
+
+curl -Ls ${DISCORD_RPC_LINK} -o discord_rpc.zip
+unzip -qq discord_rpc.zip
+cp discord-rpc/osx-dynamic/lib/libdiscord-rpc.dylib ${LIB_TARGET}
+
+curl -Ls ${APNG_LINK} -o apng.tar.xz
+tar -xf apng.tar.xz
+cp clang_64/plugins/imageformats/libqapng.dylib ../../lib
+
+cd ..
+rm -rf tmp
diff --git a/scripts/configure_ubuntu.sh b/scripts/configure_ubuntu.sh
index 3817997..a3d07ed 100755
--- a/scripts/configure_ubuntu.sh
+++ b/scripts/configure_ubuntu.sh
@@ -1,10 +1,18 @@
#!/bin/bash
-#assumes a somewhat recent 64-bit ubuntu
+# Assumes a somewhat recent 64-bit ubuntu
+
+# Exit on errors and unset variables
+set -eu
+
+ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/"
+
+cd "${ROOT_DIR}"
#need some openGL stuff
sudo apt install libgl1-mesa-dev
-
+#install curl incase of fresh vm
+sudo apt install curl
mkdir tmp
cd tmp
diff --git a/scripts/macos_build.sh b/scripts/macos_build.sh
new file mode 100755
index 0000000..efb7653
--- /dev/null
+++ b/scripts/macos_build.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+set -Eexo pipefail
+
+export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/local/opt/openssl/lib/pkgconfig
+export PATH=$PATH:/usr/local/opt/qt5/bin:/usr/local/bin
+
+mkdir bass
+cd bass
+curl http://www.un4seen.com/files/bass24-osx.zip -o bass.zip
+unzip bass.zip
+cp libbass.dylib ../lib
+
+curl http://www.un4seen.com/files/bassopus24-osx.zip -o bassopus.zip
+unzip bassopus.zip
+cp libbassopus.dylib ../lib
+cd ..
+
+mkdir discord-rpc
+cd discord-rpc
+curl -L https://github.com/discordapp/discord-rpc/releases/download/v3.4.0/discord-rpc-osx.zip -o discord_rpc_osx.zip
+unzip discord_rpc_osx.zip
+cp discord-rpc/osx-static/lib/libdiscord-rpc.a ../lib
+cd ..
+
+mkdir qtapng
+cd qtapng
+curl -L https://github.com/Skycoder42/QtApng/releases/download/1.1.0-5/build_clang_64_5.12.0.tar.xz -o apng.tar.xz
+tar -xvf apng.tar.xz
+cp clang_64/plugins/imageformats/libqapng.dylib ../lib
+cd ..
+
+/usr/local/opt/qt/bin/qmake && make -j2
diff --git a/scripts/macos_post_build.sh b/scripts/macos_post_build.sh
old mode 100644
new mode 100755
index d69da0b..25400bf
--- a/scripts/macos_post_build.sh
+++ b/scripts/macos_post_build.sh
@@ -1,16 +1,22 @@
-#!/bin/bash
+#!/bin/sh
-DST_FOLDER="./bin/Attorney_Online.app/Contents/Frameworks"
+# This script prepares the compiled bundle for shipping as a standalone release
+# Assumes the Qt bin folder is in PATH
+# Should be used on a "Release" build from QT creator
+# Note that this DOES NOT add the base/ folder
-cd ..
+# Exit on errors and unset variables
+set -eu
-mkdir $DST_FOLDER
+ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/"
-cp ./lib/libbass.dylib $DST_FOLDER
-cp ./lib/libbassopus.dylib $DST_FOLDER
+cd ${ROOT_DIR}
-install_name_tool -id @executable_path/../Frameworks/libbass.dylib $DST_FOLDER/libbass.dylib
+# This thing basically does all the work
+/usr/local/opt/qt/bin/macdeployqt ../bin/Attorney_Online.app
-install_name_tool -id @executable_path/../Frameworks/libbassopus.dylib $DST_FOLDER/libbassopus.dylib
+# Need to add the dependencies
+cp ../lib/* ../bin/Attorney_Online.app/Contents/Frameworks
-install_name_tool -change @loader_path/libbass.dylib @executable_path/../Frameworks/libbass.dylib ./bin/Attorney_Online.app/Contents/MacOS/Attorney_Online
+# libbass has a funny path for some reason, just use rpath
+install_name_tool -change @loader_path/libbass.dylib @rpath/libbass.dylib ../bin/Attorney_Online.app/Contents/MacOS/Attorney_Online
diff --git a/scripts/package.json b/scripts/package.json
new file mode 100644
index 0000000..126a392
--- /dev/null
+++ b/scripts/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "ao-ci-scripts",
+ "version": "1.0.0",
+ "main": "update_manifest.js",
+ "dependencies": {
+ "argparse": "^1.0.10"
+ },
+ "license": "ISC"
+}
diff --git a/scripts/release_macos.sh b/scripts/release_macos.sh
new file mode 100755
index 0000000..50acb40
--- /dev/null
+++ b/scripts/release_macos.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# This script prepares the compiled bundle for shipping as a standalone release
+# Assumes the Qt bin folder is in PATH
+# Should be used on a "Release" build from QT creator
+# Note that this DOES NOT add the base/ folder
+
+# Exit on errors and unset variables
+set -eu
+
+ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/"
+
+cd ${ROOT_DIR}
+
+# This thing basically does all the work
+macdeployqt ../bin/Attorney_Online.app
+
+# Need to add the dependencies
+cp ../lib/* ../bin/Attorney_Online.app/Contents/Frameworks
+
+# libbass has a funny path for some reason, just use rpath
+install_name_tool -change @loader_path/libbass.dylib @rpath/libbass.dylib ../bin/Attorney_Online.app/Contents/MacOS/Attorney_Online
diff --git a/scripts/update_manifest.js b/scripts/update_manifest.js
new file mode 100755
index 0000000..1a06a2d
--- /dev/null
+++ b/scripts/update_manifest.js
@@ -0,0 +1,151 @@
+#!/usr/bin/env node
+
+const fs = require("fs");
+const crypto = require("crypto");
+const path = require("path");
+const ArgumentParser = require("argparse").ArgumentParser;
+
+function isFile(file) {
+ if (!fs.existsSync(file)) {
+ console.error(`File '${file}' not found. Try again.`);
+ throw Error();
+ }
+ return file;
+}
+
+const argParser = new ArgumentParser({
+ addHelp: true,
+ description: "Adds a new latest version to the manifest file based on the " +
+ "provided zip file, including an incremental update."
+});
+argParser.addArgument("manifestFile", {
+ metavar: "", type: isFile
+});
+argParser.addArgument("version", {
+ metavar: ""
+});
+argParser.addArgument([ "-f", "--full" ], {
+ metavar: "", type: isFile, nargs: 1,
+ dest: "fullZipFileArgs"
+});
+argParser.addArgument([ "-i", "--incremental" ], {
+ type: isFile, nargs: 2, dest: "incrementalArgs",
+ metavar: ["", ""]
+});
+argParser.addArgument([ "-e", "--executable" ], {
+ metavar: "[executable file]", nargs: 1,
+ dest: "executableArgs"
+});
+
+const {
+ manifestFile,
+ version,
+ fullZipFileArgs,
+ incrementalArgs,
+ executableArgs
+} = argParser.parseArgs();
+
+const [incrementalZipFile, changesFile] = incrementalArgs || [];
+const [fullZipFile] = fullZipFileArgs || [];
+const [executable] = executableArgs || [];
+
+// Do one final check
+if (!incrementalZipFile && !fullZipFile) {
+ console.error("No download archive specified! Abort.");
+ process.exit(1);
+}
+
+// Do a quick litmus test to prevent deleting everything incorrectly
+if (changesFile && !fs.existsSync("base")) {
+ console.error("The working directory must be set to an " +
+ "asset folder in order for deleted directories " +
+ "to be calculated correctly. Abort.");
+ process.exit(1);
+}
+
+const manifest = JSON.parse(fs.readFileSync(manifestFile));
+
+const dirsDeleted = new Set();
+const specialActions = changesFile ?
+ fs.readFileSync(changesFile)
+ .toString()
+ .trim()
+ .split("\n")
+ .map(line => line.split("\t"))
+ .map(([mode, target, source]) => {
+ switch (mode[0]) {
+ case "D": // Deleted
+ // Check if the folder exists relative to the working
+ // directory, and if not, add it to the dirsDeleted list.
+ // Keep going up the tree to see how many directories were
+ // deleted.
+ let dir = path.dirname(target);
+ while (!dirsDeleted.has(dir) && !fs.existsSync(dir)) {
+ dirsDeleted.add(dir);
+ dir = path.dirname(dir);
+ }
+
+ return { action: "delete", target };
+ case "R": // Renamed
+ // NOTE: Make sure that the launcher's implementation of
+ // the move action also creates directories when needed.
+ return { action: "move", source, target};
+ default:
+ return null;
+ }
+ })
+ // Remove ignored file mode changes
+ .filter(action => action !== null)
+ // Create actions based on directories to be deleted.
+ // Always have deeper directories first, to guarantee that deleting
+ // higher-level directories will succeed.
+ .concat(Array.from(dirsDeleted.values())
+ .sort((a, b) => b.split("/").length - a.split("/").length)
+ .map(dir => {
+ return { action: "deleteDir", target: dir };
+ }))
+ : [];
+
+const urlBase = "https://s3.wasabisys.com/ao-downloads/";
+
+const versionEntry = {
+ version,
+ executable,
+ prev: manifest.versions[0] ? manifest.versions[0].version : undefined,
+ full: fullZipFile ? [
+ {
+ action: "dl",
+ url: urlBase + encodeURIComponent(path.basename(fullZipFile)),
+ hash: crypto.createHash("sha1")
+ .update(fs.readFileSync(fullZipFile))
+ .digest("hex")
+ }
+ ] : undefined,
+ update: incrementalArgs ? [
+ ...specialActions,
+ {
+ action: "dl",
+ url: urlBase + encodeURIComponent(path.basename(incrementalZipFile)),
+ hash: crypto.createHash("sha1")
+ .update(fs.readFileSync(incrementalZipFile))
+ .digest("hex")
+ }
+ ] : undefined
+};
+
+console.log("Generated version entry:", versionEntry);
+
+const existingVersions = manifest.versions.filter(v => v.version == version);
+if (existingVersions.length > 0) {
+ console.warn(`Warning: version ${version} already exists. Adding new values.`);
+
+ // Don't overwrite prev - it will cause headaches
+ delete versionEntry.prev;
+
+ Object.assign(existingVersions[0], versionEntry);
+ console.log("Merged version entry:", existingVersions[0]);
+} else {
+ manifest.versions = [versionEntry, ...manifest.versions];
+}
+
+fs.writeFileSync(manifestFile, JSON.stringify(manifest, null, 4));
diff --git a/scripts/update_program_manifest.js b/scripts/update_program_manifest.js
new file mode 100755
index 0000000..9efc814
--- /dev/null
+++ b/scripts/update_program_manifest.js
@@ -0,0 +1,39 @@
+#!/usr/bin/env node
+
+const fs = require("fs");
+const crypto = require("crypto");
+
+const [ _nodeExe, _jsPath, manifestFile, version, zipFile ] = process.argv;
+
+if (!manifestFile || !version || !zipFile) {
+ console.log(`Usage: update_program_manifest `);
+ console.log(`Adds a new latest version to the manifest file based on the ` +
+ `provided zip file.`);
+ process.exit(1);
+}
+
+if (!fs.existsSync(manifestFile)) {
+ console.error(`Manifest file '${manifestFile}' not found. Try again.`);
+ process.exit(2);
+}
+
+if (!fs.existsSync(zipFile)) {
+ console.error(`Zip file '${zipFile}' not found. Try again.`);
+ process.exit(2);
+}
+
+const manifest = JSON.parse(fs.readFileSync(manifestFile));
+
+manifest.versions = [{
+ version,
+ executable: "Attorney_Online.exe",
+ full: [
+ {
+ action: "dl",
+ url: "https://s3.wasabisys.com/ao-downloads/" + encodeURIComponent(zipFile),
+ hash: crypto.createHash("sha1").update(fs.readFileSync(zipFile)).digest("hex")
+ }
+ ]
+}, ...manifest.versions];
+
+fs.writeFileSync(manifestFile, JSON.stringify(manifest, null, 4));
\ No newline at end of file
diff --git a/scripts/wasabi.sh b/scripts/wasabi.sh
new file mode 100755
index 0000000..95fb3ee
--- /dev/null
+++ b/scripts/wasabi.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+# Updates the specified program manifest to a new archive and version
+# and uploads the new archive and manifest to S3/Wasabi.
+#
+# Requires:
+# MANIFEST: name of the manifest file
+# S3_ACCESS_KEY, S3_SECRET_KEY: S3 credentials
+# S3_MANIFESTS, S3_ARCHIVES: S3 paths to manifests and downloads
+# ARCHIVE_FULL: name of the full archive (if desired)
+# ARCHIVE_INCR: name of the incremental archive (if desired)
+# VERSION: name of the new version
+# EXECUTABLE: name of the executable (if program manifest)
+
+
+# -E: inherit ERR trap by shell functions
+# -e: stop script on ERR trap
+# -u: stop script on unbound variables
+# -x: print command before running it
+# -o pipefail: fail if any command in a pipeline fails
+set -Eeuxo pipefail
+
+aws configure set aws_access_key_id ${S3_ACCESS_KEY}
+aws configure set aws_secret_access_key ${S3_SECRET_KEY}
+aws configure set default.region us-east-1
+
+export S3_COPY="aws s3 cp --endpoint-url=https://s3.wasabisys.com"
+
+export ARCHIVE_FULL_ARG=""
+export ARCHIVE_INCR_ARG=""
+export EXECUTABLE_ARG=""
+
+export LAST_TAGGED_VERSION=$(git rev-list --tags --skip=1 --max-count=1)
+echo "Previous tagged version: ${LAST_TAGGED_VERSION}"
+echo "Current tagged version: ${VERSION}"
+
+if [[ -n $ARCHIVE_INCR && -n $LAST_TAGGED_VERSION ]]; then
+ echo "Incremental archive: ${ARCHIVE_INCR}"
+
+ # Get all files
+ export CHANGES_FILE="changes.txt"
+ git diff --name-status ${LAST_TAGGED_VERSION}..HEAD > ${CHANGES_FILE}
+
+ # Get added/modified files
+ git diff --name-only --diff-filter=dr ${LAST_TAGGED_VERSION}..HEAD | \
+ zip ${ARCHIVE_INCR} -@
+
+ export ARCHIVE_INCR_ARG="-i ${ARCHIVE_INCR} ${CHANGES_FILE}"
+elif [[ -n $ARCHIVE_INCR && -z $LAST_TAGGED_VERSION ]]; then
+ echo "Incremental archive was requested, but there is no previous version"
+fi
+
+if [[ -n $ARCHIVE_FULL ]]; then
+ echo "Full archive: ${ARCHIVE_INCR}"
+ export ARCHIVE_FULL_ARG="-f ${ARCHIVE_FULL}"
+fi
+
+if [[ -v EXECUTABLE ]]; then
+ export EXECUTABLE_ARG="-e ${EXECUTABLE}"
+fi
+
+${S3_COPY} ${S3_MANIFESTS}/${MANIFEST} .
+node $(dirname $0)/update_manifest.js ${MANIFEST} ${VERSION} \
+ ${ARCHIVE_FULL_ARG} ${ARCHIVE_INCR_ARG} ${EXECUTABLE_ARG}
+
+if [[ -n $ARCHIVE_INCR_ARG ]]; then
+ ${S3_COPY} ${ARCHIVE_INCR} ${S3_ARCHIVES}
+fi
+
+if [[ -n $ARCHIVE_FULL_ARG ]]; then
+ ${S3_COPY} ${ARCHIVE_FULL} ${S3_ARCHIVES}
+fi
+
+${S3_COPY} ${MANIFEST} ${S3_MANIFESTS}
diff --git a/scripts/wasabi_program.sh b/scripts/wasabi_program.sh
new file mode 100755
index 0000000..41e2e35
--- /dev/null
+++ b/scripts/wasabi_program.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+# Updates the specified program manifest to a new archive and version
+# and uploads the new archive and manifest to S3/Wasabi.
+#
+# Requires:
+# MANIFEST: name of the manifest file
+# ARTIFACT_SUFFIX: suffix of the archive to be uploaded (including extension)
+# S3_ACCESS_KEY and S3_SECRET_KEY
+
+
+# -E: inherit ERR trap by shell functions
+# -e: stop script on ERR trap
+# -u: stop script on unbound variables
+# -x: print command before running it
+# -o pipefail: fail if any command in a pipeline fails
+set -Eeuxo pipefail
+
+aws configure set aws_access_key_id ${S3_ACCESS_KEY}
+aws configure set aws_secret_access_key ${S3_SECRET_KEY}
+aws configure set default.region us-east-1
+
+export S3_COPY="aws s3 cp --endpoint-url=https://s3.wasabisys.com"
+export S3_MANIFESTS="s3://ao-manifests"
+export S3_ARCHIVES="s3://ao-downloads"
+
+export VERSION=$(git describe --tags)
+export ARCHIVE="Attorney_Online_${VERSION}_${ARTIFACT_SUFFIX}"
+
+${S3_COPY} ${S3_MANIFESTS}/${MANIFEST} .
+node $(dirname $0)/update_manifest.js ${MANIFEST} ${VERSION} \
+ -f ${ARCHIVE} -e Attorney_Online.exe
+${S3_COPY} ${ARCHIVE} ${S3_ARCHIVES}
+${S3_COPY} ${MANIFEST} ${S3_MANIFESTS}
+
+rm -f ${MANIFEST}
diff --git a/scripts/windows/Dockerfile b/scripts/windows/Dockerfile
new file mode 100644
index 0000000..f4f1a83
--- /dev/null
+++ b/scripts/windows/Dockerfile
@@ -0,0 +1,14 @@
+FROM oldmud0/mxe-qt:5.13.0-win32-static-posix
+#FROM fffaraz/qt:windows
+
+ENV TARGET_SPEC i686-w64-mingw32.static.posix
+
+# Build Discord RPC statically
+RUN git clone https://github.com/discordapp/discord-rpc
+WORKDIR discord-rpc/build
+RUN /opt/mxe/usr/bin/${TARGET_SPEC}-cmake .. -DCMAKE_INSTALL_PREFIX=/opt/mxe/usr/${TARGET_SPEC}
+RUN /opt/mxe/usr/bin/${TARGET_SPEC}-cmake --build . --config Release --target install
+WORKDIR ../..
+
+# NOTE: Do not build QtApng statically! libpng contains a self-test entry point that
+# takes precedence for some reason over the final build's entry point.
diff --git a/scripts/windows/Dockerfile-mxe b/scripts/windows/Dockerfile-mxe
new file mode 100644
index 0000000..873e144
--- /dev/null
+++ b/scripts/windows/Dockerfile-mxe
@@ -0,0 +1,44 @@
+FROM ubuntu:18.04
+
+RUN apt-get update
+RUN apt-get install -y \
+ autoconf \
+ automake \
+ autopoint \
+ bash \
+ bison \
+ bzip2 \
+ flex \
+ g++ \
+ g++-multilib \
+ gettext \
+ git \
+ gperf \
+ intltool \
+ libc6-dev-i386 \
+ libgdk-pixbuf2.0-dev \
+ libltdl-dev \
+ libssl-dev \
+ libtool-bin \
+ libxml-parser-perl \
+ lzip \
+ make \
+ openssl \
+ p7zip-full \
+ patch \
+ perl \
+ pkg-config \
+ python \
+ ruby \
+ sed \
+ unzip \
+ wget \
+ xz-utils
+
+RUN git clone https://github.com/mxe/mxe.git
+RUN mv mxe /opt/mxe
+WORKDIR /opt/mxe
+RUN make -j4 MXE_TARGETS="i686-w64-mingw32.static.posix" qtbase qtmultimedia libarchive
+ENV PATH=/opt/mxe/usr/bin:$PATH
+
+WORKDIR /
diff --git a/scripts/windows/how-to-push.md b/scripts/windows/how-to-push.md
new file mode 100644
index 0000000..8c1c18d
--- /dev/null
+++ b/scripts/windows/how-to-push.md
@@ -0,0 +1,19 @@
+When you want to build a new version of Qt:
+```docker
+docker build -t mxe-windows-static . -f Dockerfile-mxe
+docker tag mxe-windows-static oldmud0/mxe-qt:5.12.1-win32-static-posix
+docker push oldmud0/mxe-qt:5.12.1-win32-static-posix
+```
+
+Remember to log into Docker Hub before attempting to push.
+
+When you want to build a new version of any dependency required for building AO:
+```docker
+docker build -t mxe-windows-static-ao . -f Dockerfile
+docker tag mxe-windows-static-ao registry.gitlab.com/attorneyonline/ao2-client/builder-windows-i686
+docker push registry.gitlab.com/attorneyonline/ao2-client/builder-windows-i686
+```
+
+Remember to create an access token in GitLab before attempting to push.
+
+GitLab CI depends on `builder-windows-i686` image to be present in the repository's registry in order for the Windows build to succeed.
diff --git a/src/aoapplication.cpp b/src/aoapplication.cpp
index d7c3e66..6213acd 100644
--- a/src/aoapplication.cpp
+++ b/src/aoapplication.cpp
@@ -37,9 +37,9 @@ void AOApplication::construct_lobby()
w_lobby = new Lobby(this);
lobby_constructed = true;
- QRect screenGeometry = QApplication::desktop()->screenGeometry();
- int x = (screenGeometry.width()-w_lobby->width()) / 2;
- int y = (screenGeometry.height()-w_lobby->height()) / 2;
+ QRect geometry = QGuiApplication::primaryScreen()->geometry();
+ int x = (geometry.width()-w_lobby->width()) / 2;
+ int y = (geometry.height()-w_lobby->height()) / 2;
w_lobby->move(x, y);
if (is_discord_enabled())
@@ -72,9 +72,9 @@ void AOApplication::construct_courtroom()
w_courtroom = new Courtroom(this);
courtroom_constructed = true;
- QRect screenGeometry = QApplication::desktop()->screenGeometry();
- int x = (screenGeometry.width()-w_courtroom->width()) / 2;
- int y = (screenGeometry.height()-w_courtroom->height()) / 2;
+ QRect geometry = QGuiApplication::primaryScreen()->geometry();
+ int x = (geometry.width()-w_courtroom->width()) / 2;
+ int y = (geometry.height()-w_courtroom->height()) / 2;
w_courtroom->move(x, y);
}
@@ -135,7 +135,7 @@ void AOApplication::server_disconnected()
{
if (courtroom_constructed)
{
- call_notice("Disconnected from server.");
+ call_notice(tr("Disconnected from server."));
construct_lobby();
destruct_courtroom();
}
@@ -160,16 +160,15 @@ void AOApplication::ms_connect_finished(bool connected, bool will_retry)
if (will_retry)
{
if (lobby_constructed)
- w_lobby->append_error("Error connecting to master server. Will try again in "
- + QString::number(net_manager->ms_reconnect_delay_ms / 1000.f) + " seconds.");
+ w_lobby->append_error(tr("Error connecting to master server. Will try again in %1 seconds.").arg(QString::number(net_manager->ms_reconnect_delay)));
}
else
{
- call_error("There was an error connecting to the master server.\n"
+ call_error(tr("There was an error connecting to the master server.\n"
"We deploy multiple master servers to mitigate any possible downtime, "
"but the client appears to have exhausted all possible methods of finding "
"and connecting to one.\n"
- "Please check your Internet connection and firewall, and please try again.");
+ "Please check your Internet connection and firewall, and please try again."));
}
}
}
diff --git a/src/aoblipplayer.cpp b/src/aoblipplayer.cpp
index 74757c5..1c668ab 100644
--- a/src/aoblipplayer.cpp
+++ b/src/aoblipplayer.cpp
@@ -1,5 +1,6 @@
#include "aoblipplayer.h"
+#if defined(BASSAUDIO) //Using bass.dll for the blips
AOBlipPlayer::AOBlipPlayer(QWidget *parent, AOApplication *p_ao_app)
{
m_parent = parent;
@@ -17,7 +18,7 @@ void AOBlipPlayer::set_blips(QString p_sfx)
m_stream_list[n_stream] = BASS_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, BASS_UNICODE | BASS_ASYNCFILE);
}
- set_volume(m_volume);
+ set_volume_internal(m_volume);
}
void AOBlipPlayer::blip_tick()
@@ -28,20 +29,89 @@ void AOBlipPlayer::blip_tick()
m_cycle = 0;
HSTREAM f_stream = m_stream_list[f_cycle];
-
if (ao_app->get_audio_output_device() != "default")
BASS_ChannelSetDevice(f_stream, BASS_GetDevice());
BASS_ChannelPlay(f_stream, false);
}
-void AOBlipPlayer::set_volume(int p_value)
+void AOBlipPlayer::set_volume(qreal p_value)
{
- m_volume = p_value;
+ m_volume = p_value / 100;
+ set_volume_internal(m_volume);
+}
- float volume = p_value / 100.0f;
+void AOBlipPlayer::set_volume_internal(qreal p_value)
+{
+ float volume = p_value;
for (int n_stream = 0 ; n_stream < 5 ; ++n_stream)
{
BASS_ChannelSetAttribute(m_stream_list[n_stream], BASS_ATTRIB_VOL, volume);
}
}
+#elif defined(QTAUDIO) //Using Qt's QSoundEffect class
+AOBlipPlayer::AOBlipPlayer(QWidget *parent, AOApplication *p_ao_app)
+{
+ m_parent = parent;
+ ao_app = p_ao_app;
+}
+
+void AOBlipPlayer::set_blips(QString p_sfx)
+{
+ QString f_path = ao_app->get_sounds_path(p_sfx);
+
+ for (int n_stream = 0 ; n_stream < 5 ; ++n_stream)
+ {
+ m_blips.setSource(QUrl::fromLocalFile(f_path));
+ }
+
+ set_volume_internal(m_volume);
+}
+
+void AOBlipPlayer::blip_tick()
+{
+ int f_cycle = m_cycle++;
+
+ if (m_cycle == 5)
+ m_cycle = 0;
+
+ m_blips.play();
+}
+
+void AOBlipPlayer::set_volume(qreal p_value)
+{
+ m_volume = p_value / 100;
+ set_volume_internal(m_volume);
+}
+
+void AOBlipPlayer::set_volume_internal(qreal p_value)
+{
+ m_blips.setVolume(m_volume);
+}
+#else //No audio
+AOBlipPlayer::AOBlipPlayer(QWidget *parent, AOApplication *p_ao_app)
+{
+ m_parent = parent;
+ ao_app = p_ao_app;
+}
+
+void AOBlipPlayer::set_blips(QString p_sfx)
+{
+
+}
+
+void AOBlipPlayer::blip_tick()
+{
+
+}
+
+void AOBlipPlayer::set_volume(qreal p_value)
+{
+
+}
+
+void AOBlipPlayer::set_volume_internal(qreal p_value)
+{
+
+}
+#endif
diff --git a/src/aoevidencebutton.cpp b/src/aoevidencebutton.cpp
index ab654bf..4dc0395 100644
--- a/src/aoevidencebutton.cpp
+++ b/src/aoevidencebutton.cpp
@@ -86,6 +86,7 @@ void AOEvidenceButton::mouseDoubleClickEvent(QMouseEvent *e)
evidence_double_clicked(m_id);
}
+/*
void AOEvidenceButton::dragLeaveEvent(QMouseEvent *e)
{
//QWidget::dragLeaveEvent(e);
@@ -95,6 +96,7 @@ void AOEvidenceButton::dragEnterEvent(QMouseEvent *e)
{
//QWidget::dragEnterEvent(e);
}
+*/
void AOEvidenceButton::enterEvent(QEvent * e)
{
diff --git a/src/aomusicplayer.cpp b/src/aomusicplayer.cpp
index 7fcb277..32848fb 100644
--- a/src/aomusicplayer.cpp
+++ b/src/aomusicplayer.cpp
@@ -1,6 +1,6 @@
#include "aomusicplayer.h"
-
+#if defined(BASSAUDIO)
AOMusicPlayer::AOMusicPlayer(QWidget *parent, AOApplication *p_ao_app): QObject()
{
m_parent = parent;
@@ -55,3 +55,75 @@ void AOMusicPlayer::kill_loop()
BASS_ChannelStop(m_stream);
}
+#elif defined(QTAUDIO)
+AOMusicPlayer::AOMusicPlayer(QWidget *parent, AOApplication *p_ao_app): QObject()
+{
+ m_parent = parent;
+ ao_app = p_ao_app;
+}
+
+AOMusicPlayer::~AOMusicPlayer()
+{
+ m_player.stop();
+}
+
+void AOMusicPlayer::play(QString p_song)
+{
+ m_player.stop();
+
+ QString f_path = ao_app->get_music_path(p_song);
+
+ m_player.setMedia(QUrl::fromLocalFile(f_path));
+
+ this->set_volume(m_volume);
+
+ m_player.play();
+}
+
+void AOMusicPlayer::set_volume(int p_value)
+{
+ m_volume = p_value;
+ m_player.setVolume(m_volume);
+}
+
+QString AOMusicPlayer::get_path()
+{
+ return f_path;
+}
+
+void AOMusicPlayer::kill_loop()
+{
+ // TODO QTAUDIO
+}
+#else
+AOMusicPlayer::AOMusicPlayer(QWidget *parent, AOApplication *p_ao_app): QObject()
+{
+ m_parent = parent;
+ ao_app = p_ao_app;
+}
+
+AOMusicPlayer::~AOMusicPlayer()
+{
+
+}
+
+void AOMusicPlayer::play(QString p_song)
+{
+
+}
+
+void AOMusicPlayer::set_volume(int p_value)
+{
+
+}
+
+QString AOMusicPlayer::get_path()
+{
+ return f_path;
+}
+
+void AOMusicPlayer::kill_loop()
+{
+
+}
+#endif
\ No newline at end of file
diff --git a/src/aooptionsdialog.cpp b/src/aooptionsdialog.cpp
index b0e1e99..30146c9 100644
--- a/src/aooptionsdialog.cpp
+++ b/src/aooptionsdialog.cpp
@@ -1,6 +1,5 @@
#include "aooptionsdialog.h"
#include "aoapplication.h"
-#include "bass.h"
AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) : QDialog(parent)
{
@@ -178,6 +177,21 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) : QDi
ui_gameplay_form->setWidget(10, QFormLayout::FieldRole, ui_epilepsy_cb);
+ ui_language_label = new QLabel(ui_form_layout_widget);
+ ui_language_label->setText(tr("Language:"));
+ ui_language_label->setToolTip(tr("Sets the language if you don't want to use your system language."));
+ ui_gameplay_form->setWidget(10, QFormLayout::LabelRole, ui_language_label);
+
+ ui_language_combobox = new QComboBox(ui_form_layout_widget);
+ ui_language_combobox->addItem(configini->value("language", " ").value() + " - Keep current setting");
+ ui_language_combobox->addItem(" - Default");
+ ui_language_combobox->addItem("en - English");
+ ui_language_combobox->addItem("de - Deutsch");
+ ui_language_combobox->addItem("es - Español");
+ ui_language_combobox->addItem("jp - 日本語");
+ ui_language_combobox->addItem("ru - Русский");
+ ui_gameplay_form->setWidget(10, QFormLayout::FieldRole, ui_language_combobox);
+
// Here we start the callwords tab.
ui_callwords_tab = new QWidget();
ui_settings_tabs->addTab(ui_callwords_tab, tr("Callwords"));
@@ -229,22 +243,30 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) : QDi
ui_audio_device_combobox = new QComboBox(ui_audio_widget);
- // Let's fill out the combobox with the available audio devices.
+ // Let's fill out the combobox with the available audio devices. Or don't if there is no audio
int a = 0;
- BASS_DEVICEINFO info;
-
if (needs_default_audiodev())
{
- ui_audio_device_combobox->addItem("default");
- }
+ ui_audio_device_combobox->addItem("default");
+
+ }
+ #ifdef BASSAUDIO
+ BASS_DEVICEINFO info;
for (a = 0; BASS_GetDeviceInfo(a, &info); a++)
{
ui_audio_device_combobox->addItem(info.name);
if (p_ao_app->get_audio_output_device() == info.name)
ui_audio_device_combobox->setCurrentIndex(ui_audio_device_combobox->count()-1);
}
-
+ #elif defined QTAUDIO
+ foreach (const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))
+ {
+ ui_audio_device_combobox->addItem(deviceInfo.deviceName());
+ if (p_ao_app->get_audio_output_device() == deviceInfo.deviceName())
+ ui_audio_device_combobox->setCurrentIndex(ui_audio_device_combobox->count()-1);
+ }
+ #endif
ui_audio_layout->setWidget(0, QFormLayout::FieldRole, ui_audio_device_combobox);
ui_audio_volume_divider = new QFrame(ui_audio_widget);
@@ -510,6 +532,7 @@ void AOOptionsDialog::save_pressed()
configini->setValue("master", ui_ms_textbox->text());
configini->setValue("discord", ui_discord_cb->isChecked());
configini->setValue("shakeandflash", ui_epilepsy_cb->isChecked());
+ configini->setValue("language", ui_language_combobox->currentText().left(2));
QFile* callwordsini = new QFile(ao_app->get_base_path() + "callwords.ini");
diff --git a/src/aosfxplayer.cpp b/src/aosfxplayer.cpp
index e39071e..80b6ea3 100644
--- a/src/aosfxplayer.cpp
+++ b/src/aosfxplayer.cpp
@@ -1,6 +1,7 @@
#include "aosfxplayer.h"
#include "file_functions.h"
+#if defined(BASSAUDIO) //Using bass.dll for sfx
AOSfxPlayer::AOSfxPlayer(QWidget *parent, AOApplication *p_ao_app): QObject()
{
m_parent = parent;
@@ -31,7 +32,7 @@ void AOSfxPlayer::play(QString p_sfx, QString p_char, QString shout)
BASS_ChannelStop(m_stream);
m_stream = BASS_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, BASS_STREAM_AUTOFREE | BASS_UNICODE | BASS_ASYNCFILE);
- set_volume(m_volume);
+ set_volume_internal(m_volume);
if (ao_app->get_audio_output_device() != "default")
BASS_ChannelSetDevice(m_stream, BASS_GetDevice());
@@ -56,9 +57,95 @@ void AOSfxPlayer::stop()
BASS_ChannelStop(m_stream);
}
-void AOSfxPlayer::set_volume(int p_value)
+void AOSfxPlayer::set_volume(qreal p_value)
{
- m_volume = p_value;
- float volume = p_value / 100.0f;
- BASS_ChannelSetAttribute(m_stream, BASS_ATTRIB_VOL, volume);
+ m_volume = p_value / 100;
+ set_volume_internal(m_volume);
}
+
+void AOSfxPlayer::set_volume_internal(qreal p_value)
+{
+ float volume = p_value;
+ BASS_ChannelSetAttribute(m_stream, BASS_ATTRIB_VOL, volume);
+}
+#elif defined(QTAUDIO) //Using Qt's QSoundEffect class
+AOSfxPlayer::AOSfxPlayer(QWidget *parent, AOApplication *p_ao_app): QObject()
+{
+ m_parent = parent;
+ ao_app = p_ao_app;
+}
+
+void AOSfxPlayer::play(QString p_sfx, QString p_char, QString shout)
+{
+ m_sfx.stop();
+
+ QString misc_path = "";
+ QString char_path = "";
+ QString sound_path = ao_app->get_sounds_path(p_sfx);
+
+ if (shout != "")
+ misc_path = ao_app->get_base_path() + "misc/" + shout + "/" + p_sfx;
+ if (p_char != "")
+ char_path = ao_app->get_character_path(p_char, p_sfx);
+
+ QString f_path;
+
+ if (file_exists(char_path))
+ f_path = char_path;
+ else if (file_exists(misc_path))
+ f_path = misc_path;
+ else
+ f_path = sound_path;
+
+ if (file_exists(f_path)) //if its missing, it will glitch out
+ {
+ m_sfx.setSource(QUrl::fromLocalFile(f_path));
+
+ set_volume_internal(m_volume);
+
+ m_sfx.play();
+ }
+}
+
+void AOSfxPlayer::stop()
+{
+ m_sfx.stop();
+}
+
+void AOSfxPlayer::set_volume(qreal p_value)
+{
+ m_volume = p_value/100;
+ set_volume_internal(m_volume);
+}
+
+void AOSfxPlayer::set_volume_internal(qreal p_value)
+{
+ m_sfx.setVolume(m_volume);
+}
+#else
+AOSfxPlayer::AOSfxPlayer(QWidget *parent, AOApplication *p_ao_app): QObject()
+{
+ m_parent = parent;
+ ao_app = p_ao_app;
+}
+
+void AOSfxPlayer::play(QString p_sfx, QString p_char, QString shout)
+{
+
+}
+
+void AOSfxPlayer::stop()
+{
+
+}
+
+void AOSfxPlayer::set_volume(qreal p_value)
+{
+
+}
+
+void AOSfxPlayer::set_volume_internal(qreal p_value)
+{
+
+}
+#endif
diff --git a/src/charselect.cpp b/src/charselect.cpp
index 6edd87e..875c8a5 100644
--- a/src/charselect.cpp
+++ b/src/charselect.cpp
@@ -97,7 +97,6 @@ void Courtroom::construct_char_select()
set_size_and_pos(ui_char_buttons, "char_buttons");
- connect (char_button_mapper, SIGNAL(mapped(int)), this, SLOT(char_clicked(int)));
connect(ui_back_to_lobby, SIGNAL(clicked()), this, SLOT(on_back_to_lobby_clicked()));
connect(ui_char_select_left, SIGNAL(clicked()), this, SLOT(on_char_select_left_clicked()));
@@ -105,9 +104,9 @@ void Courtroom::construct_char_select()
connect(ui_spectator, SIGNAL(clicked()), this, SLOT(on_spectator_clicked()));
- connect(ui_char_search, SIGNAL(textEdited(const QString&)), this, SLOT(on_char_search_changed(const QString&)));
- connect(ui_char_passworded, SIGNAL(stateChanged(int)), this, SLOT(on_char_passworded_clicked(int)));
- connect(ui_char_taken, SIGNAL(stateChanged(int)), this, SLOT(on_char_taken_clicked(int)));
+ connect(ui_char_search, SIGNAL(textEdited(const QString&)), this, SLOT(on_char_search_changed()));
+ connect(ui_char_passworded, SIGNAL(stateChanged(int)), this, SLOT(on_char_passworded_clicked()));
+ connect(ui_char_taken, SIGNAL(stateChanged(int)), this, SLOT(on_char_taken_clicked()));
}
void Courtroom::set_char_select()
@@ -189,6 +188,7 @@ void Courtroom::char_clicked(int n_char)
}
else
{
+ ao_app->send_server_packet(new AOPacket("PW#" + ui_char_password->text() + "#%"));
ao_app->send_server_packet(new AOPacket("CC#" + QString::number(ao_app->s_pv) + "#" + QString::number(n_char) + "#" + get_hdid() + "#%"));
}
@@ -280,17 +280,17 @@ void Courtroom::filter_character_list()
set_char_select_page();
}
-void Courtroom::on_char_search_changed(const QString& newtext)
+void Courtroom::on_char_search_changed()
{
filter_character_list();
}
-void Courtroom::on_char_passworded_clicked(int newstate)
+void Courtroom::on_char_passworded_clicked()
{
filter_character_list();
}
-void Courtroom::on_char_taken_clicked(int newstate)
+void Courtroom::on_char_taken_clicked()
{
filter_character_list();
}
diff --git a/src/courtroom.cpp b/src/courtroom.cpp
index 0905528..054603d 100644
--- a/src/courtroom.cpp
+++ b/src/courtroom.cpp
@@ -2,10 +2,10 @@
Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow()
{
ao_app = p_ao_app;
-
+ #ifdef BASSAUDIO
// Change the default audio output device to be the one the user has given
// in his config.ini file for now.
- int a = 0;
+ unsigned int a = 0;
BASS_DEVICEINFO info;
if (ao_app->get_audio_output_device() == "default")
@@ -20,13 +20,29 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow()
if (ao_app->get_audio_output_device() == info.name)
{
BASS_SetDevice(a);
- BASS_Init(a, 48000, BASS_DEVICE_LATENCY, nullptr, nullptr);
+ BASS_Init(static_cast(a), 48000, BASS_DEVICE_LATENCY, nullptr, nullptr);
load_bass_opus_plugin();
qDebug() << info.name << "was set as the default audio output device.";
break;
}
}
}
+ #elif defined QTAUDIO
+
+ if (ao_app->get_audio_output_device() != "default")
+ {
+ foreach (const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))
+ {
+ if (ao_app->get_audio_output_device() == deviceInfo.deviceName())
+ {
+ ao_app->QtAudioDevice = deviceInfo;
+ qDebug() << deviceInfo.deviceName() << "was set as the default audio output device.";
+ break;
+ }
+ }
+ }
+ #endif
+
keepalive_timer = new QTimer(this);
keepalive_timer->start(60000);
@@ -47,20 +63,22 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow()
testimony_hide_timer = new QTimer(this);
testimony_hide_timer->setSingleShot(true);
- char_button_mapper = new QSignalMapper(this);
-
music_player = new AOMusicPlayer(this, ao_app);
music_player->set_volume(0);
+
sfx_player = new AOSfxPlayer(this, ao_app);
sfx_player->set_volume(0);
+
objection_player = new AOSfxPlayer(this, ao_app);
objection_player->set_volume(0);
+
misc_sfx_player = new AOSfxPlayer(this, ao_app);
misc_sfx_player->set_volume(0);
frame_emote_sfx_player = new AOSfxPlayer(this, ao_app);
frame_emote_sfx_player->set_volume(0);
pair_frame_emote_sfx_player = new AOSfxPlayer(this, ao_app); // todo: recode pair // todo: recode fucking everything
pair_frame_emote_sfx_player->set_volume(0);
+
blip_player = new AOBlipPlayer(this, ao_app);
blip_player->set_volume(0);
@@ -133,7 +151,7 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow()
ui_ooc_chat_name = new QLineEdit(this);
ui_ooc_chat_name->setFrame(false);
- ui_ooc_chat_name->setPlaceholderText("Name");
+ ui_ooc_chat_name->setPlaceholderText(tr("Name"));
ui_ooc_chat_name->setMaxLength(30);
ui_ooc_chat_name->setText(p_ao_app->get_default_username());
@@ -186,13 +204,18 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow()
ui_switch_area_music = new AOButton(this, ao_app);
ui_pre = new QCheckBox(this);
- ui_pre->setText("Pre");
+ ui_pre->setText(tr("Pre"));
+
ui_flip = new QCheckBox(this);
- ui_flip->setText("Flip");
+ ui_flip->setText(tr("Flip"));
ui_flip->hide();
+
ui_guard = new QCheckBox(this);
- ui_guard->setText("Disable Modcalls");
+
+ ui_guard->setText(tr("Disable Modcalls"));
+
ui_guard->hide();
+
ui_casing = new QCheckBox(this);
ui_casing->setChecked(ao_app->get_casing_enabled());
ui_casing->setText(tr("Casing"));
@@ -218,15 +241,15 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow()
ui_prosecution_minus = new AOButton(this, ao_app);
ui_text_color = new QComboBox(this);
- ui_text_color->addItem("White");
- ui_text_color->addItem("Green");
- ui_text_color->addItem("Red");
- ui_text_color->addItem("Orange");
- ui_text_color->addItem("Blue");
- ui_text_color->addItem("Yellow");
- ui_text_color->addItem("Rainbow");
- ui_text_color->addItem("Pink");
- ui_text_color->addItem("Cyan");
+ ui_text_color->addItem(tr("White"));
+ ui_text_color->addItem(tr("Green"));
+ ui_text_color->addItem(tr("Red"));
+ ui_text_color->addItem(tr("Orange"));
+ ui_text_color->addItem(tr("Blue"));
+ ui_text_color->addItem(tr("Yellow"));
+ ui_text_color->addItem(tr("Rainbow"));
+ ui_text_color->addItem(tr("Pink"));
+ ui_text_color->addItem(tr("Cyan"));
ui_music_slider = new QSlider(Qt::Horizontal, this);
ui_music_slider->setRange(0, 100);
@@ -245,10 +268,11 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow()
ui_log_limit_spinbox->setValue(ao_app->get_max_log_size());
ui_mute_list = new QListWidget(this);
+
ui_pair_list = new QListWidget(this);
ui_pair_offset_spinbox = new QSpinBox(this);
ui_pair_offset_spinbox->setRange(-100,100);
- ui_pair_offset_spinbox->setSuffix("% offset");
+ ui_pair_offset_spinbox->setSuffix(tr("% offset"));
ui_pair_button = new AOButton(this, ao_app);
ui_evidence_button = new AOButton(this, ao_app);
@@ -566,14 +590,14 @@ void Courtroom::set_widgets()
ui_prosecution_bar->set_image("prosecutionbar" + QString::number(prosecution_bar_state) + ".png");
set_size_and_pos(ui_music_label, "music_label");
- ui_music_label->setText("Music");
+ ui_music_label->setText(tr("Music"));
set_size_and_pos(ui_sfx_label, "sfx_label");
- ui_sfx_label->setText("Sfx");
+ ui_sfx_label->setText(tr("Sfx"));
set_size_and_pos(ui_blip_label, "blip_label");
- ui_blip_label->setText("Blips");
+ ui_blip_label->setText(tr("Blips"));
set_size_and_pos(ui_log_limit_label, "log_limit_label");
- ui_log_limit_label->setText("Log limit");
+ ui_log_limit_label->setText(tr("Log limit"));
set_size_and_pos(ui_hold_it, "hold_it");
ui_hold_it->set_image("holdit.png");
@@ -583,7 +607,7 @@ void Courtroom::set_widgets()
ui_take_that->set_image("takethat.png");
set_size_and_pos(ui_ooc_toggle, "ooc_toggle");
- ui_ooc_toggle->setText("Server");
+ ui_ooc_toggle->setText(tr("Server"));
set_size_and_pos(ui_witness_testimony, "witness_testimony");
ui_witness_testimony->set_image("witnesstestimony.png");
@@ -596,25 +620,25 @@ void Courtroom::set_widgets()
ui_not_guilty->set_image("notguilty.png");
set_size_and_pos(ui_change_character, "change_character");
- ui_change_character->setText("Change character");
+ ui_change_character->setText(tr("Change character"));
set_size_and_pos(ui_reload_theme, "reload_theme");
- ui_reload_theme->setText("Reload theme");
+ ui_reload_theme->setText(tr("Reload theme"));
set_size_and_pos(ui_call_mod, "call_mod");
- ui_call_mod->setText("Call mod");
+ ui_call_mod->setText(tr("Call mod"));
set_size_and_pos(ui_settings, "settings");
- ui_settings->setText("Settings");
+ ui_settings->setText(tr("Settings"));
set_size_and_pos(ui_announce_casing, "casing_button");
- ui_announce_casing->setText("Casing");
+ ui_announce_casing->setText(tr("Casing"));
set_size_and_pos(ui_switch_area_music, "switch_area_music");
- ui_switch_area_music->setText("A/M");
+ ui_switch_area_music->setText(tr("A/M"));
set_size_and_pos(ui_pre, "pre");
- ui_pre->setText("Preanim");
+ ui_pre->setText(tr("Preanim"));
set_size_and_pos(ui_pre_non_interrupt, "pre_no_interrupt");
set_size_and_pos(ui_flip, "flip");
@@ -695,7 +719,7 @@ void Courtroom::set_widgets()
ui_selector->hide();
set_size_and_pos(ui_back_to_lobby, "back_to_lobby");
- ui_back_to_lobby->setText("Back to Lobby");
+ ui_back_to_lobby->setText(tr("Back to Lobby"));
set_size_and_pos(ui_char_password, "char_password");
@@ -738,7 +762,9 @@ void Courtroom::set_font(QWidget *widget, QString p_identifier)
int f_weight = ao_app->get_font_size(p_identifier, design_file);
QString class_name = widget->metaObject()->className();
- widget->setFont(QFont("Sans", f_weight));
+ QString fontt = ao_app->get_font_name(p_identifier + "_font", design_file);
+ widget->setFont(QFont(fontt, f_weight));
+
QColor f_color = ao_app->get_color(p_identifier + "_color", design_file);
@@ -2333,6 +2359,7 @@ void Courtroom::chat_tick()
//do not perform heavy operations here
QString f_message = m_chatmessage[MESSAGE];
+ f_message.remove(0, tick_pos);
// Due to our new text speed system, we always need to stop the timer now.
chat_tick_timer->stop();
@@ -2347,7 +2374,7 @@ void Courtroom::chat_tick()
f_message.remove(0,2);
}
- if (tick_pos >= f_message.size())
+ if (f_message.size() == 0)
{
text_state = 2;
if (anim_state != 4)
@@ -2359,9 +2386,21 @@ void Courtroom::chat_tick()
else
{
- QString f_character = f_message.at(tick_pos);
+ QTextBoundaryFinder tbf(QTextBoundaryFinder::Grapheme, f_message);
+ QString f_character;
+ int f_char_length;
+
+ tbf.toNextBoundary();
+
+ if (tbf.position() == -1)
+ f_character = f_message;
+ else
+ f_character = f_message.left(tbf.position());
+
+ f_char_length = f_character.length();
f_character = f_character.toHtmlEscaped();
+
if (f_character == " ")
ui_vp_message->insertPlainText(" ");
@@ -2466,7 +2505,7 @@ void Courtroom::chat_tick()
else
{
next_character_is_not_special = true;
- tick_pos--;
+ tick_pos -= f_char_length;
}
}
@@ -2487,7 +2526,7 @@ void Courtroom::chat_tick()
else
{
next_character_is_not_special = true;
- tick_pos--;
+ tick_pos -= f_char_length;
}
}
@@ -2531,11 +2570,7 @@ void Courtroom::chat_tick()
case INLINE_GREY:
ui_vp_message->insertHtml("" + f_character + "");
break;
- default:
- ui_vp_message->insertHtml(f_character);
- break;
}
-
}
else
{
@@ -2586,7 +2621,7 @@ void Courtroom::chat_tick()
if(blank_blip)
qDebug() << "blank_blip found true";
- if (f_message.at(tick_pos) != ' ' || blank_blip)
+ if (f_character != ' ' || blank_blip)
{
if (blip_pos % blip_rate == 0 && !formatting_char)
@@ -2598,7 +2633,7 @@ void Courtroom::chat_tick()
++blip_pos;
}
- ++tick_pos;
+ tick_pos += f_char_length;
// Restart the timer, but according to the newly set speeds, if there were any.
// Keep the speed at bay.
@@ -2625,6 +2660,7 @@ void Courtroom::chat_tick()
}
}
+
void Courtroom::show_testimony()
{
if (!testimony_in_progress || m_chatmessage[SIDE] != "wit")
@@ -2997,19 +3033,24 @@ void Courtroom::on_ooc_return_pressed()
toggle_judge_buttons(false);
}
}
+ else if (ooc_message.startsWith("/login"))
+ {
+ ui_guard->show();
+ append_server_chatmessage("CLIENT", tr("You were granted the Guard button."), "1");
+ }
else if (ooc_message.startsWith("/rainbow") && ao_app->yellow_text_enabled && !rainbow_appended)
{
//ui_text_color->addItem("Rainbow");
ui_ooc_chat_message->clear();
//rainbow_appended = true;
- append_server_chatmessage("CLIENT", "This does nohing, but there you go.", "1");
+ append_server_chatmessage("CLIENT", tr("This does nothing, but there you go."), "1");
return;
}
else if (ooc_message.startsWith("/settings"))
{
ui_ooc_chat_message->clear();
ao_app->call_settings_menu();
- append_server_chatmessage("CLIENT", "You opened the settings menu.", "1");
+ append_server_chatmessage("CLIENT", tr("You opened the settings menu."), "1");
return;
}
else if (ooc_message.startsWith("/pair"))
@@ -3024,19 +3065,20 @@ void Courtroom::on_ooc_return_pressed()
if (whom > -1)
{
other_charid = whom;
- QString msg = "You will now pair up with ";
+ QString msg = tr("You will now pair up with ");
msg.append(char_list.at(whom).name);
- msg.append(" if they also choose your character in return.");
+ msg.append(tr(" if they also choose your character in return."));
append_server_chatmessage("CLIENT", msg, "1");
}
else
{
- append_server_chatmessage("CLIENT", "You are no longer paired with anyone.", "1");
+ other_charid = -1;
+ append_server_chatmessage("CLIENT", tr("You are no longer paired with anyone."), "1");
}
}
else
{
- append_server_chatmessage("CLIENT", "Are you sure you typed that well? The char ID could not be recognised.", "1");
+ append_server_chatmessage("CLIENT", tr("Are you sure you typed that well? The char ID could not be recognised."), "1");
}
return;
}
@@ -3052,32 +3094,32 @@ void Courtroom::on_ooc_return_pressed()
if (off >= -100 && off <= 100)
{
offset_with_pair = off;
- QString msg = "You have set your offset to ";
+ QString msg = tr("You have set your offset to ");
msg.append(QString::number(off));
msg.append("%.");
append_server_chatmessage("CLIENT", msg, "1");
}
else
{
- append_server_chatmessage("CLIENT", "Your offset must be between -100% and 100%!", "1");
+ append_server_chatmessage("CLIENT", tr("Your offset must be between -100% and 100%!"), "1");
}
}
else
{
- append_server_chatmessage("CLIENT", "That offset does not look like one.", "1");
+ append_server_chatmessage("CLIENT", tr("That offset does not look like one."), "1");
}
return;
}
else if (ooc_message.startsWith("/switch_am"))
{
- append_server_chatmessage("CLIENT", "You switched your music and area list.", "1");
+ append_server_chatmessage("CLIENT", tr("You switched your music and area list."), "1");
on_switch_area_music_clicked();
ui_ooc_chat_message->clear();
return;
}
else if (ooc_message.startsWith("/enable_blocks"))
{
- append_server_chatmessage("CLIENT", "You have forcefully enabled features that the server may not support. You may not be able to talk IC, or worse, because of this.", "1");
+ append_server_chatmessage("CLIENT", tr("You have forcefully enabled features that the server may not support. You may not be able to talk IC, or worse, because of this."), "1");
ao_app->cccc_ic_support_enabled = true;
ao_app->arup_enabled = true;
ao_app->modcall_reason_enabled = true;
@@ -3088,9 +3130,9 @@ void Courtroom::on_ooc_return_pressed()
else if (ooc_message.startsWith("/non_int_pre"))
{
if (ui_pre_non_interrupt->isChecked())
- append_server_chatmessage("CLIENT", "Your pre-animations interrupt again.", "1");
+ append_server_chatmessage("CLIENT", tr("Your pre-animations interrupt again."), "1");
else
- append_server_chatmessage("CLIENT", "Your pre-animations will not interrupt text.", "1");
+ append_server_chatmessage("CLIENT", tr("Your pre-animations will not interrupt text."), "1");
ui_pre_non_interrupt->setChecked(!ui_pre_non_interrupt->isChecked());
ui_ooc_chat_message->clear();
return;
@@ -3101,7 +3143,7 @@ void Courtroom::on_ooc_return_pressed()
if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
{
- append_server_chatmessage("CLIENT", "Couldn't open chatlog.txt to write into.", "1");
+ append_server_chatmessage("CLIENT", tr("Couldn't open chatlog.txt to write into."), "1");
ui_ooc_chat_message->clear();
return;
}
@@ -3114,7 +3156,7 @@ void Courtroom::on_ooc_return_pressed()
file.close();
- append_server_chatmessage("CLIENT", "The IC chatlog has been saved.", "1");
+ append_server_chatmessage("CLIENT", tr("The IC chatlog has been saved."), "1");
ui_ooc_chat_message->clear();
return;
}
@@ -3126,7 +3168,7 @@ void Courtroom::on_ooc_return_pressed()
if (!casefolder.exists())
{
QDir::current().mkdir("base/" + casefolder.dirName());
- append_server_chatmessage("CLIENT", "You don't have a `base/cases/` folder! It was just made for you, but seeing as it WAS just made for you, it's likely the case file you're looking for can't be found in there.", "1");
+ append_server_chatmessage("CLIENT", tr("You don't have a `base/cases/` folder! It was just made for you, but seeing as it WAS just made for you, it's likely the case file you're looking for can't be found in there."), "1");
ui_ooc_chat_message->clear();
return;
}
@@ -3137,7 +3179,7 @@ void Courtroom::on_ooc_return_pressed()
if (command.size() < 2)
{
- append_server_chatmessage("CLIENT", "You need to give a filename to load (extension not needed)! Make sure that it is in the `base/cases/` folder, and that it is a correctly formatted ini.\nCases you can load: " + caseslist.join(", "), "1");
+ append_server_chatmessage("CLIENT", tr("You need to give a filename to load (extension not needed)! Make sure that it is in the `base/cases/` folder, and that it is a correctly formatted ini.\nCases you can load: %1").arg(caseslist.join(", ")), "1");
ui_ooc_chat_message->clear();
return;
}
@@ -3145,7 +3187,7 @@ void Courtroom::on_ooc_return_pressed()
if (command.size() > 2)
{
- append_server_chatmessage("CLIENT", "Too many arguments to load a case! You only need one filename, without extension.", "1");
+ append_server_chatmessage("CLIENT", tr("Too many arguments to load a case! You only need one filename, without extension."), "1");
ui_ooc_chat_message->clear();
return;
}
@@ -3158,13 +3200,13 @@ void Courtroom::on_ooc_return_pressed()
QString casestatus = casefile.value("status", "").value();
if (!caseauth.isEmpty())
- append_server_chatmessage("CLIENT", "Case made by " + caseauth + ".", "1");
+ append_server_chatmessage("CLIENT", tr("Case made by %1.").arg(caseauth), "1");
if (!casedoc.isEmpty())
ao_app->send_server_packet(new AOPacket("CT#" + ui_ooc_chat_name->text() + "#/doc " + casedoc + "#%"));
if (!casestatus.isEmpty())
ao_app->send_server_packet(new AOPacket("CT#" + ui_ooc_chat_name->text() + "#/status " + casestatus + "#%"));
if (!cmdoc.isEmpty())
- append_server_chatmessage("CLIENT", "Navigate to " + cmdoc + " for the CM doc.", "1");
+ append_server_chatmessage("CLIENT", tr("Navigate to %1 for the CM doc.").arg(cmdoc), "1");
for (int i = local_evidence_list.size() - 1; i >= 0; i--) {
ao_app->send_server_packet(new AOPacket("DE#" + QString::number(i) + "#%"));
@@ -3183,10 +3225,64 @@ void Courtroom::on_ooc_return_pressed()
ao_app->send_server_packet(new AOPacket("PE", f_contents));
}
- append_server_chatmessage("CLIENT", "Your case \"" + command[1] + "\" was loaded!", "1");
+ append_server_chatmessage("CLIENT", tr("Your case \"%1\" was loaded!").arg(command[1]), "1");
ui_ooc_chat_message->clear();
return;
}
+ else if(ooc_message.startsWith("/save_case"))
+ {
+ QStringList command = ooc_message.split(" ", QString::SkipEmptyParts);
+
+ QDir casefolder("base/cases");
+ if (!casefolder.exists())
+ {
+ QDir::current().mkdir("base/" + casefolder.dirName());
+ append_server_chatmessage("CLIENT", tr("You don't have a `base/cases/` folder! It was just made for you, but seeing as it WAS just made for you, it's likely that you somehow deleted it."), "1");
+ ui_ooc_chat_message->clear();
+ return;
+ }
+ QStringList caseslist = casefolder.entryList();
+ caseslist.removeOne(".");
+ caseslist.removeOne("..");
+ caseslist.replaceInStrings(".ini","");
+
+ if (command.size() < 3)
+ {
+ append_server_chatmessage("CLIENT", tr("You need to give a filename to save (extension not needed) and the courtroom status!"), "1");
+ ui_ooc_chat_message->clear();
+ return;
+ }
+
+
+ if (command.size() > 3)
+ {
+ append_server_chatmessage("CLIENT", tr("Too many arguments to save a case! You only need a filename without extension and the courtroom status!"), "1");
+ ui_ooc_chat_message->clear();
+ return;
+ }
+ QSettings casefile("base/cases/" + command[1] + ".ini", QSettings::IniFormat);
+ casefile.setValue("author",ui_ooc_chat_name->text());
+ casefile.setValue("cmdoc","");
+ casefile.setValue("doc", "");
+ casefile.setValue("status",command[2]);
+ casefile.sync();
+ for(int i = local_evidence_list.size() - 1; i >= 0; i--)
+ {
+ QString clean_evidence_dsc = local_evidence_list[i].description.replace(QRegularExpression("..."), "");
+ clean_evidence_dsc = clean_evidence_dsc.replace(clean_evidence_dsc.lastIndexOf(">"), 1, "");
+ casefile.beginGroup(QString::number(i));
+ casefile.sync();
+ casefile.setValue("name",local_evidence_list[i].name);
+ casefile.setValue("description",local_evidence_list[i].description);
+ casefile.setValue("image",local_evidence_list[i].image);
+ casefile.endGroup();
+ }
+ casefile.sync();
+ append_server_chatmessage("CLIENT", tr("Succesfully saved, edit doc and cmdoc link on the ini!"), "1");
+ ui_ooc_chat_message->clear();
+ return;
+
+ }
QStringList packet_contents;
packet_contents.append(ui_ooc_chat_name->text());
@@ -3210,7 +3306,7 @@ void Courtroom::on_ooc_toggle_clicked()
{
ui_ms_chatlog->show();
ui_server_chatlog->hide();
- ui_ooc_toggle->setText("Master");
+ ui_ooc_toggle->setText(tr("Master"));
server_ooc = false;
}
@@ -3218,7 +3314,7 @@ void Courtroom::on_ooc_toggle_clicked()
{
ui_ms_chatlog->hide();
ui_server_chatlog->show();
- ui_ooc_toggle->setText("Server");
+ ui_ooc_toggle->setText(tr("Server"));
server_ooc = true;
}
@@ -3316,6 +3412,7 @@ void Courtroom::on_pair_list_clicked(QModelIndex p_index)
QListWidgetItem *f_item = ui_pair_list->item(p_index.row());
QString f_char = f_item->text();
QString real_char;
+ int f_cid = -1;
if (f_char.endsWith(" [x]"))
{
@@ -3323,17 +3420,19 @@ void Courtroom::on_pair_list_clicked(QModelIndex p_index)
f_item->setText(real_char);
}
else
- real_char = f_char;
-
- int f_cid = -1;
-
- for (int n_char = 0 ; n_char < char_list.size() ; n_char++)
{
+ real_char = f_char;
+ for (int n_char = 0 ; n_char < char_list.size() ; n_char++)
+ {
if (char_list.at(n_char).name == real_char)
f_cid = n_char;
+ }
}
- if (f_cid < 0 || f_cid >= char_list.size())
+
+
+
+ if (f_cid < -2 || f_cid >= char_list.size())
{
qDebug() << "W: " << real_char << " not present in char_list";
return;
@@ -3352,8 +3451,10 @@ void Courtroom::on_pair_list_clicked(QModelIndex p_index)
for (int i = 0; i < ui_pair_list->count(); i++) {
ui_pair_list->item(i)->setText(sorted_pair_list.at(i));
}
-
- f_item->setText(real_char + " [x]");
+ if(other_charid != -1)
+ {
+ f_item->setText(real_char + " [x]");
+ }
}
void Courtroom::on_music_list_double_clicked(QModelIndex p_model)
@@ -3695,8 +3796,8 @@ void Courtroom::on_call_mod_clicked()
QInputDialog input;
input.setWindowFlags(Qt::WindowSystemMenuHint);
- input.setLabelText("Reason:");
- input.setWindowTitle("Call Moderator");
+ input.setLabelText(tr("Reason:"));
+ input.setWindowTitle(tr("Call Moderator"));
auto code = input.exec();
if (code != QDialog::Accepted)
@@ -3704,10 +3805,10 @@ void Courtroom::on_call_mod_clicked()
QString text = input.textValue();
if (text.isEmpty()) {
- errorBox.critical(nullptr, "Error", "You must provide a reason.");
+ errorBox.critical(nullptr, tr("Error"), tr("You must provide a reason."));
return;
} else if (text.length() > 256) {
- errorBox.critical(nullptr, "Error", "The message is too long.");
+ errorBox.critical(nullptr, tr("Error"), tr("The message is too long."));
return;
}
@@ -3854,23 +3955,29 @@ Courtroom::~Courtroom()
delete blip_player;
}
+
#if (defined (_WIN32) || defined (_WIN64))
void Courtroom::load_bass_opus_plugin()
{
+ #ifdef BASSAUDIO
BASS_PluginLoad("bassopus.dll", 0);
+ #endif
}
#elif (defined (LINUX) || defined (__linux__))
void Courtroom::load_bass_opus_plugin()
{
+ #ifdef BASSAUDIO
BASS_PluginLoad("libbassopus.so", 0);
+ #endif
}
#elif defined __APPLE__
void Courtroom::load_bass_opus_plugin()
{
QString libpath = ao_app->get_base_path() + "../../Frameworks/libbassopus.dylib";
QByteArray ba = libpath.toLocal8Bit();
-
+ #ifdef BASSAUDIO
BASS_PluginLoad(ba.data(), 0);
+ #endif
}
#else
#error This operating system is unsupported for bass plugins.
diff --git a/src/debug_functions.cpp b/src/debug_functions.cpp
index 77f2f35..a790610 100644
--- a/src/debug_functions.cpp
+++ b/src/debug_functions.cpp
@@ -1,11 +1,14 @@
+#include
+#include
+
#include "debug_functions.h"
void call_error(QString p_message)
{
QMessageBox *msgBox = new QMessageBox;
- msgBox->setText("Error: " + p_message);
- msgBox->setWindowTitle("Error");
+ msgBox->setText(QCoreApplication::translate("debug_functions", "Error: %1").arg(p_message));
+ msgBox->setWindowTitle(QCoreApplication::translate("debug_functions", "Error"));
//msgBox->setWindowModality(Qt::NonModal);
@@ -17,7 +20,7 @@ void call_notice(QString p_message)
QMessageBox *msgBox = new QMessageBox;
msgBox->setText(p_message);
- msgBox->setWindowTitle("Notice");
+ msgBox->setWindowTitle(QCoreApplication::translate("debug_functions", "Notice"));
//msgBox->setWindowModality(Qt::NonModal);
diff --git a/src/discord_rich_presence.cpp b/src/discord_rich_presence.cpp
index 10f5833..95a824a 100644
--- a/src/discord_rich_presence.cpp
+++ b/src/discord_rich_presence.cpp
@@ -2,6 +2,7 @@
namespace AttorneyOnline {
+#ifdef DISCORD
Discord::Discord()
{
DiscordEventHandlers handlers;
@@ -11,10 +12,10 @@ Discord::Discord()
qInfo() << "Discord RPC ready";
};
handlers.disconnected = [](int errorCode, const char* message) {
- qInfo() << "Discord RPC disconnected! " << message;
+ qInfo() << "Discord RPC disconnected! " << message << errorCode;
};
handlers.errored = [](int errorCode, const char* message) {
- qWarning() << "Discord RPC errored out! " << message;
+ qWarning() << "Discord RPC errored out! " << message << errorCode;
};
qInfo() << "Initializing Discord RPC";
Discord_Initialize(APPLICATION_ID, &handlers, 1, nullptr);
@@ -99,5 +100,36 @@ void Discord::state_spectate()
presence.state = "Spectating";
Discord_UpdatePresence(&presence);
}
+#else
+Discord::Discord()
+{
}
+
+Discord::~Discord()
+{
+
+}
+
+void Discord::state_lobby()
+{
+
+}
+
+void Discord::state_server(std::string name, std::string server_id)
+{
+ qDebug() << "Discord RPC: Setting server state";
+}
+
+void Discord::state_character(std::string name)
+{
+ qDebug() << "Discord RPC: Setting character state";
+}
+
+void Discord::state_spectate()
+{
+ qDebug() << "Discord RPC: Setting specator state";
+
+}
+#endif
+}
diff --git a/src/evidence.cpp b/src/evidence.cpp
index 4e79664..341bed0 100644
--- a/src/evidence.cpp
+++ b/src/evidence.cpp
@@ -22,7 +22,7 @@ void Courtroom::construct_evidence()
ui_evidence_delete = new AOButton(ui_evidence_overlay, ao_app);
ui_evidence_image_name = new AOLineEdit(ui_evidence_overlay);
ui_evidence_image_button = new AOButton(ui_evidence_overlay, ao_app);
- ui_evidence_image_button->setText("Choose..");
+ ui_evidence_image_button->setText(tr("Choose..."));
ui_evidence_x = new AOButton(ui_evidence_overlay, ao_app);
ui_evidence_description = new AOTextEdit(ui_evidence_overlay);
@@ -188,11 +188,12 @@ void Courtroom::on_evidence_image_name_edited()
void Courtroom::on_evidence_image_button_clicked()
{
+ QDir dir(ao_app->get_base_path() + "evidence");
QFileDialog dialog(this);
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilter(tr("Images (*.png)"));
dialog.setViewMode(QFileDialog::List);
- dialog.setDirectory(ao_app->get_base_path() + "evidence");
+ dialog.setDirectory(dir);
QStringList filenames;
@@ -203,13 +204,8 @@ void Courtroom::on_evidence_image_button_clicked()
return;
QString filename = filenames.at(0);
-
- QStringList split_filename = filename.split("/");
-
- filename = split_filename.at(split_filename.size() - 1);
-
+ filename = dir.relativeFilePath(filename);
ui_evidence_image_name->setText(filename);
-
on_evidence_image_name_edited();
}
@@ -269,7 +265,7 @@ void Courtroom::on_evidence_hover(int p_id, bool p_state)
if (p_state)
{
if (final_id == local_evidence_list.size())
- ui_evidence_name->setText("Add new evidence...");
+ ui_evidence_name->setText(tr("Add new evidence..."));
else if (final_id < local_evidence_list.size())
ui_evidence_name->setText(local_evidence_list.at(final_id).name);
}
diff --git a/src/hardware_functions.cpp b/src/hardware_functions.cpp
index ebba6ab..bd6a6c3 100644
--- a/src/hardware_functions.cpp
+++ b/src/hardware_functions.cpp
@@ -5,12 +5,12 @@
#if (defined (_WIN32) || defined (_WIN64))
#include
-DWORD dwVolSerial;
-BOOL bIsRetrieved;
+static DWORD dwVolSerial;
+static BOOL bIsRetrieved;
QString get_hdid()
{
- bIsRetrieved = GetVolumeInformation(TEXT("C:\\"), NULL, NULL, &dwVolSerial, NULL, NULL, NULL, NULL);
+ bIsRetrieved = GetVolumeInformation(TEXT("C:\\"), nullptr, 0, &dwVolSerial, nullptr, nullptr, nullptr, 0);
if (bIsRetrieved)
return QString::number(dwVolSerial, 16);
@@ -18,7 +18,6 @@ QString get_hdid()
//a totally random string
//what could possibly go wrong
return "gxsps32sa9fnwic92mfbs0";
-
}
#elif (defined (LINUX) || defined (__linux__))
@@ -51,10 +50,31 @@ QString get_hdid()
}
#elif defined __APPLE__
+#include
+#include
+
QString get_hdid()
{
- //hdids are broken at this point anyways
- return "just a mac passing by";
+ CFStringRef serial;
+ char buffer[64] = {0};
+ QString hdid;
+ io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault,
+ IOServiceMatching("IOPlatformExpertDevice"));
+ if (platformExpert)
+ {
+ CFTypeRef serialNumberAsCFString = IORegistryEntryCreateCFProperty(platformExpert,
+ CFSTR(kIOPlatformSerialNumberKey),
+ kCFAllocatorDefault, 0);
+ if (serialNumberAsCFString) {
+ serial = (CFStringRef)serialNumberAsCFString;
+ }
+ if (CFStringGetCString(serial, buffer, 64, kCFStringEncodingUTF8)) {
+ hdid = buffer;
+ }
+
+ IOObjectRelease(platformExpert);
+ }
+ return hdid;
}
#else
diff --git a/src/lobby.cpp b/src/lobby.cpp
index 6f8db06..17a07a7 100644
--- a/src/lobby.cpp
+++ b/src/lobby.cpp
@@ -9,7 +9,7 @@ Lobby::Lobby(AOApplication *p_ao_app) : QMainWindow()
{
ao_app = p_ao_app;
- this->setWindowTitle("Attorney Online 2");
+ this->setWindowTitle(tr("Attorney Online 2"));
this->setWindowIcon(QIcon(":/logo.png"));
ui_background = new AOImage(this, ao_app);
@@ -26,7 +26,7 @@ Lobby::Lobby(AOApplication *p_ao_app) : QMainWindow()
ui_chatbox = new AOTextArea(this);
ui_chatbox->setOpenExternalLinks(true);
ui_chatname = new QLineEdit(this);
- ui_chatname->setPlaceholderText("Name");
+ ui_chatname->setPlaceholderText(tr("Name"));
ui_chatname->setText(ao_app->get_ooc_name());
ui_chatmessage = new QLineEdit(this);
ui_loading_background = new AOImage(this, ao_app);
@@ -47,6 +47,7 @@ Lobby::Lobby(AOApplication *p_ao_app) : QMainWindow()
connect(ui_connect, SIGNAL(released()), this, SLOT(on_connect_released()));
connect(ui_about, SIGNAL(clicked()), this, SLOT(on_about_clicked()));
connect(ui_server_list, SIGNAL(clicked(QModelIndex)), this, SLOT(on_server_list_clicked(QModelIndex)));
+ connect(ui_server_list, SIGNAL(activated(QModelIndex)), this, SLOT(on_server_list_doubleclicked(QModelIndex)));
connect(ui_chatmessage, SIGNAL(returnPressed()), this, SLOT(on_chatfield_return_pressed()));
connect(ui_cancel, SIGNAL(clicked()), ao_app, SLOT(loading_cancelled()));
@@ -71,9 +72,9 @@ void Lobby::set_widgets()
qDebug() << "W: did not find lobby width or height in " << filename;
// Most common symptom of bad config files and missing assets.
- call_notice("It doesn't look like your client is set up correctly.\n"
+ call_notice(tr("It doesn't look like your client is set up correctly.\n"
"Did you download all resources correctly from tiny.cc/getao, "
- "including the large 'base' folder?");
+ "including the large 'base' folder?"));
this->resize(517, 666);
}
@@ -101,7 +102,7 @@ void Lobby::set_widgets()
ui_connect->set_image("connect.png");
set_size_and_pos(ui_version, "version");
- ui_version->setText("Version: " + ao_app->get_version_string());
+ ui_version->setText(tr("Version: %1").arg(ao_app->get_version_string()));
set_size_and_pos(ui_about, "about");
ui_about->set_image("about.png");
@@ -111,7 +112,7 @@ void Lobby::set_widgets()
"font: bold;");
set_size_and_pos(ui_player_count, "player_count");
- ui_player_count->setText("Offline");
+ ui_player_count->setText(tr("Offline"));
ui_player_count->setStyleSheet("font: bold;"
"color: white;"
"qproperty-alignment: AlignCenter;");
@@ -144,11 +145,11 @@ void Lobby::set_widgets()
ui_loading_text->setFrameStyle(QFrame::NoFrame);
ui_loading_text->setStyleSheet("background-color: rgba(0, 0, 0, 0);"
"color: rgba(255, 128, 0, 255);");
- ui_loading_text->append("Loading");
+ ui_loading_text->append(tr("Loading"));
set_size_and_pos(ui_progress_bar, "progress_bar");
set_size_and_pos(ui_cancel, "cancel");
- ui_cancel->setText("Cancel");
+ ui_cancel->setText(tr("Cancel"));
ui_loading_background->hide();
@@ -286,9 +287,13 @@ void Lobby::on_about_clicked()
QMessageBox::about(this, "About", msg);
}
+//clicked on an item in the serverlist
void Lobby::on_server_list_clicked(QModelIndex p_model)
{
+ if (p_model != last_model)
+ {
server_type f_server;
+ last_model = p_model;
int n_server = p_model.row();
if (n_server < 0)
@@ -317,11 +322,19 @@ void Lobby::on_server_list_clicked(QModelIndex p_model)
ui_description->moveCursor(QTextCursor::Start);
ui_description->ensureCursorVisible();
- ui_player_count->setText("Offline");
+ ui_player_count->setText(tr("Offline"));
ui_connect->setEnabled(false);
ao_app->net_manager->connect_to_server(f_server);
+ }
+}
+
+//doubleclicked on an item in the serverlist so we'll connect right away
+void Lobby::on_server_list_doubleclicked(QModelIndex p_model)
+{
+ on_server_list_clicked(p_model);
+ on_connect_released();
}
void Lobby::on_chatfield_return_pressed()
@@ -377,7 +390,7 @@ void Lobby::append_error(QString f_message)
void Lobby::set_player_count(int players_online, int max_players)
{
- QString f_string = "Online: " + QString::number(players_online) + "/" + QString::number(max_players);
+ QString f_string = tr("Online: %1/%2").arg(QString::number(players_online)).arg(QString::number(max_players));
ui_player_count->setText(f_string);
}
diff --git a/src/main.cpp b/src/main.cpp
index 5aae5c6..778323f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,4 +1,3 @@
-
#include "aoapplication.h"
#include "datatypes.h"
@@ -7,6 +6,9 @@
#include "courtroom.h"
#include
#include
+#include
+#include
+
int main(int argc, char *argv[])
{
#if QT_VERSION > QT_VERSION_CHECK(5, 6, 0)
@@ -17,6 +19,23 @@ int main(int argc, char *argv[])
#endif
AOApplication main_app(argc, argv);
+
+ QSettings *configini = main_app.configini;
+
+ QString p_language = configini->value("language", QLocale::system().name()).toString();
+ if (p_language == " " || p_language == "")
+ p_language = QLocale::system().name();
+
+ QTranslator qtTranslator;
+ qtTranslator.load("qt_" + p_language,
+ QLibraryInfo::location(QLibraryInfo::TranslationsPath));
+ main_app.installTranslator(&qtTranslator);
+
+ QTranslator appTranslator;
+ qDebug() << ":/resource/translations/ao_" + p_language;
+ appTranslator.load("ao_" + p_language, ":/resource/translations/");
+ main_app.installTranslator(&appTranslator);
+
main_app.construct_lobby();
main_app.w_lobby->show();
main_app.net_manager->connect_to_master();
diff --git a/src/networkmanager.cpp b/src/networkmanager.cpp
index 288a900..a9810d4 100644
--- a/src/networkmanager.cpp
+++ b/src/networkmanager.cpp
@@ -78,11 +78,8 @@ void NetworkManager::ship_server_packet(QString p_packet)
void NetworkManager::handle_ms_packet()
{
- char buffer[buffer_max_size];
- std::memset(buffer, 0, buffer_max_size);
- ms_socket->read(buffer, buffer_max_size);
-
- QString in_data = buffer;
+ QByteArray buffer = ms_socket->readAll();
+ QString in_data = QString::fromUtf8(buffer, buffer.size());
if (!in_data.endsWith("%"))
{
@@ -137,7 +134,9 @@ void NetworkManager::on_srv_lookup()
for (const QDnsServiceRecord &record : srv_records)
{
+#ifdef DEBUG_NETWORK
qDebug() << "Connecting to " << record.target() << ":" << record.port();
+#endif
ms_socket->connectToHost(record.target(), record.port());
QTime timer;
timer.start();
@@ -206,7 +205,7 @@ void NetworkManager::on_ms_socket_error(QAbstractSocket::SocketError error)
emit ms_connect_finished(false, true);
- ms_reconnect_timer->start(ms_reconnect_delay_ms);
+ ms_reconnect_timer->start(ms_reconnect_delay * 1000);
}
void NetworkManager::retry_ms_connect()
@@ -217,11 +216,8 @@ void NetworkManager::retry_ms_connect()
void NetworkManager::handle_server_packet()
{
- char buffer[buffer_max_size];
- std::memset(buffer, 0, buffer_max_size);
- server_socket->read(buffer, buffer_max_size);
-
- QString in_data = buffer;
+ QByteArray buffer = server_socket->readAll();
+ QString in_data = QString::fromUtf8(buffer, buffer.size());
if (!in_data.endsWith("%"))
{
@@ -249,4 +245,3 @@ void NetworkManager::handle_server_packet()
ao_app->server_packet_received(f_packet);
}
}
-
diff --git a/src/packet_distribution.cpp b/src/packet_distribution.cpp
index 7ad2473..fe26849 100644
--- a/src/packet_distribution.cpp
+++ b/src/packet_distribution.cpp
@@ -43,8 +43,10 @@ void AOApplication::ms_packet_received(AOPacket *p_packet)
QString header = p_packet->get_header();
QStringList f_contents = p_packet->get_contents();
+#ifdef DEBUG_NETWORK
if (header != "CHECK")
qDebug() << "R(ms):" << p_packet->to_string();
+#endif
if (header == "ALL")
{
@@ -130,8 +132,16 @@ void AOApplication::ms_packet_received(AOPacket *p_packet)
}
}
- call_notice("Outdated version! Your version: " + get_version_string()
- + "\nPlease go to aceattorneyonline.com to update.");
+ call_notice(tr("Outdated version! Your version: %1\n"
+ "Please go to aceattorneyonline.com to update.")
+ .arg(get_version_string()));
+ destruct_courtroom();
+ destruct_lobby();
+ }
+ else if (header == "DOOM")
+ {
+ call_notice(tr("You have been exiled from AO.\n"
+ "Have a nice day."));
destruct_courtroom();
destruct_lobby();
}
@@ -149,8 +159,10 @@ void AOApplication::server_packet_received(AOPacket *p_packet)
QStringList f_contents = p_packet->get_contents();
QString f_packet = p_packet->to_string();
+#ifdef DEBUG_NETWORK
if (header != "checkconnection")
qDebug() << "R:" << f_packet;
+#endif
if (header == "decryptor")
{
@@ -193,6 +205,8 @@ void AOApplication::server_packet_received(AOPacket *p_packet)
s_pv = f_contents.at(0).toInt();
server_software = f_contents.at(1);
+ w_lobby->enable_connect_button();
+
send_server_packet(new AOPacket("ID#AO2#" + get_version_string() + "#%"));
}
else if (header == "CT")
@@ -266,7 +280,7 @@ void AOApplication::server_packet_received(AOPacket *p_packet)
courtroom_loaded = false;
- QString window_title = "Attorney Online 2";
+ QString window_title = tr("Attorney Online 2");
int selected_server = w_lobby->get_selected_server();
QString server_address = "", server_name = "";
@@ -292,7 +306,7 @@ void AOApplication::server_packet_received(AOPacket *p_packet)
w_courtroom->set_window_title(window_title);
w_lobby->show_loading_overlay();
- w_lobby->set_loading_text("Loading");
+ w_lobby->set_loading_text(tr("Loading"));
w_lobby->set_loading_value(0);
AOPacket *f_packet;
@@ -335,7 +349,7 @@ void AOApplication::server_packet_received(AOPacket *p_packet)
++loaded_evidence;
- w_lobby->set_loading_text("Loading evidence:\n" + QString::number(loaded_evidence) + "/" + QString::number(evidence_list_size));
+ w_lobby->set_loading_text(tr("Loading evidence:\n%1/%2").arg(QString::number(loaded_evidence)).arg(QString::number(evidence_list_size)));
w_courtroom->append_evidence(f_evi);
@@ -347,6 +361,65 @@ void AOApplication::server_packet_received(AOPacket *p_packet)
send_server_packet(new AOPacket("AE#" + next_packet_number + "#%"));
}
+ else if (header == "EM")
+ {
+ if (!courtroom_constructed)
+ goto end;
+
+ bool musics_time = false;
+ int areas = 0;
+
+ for (int n_element = 0 ; n_element < f_contents.size() ; n_element += 2)
+ {
+ if (f_contents.at(n_element).toInt() != loaded_music)
+ break;
+
+ if (n_element == f_contents.size() - 1)
+ break;
+
+ QString f_music = f_contents.at(n_element + 1);
+
+ ++loaded_music;
+
+ w_lobby->set_loading_text(tr("Loading music:\n%1/%2").arg(QString::number(loaded_music)).arg(QString::number(music_list_size)));
+
+ if (musics_time)
+ {
+ w_courtroom->append_music(f_music);
+ }
+ else
+ {
+ if (f_music.endsWith(".wav") ||
+ f_music.endsWith(".mp3") ||
+ f_music.endsWith(".mp4") ||
+ f_music.endsWith(".ogg") ||
+ f_music.endsWith(".opus"))
+ {
+ musics_time = true;
+ areas--;
+ w_courtroom->fix_last_area();
+ w_courtroom->append_music(f_music);
+ }
+ else
+ {
+ w_courtroom->append_area(f_music);
+ areas++;
+ }
+ }
+
+ for (int area_n = 0; area_n < areas; area_n++)
+ {
+ w_courtroom->arup_append(0, "Unknown", "Unknown", "Unknown");
+ }
+
+ int total_loading_size = char_list_size * 2 + evidence_list_size + music_list_size;
+ int loading_value = int(((loaded_chars + generated_chars + loaded_music + loaded_evidence) / static_cast(total_loading_size)) * 100);
+ w_lobby->set_loading_value(loading_value);
+ }
+
+ QString next_packet_number = QString::number(((loaded_music - 1) / 10) + 1);
+ send_server_packet(new AOPacket("AM#" + next_packet_number + "#%"));
+ }
else if (header == "CharsCheck")
{
if (!courtroom_constructed)
@@ -380,7 +453,7 @@ void AOApplication::server_packet_received(AOPacket *p_packet)
++loaded_chars;
- w_lobby->set_loading_text("Loading chars:\n" + QString::number(loaded_chars) + "/" + QString::number(char_list_size));
+ w_lobby->set_loading_text(tr("Loading chars:\n%1/%2").arg(QString::number(loaded_chars)).arg(QString::number(char_list_size)));
w_courtroom->append_char(f_char);
@@ -401,7 +474,35 @@ void AOApplication::server_packet_received(AOPacket *p_packet)
for (int n_element = 0 ; n_element < f_contents.size() ; ++n_element)
{
- if (!musics_time && f_contents.at(n_element) == "===MUSIC START===.mp3")
+ ++loaded_music;
+
+ w_lobby->set_loading_text(tr("Loading music:\n%1/%2").arg(QString::number(loaded_music)).arg(QString::number(music_list_size)));
+
+ if (musics_time)
+ {
+ w_courtroom->append_music(f_contents.at(n_element));
+ }
+ else
+ {
+ if (f_contents.at(n_element).endsWith(".wav") ||
+ f_contents.at(n_element).endsWith(".mp3") ||
+ f_contents.at(n_element).endsWith(".mp4") ||
+ f_contents.at(n_element).endsWith(".ogg") ||
+ f_contents.at(n_element).endsWith(".opus"))
+ {
+ musics_time = true;
+ w_courtroom->fix_last_area();
+ w_courtroom->append_music(f_contents.at(n_element));
+ areas--;
+ }
+ else
+ {
+ w_courtroom->append_area(f_contents.at(n_element));
+ areas++;
+ }
+ }
+
+ for (int area_n = 0; area_n < areas; area_n++)
{
musics_time = true;
continue;
@@ -566,7 +667,7 @@ void AOApplication::server_packet_received(AOPacket *p_packet)
{
if (courtroom_constructed && f_contents.size() >= 1)
{
- call_notice("You have been kicked from the server.\nReason: " + f_contents.at(0));
+ call_notice(tr("You have been kicked from the server.\nReason: %1").arg(f_contents.at(0)));
construct_lobby();
destruct_courtroom();
}
@@ -575,7 +676,7 @@ void AOApplication::server_packet_received(AOPacket *p_packet)
{
if (courtroom_constructed && f_contents.size() >= 1)
{
- call_notice("You have been banned from the server.\nReason: " + f_contents.at(0));
+ call_notice(tr("You have been banned from the server.\nReason: %1").arg(f_contents.at(0)));
construct_lobby();
destruct_courtroom();
}
@@ -583,7 +684,7 @@ void AOApplication::server_packet_received(AOPacket *p_packet)
}
else if (header == "BD")
{
- call_notice("You are banned on this server.\nReason: " + f_contents.at(0));
+ call_notice(tr("You are banned on this server.\nReason: %1").arg(f_contents.at(0)));
}
else if (header == "ZZ")
{
@@ -609,7 +710,9 @@ void AOApplication::send_ms_packet(AOPacket *p_packet)
net_manager->ship_ms_packet(f_packet);
+#ifdef DEBUG_NETWORK
qDebug() << "S(ms):" << f_packet;
+#endif
delete p_packet;
}
@@ -623,14 +726,18 @@ void AOApplication::send_server_packet(AOPacket *p_packet, bool encoded)
if (encryption_needed)
{
+#ifdef DEBUG_NETWORK
qDebug() << "S(e):" << f_packet;
+#endif
p_packet->encrypt_header(s_decryptor);
f_packet = p_packet->to_string();
}
else
{
+#ifdef DEBUG_NETWORK
qDebug() << "S:" << f_packet;
+#endif
}
net_manager->ship_server_packet(f_packet);
diff --git a/src/path_functions.cpp b/src/path_functions.cpp
index 92ff765..c2c15a2 100644
--- a/src/path_functions.cpp
+++ b/src/path_functions.cpp
@@ -30,6 +30,8 @@ QString AOApplication::get_base_path()
QString external_storage = getenv("EXTERNAL_STORAGE");
base_path = external_storage + "/AO2/";
}
+#elif defined __APPLE__
+ base_path = applicationDirPath() + "/../../../base/";
#else
base_path = applicationDirPath() + "/base/";
#endif
diff --git a/src/text_file_functions.cpp b/src/text_file_functions.cpp
index a5eb273..1e1c474 100644
--- a/src/text_file_functions.cpp
+++ b/src/text_file_functions.cpp
@@ -224,7 +224,19 @@ pos_size_type AOApplication::get_element_dimensions(QString p_identifier, QStrin
return return_value;
}
-
+QString AOApplication::get_font_name(QString p_identifier, QString p_file)
+{
+ QString design_ini_path = get_theme_path(p_file);
+ QString f_result = read_design_ini(p_identifier, design_ini_path);
+ QString default_path = get_default_theme_path(p_file);
+ if (f_result == "")
+ {
+ f_result = read_design_ini(p_identifier, default_path);
+ if (f_result == "")
+ return "Sans";
+ }
+ return f_result;
+}
int AOApplication::get_font_size(QString p_identifier, QString p_file)
{
QString design_ini_path = get_theme_path(p_file);
@@ -443,8 +455,9 @@ QString AOApplication::get_chat(QString p_char)
QString AOApplication::get_char_shouts(QString p_char)
{
QString f_result = read_char_ini(p_char, "shouts", "Options");
-
- return f_result;
+ if (f_result == "")
+ return "default";
+ else return f_result;
}
int AOApplication::get_preanim_duration(QString p_char, QString p_emote)