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.ttf resource/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)